mirror of
https://github.com/nzbget/nzbget.git
synced 2025-12-24 06:37:44 -05:00
Compare commits
225 Commits
v20.0-r210
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e26d52d70 | ||
|
|
ae81c9403d | ||
|
|
b0d35f9a09 | ||
|
|
ce7cd631c2 | ||
|
|
0432cf13d3 | ||
|
|
799de88b3e | ||
|
|
7ff3251dcf | ||
|
|
97ae03bbd3 | ||
|
|
6bbfb6b7b7 | ||
|
|
f02bbbefd7 | ||
|
|
4d19c899bd | ||
|
|
1d008bd1f5 | ||
|
|
8c1e62ef49 | ||
|
|
e18c25c231 | ||
|
|
6dbe6edbab | ||
|
|
1ee8e02586 | ||
|
|
f8f9dd2b6d | ||
|
|
414ffcbc35 | ||
|
|
0522b5f49d | ||
|
|
575b823758 | ||
|
|
a124a91a84 | ||
|
|
4546b0f368 | ||
|
|
625e7a61e1 | ||
|
|
f1c1373c7d | ||
|
|
5dda6b2e49 | ||
|
|
81aa56324f | ||
|
|
a8533e7f0a | ||
|
|
bbfcf07689 | ||
|
|
fd35e05b61 | ||
|
|
3e0be12cb3 | ||
|
|
d6e8f67927 | ||
|
|
a159a1ff5a | ||
|
|
15f4955f38 | ||
|
|
aac98b53ee | ||
|
|
fa4a5bb261 | ||
|
|
d19c9b80e7 | ||
|
|
c7716ae9b7 | ||
|
|
e07a6b9443 | ||
|
|
fa57474d78 | ||
|
|
4299ac1354 | ||
|
|
82dfec471b | ||
|
|
3a5bc85962 | ||
|
|
25dc60e71f | ||
|
|
855f3e8649 | ||
|
|
bdc7ba38db | ||
|
|
89427f42ce | ||
|
|
a665dc5375 | ||
|
|
adf3e05e1d | ||
|
|
e3bd94189a | ||
|
|
bb1cb68653 | ||
|
|
e91f37d566 | ||
|
|
57f4d2864b | ||
|
|
05c841880f | ||
|
|
92828acab0 | ||
|
|
137c936830 | ||
|
|
4826f04778 | ||
|
|
15b4f55310 | ||
|
|
0461f2ad55 | ||
|
|
67ca371c6b | ||
|
|
a97a6d7c7f | ||
|
|
b6927e992e | ||
|
|
59cae49344 | ||
|
|
6bf097f1c3 | ||
|
|
ad0592843c | ||
|
|
8a09de775f | ||
|
|
adf7ec225b | ||
|
|
d15722c72d | ||
|
|
0776c6b057 | ||
|
|
8f63eef312 | ||
|
|
8a59079627 | ||
|
|
491d816bff | ||
|
|
6dfe17c1d8 | ||
|
|
f3cf9317a6 | ||
|
|
b0356d88d6 | ||
|
|
c0d7a15afa | ||
|
|
fbfa793b20 | ||
|
|
a329c65eb3 | ||
|
|
b9c4c5b19e | ||
|
|
a5f2c1c7c5 | ||
|
|
e2ea481799 | ||
|
|
c2b93c588b | ||
|
|
3934244a70 | ||
|
|
62ba9a5609 | ||
|
|
e7d4556f8b | ||
|
|
43c9bb78f3 | ||
|
|
e824c5b940 | ||
|
|
32a6bf18ad | ||
|
|
2cb419691d | ||
|
|
a74722d8cc | ||
|
|
0602e9d2f1 | ||
|
|
9713cbad5e | ||
|
|
009cf9eee2 | ||
|
|
85995ad56f | ||
|
|
1f3067c1e3 | ||
|
|
794f240f48 | ||
|
|
49e8fea0e2 | ||
|
|
fa8f8855f9 | ||
|
|
2c85def959 | ||
|
|
fb3a27fde9 | ||
|
|
b29131ffb8 | ||
|
|
31a34b58ea | ||
|
|
4a10fdb2df | ||
|
|
541a695e2f | ||
|
|
07b7a766a2 | ||
|
|
1057e9194c | ||
|
|
9eaf9fae9a | ||
|
|
34d157990d | ||
|
|
c93eb2087f | ||
|
|
1f89c037b9 | ||
|
|
20036b73b8 | ||
|
|
da3425af3f | ||
|
|
4c482a91da | ||
|
|
458a1afb13 | ||
|
|
3339a2c520 | ||
|
|
17c5a9cbc8 | ||
|
|
4db9ef2535 | ||
|
|
5a0eae7bf4 | ||
|
|
86ac23b6aa | ||
|
|
fa1aa45fa7 | ||
|
|
f842a19544 | ||
|
|
f3cb44e7b2 | ||
|
|
e54ffbaaaa | ||
|
|
2d049f1904 | ||
|
|
5106979d5d | ||
|
|
75d05bce4a | ||
|
|
ea4ea2c901 | ||
|
|
5e15677218 | ||
|
|
93ad31b9d8 | ||
|
|
14c5a1caf7 | ||
|
|
f52f5b5de9 | ||
|
|
0916c2a908 | ||
|
|
ab1238dde4 | ||
|
|
758ce4047b | ||
|
|
c24bf0e8ce | ||
|
|
1264878a97 | ||
|
|
a85ff314f3 | ||
|
|
a349ab08f7 | ||
|
|
064de49edf | ||
|
|
ae79c56c07 | ||
|
|
d6353e9cee | ||
|
|
d9d824631e | ||
|
|
2bd765b06f | ||
|
|
f51c216417 | ||
|
|
78b270d23e | ||
|
|
a4252a1e79 | ||
|
|
1ac2be47d5 | ||
|
|
9437a227ee | ||
|
|
0d19722881 | ||
|
|
adfe5eef26 | ||
|
|
321cddeeba | ||
|
|
44f08325f9 | ||
|
|
e601e77e5e | ||
|
|
8e6ccfa8a7 | ||
|
|
3eebee20aa | ||
|
|
b83a9b9aff | ||
|
|
05d7a8ede2 | ||
|
|
4d771036e2 | ||
|
|
7e659d8d97 | ||
|
|
137ac1a3ee | ||
|
|
3a4e6623db | ||
|
|
c2669b359e | ||
|
|
8bffb51974 | ||
|
|
81be21b540 | ||
|
|
222d6a1f6d | ||
|
|
cd6bf682f9 | ||
|
|
d93769021a | ||
|
|
cf0d086b57 | ||
|
|
bf53c6eaa6 | ||
|
|
9b50760006 | ||
|
|
b7102894d7 | ||
|
|
db102f5a15 | ||
|
|
d9cb0026bd | ||
|
|
5893d03f1b | ||
|
|
18d138648b | ||
|
|
93a43e711f | ||
|
|
2b52dc5bfe | ||
|
|
ce844367e7 | ||
|
|
64a5a78866 | ||
|
|
6f9fb29595 | ||
|
|
0ee60ab844 | ||
|
|
8dfca2a542 | ||
|
|
76bdd63e60 | ||
|
|
ef78cbfc74 | ||
|
|
74768b2183 | ||
|
|
801bf1ae7c | ||
|
|
a901deff03 | ||
|
|
67245d6ca8 | ||
|
|
2d70e1de21 | ||
|
|
7deb3c1b68 | ||
|
|
d4886ac7d1 | ||
|
|
5b3372107d | ||
|
|
07c54740a7 | ||
|
|
af111adbde | ||
|
|
d31a734a5c | ||
|
|
54f14f5efa | ||
|
|
18fbd12f2c | ||
|
|
ff671e722d | ||
|
|
15c292653e | ||
|
|
c0aed9af48 | ||
|
|
597e4fd034 | ||
|
|
3c2575bc26 | ||
|
|
50c1ca588c | ||
|
|
da9c8b1138 | ||
|
|
c59ab2d9dc | ||
|
|
35fca1479c | ||
|
|
54c5a061c8 | ||
|
|
3a0489a4a9 | ||
|
|
a31fb733a2 | ||
|
|
2691eff535 | ||
|
|
37b04c593a | ||
|
|
b9b1c76ada | ||
|
|
69a0db63f6 | ||
|
|
e9926d92e0 | ||
|
|
f5aa27979c | ||
|
|
24a4542c14 | ||
|
|
bb95e1f274 | ||
|
|
186da63056 | ||
|
|
1facedb694 | ||
|
|
54eb8e1291 | ||
|
|
80b67383e3 | ||
|
|
406a78218a | ||
|
|
262df77f74 | ||
|
|
71505340d0 | ||
|
|
bddb0bb26d | ||
|
|
d90a40909b |
17
.lgtm.yml
Normal file
17
.lgtm.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
# Configuration file for integration with http://lgtm.com
|
||||
|
||||
path_classifiers:
|
||||
library:
|
||||
# exclude these directories from default alerts report:
|
||||
- lib
|
||||
- webui/lib
|
||||
|
||||
extraction:
|
||||
cpp:
|
||||
configure:
|
||||
command:
|
||||
# compile with tests to activate scanning of C++ sources for tests
|
||||
- ./configure --enable-tests
|
||||
|
||||
queries:
|
||||
- exclude: js/incomplete-sanitization # this one gives false positives only and nothing useful
|
||||
41
COPYING
41
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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
|
||||
|
||||
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.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
314
ChangeLog
314
ChangeLog
@@ -1,7 +1,213 @@
|
||||
nzbget-20.0:
|
||||
nzbget-21.2-testing:
|
||||
- please see repository change log at
|
||||
https://github.com/nzbget/nzbget/commits/develop
|
||||
|
||||
nzbget-21.1:
|
||||
- fixed crash on systems with 64-bit time;
|
||||
- corrected icon in Windows "uninstall program" list;
|
||||
- allow special characters in URL for username and password;
|
||||
- improved reporting for binding errors on Windows;
|
||||
- fixed unicode space characters in javascript files, which could cause issues
|
||||
with nginx proxy;
|
||||
- fixed negative values for "FileSizeLo" in json-rpc;
|
||||
- corrected url detection in rpc-method "append";
|
||||
- added support for new error messages in unrar 5.80;
|
||||
- now always using snapshots when reading directory contents:
|
||||
- in previous versions snapshots were used on macOS only;
|
||||
- now they are used on all OSes;
|
||||
- this solves issue with leftovers during directory cleanup, which could
|
||||
happen on certain OSes when working with network drives;
|
||||
- fixed file allocating on file systems where sparse files are not supported:
|
||||
- the issue could happen when InterDir was located on a network drive;
|
||||
- fixed crash caused by malformed nzb files;
|
||||
- fixed GROUP command in nserv;
|
||||
- updated url of the global certificate storage file in the build scripts;
|
||||
- fixed: file selector in WebKit based browsers doesn't allow to choose the
|
||||
same file again;
|
||||
- removed outdated links from web interface;
|
||||
- fixed PC sleep mode not working (Windows only);
|
||||
- set "SameSite" attribute for cookies;
|
||||
- corrected typo in about dialog of web interface;
|
||||
- updated license text: changed address of Free Software Foundation and minor
|
||||
formatting changes.
|
||||
|
||||
nzbget-21.0:
|
||||
- reworked duplicate handling to support URLs, especially when using RSS
|
||||
feeds:
|
||||
- queue items added via URLs (to be fetched by nzbget) are no longer
|
||||
immediately fetched;
|
||||
- instead url-items are handled by duplicate check similar to nzb-items
|
||||
and may be placed into history as duplicate backups;
|
||||
- if an url-item needs to be downloaded as backup for a failed other item
|
||||
the nzb-file is fetched via provided URL;
|
||||
- this greatly reduces the number of nzbs fetched from indexers when using
|
||||
RSS feeds and duplicate handling;
|
||||
- improved support for Android devices:
|
||||
- now providing a separate installer package for Android;
|
||||
- the package contains binaries built using Android NDK;
|
||||
- this improves compatibility with Android, in particular with Android 8,
|
||||
where general Linux installer version of NZBGet didn't work anymore due
|
||||
to security changes in Android;
|
||||
- android installer app is updated to use the new android installer package
|
||||
instead of general Linux package;
|
||||
- thoroughly optimised the program to reduce power consumption in idle state:
|
||||
- number of CPU wake ups in idle state has been reduced from hundreds times
|
||||
per second to about only one per second;
|
||||
- optimisations for large queues with thousands of items:
|
||||
- speed up saving of queue state and reduced number of queue state savings;
|
||||
- improved queue state format to reduce amount of state data saved during
|
||||
downloading;
|
||||
- in tests download speed for very large queue (16000 items) has been
|
||||
increased from 45 MB/s to 300 MB/s (comparing to 400 MB/s with small
|
||||
queue);
|
||||
- added native support for aarch64 architecture (ARM 64 Bit CPU) in Linux and
|
||||
Android installers;
|
||||
- force par-check for nzbs without archives;
|
||||
- added functional tests for unpack CRC error;
|
||||
- click on nzbget logo in web-interface now switches to downloads tab instead
|
||||
of showing "About dialog" which has been moved into settings;
|
||||
- improved handling of files splitted via par2;
|
||||
- added python 3 compatibility to EMail.py script;
|
||||
- added python 3 compatibility to Logger.py script;
|
||||
- proper UTF-8 encoding of email content in EMail.py script;
|
||||
- improved error reporting for queue disk state corruption;
|
||||
- updated unrar to 5.7 and 7-zip to 19.0;
|
||||
- Windows installer now includes unrar in 32 bit and 64 bit variants;
|
||||
- allowing wildcards in option AuthorizedIP;
|
||||
- removed suggestion of RC4 cipher;
|
||||
- better description of option UMask;
|
||||
- integrated LGTM code analyser tool into project;
|
||||
- fixed: failed downloads not having any par2- or archive- files were moved to
|
||||
DestDir instead of remaining in InterDir;
|
||||
- fixed crash when using FIPS version of OpenSSL;
|
||||
- fixed compatibility issue with OpenSSL compiled without compression support;
|
||||
- fixed deprecated OpenSSL calls;
|
||||
- fixed potential crash in built-in web-server;
|
||||
- fixed: statistics for session download time and speed may be way off on high
|
||||
load;
|
||||
- fixed many compilation warnings in GCC;
|
||||
- fixed: macOS menubar widget could not connect if password contained special
|
||||
characters;
|
||||
- fixed: remote clients not displaying current download speed;
|
||||
- fixed: remote server could crash when feed with invalid api request;
|
||||
- fixed trimming of relative paths in config.
|
||||
|
||||
nzbget-20.0:
|
||||
- massive performance optimisations in downloader:
|
||||
- improved yEnc decoder;
|
||||
- improved CRC32 calculation;
|
||||
- processing data in one pass;
|
||||
- SIMD implementation of decoder and CRC functions on x86 and ARM CPUs;
|
||||
SIMD code relies on node-yencode library by Anime Tosho
|
||||
(https://github.com/animetosho/node-yencode);
|
||||
- overall performance improvement up to +500% on x86 and +250% on ARM
|
||||
(better speed or less CPU usage);
|
||||
- using glibc instead of uClibc in universal installer builds for Linux:
|
||||
- this significantly improves performance;
|
||||
- compatibility with Android and other systems is hopefully also improved;
|
||||
- in universal installer glibc is used on x86 and ARM;
|
||||
- uClibc is still used on MIPS and PPC;
|
||||
- performance optimisations in web-interface:
|
||||
- reduced number of requests when loading webui by combining all
|
||||
javascript-files into one and all css-files into one;
|
||||
- reduced load time by calling multiple API-methods simultaneously;
|
||||
- extensive use of browser caching for static files significantly
|
||||
reduces the amount of data transferred on webui initialisation;
|
||||
- extensive use of browser caching for API requests reduces the amount
|
||||
of data transferred during webui status updates, especially when
|
||||
nzbget is in idle state and there are no changes in download queue or
|
||||
history;
|
||||
- avoid work in browser on status updates if there are no changes in
|
||||
download queue or history;
|
||||
- support for keep alive connections significantly reduces overhead for
|
||||
establishing connections on webui status updates, especially when
|
||||
connecting to nzbget via TLS/SSL (avoiding TLS handshakes);
|
||||
- a number of performance optimisations for large download queue with
|
||||
thousands of items:
|
||||
- much faster loading of queue from disk greatly improves program start
|
||||
up time;
|
||||
- improved queue management for faster download speed;
|
||||
- now offering 64 bit binaries for Windows:
|
||||
- installer includes 32 and 64 bit nzbget binaries;
|
||||
- when updating from older versions the 64 bit binary is installed
|
||||
automatically, although into the old location to keep all your
|
||||
shortcuts intact;
|
||||
- using word "windows" instead of "win32" in the setup file name;
|
||||
- automatic update check:
|
||||
- new option "UpdateCheck" to check for stable or testing versions (or
|
||||
disable);
|
||||
- when a new version is found a notification is shown;
|
||||
- the update check is enabled by default for stable versions;
|
||||
- significantly improved logging performance, especially in debug builds;
|
||||
- par-check prints par2 creator application to help identify creator app
|
||||
issues;
|
||||
- added support for Unix domain sockets (POSIX only);
|
||||
- better error handling when fetching rss feeds;
|
||||
- updated POSIX build files to newer autotools version:
|
||||
- compatibility with newer autotools;
|
||||
- compatibility with newer platforms such as aarch64;
|
||||
- better username/password validation when testing connection on settings
|
||||
page;
|
||||
- improved rar-renamer to better handle certain cases;
|
||||
- new option "SkipWrite" for easier speed tests;
|
||||
- support for redirect codes 303, 307 and 308 in web-client for fetching of
|
||||
rss feeds and nzb-files;
|
||||
- installer for FreeBSD is now built using Clang instead of GCC; this fixes
|
||||
incompatibility with FreeBSD 11;
|
||||
- universal Linux installer can now be used on FreeBSD (via Linux
|
||||
compatibility mode);
|
||||
- updated unrar to v5.50;
|
||||
- more robust news server connection test;
|
||||
- enhancements in NServ:
|
||||
- memory cache to reduce disk load during speed tests - new command line
|
||||
switch "-m";
|
||||
- speed control - new command line switches "-w" and "-r";
|
||||
- show IP address of incoming connection;
|
||||
- changed default location of log-file;
|
||||
- better handling of broken connections;
|
||||
- removed obsolete or less useful options "SaveQueue", "ReloadQueue",
|
||||
"TerminateTimeout", "AccurateRate", "BrokenLog";
|
||||
- renamed option "LogBufferSize" to "LogBuffer";
|
||||
- passwords of failed login attempts are no longer printed to log to improve
|
||||
security;
|
||||
- cookies in web interface are now saved with "httpOnly" attribute to improve
|
||||
security;
|
||||
- titles and duplicate keys in duplicate check are now case insensitive;
|
||||
- added LibreSSL support;
|
||||
- web interface now has a better icon for favorites or home screen of mobile
|
||||
devices;
|
||||
- improved duplicate detection for obfuscated downloads having files with
|
||||
same subjects;
|
||||
- direct rename and direct unpack are now active by default on new
|
||||
installations, except for slow Linux systems;
|
||||
- added advice for letsencrypt in option descriptions;
|
||||
- fixed incorrect renaming in rar-renamer which could cause some downloads to
|
||||
fail;
|
||||
- fixed race condition in queue script coordinator which could cause crashes;
|
||||
- fixed: post-processing parameters were sometimes case sensitive causing
|
||||
issues;
|
||||
- fixed DNS resolving issues on Android;
|
||||
- fixed: backup servers not used on certain article decoding errors;
|
||||
- fixed: when direct rename was active certain downloads with damaged
|
||||
par2-files become paused at near completion and required manual resuming;
|
||||
- fixed: crash when flushing article cache after direct rename;
|
||||
- fixed: deleting active par-job may crash the program;
|
||||
- fixed: functional tests may fail on Windows due to locked files;
|
||||
- fixed: unpack using password file doesn't work on Windows;
|
||||
- fixed: compiler error when building using GnuTLS;
|
||||
- fixed: Linux installer failure on android emulator;
|
||||
- fixed: options formatted as password fields when they shouldn't;
|
||||
- fixed: slightly off article statistics after direct rename;
|
||||
- fixed: NServ terminated if client interrupted connection;
|
||||
- fixed: example pp-scripts may not work properly if nzbget password or
|
||||
username contained special characters;
|
||||
- fix in functional tests to not rely on sizes of externally generated files;
|
||||
- fixed: option AuthorizedIP did not work with IPv6 addresses;
|
||||
- fixed crash on certain malformed articles;
|
||||
- fixed crash which could happen on Windows when reloading or terminating the
|
||||
program;
|
||||
- fixed logging of IPv6 addresses.
|
||||
|
||||
nzbget-19.1:
|
||||
- proper handling of changing category (and destination path) during direct
|
||||
unpack; direct unpack now gracefully aborts with cleanup; the files will
|
||||
@@ -14,66 +220,67 @@ nzbget-19.1:
|
||||
|
||||
nzbget-19.0:
|
||||
- unpack during downloading:
|
||||
- downloaded files can now be unpacked as soon as every archive part is
|
||||
downloaded;
|
||||
- new option "DirectUnpack" to activate direct unpacking;
|
||||
- direct unpack works even with obfuscated downloads; option "DirectRename"
|
||||
(see below) must be active for that;
|
||||
- option "ReorderFiles" (see below) should be also active for optimal file
|
||||
download order;
|
||||
- direct unpack works for rar-archives; 7-zip archives and simply splitted
|
||||
files are processed by default unpack module;
|
||||
- direct unpack obviously works only for healthy download; if download is
|
||||
damaged the direct unpack cancels and the download is unpacked during
|
||||
post-processing stage after files are repaired;
|
||||
- direct unpack reduces the time needed to complete download and
|
||||
- downloaded files can now be unpacked as soon as every archive part is
|
||||
downloaded;
|
||||
- new option "DirectUnpack" to activate direct unpacking;
|
||||
- direct unpack works even with obfuscated downloads; option
|
||||
"DirectRename" (see below) must be active for that;
|
||||
- option "ReorderFiles" (see below) should be also active for optimal
|
||||
file download order;
|
||||
- direct unpack works for rar-archives; 7-zip archives and simply
|
||||
splitted files are processed by default unpack module;
|
||||
- direct unpack obviously works only for healthy download; if download
|
||||
is damaged the direct unpack cancels and the download is unpacked
|
||||
during post-processing stage after files are repaired;
|
||||
- direct unpack reduces the time needed to complete download and
|
||||
post-processing;
|
||||
- it also allows to start watching of video files during download (requires
|
||||
compatible video player software);
|
||||
- it also allows to start watching of video files during download
|
||||
(requires compatible video player software);
|
||||
- renaming of obfuscated file names during downloading:
|
||||
- correct file names for obfuscated downloads are now determined during
|
||||
download stage (instead of post-processing stage);
|
||||
- downloaded files are saved into disk directly with correct names;
|
||||
- direct renaming uses par2-files to restore correct file names;
|
||||
- new option "DirectRename" to activate direct renaming;
|
||||
- new queue-event NZB_NAMED, sent after the inner files are renamed;
|
||||
- correct file names for obfuscated downloads are now determined during
|
||||
download stage (instead of post-processing stage);
|
||||
- downloaded files are saved into disk directly with correct names;
|
||||
- direct renaming uses par2-files to restore correct file names;
|
||||
- new option "DirectRename" to activate direct renaming;
|
||||
- new queue-event NZB_NAMED, sent after the inner files are renamed;
|
||||
- automatic reordering of files:
|
||||
- inner files within nzb reordered to ensure download of files in archive
|
||||
parts order;
|
||||
- the files are reordered when nzb is added to queue;
|
||||
- if direct renaming is active (option "DirectRename") the files are
|
||||
- inner files within nzb reordered to ensure download of files in
|
||||
archive parts order;
|
||||
- the files are reordered when nzb is added to queue;
|
||||
- if direct renaming is active (option "DirectRename") the files are
|
||||
reordered again after the correct names becomes known;
|
||||
- new option "ReorderFiles";
|
||||
- new command "GroupSortFiles" in api-method "editqueue";
|
||||
- new subcommand "SF" of remote command "-E/--edit";
|
||||
- new option "ReorderFiles";
|
||||
- new command "GroupSortFiles" in api-method "editqueue";
|
||||
- new subcommand "SF" of remote command "-E/--edit";
|
||||
- new option "FileNaming" to control how to name obfuscated files (before they
|
||||
get renamed by par-rename, rar-rename or direct-rename);
|
||||
- TLS certificate verification:
|
||||
- when connecting to a news server (for downloading) or a web server (for
|
||||
fetching of rss feeds and nzb-files) the authenticity of the server is
|
||||
validated using server security certificate. If the check fails that means
|
||||
the connection cannot be trusted and must be closed with an error message
|
||||
explaining the security issue;
|
||||
- new options "CertCheck" and "CertStore";
|
||||
- official NZBGet packages come with activated certificate check;
|
||||
- when updating from an older NZBGet version the option CertCheck will be
|
||||
automatically activated when the settings is saved (switch to Settings
|
||||
page in web-interface and click "Save all changed");
|
||||
- when connecting to a news server (for downloading) or a web server
|
||||
(for fetching of rss feeds and nzb-files) the authenticity of the
|
||||
server is validated using server security certificate. If the check
|
||||
fails that means the connection cannot be trusted and must be closed
|
||||
with an error message explaining the security issue;
|
||||
- new options "CertCheck" and "CertStore";
|
||||
- official NZBGet packages come with activated certificate check;
|
||||
- when updating from an older NZBGet version the option CertCheck will
|
||||
be automatically activated when the settings is saved (switch to
|
||||
Settings page in web-interface and click "Save all changed");
|
||||
- authentication via form in web-interface as alternative to HTTP
|
||||
authentication:
|
||||
- that must help with password tools having issues with HTTP authentication
|
||||
dialog;
|
||||
- new option "FormAuth";
|
||||
- that must help with password tools having issues with HTTP
|
||||
authentication dialog;
|
||||
- new option "FormAuth";
|
||||
- drop-downs (context menus) for priority, category and status columns:
|
||||
- quicker changing of priority and category;
|
||||
- easier access to actions via drop-down (context menu) in status column;
|
||||
- quicker changing of priority and category;
|
||||
- easier access to actions via drop-down (context menu) in status
|
||||
column;
|
||||
- extensions scripts can now be executed from settings page:
|
||||
- script authors define custom buttons;
|
||||
- when clicked the script is executed in a special mode and obtain extra
|
||||
parameters;
|
||||
- example script "Email.py" extended with button "Send test e-mail";
|
||||
- script authors define custom buttons;
|
||||
- when clicked the script is executed in a special mode and obtain extra
|
||||
parameters;
|
||||
- example script "Email.py" extended with button "Send test e-mail";
|
||||
- on Windows NZBGet can now associate itself with nzb-files:
|
||||
- use option in Windows installer to register NZBGet for nzb-files;
|
||||
- use option in Windows installer to register NZBGet for nzb-files;
|
||||
- unrar shipped within Linux package is now compiled with "fallocate" option
|
||||
to improve compatibility with media players when watching videos during
|
||||
downloading and unpacking;
|
||||
@@ -87,9 +294,6 @@ nzbget-19.0:
|
||||
- save changes before performing actions in history dialog;
|
||||
- proper exit code on client command success or failure.
|
||||
- added host name to all error messages regarding connection issues;
|
||||
- improved continuos integration with Travis CI:
|
||||
- added gcc 4.8 to test matrix;
|
||||
- installing unrar into test system to allow unit tests requiring unrar;
|
||||
- new button "Volume Statistics" in section "News Servers" of settings page;
|
||||
shows the same volume data as in global statistics dialog;
|
||||
- new option "ServerX.Notes" for user comments on news servers;
|
||||
@@ -100,9 +304,9 @@ nzbget-19.0:
|
||||
- updated unrar to v5.40;
|
||||
- clear script execution log before executing script;
|
||||
- added support for crash dumps on Windows:
|
||||
- renamed option "DumpCore" to "CrashDump";
|
||||
- new option "CrashTrace" to make it possible to disable default printing off
|
||||
call stack in order to produce more relevant crash dumps;
|
||||
- renamed option "DumpCore" to "CrashDump";
|
||||
- new option "CrashTrace" to make it possible to disable default
|
||||
printing off call stack in order to produce more relevant crash dumps;
|
||||
- fixed: startup scheduler tasks can be executed again;
|
||||
- fixed: "fatal" messages when compiling from sources.
|
||||
- fixed: per-nzb download statistics could be wrong if the program was
|
||||
|
||||
43
Makefile.am
43
Makefile.am
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
# Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# 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
|
||||
@@ -58,8 +58,8 @@ nzbget_SOURCES = \
|
||||
daemon/frontend/LoggableFrontend.h \
|
||||
daemon/frontend/NCursesFrontend.cpp \
|
||||
daemon/frontend/NCursesFrontend.h \
|
||||
daemon/main/CommandLineParser.cpp \
|
||||
daemon/main/CommandLineParser.h \
|
||||
daemon/main/CommandLineParser.cpp \
|
||||
daemon/main/CommandLineParser.h \
|
||||
daemon/main/DiskService.cpp \
|
||||
daemon/main/DiskService.h \
|
||||
daemon/main/Maintenance.cpp \
|
||||
@@ -68,6 +68,8 @@ nzbget_SOURCES = \
|
||||
daemon/main/nzbget.h \
|
||||
daemon/main/Options.cpp \
|
||||
daemon/main/Options.h \
|
||||
daemon/main/WorkState.cpp \
|
||||
daemon/main/WorkState.h \
|
||||
daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h \
|
||||
daemon/main/StackTrace.cpp \
|
||||
@@ -214,6 +216,25 @@ nzbget_SOURCES += \
|
||||
lib/par2/verificationpacket.h
|
||||
endif
|
||||
|
||||
# Simd decoder and Crc32
|
||||
nzbget_SOURCES += \
|
||||
lib/yencode/YEncode.h \
|
||||
lib/yencode/SimdInit.cpp \
|
||||
lib/yencode/SimdDecoder.cpp \
|
||||
lib/yencode/ScalarDecoder.cpp \
|
||||
lib/yencode/Sse2Decoder.cpp \
|
||||
lib/yencode/Ssse3Decoder.cpp \
|
||||
lib/yencode/PclmulCrc.cpp \
|
||||
lib/yencode/NeonDecoder.cpp \
|
||||
lib/yencode/AcleCrc.cpp \
|
||||
lib/yencode/SliceCrc.cpp
|
||||
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT) : CXXFLAGS+=$(SSE2_CXXFLAGS)
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT) : CXXFLAGS+=$(SSSE3_CXXFLAGS)
|
||||
lib/yencode/PclmulCrc.$(OBJEXT) : CXXFLAGS+=$(PCLMUL_CXXFLAGS)
|
||||
lib/yencode/NeonDecoder.$(OBJEXT) : CXXFLAGS+=$(NEON_CXXFLAGS)
|
||||
lib/yencode/AcleCrc.$(OBJEXT) : CXXFLAGS+=$(ACLECRC_CXXFLAGS)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(srcdir)/daemon/connect \
|
||||
-I$(srcdir)/daemon/extension \
|
||||
@@ -226,7 +247,8 @@ AM_CPPFLAGS = \
|
||||
-I$(srcdir)/daemon/remote \
|
||||
-I$(srcdir)/daemon/util \
|
||||
-I$(srcdir)/daemon/nserv \
|
||||
-I$(srcdir)/lib/par2
|
||||
-I$(srcdir)/lib/par2 \
|
||||
-I$(srcdir)/lib/yencode
|
||||
|
||||
if WITH_TESTS
|
||||
nzbget_SOURCES += \
|
||||
@@ -325,6 +347,7 @@ linux_FILES = \
|
||||
linux/build-info.txt \
|
||||
linux/build-nzbget \
|
||||
linux/build-unpack \
|
||||
linux/build-toolchain-android \
|
||||
linux/build-toolchain-freebsd
|
||||
|
||||
doc_FILES = \
|
||||
@@ -369,7 +392,9 @@ webui_FILES = \
|
||||
webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png \
|
||||
webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif
|
||||
webui/img/transmit-reload-2x.gif \
|
||||
webui/img/favicon-256x256-opaque.png \
|
||||
webui/img/favicon-256x256.png
|
||||
|
||||
scripts_FILES = \
|
||||
scripts/EMail.py \
|
||||
@@ -391,6 +416,14 @@ testdata_FILES = \
|
||||
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
|
||||
tests/testdata/parchecker2/crc.txt \
|
||||
tests/testdata/parchecker2/testfile.7z.001 \
|
||||
tests/testdata/parchecker2/testfile.7z.002 \
|
||||
tests/testdata/parchecker2/testfile.7z.003 \
|
||||
tests/testdata/parchecker2/testfile.7z.par2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol0+1.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol1+2.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol3+3.PAR2 \
|
||||
tests/testdata/rarrenamer/testfile3.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part03.rar \
|
||||
|
||||
106
Makefile.in
vendored
106
Makefile.in
vendored
@@ -17,7 +17,7 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
# Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# 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
|
||||
@@ -232,7 +232,8 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
|
||||
daemon/main/DiskService.h daemon/main/Maintenance.cpp \
|
||||
daemon/main/Maintenance.h daemon/main/nzbget.cpp \
|
||||
daemon/main/nzbget.h daemon/main/Options.cpp \
|
||||
daemon/main/Options.h daemon/main/Scheduler.cpp \
|
||||
daemon/main/Options.h daemon/main/WorkState.cpp \
|
||||
daemon/main/WorkState.h daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h daemon/main/StackTrace.cpp \
|
||||
daemon/main/StackTrace.h daemon/nntp/ArticleDownloader.cpp \
|
||||
daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \
|
||||
@@ -312,6 +313,11 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
|
||||
lib/par2/reedsolomon.h lib/par2/verificationhashtable.cpp \
|
||||
lib/par2/verificationhashtable.h \
|
||||
lib/par2/verificationpacket.cpp lib/par2/verificationpacket.h \
|
||||
lib/yencode/YEncode.h lib/yencode/SimdInit.cpp \
|
||||
lib/yencode/SimdDecoder.cpp lib/yencode/ScalarDecoder.cpp \
|
||||
lib/yencode/Sse2Decoder.cpp lib/yencode/Ssse3Decoder.cpp \
|
||||
lib/yencode/PclmulCrc.cpp lib/yencode/NeonDecoder.cpp \
|
||||
lib/yencode/AcleCrc.cpp lib/yencode/SliceCrc.cpp \
|
||||
lib/catch/catch.h tests/suite/TestMain.cpp \
|
||||
tests/suite/TestMain.h tests/suite/TestUtil.cpp \
|
||||
tests/suite/TestUtil.h tests/main/CommandLineParserTest.cpp \
|
||||
@@ -382,7 +388,8 @@ am_nzbget_OBJECTS = daemon/connect/Connection.$(OBJEXT) \
|
||||
daemon/main/CommandLineParser.$(OBJEXT) \
|
||||
daemon/main/DiskService.$(OBJEXT) \
|
||||
daemon/main/Maintenance.$(OBJEXT) daemon/main/nzbget.$(OBJEXT) \
|
||||
daemon/main/Options.$(OBJEXT) daemon/main/Scheduler.$(OBJEXT) \
|
||||
daemon/main/Options.$(OBJEXT) daemon/main/WorkState.$(OBJEXT) \
|
||||
daemon/main/Scheduler.$(OBJEXT) \
|
||||
daemon/main/StackTrace.$(OBJEXT) \
|
||||
daemon/nntp/ArticleDownloader.$(OBJEXT) \
|
||||
daemon/nntp/ArticleWriter.$(OBJEXT) \
|
||||
@@ -425,7 +432,15 @@ am_nzbget_OBJECTS = daemon/connect/Connection.$(OBJEXT) \
|
||||
daemon/nserv/NntpServer.$(OBJEXT) \
|
||||
daemon/nserv/NzbGenerator.$(OBJEXT) \
|
||||
daemon/nserv/YEncoder.$(OBJEXT) code_revision.$(OBJEXT) \
|
||||
$(am__objects_1) $(am__objects_2) $(am__objects_3)
|
||||
$(am__objects_1) lib/yencode/SimdInit.$(OBJEXT) \
|
||||
lib/yencode/SimdDecoder.$(OBJEXT) \
|
||||
lib/yencode/ScalarDecoder.$(OBJEXT) \
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT) \
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT) \
|
||||
lib/yencode/PclmulCrc.$(OBJEXT) \
|
||||
lib/yencode/NeonDecoder.$(OBJEXT) \
|
||||
lib/yencode/AcleCrc.$(OBJEXT) lib/yencode/SliceCrc.$(OBJEXT) \
|
||||
$(am__objects_2) $(am__objects_3)
|
||||
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
|
||||
nzbget_LDADD = $(LDADD)
|
||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||
@@ -550,6 +565,7 @@ DIST_TARGETS = dist-gzip
|
||||
distuninstallcheck_listfiles = find . -type f -print
|
||||
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
|
||||
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
|
||||
ACLECRC_CXXFLAGS = @ACLECRC_CXXFLAGS@
|
||||
ACLOCAL = @ACLOCAL@
|
||||
AMTAR = @AMTAR@
|
||||
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
|
||||
@@ -585,6 +601,7 @@ MAINT = @MAINT@
|
||||
MAKE = @MAKE@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
NEON_CXXFLAGS = @NEON_CXXFLAGS@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
@@ -594,11 +611,14 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_URL = @PACKAGE_URL@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
PCLMUL_CXXFLAGS = @PCLMUL_CXXFLAGS@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
|
||||
PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
SSE2_CXXFLAGS = @SSE2_CXXFLAGS@
|
||||
SSSE3_CXXFLAGS = @SSSE3_CXXFLAGS@
|
||||
STRIP = @STRIP@
|
||||
TAR = @TAR@
|
||||
VERSION = @VERSION@
|
||||
@@ -668,6 +688,8 @@ top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
zlib_CFLAGS = @zlib_CFLAGS@
|
||||
zlib_LIBS = @zlib_LIBS@
|
||||
|
||||
# Simd decoder and Crc32
|
||||
nzbget_SOURCES = daemon/connect/Connection.cpp \
|
||||
daemon/connect/Connection.h daemon/connect/TlsSocket.cpp \
|
||||
daemon/connect/TlsSocket.h daemon/connect/WebDownloader.cpp \
|
||||
@@ -700,7 +722,8 @@ nzbget_SOURCES = daemon/connect/Connection.cpp \
|
||||
daemon/main/DiskService.h daemon/main/Maintenance.cpp \
|
||||
daemon/main/Maintenance.h daemon/main/nzbget.cpp \
|
||||
daemon/main/nzbget.h daemon/main/Options.cpp \
|
||||
daemon/main/Options.h daemon/main/Scheduler.cpp \
|
||||
daemon/main/Options.h daemon/main/WorkState.cpp \
|
||||
daemon/main/WorkState.h daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h daemon/main/StackTrace.cpp \
|
||||
daemon/main/StackTrace.h daemon/nntp/ArticleDownloader.cpp \
|
||||
daemon/nntp/ArticleDownloader.h daemon/nntp/ArticleWriter.cpp \
|
||||
@@ -760,14 +783,19 @@ nzbget_SOURCES = daemon/connect/Connection.cpp \
|
||||
daemon/nserv/NntpServer.h daemon/nserv/NntpServer.cpp \
|
||||
daemon/nserv/NzbGenerator.h daemon/nserv/NzbGenerator.cpp \
|
||||
daemon/nserv/YEncoder.h daemon/nserv/YEncoder.cpp \
|
||||
code_revision.cpp $(am__append_1) $(am__append_2) \
|
||||
$(am__append_3)
|
||||
code_revision.cpp $(am__append_1) lib/yencode/YEncode.h \
|
||||
lib/yencode/SimdInit.cpp lib/yencode/SimdDecoder.cpp \
|
||||
lib/yencode/ScalarDecoder.cpp lib/yencode/Sse2Decoder.cpp \
|
||||
lib/yencode/Ssse3Decoder.cpp lib/yencode/PclmulCrc.cpp \
|
||||
lib/yencode/NeonDecoder.cpp lib/yencode/AcleCrc.cpp \
|
||||
lib/yencode/SliceCrc.cpp $(am__append_2) $(am__append_3)
|
||||
AM_CPPFLAGS = -I$(srcdir)/daemon/connect -I$(srcdir)/daemon/extension \
|
||||
-I$(srcdir)/daemon/feed -I$(srcdir)/daemon/frontend \
|
||||
-I$(srcdir)/daemon/main -I$(srcdir)/daemon/nntp \
|
||||
-I$(srcdir)/daemon/postprocess -I$(srcdir)/daemon/queue \
|
||||
-I$(srcdir)/daemon/remote -I$(srcdir)/daemon/util \
|
||||
-I$(srcdir)/daemon/nserv -I$(srcdir)/lib/par2 $(am__append_4)
|
||||
-I$(srcdir)/daemon/nserv -I$(srcdir)/lib/par2 \
|
||||
-I$(srcdir)/lib/yencode $(am__append_4)
|
||||
EXTRA_DIST = \
|
||||
$(windows_FILES) \
|
||||
$(osx_FILES) \
|
||||
@@ -834,6 +862,7 @@ linux_FILES = \
|
||||
linux/build-info.txt \
|
||||
linux/build-nzbget \
|
||||
linux/build-unpack \
|
||||
linux/build-toolchain-android \
|
||||
linux/build-toolchain-freebsd
|
||||
|
||||
doc_FILES = \
|
||||
@@ -878,7 +907,9 @@ webui_FILES = \
|
||||
webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png \
|
||||
webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif
|
||||
webui/img/transmit-reload-2x.gif \
|
||||
webui/img/favicon-256x256-opaque.png \
|
||||
webui/img/favicon-256x256.png
|
||||
|
||||
scripts_FILES = \
|
||||
scripts/EMail.py \
|
||||
@@ -900,6 +931,14 @@ testdata_FILES = \
|
||||
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
|
||||
tests/testdata/parchecker2/crc.txt \
|
||||
tests/testdata/parchecker2/testfile.7z.001 \
|
||||
tests/testdata/parchecker2/testfile.7z.002 \
|
||||
tests/testdata/parchecker2/testfile.7z.003 \
|
||||
tests/testdata/parchecker2/testfile.7z.par2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol0+1.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol1+2.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol3+3.PAR2 \
|
||||
tests/testdata/rarrenamer/testfile3.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part03.rar \
|
||||
@@ -1122,6 +1161,8 @@ daemon/main/nzbget.$(OBJEXT): daemon/main/$(am__dirstamp) \
|
||||
daemon/main/$(DEPDIR)/$(am__dirstamp)
|
||||
daemon/main/Options.$(OBJEXT): daemon/main/$(am__dirstamp) \
|
||||
daemon/main/$(DEPDIR)/$(am__dirstamp)
|
||||
daemon/main/WorkState.$(OBJEXT): daemon/main/$(am__dirstamp) \
|
||||
daemon/main/$(DEPDIR)/$(am__dirstamp)
|
||||
daemon/main/Scheduler.$(OBJEXT): daemon/main/$(am__dirstamp) \
|
||||
daemon/main/$(DEPDIR)/$(am__dirstamp)
|
||||
daemon/main/StackTrace.$(OBJEXT): daemon/main/$(am__dirstamp) \
|
||||
@@ -1313,6 +1354,30 @@ lib/par2/verificationhashtable.$(OBJEXT): lib/par2/$(am__dirstamp) \
|
||||
lib/par2/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/par2/verificationpacket.$(OBJEXT): lib/par2/$(am__dirstamp) \
|
||||
lib/par2/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/$(am__dirstamp):
|
||||
@$(MKDIR_P) lib/yencode
|
||||
@: > lib/yencode/$(am__dirstamp)
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp):
|
||||
@$(MKDIR_P) lib/yencode/$(DEPDIR)
|
||||
@: > lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/SimdInit.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/SimdDecoder.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/ScalarDecoder.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/PclmulCrc.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/NeonDecoder.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/AcleCrc.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
lib/yencode/SliceCrc.$(OBJEXT): lib/yencode/$(am__dirstamp) \
|
||||
lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
tests/suite/$(am__dirstamp):
|
||||
@$(MKDIR_P) tests/suite
|
||||
@: > tests/suite/$(am__dirstamp)
|
||||
@@ -1455,6 +1520,7 @@ mostlyclean-compile:
|
||||
-rm -f daemon/remote/*.$(OBJEXT)
|
||||
-rm -f daemon/util/*.$(OBJEXT)
|
||||
-rm -f lib/par2/*.$(OBJEXT)
|
||||
-rm -f lib/yencode/*.$(OBJEXT)
|
||||
-rm -f tests/feed/*.$(OBJEXT)
|
||||
-rm -f tests/main/*.$(OBJEXT)
|
||||
-rm -f tests/nntp/*.$(OBJEXT)
|
||||
@@ -1492,6 +1558,7 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/Options.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/Scheduler.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/StackTrace.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/WorkState.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/main/$(DEPDIR)/nzbget.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/nntp/$(DEPDIR)/ArticleDownloader.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@daemon/nntp/$(DEPDIR)/ArticleWriter.Po@am__quote@
|
||||
@@ -1559,6 +1626,15 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/par2/$(DEPDIR)/reedsolomon.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/par2/$(DEPDIR)/verificationhashtable.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/par2/$(DEPDIR)/verificationpacket.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/AcleCrc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/NeonDecoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/PclmulCrc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/ScalarDecoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/SimdDecoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/SimdInit.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/SliceCrc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/Sse2Decoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@lib/yencode/$(DEPDIR)/Ssse3Decoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@tests/feed/$(DEPDIR)/FeedFilterTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@tests/main/$(DEPDIR)/CommandLineParserTest.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@tests/main/$(DEPDIR)/OptionsTest.Po@am__quote@
|
||||
@@ -1940,6 +2016,8 @@ distclean-generic:
|
||||
-rm -f daemon/util/$(am__dirstamp)
|
||||
-rm -f lib/par2/$(DEPDIR)/$(am__dirstamp)
|
||||
-rm -f lib/par2/$(am__dirstamp)
|
||||
-rm -f lib/yencode/$(DEPDIR)/$(am__dirstamp)
|
||||
-rm -f lib/yencode/$(am__dirstamp)
|
||||
-rm -f tests/feed/$(DEPDIR)/$(am__dirstamp)
|
||||
-rm -f tests/feed/$(am__dirstamp)
|
||||
-rm -f tests/main/$(DEPDIR)/$(am__dirstamp)
|
||||
@@ -1964,7 +2042,7 @@ clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
|
||||
|
||||
distclean: distclean-am
|
||||
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
|
||||
-rm -rf ./$(DEPDIR) daemon/connect/$(DEPDIR) daemon/extension/$(DEPDIR) daemon/feed/$(DEPDIR) daemon/frontend/$(DEPDIR) daemon/main/$(DEPDIR) daemon/nntp/$(DEPDIR) daemon/nserv/$(DEPDIR) daemon/postprocess/$(DEPDIR) daemon/queue/$(DEPDIR) daemon/remote/$(DEPDIR) daemon/util/$(DEPDIR) lib/par2/$(DEPDIR) tests/feed/$(DEPDIR) tests/main/$(DEPDIR) tests/nntp/$(DEPDIR) tests/postprocess/$(DEPDIR) tests/queue/$(DEPDIR) tests/suite/$(DEPDIR) tests/util/$(DEPDIR)
|
||||
-rm -rf ./$(DEPDIR) daemon/connect/$(DEPDIR) daemon/extension/$(DEPDIR) daemon/feed/$(DEPDIR) daemon/frontend/$(DEPDIR) daemon/main/$(DEPDIR) daemon/nntp/$(DEPDIR) daemon/nserv/$(DEPDIR) daemon/postprocess/$(DEPDIR) daemon/queue/$(DEPDIR) daemon/remote/$(DEPDIR) daemon/util/$(DEPDIR) lib/par2/$(DEPDIR) lib/yencode/$(DEPDIR) tests/feed/$(DEPDIR) tests/main/$(DEPDIR) tests/nntp/$(DEPDIR) tests/postprocess/$(DEPDIR) tests/queue/$(DEPDIR) tests/suite/$(DEPDIR) tests/util/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
distclean-am: clean-am distclean-compile distclean-generic \
|
||||
distclean-hdr distclean-tags
|
||||
@@ -2015,7 +2093,7 @@ installcheck-am:
|
||||
maintainer-clean: maintainer-clean-am
|
||||
-rm -f $(am__CONFIG_DISTCLEAN_FILES)
|
||||
-rm -rf $(top_srcdir)/autom4te.cache
|
||||
-rm -rf ./$(DEPDIR) daemon/connect/$(DEPDIR) daemon/extension/$(DEPDIR) daemon/feed/$(DEPDIR) daemon/frontend/$(DEPDIR) daemon/main/$(DEPDIR) daemon/nntp/$(DEPDIR) daemon/nserv/$(DEPDIR) daemon/postprocess/$(DEPDIR) daemon/queue/$(DEPDIR) daemon/remote/$(DEPDIR) daemon/util/$(DEPDIR) lib/par2/$(DEPDIR) tests/feed/$(DEPDIR) tests/main/$(DEPDIR) tests/nntp/$(DEPDIR) tests/postprocess/$(DEPDIR) tests/queue/$(DEPDIR) tests/suite/$(DEPDIR) tests/util/$(DEPDIR)
|
||||
-rm -rf ./$(DEPDIR) daemon/connect/$(DEPDIR) daemon/extension/$(DEPDIR) daemon/feed/$(DEPDIR) daemon/frontend/$(DEPDIR) daemon/main/$(DEPDIR) daemon/nntp/$(DEPDIR) daemon/nserv/$(DEPDIR) daemon/postprocess/$(DEPDIR) daemon/queue/$(DEPDIR) daemon/remote/$(DEPDIR) daemon/util/$(DEPDIR) lib/par2/$(DEPDIR) lib/yencode/$(DEPDIR) tests/feed/$(DEPDIR) tests/main/$(DEPDIR) tests/nntp/$(DEPDIR) tests/postprocess/$(DEPDIR) tests/queue/$(DEPDIR) tests/suite/$(DEPDIR) tests/util/$(DEPDIR)
|
||||
-rm -f Makefile
|
||||
maintainer-clean-am: distclean-am maintainer-clean-generic
|
||||
|
||||
@@ -2064,6 +2142,12 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
|
||||
.PRECIOUS: Makefile
|
||||
|
||||
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT) : CXXFLAGS+=$(SSE2_CXXFLAGS)
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT) : CXXFLAGS+=$(SSSE3_CXXFLAGS)
|
||||
lib/yencode/PclmulCrc.$(OBJEXT) : CXXFLAGS+=$(PCLMUL_CXXFLAGS)
|
||||
lib/yencode/NeonDecoder.$(OBJEXT) : CXXFLAGS+=$(NEON_CXXFLAGS)
|
||||
lib/yencode/AcleCrc.$(OBJEXT) : CXXFLAGS+=$(ACLECRC_CXXFLAGS)
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
|
||||
21
README.md
21
README.md
@@ -1,18 +1,19 @@
|
||||
# NZBGet #
|
||||
[](http://www.gnu.org/licenses/)
|
||||
[](https://travis-ci.org/nzbget/nzbget)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:cpp)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:javascript)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/alerts)
|
||||
|
||||
[](https://github.com/nzbget/nzbget/releases)
|
||||
[](https://github.com/nzbget/nzbget/releases/latest)
|
||||
|
||||
NZBGet is a binary downloader, which downloads files from Usenet
|
||||
based on information given in nzb-files.
|
||||
|
||||
NZBGet is written in C++ and is known for its extraordinary performance and efficiency.
|
||||
NZBGet is written in C++ and is known for its performance and efficiency.
|
||||
|
||||
NZBGet can be run at almost every platform - classic PCs, NAS, media players, SAT-receivers, WLAN-routers, etc.
|
||||
The download area provides precompiled binaries
|
||||
for Windows, Mac OS X and Linux (compatible with many CPUs and platform variants). For other platforms
|
||||
the program can be compiled from sources.
|
||||
|
||||
- [Home page (nzbget.net)](http://nzbget.net) - for first time visitors, learn more about NZBGet;
|
||||
- [Downloads](http://nzbget.net/download) - get the binaries and sources;
|
||||
- [Documentation](http://nzbget.net/documentation) - installation manuals, HOW-TOs, API;
|
||||
- [Forum](http://forum.nzbget.net) - get support, share your ideas, scripts, add-ons.
|
||||
NZBGet can run on almost any device - classic PC, NAS, media player, SAT-receiver, WLAN-router, etc.
|
||||
The download area provides precompiled binaries for Windows, macOS, Linux (compatible with
|
||||
many CPUs and platform variants), FreeBSD and Android. For other platforms
|
||||
the program can be compiled from sources.
|
||||
28
config.h.in
28
config.h.in
@@ -1,21 +1,17 @@
|
||||
/* config.h.in. Generated from configure.ac by autoheader. */
|
||||
|
||||
/* Define if building universal (internal helper macro) */
|
||||
#undef AC_APPLE_UNIVERSAL_BUILD
|
||||
|
||||
/* Define to 1 to include debug-code */
|
||||
#undef DEBUG
|
||||
|
||||
/* Define to 1 if deleting of files during reading of directory is not
|
||||
properly supported by OS */
|
||||
#undef DIRBROWSER_SNAPSHOT
|
||||
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to not use libxml2, only for development purposes */
|
||||
#undef DISABLE_LIBXML2
|
||||
|
||||
/* Define to 1 to disable par-verification and repair */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
@@ -86,6 +82,9 @@
|
||||
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
|
||||
#undef HAVE_LIBGNUTLS
|
||||
|
||||
/* Define to 1 if lockf is supported */
|
||||
#undef HAVE_LOCKF
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -101,6 +100,9 @@
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if pthread_cancel is supported */
|
||||
#undef HAVE_PTHREAD_CANCEL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
@@ -176,18 +178,6 @@
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
|
||||
significant byte first (like Motorola and SPARC, unlike Intel). */
|
||||
#if defined AC_APPLE_UNIVERSAL_BUILD
|
||||
# if defined __BIG_ENDIAN__
|
||||
# define WORDS_BIGENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# ifndef WORDS_BIGENDIAN
|
||||
# undef WORDS_BIGENDIAN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
|
||||
403
configure
vendored
403
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.69 for nzbget 20.0-testing.
|
||||
# Generated by GNU Autoconf 2.69 for nzbget 21.2-testing.
|
||||
#
|
||||
# Report bugs to <hugbug@users.sourceforge.net>.
|
||||
#
|
||||
@@ -580,8 +580,8 @@ MAKEFLAGS=
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='nzbget'
|
||||
PACKAGE_TARNAME='nzbget'
|
||||
PACKAGE_VERSION='20.0-testing'
|
||||
PACKAGE_STRING='nzbget 20.0-testing'
|
||||
PACKAGE_VERSION='21.2-testing'
|
||||
PACKAGE_STRING='nzbget 21.2-testing'
|
||||
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
|
||||
PACKAGE_URL=''
|
||||
|
||||
@@ -628,6 +628,11 @@ LTLIBOBJS
|
||||
LIBOBJS
|
||||
WITH_TESTS_FALSE
|
||||
WITH_TESTS_TRUE
|
||||
ACLECRC_CXXFLAGS
|
||||
NEON_CXXFLAGS
|
||||
PCLMUL_CXXFLAGS
|
||||
SSSE3_CXXFLAGS
|
||||
SSE2_CXXFLAGS
|
||||
zlib_LIBS
|
||||
zlib_CFLAGS
|
||||
nettle_LIBS
|
||||
@@ -756,6 +761,7 @@ enable_maintainer_mode
|
||||
enable_dependency_tracking
|
||||
enable_cpp_check
|
||||
enable_largefile
|
||||
enable_libxml2
|
||||
with_libxml2_includes
|
||||
with_libxml2_libraries
|
||||
enable_curses
|
||||
@@ -1342,7 +1348,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures nzbget 20.0-testing to adapt to many kinds of systems.
|
||||
\`configure' configures nzbget 21.2-testing to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1413,7 +1419,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of nzbget 20.0-testing:";;
|
||||
short | recursive ) echo "Configuration of nzbget 21.2-testing:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1432,6 +1438,8 @@ Optional Features:
|
||||
speeds up one-time build
|
||||
--disable-cpp-check disable check for C++14 compiler features
|
||||
--disable-largefile omit support for large files
|
||||
--disable-libxml2 do not use libxml2 (removes dependency from
|
||||
libxml2-library, only for development purposes)
|
||||
--disable-curses do not use curses (removes dependency from
|
||||
curses-library)
|
||||
--disable-parcheck do not include par-check/-repair-support
|
||||
@@ -1576,7 +1584,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
nzbget configure 20.0-testing
|
||||
nzbget configure 21.2-testing
|
||||
generated by GNU Autoconf 2.69
|
||||
|
||||
Copyright (C) 2012 Free Software Foundation, Inc.
|
||||
@@ -2045,7 +2053,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by nzbget $as_me 20.0-testing, which was
|
||||
It was created by nzbget $as_me 21.2-testing, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -3018,7 +3026,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE='nzbget'
|
||||
VERSION='20.0-testing'
|
||||
VERSION='21.2-testing'
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -5756,24 +5764,13 @@ fi
|
||||
done
|
||||
|
||||
|
||||
for ac_header in sys/prctl.h
|
||||
for ac_header in sys/prctl.h regex.h endian.h getopt.h
|
||||
do :
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "sys/prctl.h" "ac_cv_header_sys_prctl_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_sys_prctl_h" = xyes; then :
|
||||
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
|
||||
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_SYS_PRCTL_H 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
for ac_header in regex.h
|
||||
do :
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "regex.h" "ac_cv_header_regex_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_regex_h" = xyes; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define HAVE_REGEX_H 1
|
||||
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
@@ -6008,6 +6005,32 @@ fi
|
||||
|
||||
|
||||
|
||||
ac_fn_cxx_check_func "$LINENO" "lockf" "ac_cv_func_lockf"
|
||||
if test "x$ac_cv_func_lockf" = xyes; then :
|
||||
ac_fn_cxx_check_decl "$LINENO" "lockf" "ac_cv_have_decl_lockf" "#include <unistd.h>
|
||||
"
|
||||
if test "x$ac_cv_have_decl_lockf" = xyes; then :
|
||||
|
||||
$as_echo "#define HAVE_LOCKF 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
ac_fn_cxx_check_func "$LINENO" "pthread_cancel" "ac_cv_func_pthread_cancel"
|
||||
if test "x$ac_cv_func_pthread_cancel" = xyes; then :
|
||||
ac_fn_cxx_check_decl "$LINENO" "pthread_cancel" "ac_cv_have_decl_pthread_cancel" "#include <pthread.h>
|
||||
"
|
||||
if test "x$ac_cv_have_decl_pthread_cancel" = xyes; then :
|
||||
|
||||
$as_echo "#define HAVE_PTHREAD_CANCEL 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
ac_fn_cxx_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long"
|
||||
if test "x$ac_cv_func_getopt_long" = xyes; then :
|
||||
|
||||
@@ -6545,12 +6568,12 @@ main ()
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: size_t" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: size_t" >&5
|
||||
$as_echo "size_t" >&6; }
|
||||
SOCKLEN_T=size_t
|
||||
SOCKLEN_T=size_t
|
||||
else
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
#include <stddef.h>
|
||||
@@ -6567,14 +6590,14 @@ main ()
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: int" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: int" >&5
|
||||
$as_echo "int" >&6; }
|
||||
SOCKLEN_T=int
|
||||
SOCKLEN_T=int
|
||||
else
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not determine" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not determine" >&5
|
||||
$as_echo "$as_me: WARNING: could not determine" >&2;}
|
||||
SOCKLEN_T=int
|
||||
SOCKLEN_T=int
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
@@ -6588,20 +6611,6 @@ _ACEOF
|
||||
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether dir-browser snapshot workaround is needed" >&5
|
||||
$as_echo_n "checking whether dir-browser snapshot workaround is needed... " >&6; }
|
||||
if test "$target_vendor" == "apple"; then
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
|
||||
$as_echo "#define DIRBROWSER_SNAPSHOT 1" >>confdefs.h
|
||||
|
||||
else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
$as_echo "no" >&6; }
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for cpu cores via sysconf" >&5
|
||||
$as_echo_n "checking for cpu cores via sysconf... " >&6; }
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
@@ -6628,11 +6637,23 @@ fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libxml2" >&5
|
||||
$as_echo_n "checking whether to use libxml2... " >&6; }
|
||||
# Check whether --enable-libxml2 was given.
|
||||
if test "${enable_libxml2+set}" = set; then :
|
||||
enableval=$enable_libxml2; USELIBXML2=$enableval
|
||||
else
|
||||
USELIBXML2=yes
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $USELIBXML2" >&5
|
||||
$as_echo "$USELIBXML2" >&6; }
|
||||
if test "$USELIBXML2" = "yes"; then
|
||||
|
||||
# Check whether --with-libxml2_includes was given.
|
||||
if test "${with_libxml2_includes+set}" = set; then :
|
||||
withval=$with_libxml2_includes; CPPFLAGS="${CPPFLAGS} -I${withval}"
|
||||
INCVAL="yes"
|
||||
INCVAL="yes"
|
||||
else
|
||||
INCVAL="no"
|
||||
fi
|
||||
@@ -6641,12 +6662,12 @@ fi
|
||||
# Check whether --with-libxml2_libraries was given.
|
||||
if test "${with_libxml2_libraries+set}" = set; then :
|
||||
withval=$with_libxml2_libraries; LDFLAGS="${LDFLAGS} -L${withval}"
|
||||
LIBVAL="yes"
|
||||
LIBVAL="yes"
|
||||
else
|
||||
LIBVAL="no"
|
||||
fi
|
||||
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
|
||||
|
||||
|
||||
@@ -6837,10 +6858,10 @@ else
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
$as_echo "yes" >&6; }
|
||||
LIBS="${LIBS} $libxml2_LIBS"
|
||||
CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"
|
||||
CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"
|
||||
fi
|
||||
fi
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "libxml/tree.h" "ac_cv_header_libxml_tree_h" "$ac_includes_default"
|
||||
fi
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "libxml/tree.h" "ac_cv_header_libxml_tree_h" "$ac_includes_default"
|
||||
if test "x$ac_cv_header_libxml_tree_h" = xyes; then :
|
||||
|
||||
else
|
||||
@@ -6848,7 +6869,7 @@ else
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing xmlNewNode" >&5
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing xmlNewNode" >&5
|
||||
$as_echo_n "checking for library containing xmlNewNode... " >&6; }
|
||||
if ${ac_cv_search_xmlNewNode+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
@@ -6906,6 +6927,11 @@ else
|
||||
as_fn_error $? "\"libxml2 library not found\"" "$LINENO" 5
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
$as_echo "#define DISABLE_LIBXML2 1" >>confdefs.h
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use curses" >&5
|
||||
@@ -7188,20 +7214,7 @@ fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ENABLEPARCHECK" >&5
|
||||
$as_echo "$ENABLEPARCHECK" >&6; }
|
||||
if test "$ENABLEPARCHECK" = "yes"; then
|
||||
for ac_header in endian.h getopt.h
|
||||
do :
|
||||
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
ac_fn_cxx_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
|
||||
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
ac_fn_cxx_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
|
||||
ac_fn_cxx_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
|
||||
if test "x$ac_cv_type_size_t" = xyes; then :
|
||||
|
||||
else
|
||||
@@ -7212,230 +7225,6 @@ _ACEOF
|
||||
|
||||
fi
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
|
||||
$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
|
||||
if ${ac_cv_c_bigendian+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
ac_cv_c_bigendian=unknown
|
||||
# See if we're dealing with a universal compiler.
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#ifndef __APPLE_CC__
|
||||
not a universal capable compiler
|
||||
#endif
|
||||
typedef int dummy;
|
||||
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
|
||||
# Check for potential -arch flags. It is not universal unless
|
||||
# there are at least two -arch flags with different values.
|
||||
ac_arch=
|
||||
ac_prev=
|
||||
for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do
|
||||
if test -n "$ac_prev"; then
|
||||
case $ac_word in
|
||||
i?86 | x86_64 | ppc | ppc64)
|
||||
if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then
|
||||
ac_arch=$ac_word
|
||||
else
|
||||
ac_cv_c_bigendian=universal
|
||||
break
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
ac_prev=
|
||||
elif test "x$ac_word" = "x-arch"; then
|
||||
ac_prev=arch
|
||||
fi
|
||||
done
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
if test $ac_cv_c_bigendian = unknown; then
|
||||
# See if sys/param.h defines the BYTE_ORDER macro.
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
|
||||
&& defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
|
||||
&& LITTLE_ENDIAN)
|
||||
bogus endian macros
|
||||
#endif
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
# It does; now see whether it defined to BIG_ENDIAN or not.
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
#if BYTE_ORDER != BIG_ENDIAN
|
||||
not big endian
|
||||
#endif
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
ac_cv_c_bigendian=yes
|
||||
else
|
||||
ac_cv_c_bigendian=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
if test $ac_cv_c_bigendian = unknown; then
|
||||
# See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <limits.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
|
||||
bogus endian macros
|
||||
#endif
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
# It does; now see whether it defined to _BIG_ENDIAN or not.
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <limits.h>
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
#ifndef _BIG_ENDIAN
|
||||
not big endian
|
||||
#endif
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
ac_cv_c_bigendian=yes
|
||||
else
|
||||
ac_cv_c_bigendian=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
fi
|
||||
if test $ac_cv_c_bigendian = unknown; then
|
||||
# Compile a test program.
|
||||
if test "$cross_compiling" = yes; then :
|
||||
# Try to guess by grepping values from an object file.
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
short int ascii_mm[] =
|
||||
{ 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
|
||||
short int ascii_ii[] =
|
||||
{ 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
|
||||
int use_ascii (int i) {
|
||||
return ascii_mm[i] + ascii_ii[i];
|
||||
}
|
||||
short int ebcdic_ii[] =
|
||||
{ 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
|
||||
short int ebcdic_mm[] =
|
||||
{ 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
|
||||
int use_ebcdic (int i) {
|
||||
return ebcdic_mm[i] + ebcdic_ii[i];
|
||||
}
|
||||
extern int foo;
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return use_ascii (foo) == use_ebcdic (foo);
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
|
||||
ac_cv_c_bigendian=yes
|
||||
fi
|
||||
if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then
|
||||
if test "$ac_cv_c_bigendian" = unknown; then
|
||||
ac_cv_c_bigendian=no
|
||||
else
|
||||
# finding both strings is unlikely to happen, but who knows?
|
||||
ac_cv_c_bigendian=unknown
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
else
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
$ac_includes_default
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
/* Are we little or big endian? From Harbison&Steele. */
|
||||
union
|
||||
{
|
||||
long int l;
|
||||
char c[sizeof (long int)];
|
||||
} u;
|
||||
u.l = 1;
|
||||
return u.c[sizeof (long int) - 1] == 1;
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_run "$LINENO"; then :
|
||||
ac_cv_c_bigendian=no
|
||||
else
|
||||
ac_cv_c_bigendian=yes
|
||||
fi
|
||||
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
|
||||
conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
fi
|
||||
|
||||
fi
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
|
||||
$as_echo "$ac_cv_c_bigendian" >&6; }
|
||||
case $ac_cv_c_bigendian in #(
|
||||
yes)
|
||||
$as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
|
||||
;; #(
|
||||
no)
|
||||
;; #(
|
||||
universal)
|
||||
|
||||
$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
|
||||
|
||||
;; #(
|
||||
*)
|
||||
as_fn_error $? "unknown endianness
|
||||
presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;;
|
||||
esac
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
|
||||
$as_echo_n "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
|
||||
if ${ac_cv_sys_largefile_source+:} false; then :
|
||||
@@ -8579,6 +8368,35 @@ $as_echo "#define DISABLE_GZIP 1" >>confdefs.h
|
||||
fi
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use SIMD-optimized routines" >&5
|
||||
$as_echo_n "checking whether to use SIMD-optimized routines... " >&6; }
|
||||
USE_SIMD=no
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
SSE2_CXXFLAGS="-msse2"
|
||||
SSSE3_CXXFLAGS="-mssse3"
|
||||
PCLMUL_CXXFLAGS="-msse4.1 -mpclmul"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
arm*)
|
||||
NEON_CXXFLAGS="-mfpu=neon"
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
aarch64)
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
esac
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_SIMD" >&5
|
||||
$as_echo "$USE_SIMD" >&6; }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use an empty SIGCHLD handler" >&5
|
||||
$as_echo_n "checking whether to use an empty SIGCHLD handler... " >&6; }
|
||||
# Check whether --enable-sigchld-handler was given.
|
||||
@@ -8932,7 +8750,6 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
|
||||
as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
|
||||
if test -z "${WITH_PAR2_TRUE}" && test -z "${WITH_PAR2_FALSE}"; then
|
||||
as_fn_error $? "conditional \"WITH_PAR2\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
@@ -9346,7 +9163,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by nzbget $as_me 20.0-testing, which was
|
||||
This file was extended by nzbget $as_me 21.2-testing, which was
|
||||
generated by GNU Autoconf 2.69. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -9412,7 +9229,7 @@ _ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
|
||||
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
|
||||
ac_cs_version="\\
|
||||
nzbget config.status 20.0-testing
|
||||
nzbget config.status 21.2-testing
|
||||
configured by $0, generated by GNU Autoconf 2.69,
|
||||
with options \\"\$ac_cs_config\\"
|
||||
|
||||
|
||||
127
configure.ac
127
configure.ac
@@ -1,7 +1,7 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
# Copyright (C) 2008-2021 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# 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
|
||||
@@ -21,7 +21,7 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.65)
|
||||
AC_INIT(nzbget, 20.0-testing, hugbug@users.sourceforge.net)
|
||||
AC_INIT(nzbget, 21.2-testing, hugbug@users.sourceforge.net)
|
||||
AC_CONFIG_AUX_DIR(posix)
|
||||
AC_CANONICAL_TARGET
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
@@ -65,8 +65,7 @@ fi
|
||||
dnl
|
||||
dnl Checks for header files.
|
||||
dnl
|
||||
AC_CHECK_HEADERS(sys/prctl.h)
|
||||
AC_CHECK_HEADERS(regex.h)
|
||||
AC_CHECK_HEADERS(sys/prctl.h regex.h endian.h getopt.h)
|
||||
|
||||
|
||||
dnl
|
||||
@@ -78,6 +77,19 @@ AC_SEARCH_LIBS([inet_addr], [nsl])
|
||||
AC_SEARCH_LIBS([hstrerror], [resolv])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Android NDK restrictions
|
||||
dnl
|
||||
AC_CHECK_FUNC(lockf,
|
||||
[AC_CHECK_DECL(lockf,
|
||||
[AC_DEFINE([HAVE_LOCKF], 1, [Define to 1 if lockf is supported])],,
|
||||
[#include <unistd.h>])])
|
||||
AC_CHECK_FUNC(pthread_cancel,
|
||||
[AC_CHECK_DECL(pthread_cancel,
|
||||
[AC_DEFINE([HAVE_PTHREAD_CANCEL], 1, [Define to 1 if pthread_cancel is supported])],,
|
||||
[#include <pthread.h>])])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Getopt
|
||||
dnl
|
||||
@@ -146,7 +158,7 @@ if test "$FOUND" = "no"; then
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
|
||||
@@ -196,32 +208,20 @@ AC_TRY_COMPILE([
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
|
||||
AC_MSG_RESULT(size_t)
|
||||
SOCKLEN_T=size_t],[
|
||||
AC_TRY_COMPILE([
|
||||
AC_MSG_RESULT(size_t)
|
||||
SOCKLEN_T=size_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
|
||||
AC_MSG_RESULT(int)
|
||||
SOCKLEN_T=int],[
|
||||
AC_MSG_WARN(could not determine)
|
||||
SOCKLEN_T=int])])])
|
||||
AC_MSG_RESULT(int)
|
||||
SOCKLEN_T=int],[
|
||||
AC_MSG_WARN(could not determine)
|
||||
SOCKLEN_T=int])])])
|
||||
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Dir-browser's snapshot
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether dir-browser snapshot workaround is needed)
|
||||
if test "$target_vendor" == "apple"; then
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([DIRBROWSER_SNAPSHOT], 1, [Define to 1 if deleting of files during reading of directory is not properly supported by OS])
|
||||
else
|
||||
AC_MSG_RESULT([[no]])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check cpu cores via sysconf
|
||||
dnl
|
||||
@@ -238,26 +238,36 @@ AC_TRY_COMPILE(
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libxml2, libxml-2.0,
|
||||
[LIBS="${LIBS} $libxml2_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
|
||||
AC_MSG_CHECKING(whether to use libxml2)
|
||||
AC_ARG_ENABLE(libxml2,
|
||||
[AS_HELP_STRING([--disable-libxml2], [do not use libxml2 (removes dependency from libxml2-library, only for development purposes)])],
|
||||
[USELIBXML2=$enableval],
|
||||
[USELIBXML2=yes] )
|
||||
AC_MSG_RESULT($USELIBXML2)
|
||||
if test "$USELIBXML2" = "yes"; then
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libxml2, libxml-2.0,
|
||||
[LIBS="${LIBS} $libxml2_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_LIBXML2],1,[Define to 1 to not use libxml2, only for development purposes])
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
|
||||
|
||||
dnl
|
||||
@@ -327,11 +337,8 @@ AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
if test "$ENABLEPARCHECK" = "yes"; then
|
||||
dnl PAR2 checks.
|
||||
dnl
|
||||
dnl Checks for header files.
|
||||
AC_CHECK_HEADERS([endian.h] [getopt.h])
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_C_BIGENDIAN
|
||||
AC_FUNC_FSEEKO
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS([stricmp])
|
||||
@@ -541,6 +548,36 @@ else
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Determine if CPU supports SIMD instructions
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use SIMD-optimized routines)
|
||||
USE_SIMD=no
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
SSE2_CXXFLAGS="-msse2"
|
||||
SSSE3_CXXFLAGS="-mssse3"
|
||||
PCLMUL_CXXFLAGS="-msse4.1 -mpclmul"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
arm*)
|
||||
NEON_CXXFLAGS="-mfpu=neon"
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
aarch64)
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT($USE_SIMD)
|
||||
AC_SUBST([SSE2_CXXFLAGS])
|
||||
AC_SUBST([SSSE3_CXXFLAGS])
|
||||
AC_SUBST([PCLMUL_CXXFLAGS])
|
||||
AC_SUBST([NEON_CXXFLAGS])
|
||||
AC_SUBST([ACLECRC_CXXFLAGS])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Some Linux systems require an empty signal handler for SIGCHLD
|
||||
dnl in order for exit codes to be correctly delivered to parent process.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
@@ -30,16 +31,12 @@ std::unique_ptr<Mutex> Connection::m_getHostByNameMutex;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class ConnectionFinalizer
|
||||
{
|
||||
public:
|
||||
~ConnectionFinalizer()
|
||||
{
|
||||
Connection::Final();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ConnectionFinalizer> m_connectionFinalizer;
|
||||
#if defined(__linux__) && !defined(__ANDROID__)
|
||||
// Activate DNS resolving workaround for Android:
|
||||
// - this is only necessary in general Linux build if we want it to run on Android.
|
||||
// - the workaround isn't needed when targeting specifically Android using Android NDK.
|
||||
#define ANDROID_RESOLVE
|
||||
#endif
|
||||
|
||||
void closesocket_gracefully(SOCKET socket)
|
||||
{
|
||||
@@ -79,6 +76,10 @@ void closesocket_gracefully(SOCKET socket)
|
||||
closesocket(socket);
|
||||
}
|
||||
|
||||
#ifdef ANDROID_RESOLVE
|
||||
CString ResolveAndroidHost(const char* host);
|
||||
#endif
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Initializing global connection data");
|
||||
@@ -104,8 +105,6 @@ void Connection::Init()
|
||||
m_getHostByNameMutex = std::make_unique<Mutex>();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
m_connectionFinalizer = std::make_unique<ConnectionFinalizer>();
|
||||
}
|
||||
|
||||
void Connection::Final()
|
||||
@@ -146,7 +145,7 @@ Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
Disconnect();
|
||||
Connection::Disconnect();
|
||||
}
|
||||
|
||||
void Connection::SetSuppressErrors(bool suppressErrors)
|
||||
@@ -210,6 +209,8 @@ bool Connection::Bind()
|
||||
return true;
|
||||
}
|
||||
|
||||
int errcode = 0;
|
||||
|
||||
#ifndef WIN32
|
||||
if (m_host && m_host[0] == '/')
|
||||
{
|
||||
@@ -246,7 +247,7 @@ bool Connection::Bind()
|
||||
|
||||
memset(&addr_hints, 0, sizeof(addr_hints));
|
||||
addr_hints.ai_family = m_ipVersion == ipV4 ? AF_INET : m_ipVersion == ipV6 ? AF_INET6 : AF_UNSPEC;
|
||||
addr_hints.ai_socktype = SOCK_STREAM,
|
||||
addr_hints.ai_socktype = SOCK_STREAM;
|
||||
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
|
||||
|
||||
BString<100> portStr("%d", m_port);
|
||||
@@ -263,7 +264,6 @@ bool Connection::Bind()
|
||||
return false;
|
||||
}
|
||||
|
||||
m_broken = false;
|
||||
m_socket = INVALID_SOCKET;
|
||||
for (addr = addr_list; addr != nullptr; addr = addr->ai_next)
|
||||
{
|
||||
@@ -282,6 +282,7 @@ bool Connection::Bind()
|
||||
break;
|
||||
}
|
||||
// Connection failed
|
||||
errcode = GetLastNetworkError();
|
||||
closesocket(m_socket);
|
||||
m_socket = INVALID_SOCKET;
|
||||
}
|
||||
@@ -322,6 +323,7 @@ bool Connection::Bind()
|
||||
if (res == -1)
|
||||
{
|
||||
// Connection failed
|
||||
errcode = GetLastNetworkError();
|
||||
closesocket(m_socket);
|
||||
m_socket = INVALID_SOCKET;
|
||||
}
|
||||
@@ -330,7 +332,7 @@ bool Connection::Bind()
|
||||
|
||||
if (m_socket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_host, true);
|
||||
ReportError("Binding socket failed for %s", m_host, true, errcode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -357,7 +359,7 @@ int Connection::WriteLine(const char* buffer)
|
||||
int res = send(m_socket, buffer, strlen(buffer), 0);
|
||||
if (res <= 0)
|
||||
{
|
||||
m_broken = true;
|
||||
m_status = csBroken;
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -378,7 +380,7 @@ bool Connection::Send(const char* buffer, int size)
|
||||
int res = send(m_socket, buffer + bytesSent, size-bytesSent, 0);
|
||||
if (res <= 0)
|
||||
{
|
||||
m_broken = true;
|
||||
m_status = csBroken;
|
||||
return false;
|
||||
}
|
||||
bytesSent += res;
|
||||
@@ -407,13 +409,17 @@ char* Connection::ReadLine(char* buffer, int size, int* bytesReadOut)
|
||||
if (bufAvail < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket from %s", m_host, true);
|
||||
m_broken = true;
|
||||
m_status = csBroken;
|
||||
break;
|
||||
}
|
||||
else if (bufAvail == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_totalBytesRead += bufAvail;
|
||||
}
|
||||
bufPtr = m_readBuf;
|
||||
m_readBuf[bufAvail] = '\0';
|
||||
}
|
||||
@@ -456,8 +462,6 @@ char* Connection::ReadLine(char* buffer, int size, int* bytesReadOut)
|
||||
*bytesReadOut = bytesRead;
|
||||
}
|
||||
|
||||
m_totalBytesRead += bytesRead;
|
||||
|
||||
if (inpBuffer == buffer)
|
||||
{
|
||||
return nullptr;
|
||||
@@ -492,7 +496,7 @@ std::unique_ptr<Connection> Connection::Accept()
|
||||
|
||||
int Connection::TryRecv(char* buffer, int size)
|
||||
{
|
||||
debug("Receiving data");
|
||||
//debug("Receiving data");
|
||||
|
||||
memset(buffer, 0, size);
|
||||
|
||||
@@ -502,13 +506,17 @@ int Connection::TryRecv(char* buffer, int size)
|
||||
{
|
||||
ReportError("Could not receive data on socket from %s", m_host, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_totalBytesRead += received;
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
bool Connection::Recv(char * buffer, int size)
|
||||
{
|
||||
debug("Receiving data (full buffer)");
|
||||
//debug("Receiving data (full buffer)");
|
||||
|
||||
memset(buffer, 0, size);
|
||||
|
||||
@@ -537,6 +545,7 @@ bool Connection::Recv(char * buffer, int size)
|
||||
}
|
||||
bufPtr += received;
|
||||
NeedBytes -= received;
|
||||
m_totalBytesRead += received;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -546,7 +555,6 @@ bool Connection::DoConnect()
|
||||
debug("Do connecting");
|
||||
|
||||
m_socket = INVALID_SOCKET;
|
||||
m_broken = false;
|
||||
|
||||
#ifndef WIN32
|
||||
if (m_host && m_host[0] == '/')
|
||||
@@ -588,6 +596,19 @@ bool Connection::DoConnect()
|
||||
BString<100> portStr("%d", m_port);
|
||||
|
||||
int res = getaddrinfo(m_host, portStr, &addr_hints, &addr_list);
|
||||
debug("getaddrinfo for %s: %i", *m_host, res);
|
||||
|
||||
#ifdef ANDROID_RESOLVE
|
||||
if (res != 0)
|
||||
{
|
||||
CString resolvedHost = ResolveAndroidHost(m_host);
|
||||
if (!resolvedHost.Empty())
|
||||
{
|
||||
res = getaddrinfo(resolvedHost, portStr, &addr_hints, &addr_list);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (res != 0)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", m_host, true
|
||||
@@ -768,18 +789,15 @@ bool Connection::ConnectWithTimeout(void* address, int address_len)
|
||||
ret = connect(m_socket, (struct sockaddr*)address, address_len);
|
||||
if (ret < 0)
|
||||
{
|
||||
int err = GetLastNetworkError();
|
||||
#ifdef WIN32
|
||||
int err = WSAGetLastError();
|
||||
if (err != WSAEWOULDBLOCK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (errno != EINPROGRESS)
|
||||
if (err != EINPROGRESS)
|
||||
#endif
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//connect succeeded right away?
|
||||
@@ -883,10 +901,9 @@ void Connection::Cancel()
|
||||
debug("Cancelling connection");
|
||||
if (m_socket != INVALID_SOCKET)
|
||||
{
|
||||
EStatus status = m_status;
|
||||
m_status = csCancelled;
|
||||
|
||||
if (status == csListening)
|
||||
if (m_forceClose)
|
||||
{
|
||||
SOCKET socket = m_socket;
|
||||
m_socket = INVALID_SOCKET;
|
||||
@@ -900,7 +917,16 @@ void Connection::Cancel()
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* msgPrefix, const char* msgArg, bool PrintErrCode, int herrno, const char* herrMsg)
|
||||
int Connection::GetLastNetworkError()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* msgPrefix, const char* msgArg, bool printErrCode, int errCode, const char* errMsg)
|
||||
{
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_tlsError)
|
||||
@@ -913,34 +939,34 @@ void Connection::ReportError(const char* msgPrefix, const char* msgArg, bool Pri
|
||||
|
||||
BString<1024> errPrefix(msgPrefix, msgArg);
|
||||
|
||||
if (PrintErrCode)
|
||||
if (printErrCode)
|
||||
{
|
||||
#ifdef WIN32
|
||||
int ErrCode = WSAGetLastError();
|
||||
char errMsg[1024];
|
||||
errMsg[0] = '\0';
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, ErrCode, 0, errMsg, 1024, nullptr);
|
||||
errMsg[1024-1] = '\0';
|
||||
#else
|
||||
const char* errMsg = herrMsg;
|
||||
int ErrCode = herrno;
|
||||
if (herrno == 0)
|
||||
BString<1024> printErrMsg;
|
||||
if (errCode == 0)
|
||||
{
|
||||
ErrCode = errno;
|
||||
errMsg = strerror(ErrCode);
|
||||
errCode = GetLastNetworkError();
|
||||
}
|
||||
else if (!herrMsg)
|
||||
if (errMsg)
|
||||
{
|
||||
errMsg = hstrerror(ErrCode);
|
||||
}
|
||||
#endif
|
||||
if (m_suppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", *errPrefix, ErrCode, errMsg);
|
||||
printErrMsg = errMsg;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(BString<1024>("%s: ErrNo %i, %s", *errPrefix, ErrCode, errMsg));
|
||||
#ifdef WIN32
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, errCode, 0, printErrMsg, printErrMsg.Capacity(), nullptr);
|
||||
printErrMsg[1024-1] = '\0';
|
||||
#else
|
||||
printErrMsg = strerror(errCode);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (m_suppressErrors)
|
||||
{
|
||||
debug("%s: Error %i - %s", *errPrefix, errCode, (const char*)printErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(BString<1024>("%s: Error %i - %s", *errPrefix, errCode, (const char*)printErrMsg));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1058,7 +1084,7 @@ in_addr_t Connection::ResolveHostAddr(const char* host)
|
||||
#endif
|
||||
if (err)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", host, true, h_errnop);
|
||||
ReportError("Could not resolve hostname %s", host, true, h_errnop, hstrerror(h_errnop));
|
||||
return INADDR_NONE;
|
||||
}
|
||||
|
||||
@@ -1091,7 +1117,10 @@ const char* Connection::GetRemoteAddr()
|
||||
inet_ntop_t* inet_ntop = (inet_ntop_t*)GetProcAddress(module, "inet_ntop");
|
||||
if (inet_ntop)
|
||||
{
|
||||
inet_ntop(((sockaddr_in*)&peerName)->sin_family, &((sockaddr_in*)&peerName)->sin_addr,
|
||||
inet_ntop(((sockaddr_in*)&peerName)->sin_family,
|
||||
((sockaddr_in*)&peerName)->sin_family == AF_INET6 ?
|
||||
(void*)&((sockaddr_in6*)&peerName)->sin6_addr :
|
||||
(void*)&((sockaddr_in*)&peerName)->sin_addr,
|
||||
m_remoteAddr, m_remoteAddr.Capacity());
|
||||
}
|
||||
FreeLibrary(module);
|
||||
@@ -1101,7 +1130,10 @@ const char* Connection::GetRemoteAddr()
|
||||
m_remoteAddr = inet_ntoa(((sockaddr_in*)&peerName)->sin_addr);
|
||||
}
|
||||
#else
|
||||
inet_ntop(((sockaddr_in*)&peerName)->sin_family, &((sockaddr_in*)&peerName)->sin_addr,
|
||||
inet_ntop(((sockaddr_in*)&peerName)->sin_family,
|
||||
((sockaddr_in*)&peerName)->sin_family == AF_INET6 ?
|
||||
(void*)&((sockaddr_in6*)&peerName)->sin6_addr :
|
||||
(void*)&((sockaddr_in*)&peerName)->sin_addr,
|
||||
m_remoteAddr, m_remoteAddr.Capacity());
|
||||
#endif
|
||||
m_remoteAddr[m_remoteAddr.Capacity() - 1] = '\0';
|
||||
@@ -1116,3 +1148,210 @@ int Connection::FetchTotalBytesRead()
|
||||
m_totalBytesRead = 0;
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
#ifdef ANDROID_RESOLVE
|
||||
|
||||
//******************************************************************************
|
||||
// Android resolver proxy from AOSP (reworked):
|
||||
// https://github.com/aosp-mirror/platform_bionic/blob/6c1d23f059986e4ee6f52c44a4944089aae9181d/libc/dns/net/gethnamaddr.c
|
||||
|
||||
#define MAXALIASES 35
|
||||
#define MAXADDRS 35
|
||||
#define ALIGNBYTES (sizeof(uintptr_t) - 1)
|
||||
#define ALIGN(p) (((uintptr_t)(p) + ALIGNBYTES) &~ ALIGNBYTES)
|
||||
|
||||
// This should be synchronized to ResponseCode.h
|
||||
static const int DnsProxyQueryResult = 222;
|
||||
|
||||
FILE* android_open_proxy()
|
||||
{
|
||||
const char* cache_mode = getenv("ANDROID_DNS_MODE");
|
||||
bool use_proxy = (cache_mode == nullptr || strcmp(cache_mode, "local") != 0);
|
||||
if (!use_proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int s = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (s == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int one = 1;
|
||||
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
|
||||
struct sockaddr_un proxy_addr;
|
||||
memset(&proxy_addr, 0, sizeof(proxy_addr));
|
||||
proxy_addr.sun_family = AF_UNIX;
|
||||
strncpy(proxy_addr.sun_path, "/dev/socket/dnsproxyd", sizeof(proxy_addr.sun_path));
|
||||
|
||||
if (connect(s, (const struct sockaddr*) &proxy_addr, sizeof(proxy_addr)) != 0) {
|
||||
close(s);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return fdopen(s, "r+");
|
||||
}
|
||||
|
||||
static struct hostent * android_read_hostent(FILE* proxy, struct hostent* hp, char* hbuf, size_t hbuflen)
|
||||
{
|
||||
uint32_t size;
|
||||
char buf[4];
|
||||
if (fread(buf, 1, sizeof(buf), proxy) != sizeof(buf)) return nullptr;
|
||||
|
||||
// This is reading serialized data from system/netd/server/DnsProxyListener.cpp
|
||||
// and changes here need to be matched there.
|
||||
int result_code = strtol(buf, nullptr, 10);
|
||||
if (result_code != DnsProxyQueryResult) {
|
||||
fread(&size, 1, sizeof(size), proxy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return nullptr;
|
||||
size = ntohl(size);
|
||||
|
||||
memset(hp, 0, sizeof(*hp));
|
||||
char *ptr = hbuf;
|
||||
char *hbuf_end = hbuf + hbuflen;
|
||||
|
||||
if (ptr + size > hbuf_end) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fread(ptr, 1, size, proxy) != size) return nullptr;
|
||||
hp->h_name = ptr;
|
||||
ptr += size;
|
||||
|
||||
char *aliases_ptrs[MAXALIASES];
|
||||
char **aliases = &aliases_ptrs[0];
|
||||
|
||||
while (1) {
|
||||
if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return nullptr;
|
||||
size = ntohl(size);
|
||||
|
||||
if (size == 0) {
|
||||
*aliases = nullptr;
|
||||
break;
|
||||
}
|
||||
if (ptr + size > hbuf_end) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fread(ptr, 1, size, proxy) != size) return nullptr;
|
||||
if (aliases < &aliases_ptrs[MAXALIASES - 1]) {
|
||||
*aliases++ = ptr;
|
||||
}
|
||||
ptr += size;
|
||||
}
|
||||
|
||||
// Fix alignment after variable-length data.
|
||||
ptr = (char*)ALIGN(ptr);
|
||||
|
||||
int aliases_len = ((int)(aliases - aliases_ptrs) + 1) * sizeof(*hp->h_aliases);
|
||||
if (ptr + aliases_len > hbuf_end) {
|
||||
return nullptr;
|
||||
}
|
||||
hp->h_aliases = (char**)ptr;
|
||||
memcpy(ptr, aliases_ptrs, aliases_len);
|
||||
ptr += aliases_len;
|
||||
|
||||
if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return nullptr;
|
||||
hp->h_addrtype = ntohl(size);
|
||||
|
||||
if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return nullptr;
|
||||
hp->h_length = ntohl(size);
|
||||
|
||||
char *addr_ptrs[MAXADDRS];
|
||||
char **addr_p = &addr_ptrs[0];
|
||||
|
||||
while (1) {
|
||||
if (fread(&size, 1, sizeof(size), proxy) != sizeof(size)) return nullptr;
|
||||
size = ntohl(size);
|
||||
if (size == 0) {
|
||||
*addr_p = nullptr;
|
||||
break;
|
||||
}
|
||||
if (ptr + size > hbuf_end) {
|
||||
return nullptr;
|
||||
}
|
||||
if (fread(ptr, 1, size, proxy) != size) return nullptr;
|
||||
if (addr_p < &addr_ptrs[MAXADDRS - 1]) {
|
||||
*addr_p++ = ptr;
|
||||
}
|
||||
ptr += size;
|
||||
}
|
||||
|
||||
// Fix alignment after variable-length data.
|
||||
ptr = (char*)ALIGN(ptr);
|
||||
|
||||
int addrs_len = ((int)(addr_p - addr_ptrs) + 1) * sizeof(*hp->h_addr_list);
|
||||
if (ptr + addrs_len > hbuf_end) {
|
||||
return nullptr;
|
||||
}
|
||||
hp->h_addr_list = (char**)ptr;
|
||||
memcpy(ptr, addr_ptrs, addrs_len);
|
||||
return hp;
|
||||
}
|
||||
|
||||
// very similar in proxy-ness to android_getaddrinfo_proxy
|
||||
static struct hostent * android_gethostbyname_internal(const char *name, int af,
|
||||
struct hostent *hp, char *hbuf, size_t hbuflen)
|
||||
{
|
||||
FILE* proxy = android_open_proxy();
|
||||
if (proxy == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This is writing to system/netd/server/DnsProxyListener.cpp and changes
|
||||
// here need to be matched there.
|
||||
if (fprintf(proxy, "gethostbyname %s %s %d",
|
||||
"^",
|
||||
name == nullptr ? "^" : name,
|
||||
af) < 0) {
|
||||
fclose(proxy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (fputc(0, proxy) == EOF || fflush(proxy) != 0) {
|
||||
fclose(proxy);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct hostent* result = android_read_hostent(proxy, hp, hbuf, hbuflen);
|
||||
fclose(proxy);
|
||||
return result;
|
||||
}
|
||||
|
||||
CString ResolveAndroidHost(const char* host)
|
||||
{
|
||||
debug("ResolveAndroidHost");
|
||||
|
||||
bool android = FileSystem::DirectoryExists("/data/dalvik-cache");
|
||||
if (!android)
|
||||
{
|
||||
debug("Not android");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct hostent hinfobuf;
|
||||
char strbuf[1024];
|
||||
|
||||
struct hostent* hinfo = android_gethostbyname_internal(host, AF_INET, &hinfobuf, strbuf, sizeof(strbuf));
|
||||
if (hinfo == nullptr)
|
||||
{
|
||||
// trying IPv6
|
||||
hinfo = android_gethostbyname_internal(host, AF_INET6, &hinfobuf, strbuf, sizeof(strbuf));
|
||||
}
|
||||
|
||||
if (hinfo == nullptr)
|
||||
{
|
||||
debug("android_gethostbyname_r failed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BString<1024> result;
|
||||
inet_ntop(hinfo->h_addrtype, hinfo->h_addr_list[0], result, result.Capacity());
|
||||
|
||||
debug("android_gethostbyname_r returned %s", *result);
|
||||
return *result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled
|
||||
csCancelled,
|
||||
csBroken
|
||||
};
|
||||
|
||||
enum EIPVersion
|
||||
@@ -55,6 +56,7 @@ public:
|
||||
Connection(SOCKET socket, bool tls);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
virtual bool Connect();
|
||||
virtual bool Disconnect();
|
||||
bool Bind();
|
||||
@@ -79,6 +81,7 @@ public:
|
||||
const char* GetRemoteAddr();
|
||||
bool GetGracefull() { return m_gracefull; }
|
||||
void SetGracefull(bool gracefull) { m_gracefull = gracefull; }
|
||||
void SetForceClose(bool forceClose) { m_forceClose = forceClose; }
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTls(bool isClient, const char* certFile, const char* keyFile);
|
||||
#endif
|
||||
@@ -99,8 +102,8 @@ protected:
|
||||
bool m_suppressErrors = true;
|
||||
BString<100> m_remoteAddr;
|
||||
int m_totalBytesRead = 0;
|
||||
bool m_broken = false;
|
||||
bool m_gracefull = false;
|
||||
bool m_forceClose = false;
|
||||
|
||||
struct SockAddr
|
||||
{
|
||||
@@ -133,9 +136,10 @@ protected:
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ReportError(const char* msgPrefix, const char* msgArg, bool PrintErrCode, int herrno = 0,
|
||||
const char* herrMsg = nullptr);
|
||||
void ReportError(const char* msgPrefix, const char* msgArg, bool printErrCode, int errCode = 0,
|
||||
const char* errMsg = nullptr);
|
||||
virtual void PrintError(const char* errMsg);
|
||||
int GetLastNetworkError();
|
||||
bool DoConnect();
|
||||
bool DoDisconnect();
|
||||
bool InitSocketOpts(SOCKET socket);
|
||||
@@ -148,10 +152,6 @@ protected:
|
||||
int send(SOCKET s, const char* buf, int len, int flags);
|
||||
void CloseTls();
|
||||
#endif
|
||||
|
||||
private:
|
||||
static void Final();
|
||||
friend class ConnectionFinalizer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,16 +28,6 @@
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
class TlsSocketFinalizer
|
||||
{
|
||||
public:
|
||||
~TlsSocketFinalizer()
|
||||
{
|
||||
TlsSocket::Final();
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<TlsSocketFinalizer> m_tlsSocketFinalizer;
|
||||
CString TlsSocket::m_certStore;
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
@@ -189,8 +179,6 @@ void TlsSocket::Init()
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_tlsSocketFinalizer = std::make_unique<TlsSocketFinalizer>();
|
||||
}
|
||||
|
||||
void TlsSocket::Final()
|
||||
@@ -200,19 +188,25 @@ void TlsSocket::Final()
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
FIPS_mode_set(0);
|
||||
#endif
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
CRYPTO_set_locking_callback(nullptr);
|
||||
CRYPTO_set_id_callback(nullptr);
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && ! defined (LIBRESSL_VERSION_NUMBER)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
//ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
#ifndef OPENSSL_NO_COMP
|
||||
COMP_zlib_cleanup();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
@@ -224,8 +218,10 @@ TlsSocket::~TlsSocket()
|
||||
Close();
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void TlsSocket::ReportError(const char* errMsg, bool suppressable)
|
||||
@@ -569,7 +565,7 @@ bool TlsSocket::ValidateCert()
|
||||
// hostname verification
|
||||
if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
|
||||
{
|
||||
char* certHost = nullptr;
|
||||
const unsigned char* certHost = nullptr;
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
|
||||
if (common_name_loc >= 0)
|
||||
@@ -582,7 +578,11 @@ bool TlsSocket::ValidateCert()
|
||||
ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
|
||||
if (common_name_asn1 != nullptr)
|
||||
{
|
||||
certHost = (char*)ASN1_STRING_data(common_name_asn1);
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
certHost = ASN1_STRING_get0_data(common_name_asn1);
|
||||
#else
|
||||
certHost = ASN1_STRING_data(common_name_asn1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
virtual ~TlsSocket();
|
||||
static void Init();
|
||||
static void InitOptions(const char* certStore) { m_certStore = certStore; }
|
||||
static void Final();
|
||||
bool Start();
|
||||
void Close();
|
||||
int Send(const char* buffer, int size);
|
||||
@@ -44,12 +45,12 @@ protected:
|
||||
virtual void PrintError(const char* errMsg);
|
||||
|
||||
private:
|
||||
SOCKET m_socket;
|
||||
bool m_isClient;
|
||||
CString m_host;
|
||||
CString m_certFile;
|
||||
CString m_keyFile;
|
||||
CString m_cipher;
|
||||
SOCKET m_socket;
|
||||
bool m_suppressErrors = false;
|
||||
bool m_initialized = false;
|
||||
bool m_connected = false;
|
||||
@@ -62,9 +63,6 @@ private:
|
||||
|
||||
void ReportError(const char* errMsg, bool suppressable = true);
|
||||
bool ValidateCert();
|
||||
|
||||
static void Final();
|
||||
friend class TlsSocketFinalizer;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
@@ -72,19 +73,19 @@ void WebDownloader::Run()
|
||||
|
||||
if ((((Status == adFailed) && (remainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (remainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(!m_force && g_Options->GetPauseDownload()))
|
||||
&& !IsStopped() && !(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
|
||||
!(!m_force && g_Options->GetPauseDownload()))
|
||||
!(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || (!m_force && g_Options->GetPauseDownload()))
|
||||
if (IsStopped() || (!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
@@ -650,21 +651,6 @@ void WebDownloader::Stop()
|
||||
debug("WebDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool WebDownloader::Terminate()
|
||||
{
|
||||
std::unique_ptr<Connection> connection = std::move(m_connection);
|
||||
bool terminated = Kill();
|
||||
if (terminated && connection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
connection->SetSuppressErrors(true);
|
||||
connection->Cancel();
|
||||
connection->Disconnect();
|
||||
connection.reset();
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void WebDownloader::FreeConnection()
|
||||
{
|
||||
if (m_connection)
|
||||
|
||||
@@ -50,7 +50,6 @@ public:
|
||||
virtual void Stop();
|
||||
EStatus Download();
|
||||
EStatus DownloadWithRedirects(int maxRedirects);
|
||||
bool Terminate();
|
||||
void SetInfoName(const char* infoName) { m_infoName = infoName; }
|
||||
const char* GetInfoName() { return m_infoName; }
|
||||
void SetUrl(const char* url);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
|
||||
static const int POSTPROCESS_PARCHECK = 92;
|
||||
static const int POSTPROCESS_SUCCESS = 93;
|
||||
@@ -272,16 +273,16 @@ void PostScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
m_postInfo->SetProgressLabel(text);
|
||||
}
|
||||
|
||||
if (g_Options->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
|
||||
if (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
|
||||
{
|
||||
time_t stageTime = m_postInfo->GetStageTime();
|
||||
time_t startTime = m_postInfo->GetStartTime();
|
||||
time_t waitTime = Util::CurrentTime();
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_Options->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
while (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
|
||||
// update time stamps
|
||||
|
||||
|
||||
@@ -409,11 +409,10 @@ void QueueScriptCoordinator::CheckQueue()
|
||||
return;
|
||||
}
|
||||
|
||||
m_curItem.reset();
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
m_curItem.reset();
|
||||
NzbInfo* curNzbInfo = nullptr;
|
||||
Queue::iterator itCurItem;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "FeedCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
@@ -29,6 +30,7 @@
|
||||
#include "FeedScript.h"
|
||||
#include "DiskState.h"
|
||||
#include "DupeCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
|
||||
std::unique_ptr<RegEx>& FeedCoordinator::FilterHelper::GetRegEx(int id)
|
||||
{
|
||||
@@ -65,6 +67,9 @@ FeedCoordinator::FeedCoordinator()
|
||||
|
||||
m_downloadQueueObserver.m_owner = this;
|
||||
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
|
||||
|
||||
m_workStateObserver.m_owner = this;
|
||||
g_WorkState->Attach(&m_workStateObserver);
|
||||
}
|
||||
|
||||
FeedCoordinator::~FeedCoordinator()
|
||||
@@ -84,69 +89,77 @@ void FeedCoordinator::Run()
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
usleep(20 * 1000);
|
||||
Util::Sleep(20);
|
||||
}
|
||||
|
||||
if (g_Options->GetServerMode() && g_Options->GetSaveQueue() && g_Options->GetReloadQueue())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
g_DiskState->LoadFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
|
||||
int sleepInterval = 100;
|
||||
int updateCounter = 0;
|
||||
int cleanupCounter = 60000;
|
||||
|
||||
time_t lastCleanup = 0;
|
||||
while (!IsStopped())
|
||||
{
|
||||
usleep(sleepInterval * 1000);
|
||||
|
||||
updateCounter += sleepInterval;
|
||||
if (updateCounter >= 1000)
|
||||
// this code should not be called too often, once per second is OK
|
||||
if (!g_WorkState->GetPauseDownload() || m_force || g_Options->GetUrlForce())
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
if (!g_Options->GetPauseDownload() || m_force || g_Options->GetUrlForce())
|
||||
time_t current = Util::CurrentTime();
|
||||
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t current = Util::CurrentTime();
|
||||
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
|
||||
m_force = false;
|
||||
// check feed list and update feeds
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
m_force = false;
|
||||
// check feed list and update feeds
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
if (((feedInfo->GetInterval() > 0 &&
|
||||
(feedInfo->GetNextUpdate() == 0 ||
|
||||
current >= feedInfo->GetNextUpdate() ||
|
||||
current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
|
||||
feedInfo->GetFetch()) &&
|
||||
feedInfo->GetStatus() != FeedInfo::fsRunning)
|
||||
{
|
||||
if (((feedInfo->GetInterval() > 0 &&
|
||||
(feedInfo->GetNextUpdate() == 0 ||
|
||||
current >= feedInfo->GetNextUpdate() ||
|
||||
current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
|
||||
feedInfo->GetFetch()) &&
|
||||
feedInfo->GetStatus() != FeedInfo::fsRunning)
|
||||
{
|
||||
StartFeedDownload(feedInfo, feedInfo->GetFetch());
|
||||
}
|
||||
else if (feedInfo->GetFetch())
|
||||
{
|
||||
m_force = true;
|
||||
}
|
||||
StartFeedDownload(feedInfo, feedInfo->GetFetch());
|
||||
}
|
||||
else if (feedInfo->GetFetch())
|
||||
{
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckSaveFeeds();
|
||||
ResetHangingDownloads();
|
||||
updateCounter = 0;
|
||||
}
|
||||
|
||||
cleanupCounter += sleepInterval;
|
||||
if (cleanupCounter >= 60000)
|
||||
CheckSaveFeeds();
|
||||
ResetHangingDownloads();
|
||||
|
||||
if (std::abs(Util::CurrentTime() - lastCleanup) >= 60)
|
||||
{
|
||||
// clean up feed history once a minute
|
||||
CleanupHistory();
|
||||
CleanupCache();
|
||||
CheckSaveFeeds();
|
||||
cleanupCounter = 0;
|
||||
lastCleanup = Util::CurrentTime();
|
||||
}
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_force)
|
||||
{
|
||||
// don't sleep too long if there active feeds scheduled for redownload
|
||||
m_waitCond.WaitFor(m_downloadsMutex, 1000, [&]{ return IsStopped(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// no active jobs, we can sleep longer:
|
||||
// - if option "UrlForce" is active or if the feed list is empty we need to wake up
|
||||
// only when a new feed preview is requested. We could wait indefinitely for that
|
||||
// but we need to do some job every now and then and therefore we sleep only 60 seconds.
|
||||
// - if option "UrlForce" is disabled we need also to wake up when state "DownloadPaused"
|
||||
// is changed. We detect this via notification from 'WorkState'. However such
|
||||
// notifications are not 100% reliable due to possible race conditions. Therefore
|
||||
// we sleep for max. 5 seconds.
|
||||
int waitInterval = g_Options->GetUrlForce() || m_feeds.empty() ? 60000 : 5000;
|
||||
m_waitCond.WaitFor(m_downloadsMutex, waitInterval, [&]{ return m_force || IsStopped(); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +173,7 @@ void FeedCoordinator::Run()
|
||||
completed = m_activeDownloads.size() == 0;
|
||||
}
|
||||
CheckSaveFeeds();
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("FeedCoordinator: Downloads are completed");
|
||||
@@ -179,12 +192,20 @@ void FeedCoordinator::Stop()
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
debug("UrlDownloads are notified");
|
||||
|
||||
// Resume Run() to exit it
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::WorkStateUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int timeout = g_Options->GetTerminateTimeout();
|
||||
if (timeout == 0)
|
||||
if (g_Options->GetUrlTimeout() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -192,31 +213,15 @@ void FeedCoordinator::ResetHangingDownloads()
|
||||
Guard guard(m_downloadsMutex);
|
||||
time_t tm = Util::CurrentTime();
|
||||
|
||||
m_activeDownloads.erase(std::remove_if(m_activeDownloads.begin(), m_activeDownloads.end(),
|
||||
[timeout, tm](FeedDownloader* feedDownloader)
|
||||
for (FeedDownloader* feedDownloader: m_activeDownloads)
|
||||
{
|
||||
if (tm - feedDownloader->GetLastUpdateTime() > g_Options->GetUrlTimeout() + 10 &&
|
||||
feedDownloader->GetStatus() == FeedDownloader::adRunning)
|
||||
{
|
||||
if (tm - feedDownloader->GetLastUpdateTime() > timeout &&
|
||||
feedDownloader->GetStatus() == FeedDownloader::adRunning)
|
||||
{
|
||||
debug("Terminating hanging download %s", feedDownloader->GetInfoName());
|
||||
if (feedDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", feedDownloader->GetInfoName());
|
||||
feedDownloader->GetFeedInfo()->SetStatus(FeedInfo::fsUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", feedDownloader->GetInfoName());
|
||||
}
|
||||
|
||||
// it's not safe to destroy feedDownloader, because the state of object is unknown
|
||||
delete feedDownloader;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_activeDownloads.end());
|
||||
error("Cancelling hanging feed download %s", feedDownloader->GetInfoName());
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::LogDebugInfo()
|
||||
@@ -325,12 +330,10 @@ void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
|
||||
m_save = true;
|
||||
}
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
for (std::unique_ptr<NzbInfo>& nzbInfo : addedNzbs)
|
||||
{
|
||||
downloadQueue->GetQueue()->Add(std::move(nzbInfo));
|
||||
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), false);
|
||||
}
|
||||
downloadQueue->Save();
|
||||
}
|
||||
feedInfo->SetStatus(FeedInfo::fsFinished);
|
||||
}
|
||||
@@ -472,6 +475,9 @@ std::unique_ptr<NzbInfo> FeedCoordinator::CreateNzbInfo(FeedInfo* feedInfo, Feed
|
||||
nzbInfo->SetDupeKey(feedItemInfo.GetDupeKey());
|
||||
nzbInfo->SetDupeScore(feedItemInfo.GetDupeScore());
|
||||
nzbInfo->SetDupeMode(feedItemInfo.GetDupeMode());
|
||||
nzbInfo->SetSize(feedItemInfo.GetSize());
|
||||
nzbInfo->SetMinTime(feedItemInfo.GetTime());
|
||||
nzbInfo->SetMaxTime(feedItemInfo.GetTime());
|
||||
|
||||
return nzbInfo;
|
||||
}
|
||||
@@ -537,12 +543,15 @@ std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
|
||||
}
|
||||
|
||||
StartFeedDownload(feedInfo.get(), true);
|
||||
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
// wait until the download in a separate thread completes
|
||||
while (feedInfo->GetStatus() == FeedInfo::fsRunning)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
|
||||
// now can process the feed
|
||||
@@ -601,6 +610,8 @@ void FeedCoordinator::FetchFeed(int id)
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
|
||||
@@ -615,7 +626,7 @@ std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
|
||||
error("Feed file %s kept for troubleshooting (will be deleted on next successful feed fetch)", feedInfo->GetOutputFilename());
|
||||
feedFile.reset();
|
||||
}
|
||||
return std::move(feedFile);
|
||||
return feedFile;
|
||||
}
|
||||
|
||||
void FeedCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
|
||||
@@ -647,11 +658,11 @@ bool FeedCoordinator::HasActiveDownloads()
|
||||
|
||||
void FeedCoordinator::CheckSaveFeeds()
|
||||
{
|
||||
debug("CheckSaveFeeds");
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_save)
|
||||
{
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
debug("CheckSaveFeeds: save");
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->SaveFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -67,6 +67,13 @@ private:
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class WorkStateObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class FeedCacheItem
|
||||
{
|
||||
public:
|
||||
@@ -106,9 +113,12 @@ private:
|
||||
FeedHistory m_feedHistory;
|
||||
Mutex m_downloadsMutex;
|
||||
DownloadQueueObserver m_downloadQueueObserver;
|
||||
WorkStateObserver m_workStateObserver;
|
||||
bool m_force = false;
|
||||
bool m_save = false;
|
||||
FeedCache m_feedCache;
|
||||
ConditionVar m_waitCond;
|
||||
bool m_wokenUp = false;
|
||||
|
||||
void StartFeedDownload(FeedInfo* feedInfo, bool force);
|
||||
void FeedCompleted(FeedDownloader* feedDownloader);
|
||||
@@ -122,6 +132,7 @@ private:
|
||||
void CheckSaveFeeds();
|
||||
std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
|
||||
void SchedulerNextUpdate(FeedInfo* feedInfo, bool success);
|
||||
void WorkStateUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
extern FeedCoordinator* g_FeedCoordinator;
|
||||
|
||||
@@ -360,6 +360,10 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
|
||||
|
||||
bool FeedFile::Parse()
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
error("Could not parse rss feed, program was compiled without libxml2 support");
|
||||
return false;
|
||||
#else
|
||||
xmlSAXHandler SAX_handler = {0};
|
||||
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
|
||||
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
|
||||
@@ -378,6 +382,7 @@ bool FeedFile::Parse()
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::Parse_StartElement(const char *name, const char **atts)
|
||||
@@ -566,7 +571,11 @@ void FeedFile::SAX_characters(FeedFile* file, const char * xmlstr, int len)
|
||||
|
||||
void* FeedFile::SAX_getEntity(FeedFile* file, const char * name)
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
void* e = nullptr;
|
||||
#else
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
#endif
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
|
||||
@@ -51,7 +51,7 @@ bool FeedFilter::Term::MatchValue(const char* strValue, int64 intValue)
|
||||
|
||||
if (m_command < fcEqual && !strValue)
|
||||
{
|
||||
intBuf.Format("%lld", intValue);
|
||||
intBuf.Format("%" PRId64, intValue);
|
||||
strValue = intBuf;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@ private:
|
||||
int m_id;
|
||||
CString m_name;
|
||||
CString m_url;
|
||||
bool m_backlog;
|
||||
int m_interval;
|
||||
CString m_filter;
|
||||
uint32 m_filterHash;
|
||||
@@ -87,7 +88,6 @@ private:
|
||||
CString m_outputFilename;
|
||||
bool m_fetch = false;
|
||||
bool m_force = false;
|
||||
bool m_backlog;
|
||||
};
|
||||
|
||||
typedef std::deque<std::unique_ptr<FeedInfo>> Feeds;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
@@ -33,9 +34,24 @@ Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_workStateObserver.m_owner = this;
|
||||
g_WorkState->Attach(&m_workStateObserver);
|
||||
|
||||
m_updateInterval = g_Options->GetUpdateInterval();
|
||||
}
|
||||
|
||||
void Frontend::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void Frontend::WorkStateUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
@@ -57,8 +73,8 @@ bool Frontend::PrepareData()
|
||||
if (m_summary)
|
||||
{
|
||||
m_currentDownloadSpeed = g_StatMeter->CalcCurrentDownloadSpeed();
|
||||
m_pauseDownload = g_Options->GetPauseDownload();
|
||||
m_downloadLimit = g_Options->GetDownloadRate();
|
||||
m_pauseDownload = g_WorkState->GetPauseDownload();
|
||||
m_downloadLimit = g_WorkState->GetSpeedLimit();
|
||||
m_threadCount = Thread::GetThreadCount();
|
||||
g_StatMeter->CalcTotalStat(&m_upTimeSec, &m_dnTimeSec, &m_allBytes, &m_standBy);
|
||||
|
||||
@@ -108,8 +124,8 @@ void Frontend::ServerPauseUnpause(bool pause)
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Options->SetResumeTime(0);
|
||||
g_Options->SetPauseDownload(pause);
|
||||
g_WorkState->SetResumeTime(0);
|
||||
g_WorkState->SetPauseDownload(pause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +137,7 @@ void Frontend::ServerSetDownloadRate(int rate)
|
||||
}
|
||||
else
|
||||
{
|
||||
g_Options->SetDownloadRate(rate);
|
||||
g_WorkState->SetSpeedLimit(rate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,3 +323,16 @@ bool Frontend::RequestEditQueue(DownloadQueue::EEditAction action, int offset, i
|
||||
IdList ids = { id };
|
||||
return client.RequestServerEditQueue(action, offset, nullptr, &ids, nullptr, rmId);
|
||||
}
|
||||
|
||||
void Frontend::Wait(int milliseconds)
|
||||
{
|
||||
if (g_WorkState->GetPauseFrontend())
|
||||
{
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.WaitFor(m_waitMutex, 2000);
|
||||
}
|
||||
else
|
||||
{
|
||||
Util::Sleep(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "Observer.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
@@ -51,7 +52,10 @@ protected:
|
||||
int m_dnTimeSec = 0;
|
||||
int64 m_allBytes = 0;
|
||||
bool m_standBy = false;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
|
||||
virtual void Stop();
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
GuardedMessageList GuardMessages();
|
||||
@@ -63,12 +67,22 @@ protected:
|
||||
bool RequestSetDownloadRate(int rate);
|
||||
bool ServerEditQueue(DownloadQueue::EEditAction action, int offset, int entry);
|
||||
bool RequestEditQueue(DownloadQueue::EEditAction action, int offset, int id);
|
||||
void Wait(int milliseconds);
|
||||
|
||||
private:
|
||||
class WorkStateObserver : public Observer
|
||||
{
|
||||
public:
|
||||
Frontend* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
MessageList m_remoteMessages;
|
||||
WorkStateObserver m_workStateObserver;
|
||||
|
||||
bool RequestMessages();
|
||||
bool RequestFileList();
|
||||
void WorkStateUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
#include "LoggableFrontend.h"
|
||||
#include "Log.h"
|
||||
|
||||
@@ -30,7 +31,7 @@ void LoggableFrontend::Run()
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
usleep(m_updateInterval * 1000);
|
||||
Wait(m_updateInterval);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -213,8 +213,10 @@ void NCursesFrontend::Run()
|
||||
m_dataUpdatePos = m_updateInterval;
|
||||
}
|
||||
|
||||
usleep(10 * 1000);
|
||||
m_dataUpdatePos -= 10;
|
||||
// update more often (sleep shorter) if need faster reaction on user input
|
||||
int sleepInterval = m_inputMode == normal ? 100 : 10;
|
||||
Wait(sleepInterval);
|
||||
m_dataUpdatePos -= sleepInterval;
|
||||
}
|
||||
|
||||
FreeData();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -915,25 +915,18 @@ void CommandLineParser::ParseFileIdList(int argc, const char* argv[], int optind
|
||||
}
|
||||
|
||||
int editQueueIdCount = 0;
|
||||
if (editQueueIdTo != 0)
|
||||
if (editQueueIdFrom < editQueueIdTo)
|
||||
{
|
||||
if (editQueueIdFrom < editQueueIdTo)
|
||||
{
|
||||
editQueueIdCount = editQueueIdTo - editQueueIdFrom + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
editQueueIdCount = editQueueIdFrom - editQueueIdTo + 1;
|
||||
}
|
||||
editQueueIdCount = editQueueIdTo - editQueueIdFrom + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
editQueueIdCount = 1;
|
||||
editQueueIdCount = editQueueIdFrom - editQueueIdTo + 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < editQueueIdCount; i++)
|
||||
{
|
||||
if (editQueueIdFrom < editQueueIdTo || editQueueIdTo == 0)
|
||||
if (editQueueIdFrom < editQueueIdTo)
|
||||
{
|
||||
m_editQueueIdList.push_back(editQueueIdFrom + i);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,23 +21,39 @@
|
||||
#include "nzbget.h"
|
||||
#include "DiskService.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "StatMeter.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
DiskService::DiskService()
|
||||
{
|
||||
g_WorkState->Attach(this);
|
||||
}
|
||||
|
||||
void DiskService::Update(Subject* caller, void* aspect)
|
||||
{
|
||||
WakeUp();
|
||||
}
|
||||
|
||||
int DiskService::ServiceInterval()
|
||||
{
|
||||
return m_waitingRequiredDir ? 1 :
|
||||
g_Options->GetDiskSpace() <= 0 ? Service::Sleep :
|
||||
// notifications from 'WorkState' are not 100% reliable due to race conditions
|
||||
!g_WorkState->GetDownloading() ? 10 :
|
||||
1;
|
||||
}
|
||||
|
||||
void DiskService::ServiceWork()
|
||||
{
|
||||
m_interval++;
|
||||
if (m_interval == 5)
|
||||
debug("Disk service work");
|
||||
|
||||
if (g_Options->GetDiskSpace() > 0 && g_WorkState->GetDownloading())
|
||||
{
|
||||
if (!g_Options->GetPauseDownload() &&
|
||||
g_Options->GetDiskSpace() > 0 && !g_StatMeter->GetStandBy())
|
||||
{
|
||||
// check free disk space every 1 second
|
||||
CheckDiskSpace();
|
||||
}
|
||||
m_interval = 0;
|
||||
// check free disk space every 1 second
|
||||
CheckDiskSpace();
|
||||
}
|
||||
|
||||
if (m_waitingRequiredDir)
|
||||
@@ -48,11 +64,13 @@ void DiskService::ServiceWork()
|
||||
|
||||
void DiskService::CheckDiskSpace()
|
||||
{
|
||||
debug("Disk service work: check disk space");
|
||||
|
||||
int64 freeSpace = FileSystem::FreeDiskSize(g_Options->GetDestDir());
|
||||
if (freeSpace > -1 && freeSpace / 1024 / 1024 < g_Options->GetDiskSpace())
|
||||
{
|
||||
warn("Low disk space on %s. Pausing download", g_Options->GetDestDir());
|
||||
g_Options->SetPauseDownload(true);
|
||||
g_WorkState->SetPauseDownload(true);
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(g_Options->GetInterDir()))
|
||||
@@ -61,13 +79,15 @@ void DiskService::CheckDiskSpace()
|
||||
if (freeSpace > -1 && freeSpace / 1024 / 1024 < g_Options->GetDiskSpace())
|
||||
{
|
||||
warn("Low disk space on %s. Pausing download", g_Options->GetInterDir());
|
||||
g_Options->SetPauseDownload(true);
|
||||
g_WorkState->SetPauseDownload(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiskService::CheckRequiredDir()
|
||||
{
|
||||
debug("Disk service work: check required dir");
|
||||
|
||||
if (!Util::EmptyStr(g_Options->GetRequiredDir()))
|
||||
{
|
||||
bool allExist = true;
|
||||
@@ -97,7 +117,7 @@ void DiskService::CheckRequiredDir()
|
||||
info("All required directories available");
|
||||
}
|
||||
|
||||
g_Options->SetTempPauseDownload(false);
|
||||
g_Options->SetTempPausePostprocess(false);
|
||||
g_WorkState->SetTempPauseDownload(false);
|
||||
g_WorkState->SetTempPausePostprocess(false);
|
||||
m_waitingRequiredDir = false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,15 +22,19 @@
|
||||
#define DISKSERVICE_H
|
||||
|
||||
#include "Service.h"
|
||||
#include "Observer.h"
|
||||
|
||||
class DiskService : public Service
|
||||
class DiskService : public Service, public Observer
|
||||
{
|
||||
public:
|
||||
DiskService();
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 200; }
|
||||
virtual int ServiceInterval();
|
||||
virtual void ServiceWork();
|
||||
virtual void Update(Subject* caller, void* aspect);
|
||||
|
||||
private:
|
||||
int m_interval = 0;
|
||||
bool m_waitingRequiredDir = true;
|
||||
bool m_waitingReported = false;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ Maintenance::~Maintenance()
|
||||
{
|
||||
while (m_updateScriptController)
|
||||
{
|
||||
usleep(20*1000);
|
||||
Util::Sleep(20);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,7 +179,7 @@ bool Maintenance::ReadPackageInfoStr(const char* key, CString& value)
|
||||
return false;
|
||||
}
|
||||
|
||||
int len = (int)(pend - p);
|
||||
size_t len = pend - p;
|
||||
if (len >= sizeof(fileName))
|
||||
{
|
||||
error("Could not parse file %s", *fileName);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -70,20 +70,17 @@ static const char* OPTION_AUTHORIZEDIP = "AuthorizedIP";
|
||||
static const char* OPTION_ARTICLETIMEOUT = "ArticleTimeout";
|
||||
static const char* OPTION_URLTIMEOUT = "UrlTimeout";
|
||||
static const char* OPTION_REMOTETIMEOUT = "RemoteTimeout";
|
||||
static const char* OPTION_SAVEQUEUE = "SaveQueue";
|
||||
static const char* OPTION_FLUSHQUEUE = "FlushQueue";
|
||||
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
|
||||
static const char* OPTION_BROKENLOG = "BrokenLog";
|
||||
static const char* OPTION_NZBLOG = "NzbLog";
|
||||
static const char* OPTION_DECODE = "Decode";
|
||||
static const char* OPTION_RAWARTICLE = "RawArticle";
|
||||
static const char* OPTION_SKIPWRITE = "SkipWrite";
|
||||
static const char* OPTION_ARTICLERETRIES = "ArticleRetries";
|
||||
static const char* OPTION_ARTICLEINTERVAL = "ArticleInterval";
|
||||
static const char* OPTION_URLRETRIES = "UrlRetries";
|
||||
static const char* OPTION_URLINTERVAL = "UrlInterval";
|
||||
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
|
||||
static const char* OPTION_CONTINUEPARTIAL = "ContinuePartial";
|
||||
static const char* OPTION_URLCONNECTIONS = "UrlConnections";
|
||||
static const char* OPTION_LOGBUFFERSIZE = "LogBufferSize";
|
||||
static const char* OPTION_LOGBUFFER = "LogBuffer";
|
||||
static const char* OPTION_INFOTARGET = "InfoTarget";
|
||||
static const char* OPTION_WARNINGTARGET = "WarningTarget";
|
||||
static const char* OPTION_ERRORTARGET = "ErrorTarget";
|
||||
@@ -119,7 +116,6 @@ static const char* OPTION_SCRIPTPAUSEQUEUE = "ScriptPauseQueue";
|
||||
static const char* OPTION_NZBCLEANUPDISK = "NzbCleanupDisk";
|
||||
static const char* OPTION_PARTIMELIMIT = "ParTimeLimit";
|
||||
static const char* OPTION_KEEPHISTORY = "KeepHistory";
|
||||
static const char* OPTION_ACCURATERATE = "AccurateRate";
|
||||
static const char* OPTION_UNPACK = "Unpack";
|
||||
static const char* OPTION_DIRECTUNPACK = "DirectUnpack";
|
||||
static const char* OPTION_UNPACKCLEANUPDISK = "UnpackCleanupDisk";
|
||||
@@ -170,6 +166,13 @@ static const char* OPTION_HISTORYCLEANUPDISK = "HistoryCleanupDisk";
|
||||
static const char* OPTION_SCANSCRIPT = "ScanScript";
|
||||
static const char* OPTION_QUEUESCRIPT = "QueueScript";
|
||||
static const char* OPTION_FEEDSCRIPT = "FeedScript";
|
||||
static const char* OPTION_DECODE = "Decode";
|
||||
static const char* OPTION_SAVEQUEUE = "SaveQueue";
|
||||
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
|
||||
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
|
||||
static const char* OPTION_ACCURATERATE = "AccurateRate";
|
||||
static const char* OPTION_CREATEBROKENLOG = "CreateBrokenLog";
|
||||
static const char* OPTION_BROKENLOG = "BrokenLog";
|
||||
|
||||
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable", "enabled", "disabled" };
|
||||
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
|
||||
@@ -418,7 +421,7 @@ void Options::InitDefaults()
|
||||
SetOption(OPTION_QUEUEDIR, "${MainDir}/queue");
|
||||
SetOption(OPTION_NZBDIR, "${MainDir}/nzb");
|
||||
SetOption(OPTION_LOCKFILE, "${MainDir}/nzbget.lock");
|
||||
SetOption(OPTION_LOGFILE, "${DestDir}/nzbget.log");
|
||||
SetOption(OPTION_LOGFILE, "${MainDir}/nzbget.log");
|
||||
SetOption(OPTION_SCRIPTDIR, "${MainDir}/scripts");
|
||||
SetOption(OPTION_REQUIREDDIR, "");
|
||||
SetOption(OPTION_WRITELOG, "append");
|
||||
@@ -446,20 +449,17 @@ void Options::InitDefaults()
|
||||
SetOption(OPTION_ARTICLETIMEOUT, "60");
|
||||
SetOption(OPTION_URLTIMEOUT, "60");
|
||||
SetOption(OPTION_REMOTETIMEOUT, "90");
|
||||
SetOption(OPTION_SAVEQUEUE, "yes");
|
||||
SetOption(OPTION_FLUSHQUEUE, "yes");
|
||||
SetOption(OPTION_RELOADQUEUE, "yes");
|
||||
SetOption(OPTION_BROKENLOG, "yes");
|
||||
SetOption(OPTION_NZBLOG, "yes");
|
||||
SetOption(OPTION_DECODE, "yes");
|
||||
SetOption(OPTION_RAWARTICLE, "no");
|
||||
SetOption(OPTION_SKIPWRITE, "no");
|
||||
SetOption(OPTION_ARTICLERETRIES, "3");
|
||||
SetOption(OPTION_ARTICLEINTERVAL, "10");
|
||||
SetOption(OPTION_URLRETRIES, "3");
|
||||
SetOption(OPTION_URLINTERVAL, "10");
|
||||
SetOption(OPTION_TERMINATETIMEOUT, "600");
|
||||
SetOption(OPTION_CONTINUEPARTIAL, "no");
|
||||
SetOption(OPTION_URLCONNECTIONS, "4");
|
||||
SetOption(OPTION_LOGBUFFERSIZE, "1000");
|
||||
SetOption(OPTION_LOGBUFFER, "1000");
|
||||
SetOption(OPTION_INFOTARGET, "both");
|
||||
SetOption(OPTION_WARNINGTARGET, "both");
|
||||
SetOption(OPTION_ERRORTARGET, "both");
|
||||
@@ -498,7 +498,6 @@ void Options::InitDefaults()
|
||||
SetOption(OPTION_NZBCLEANUPDISK, "no");
|
||||
SetOption(OPTION_PARTIMELIMIT, "0");
|
||||
SetOption(OPTION_KEEPHISTORY, "7");
|
||||
SetOption(OPTION_ACCURATERATE, "no");
|
||||
SetOption(OPTION_UNPACK, "no");
|
||||
SetOption(OPTION_DIRECTUNPACK, "no");
|
||||
SetOption(OPTION_UNPACKCLEANUPDISK, "no");
|
||||
@@ -643,8 +642,6 @@ void Options::CheckDir(CString& dir, const char* optionName,
|
||||
|
||||
FileSystem::NormalizePathSeparators((char*)usedir2);
|
||||
dir = usedir2;
|
||||
|
||||
usedir2[usedir2.Length() - 1] = '\0';
|
||||
SetOption(optionName, usedir2);
|
||||
}
|
||||
|
||||
@@ -698,7 +695,6 @@ void Options::InitOptions()
|
||||
m_downloadRate = ParseIntValue(OPTION_DOWNLOADRATE, 10) * 1024;
|
||||
m_articleTimeout = ParseIntValue(OPTION_ARTICLETIMEOUT, 10);
|
||||
m_urlTimeout = ParseIntValue(OPTION_URLTIMEOUT, 10);
|
||||
m_terminateTimeout = ParseIntValue(OPTION_TERMINATETIMEOUT, 10);
|
||||
m_remoteTimeout = ParseIntValue(OPTION_REMOTETIMEOUT, 10);
|
||||
m_articleRetries = ParseIntValue(OPTION_ARTICLERETRIES, 10);
|
||||
m_articleInterval = ParseIntValue(OPTION_ARTICLEINTERVAL, 10);
|
||||
@@ -707,7 +703,7 @@ void Options::InitOptions()
|
||||
m_controlPort = ParseIntValue(OPTION_CONTROLPORT, 10);
|
||||
m_securePort = ParseIntValue(OPTION_SECUREPORT, 10);
|
||||
m_urlConnections = ParseIntValue(OPTION_URLCONNECTIONS, 10);
|
||||
m_logBufferSize = ParseIntValue(OPTION_LOGBUFFERSIZE, 10);
|
||||
m_logBuffer = ParseIntValue(OPTION_LOGBUFFER, 10);
|
||||
m_rotateLog = ParseIntValue(OPTION_ROTATELOG, 10);
|
||||
m_umask = ParseIntValue(OPTION_UMASK, 8);
|
||||
m_updateInterval = ParseIntValue(OPTION_UPDATEINTERVAL, 10);
|
||||
@@ -733,11 +729,9 @@ void Options::InitOptions()
|
||||
m_quotaStartDay = ParseIntValue(OPTION_QUOTASTARTDAY, 10);
|
||||
m_dailyQuota = ParseIntValue(OPTION_DAILYQUOTA, 10);
|
||||
|
||||
m_brokenLog = (bool)ParseEnumValue(OPTION_BROKENLOG, BoolCount, BoolNames, BoolValues);
|
||||
m_nzbLog = (bool)ParseEnumValue(OPTION_NZBLOG, BoolCount, BoolNames, BoolValues);
|
||||
m_appendCategoryDir = (bool)ParseEnumValue(OPTION_APPENDCATEGORYDIR, BoolCount, BoolNames, BoolValues);
|
||||
m_continuePartial = (bool)ParseEnumValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, BoolValues);
|
||||
m_saveQueue = (bool)ParseEnumValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_flushQueue = (bool)ParseEnumValue(OPTION_FLUSHQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_dupeCheck = (bool)ParseEnumValue(OPTION_DUPECHECK, BoolCount, BoolNames, BoolValues);
|
||||
m_parRepair = (bool)ParseEnumValue(OPTION_PARREPAIR, BoolCount, BoolNames, BoolValues);
|
||||
@@ -745,19 +739,18 @@ void Options::InitOptions()
|
||||
m_parRename = (bool)ParseEnumValue(OPTION_PARRENAME, BoolCount, BoolNames, BoolValues);
|
||||
m_rarRename = (bool)ParseEnumValue(OPTION_RARRENAME, BoolCount, BoolNames, BoolValues);
|
||||
m_directRename = (bool)ParseEnumValue(OPTION_DIRECTRENAME, BoolCount, BoolNames, BoolValues);
|
||||
m_reloadQueue = (bool)ParseEnumValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_cursesNzbName = (bool)ParseEnumValue(OPTION_CURSESNZBNAME, BoolCount, BoolNames, BoolValues);
|
||||
m_cursesTime = (bool)ParseEnumValue(OPTION_CURSESTIME, BoolCount, BoolNames, BoolValues);
|
||||
m_cursesGroup = (bool)ParseEnumValue(OPTION_CURSESGROUP, BoolCount, BoolNames, BoolValues);
|
||||
m_crcCheck = (bool)ParseEnumValue(OPTION_CRCCHECK, BoolCount, BoolNames, BoolValues);
|
||||
m_directWrite = (bool)ParseEnumValue(OPTION_DIRECTWRITE, BoolCount, BoolNames, BoolValues);
|
||||
m_decode = (bool)ParseEnumValue(OPTION_DECODE, BoolCount, BoolNames, BoolValues);
|
||||
m_rawArticle = (bool)ParseEnumValue(OPTION_RAWARTICLE, BoolCount, BoolNames, BoolValues);
|
||||
m_skipWrite = (bool)ParseEnumValue(OPTION_SKIPWRITE, BoolCount, BoolNames, BoolValues);
|
||||
m_crashTrace = (bool)ParseEnumValue(OPTION_CRASHTRACE, BoolCount, BoolNames, BoolValues);
|
||||
m_crashDump = (bool)ParseEnumValue(OPTION_CRASHDUMP, BoolCount, BoolNames, BoolValues);
|
||||
m_parPauseQueue = (bool)ParseEnumValue(OPTION_PARPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_scriptPauseQueue = (bool)ParseEnumValue(OPTION_SCRIPTPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_nzbCleanupDisk = (bool)ParseEnumValue(OPTION_NZBCLEANUPDISK, BoolCount, BoolNames, BoolValues);
|
||||
m_accurateRate = (bool)ParseEnumValue(OPTION_ACCURATERATE, BoolCount, BoolNames, BoolValues);
|
||||
m_formAuth = (bool)ParseEnumValue(OPTION_FORMAUTH, BoolCount, BoolNames, BoolValues);
|
||||
m_secureControl = (bool)ParseEnumValue(OPTION_SECURECONTROL, BoolCount, BoolNames, BoolValues);
|
||||
m_unpack = (bool)ParseEnumValue(OPTION_UNPACK, BoolCount, BoolNames, BoolValues);
|
||||
@@ -1606,7 +1599,13 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
|
||||
!strcasecmp(optname, OPTION_RELOADPOSTQUEUE) ||
|
||||
!strcasecmp(optname, OPTION_PARCLEANUPQUEUE) ||
|
||||
!strcasecmp(optname, OPTION_DELETECLEANUPDISK) ||
|
||||
!strcasecmp(optname, OPTION_HISTORYCLEANUPDISK))
|
||||
!strcasecmp(optname, OPTION_HISTORYCLEANUPDISK) ||
|
||||
!strcasecmp(optname, OPTION_SAVEQUEUE) ||
|
||||
!strcasecmp(optname, OPTION_RELOADQUEUE) ||
|
||||
!strcasecmp(optname, OPTION_TERMINATETIMEOUT) ||
|
||||
!strcasecmp(optname, OPTION_ACCURATERATE) ||
|
||||
!strcasecmp(optname, OPTION_CREATEBROKENLOG) ||
|
||||
!strcasecmp(optname, OPTION_BROKENLOG))
|
||||
{
|
||||
ConfigWarn("Option \"%s\" is obsolete, ignored", optname);
|
||||
return true;
|
||||
@@ -1726,15 +1725,21 @@ void Options::ConvertOldOption(CString& option, CString& value)
|
||||
option = "ArticleInterval";
|
||||
}
|
||||
|
||||
if (!strcasecmp(option, "CreateBrokenLog"))
|
||||
{
|
||||
option = "BrokenLog";
|
||||
}
|
||||
|
||||
if (!strcasecmp(option, "DumpCore"))
|
||||
{
|
||||
option = OPTION_CRASHDUMP;
|
||||
}
|
||||
|
||||
if (!strcasecmp(option, OPTION_DECODE))
|
||||
{
|
||||
option = OPTION_RAWARTICLE;
|
||||
value = !strcasecmp(value, "no") ? "yes" : "no";
|
||||
}
|
||||
|
||||
if (!strcasecmp(option, "LogBufferSize"))
|
||||
{
|
||||
option = OPTION_LOGBUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
void Options::CheckOptions()
|
||||
@@ -1808,11 +1813,16 @@ void Options::CheckOptions()
|
||||
m_certCheck = false;
|
||||
}
|
||||
|
||||
if (!m_decode)
|
||||
if (m_rawArticle)
|
||||
{
|
||||
m_directWrite = false;
|
||||
}
|
||||
|
||||
if (m_skipWrite)
|
||||
{
|
||||
m_directRename = false;
|
||||
}
|
||||
|
||||
// if option "ConfigTemplate" is not set, use "WebDir" as default location for template
|
||||
// (for compatibility with versions 9 and 10).
|
||||
if (m_configTemplate.Empty() && !m_noDiskAccess)
|
||||
@@ -1894,10 +1904,10 @@ void Options::MergeOldScriptOption(OptEntries* optEntries, const char* optname,
|
||||
{
|
||||
for (OptEntry& opt : optEntries)
|
||||
{
|
||||
const char* optname = opt.GetName();
|
||||
if (!strncasecmp(optname, "category", 8))
|
||||
const char* catoptname = opt.GetName();
|
||||
if (!strncasecmp(catoptname, "category", 8))
|
||||
{
|
||||
char* p = (char*)optname + 8;
|
||||
char* p = (char*)catoptname + 8;
|
||||
while (*p >= '0' && *p <= '9') p++;
|
||||
if (p && (!strcasecmp(p, ".extensions")))
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -203,7 +203,6 @@ public:
|
||||
const char* GetConfigTemplate() { return m_configTemplate; }
|
||||
const char* GetScriptDir() { return m_scriptDir; }
|
||||
const char* GetRequiredDir() { return m_requiredDir; }
|
||||
bool GetBrokenLog() const { return m_brokenLog; }
|
||||
bool GetNzbLog() const { return m_nzbLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_infoTarget; }
|
||||
EMessageTarget GetWarningTarget() const { return m_warningTarget; }
|
||||
@@ -212,16 +211,15 @@ public:
|
||||
EMessageTarget GetDetailTarget() const { return m_detailTarget; }
|
||||
int GetArticleTimeout() { return m_articleTimeout; }
|
||||
int GetUrlTimeout() { return m_urlTimeout; }
|
||||
int GetTerminateTimeout() { return m_terminateTimeout; }
|
||||
int GetRemoteTimeout() { return m_remoteTimeout; }
|
||||
bool GetDecode() { return m_decode; };
|
||||
bool GetRawArticle() { return m_rawArticle; };
|
||||
bool GetSkipWrite() { return m_skipWrite; };
|
||||
bool GetAppendCategoryDir() { return m_appendCategoryDir; }
|
||||
bool GetContinuePartial() { return m_continuePartial; }
|
||||
int GetArticleRetries() { return m_articleRetries; }
|
||||
int GetArticleInterval() { return m_articleInterval; }
|
||||
int GetUrlRetries() { return m_urlRetries; }
|
||||
int GetUrlInterval() { return m_urlInterval; }
|
||||
bool GetSaveQueue() { return m_saveQueue; }
|
||||
bool GetFlushQueue() { return m_flushQueue; }
|
||||
bool GetDupeCheck() { return m_dupeCheck; }
|
||||
const char* GetControlIp() { return m_controlIp; }
|
||||
@@ -243,9 +241,8 @@ public:
|
||||
const char* GetLockFile() { return m_lockFile; }
|
||||
const char* GetDaemonUsername() { return m_daemonUsername; }
|
||||
EOutputMode GetOutputMode() { return m_outputMode; }
|
||||
bool GetReloadQueue() { return m_reloadQueue; }
|
||||
int GetUrlConnections() { return m_urlConnections; }
|
||||
int GetLogBufferSize() { return m_logBufferSize; }
|
||||
int GetLogBuffer() { return m_logBuffer; }
|
||||
EWriteLog GetWriteLog() { return m_writeLog; }
|
||||
const char* GetLogFile() { return m_logFile; }
|
||||
int GetRotateLog() { return m_rotateLog; }
|
||||
@@ -280,7 +277,6 @@ public:
|
||||
bool GetNzbCleanupDisk() { return m_nzbCleanupDisk; }
|
||||
int GetParTimeLimit() { return m_parTimeLimit; }
|
||||
int GetKeepHistory() { return m_keepHistory; }
|
||||
bool GetAccurateRate() { return m_accurateRate; }
|
||||
bool GetUnpack() { return m_unpack; }
|
||||
bool GetDirectUnpack() { return m_directUnpack; }
|
||||
bool GetUnpackCleanupDisk() { return m_unpackCleanupDisk; }
|
||||
@@ -304,6 +300,7 @@ public:
|
||||
bool GetDirectRename() { return m_directRename; }
|
||||
bool GetReorderFiles() { return m_reorderFiles; }
|
||||
EFileNaming GetFileNaming() { return m_fileNaming; }
|
||||
int GetDownloadRate() const { return m_downloadRate; }
|
||||
|
||||
Categories* GetCategories() { return &m_categories; }
|
||||
Category* FindCategory(const char* name, bool searchAliases) { return m_categories.FindCategory(name, searchAliases); }
|
||||
@@ -315,24 +312,6 @@ public:
|
||||
bool GetDaemonMode() { return m_daemonMode; }
|
||||
void SetRemoteClientMode(bool remoteClientMode) { m_remoteClientMode = remoteClientMode; }
|
||||
bool GetRemoteClientMode() { return m_remoteClientMode; }
|
||||
void SetPauseDownload(bool pauseDownload) { m_pauseDownload = pauseDownload; }
|
||||
bool GetPauseDownload() const { return m_pauseDownload; }
|
||||
void SetPausePostProcess(bool pausePostProcess) { m_pausePostProcess = pausePostProcess; }
|
||||
bool GetPausePostProcess() const { return m_pausePostProcess; }
|
||||
void SetPauseScan(bool pauseScan) { m_pauseScan = pauseScan; }
|
||||
bool GetPauseScan() const { return m_pauseScan; }
|
||||
void SetTempPauseDownload(bool tempPauseDownload) { m_tempPauseDownload = tempPauseDownload; }
|
||||
bool GetTempPauseDownload() const { return m_tempPauseDownload; }
|
||||
bool GetTempPausePostprocess() const { return m_tempPausePostprocess; }
|
||||
void SetTempPausePostprocess(bool tempPausePostprocess) { m_tempPausePostprocess = tempPausePostprocess; }
|
||||
void SetDownloadRate(int rate) { m_downloadRate = rate; }
|
||||
int GetDownloadRate() const { return m_downloadRate; }
|
||||
void SetResumeTime(time_t resumeTime) { m_resumeTime = resumeTime; }
|
||||
time_t GetResumeTime() const { return m_resumeTime; }
|
||||
void SetLocalTimeOffset(int localTimeOffset) { m_localTimeOffset = localTimeOffset; }
|
||||
int GetLocalTimeOffset() { return m_localTimeOffset; }
|
||||
void SetQuotaReached(bool quotaReached) { m_quotaReached = quotaReached; }
|
||||
bool GetQuotaReached() { return m_quotaReached; }
|
||||
|
||||
private:
|
||||
OptEntries m_optEntries;
|
||||
@@ -362,12 +341,11 @@ private:
|
||||
EMessageTarget m_errorTarget = mtScreen;
|
||||
EMessageTarget m_debugTarget = mtNone;
|
||||
EMessageTarget m_detailTarget = mtScreen;
|
||||
bool m_decode = true;
|
||||
bool m_brokenLog = false;
|
||||
bool m_skipWrite = false;
|
||||
bool m_rawArticle = false;
|
||||
bool m_nzbLog = false;
|
||||
int m_articleTimeout = 0;
|
||||
int m_urlTimeout = 0;
|
||||
int m_terminateTimeout = 0;
|
||||
int m_remoteTimeout = 0;
|
||||
bool m_appendCategoryDir = false;
|
||||
bool m_continuePartial = false;
|
||||
@@ -375,7 +353,6 @@ private:
|
||||
int m_articleInterval = 0;
|
||||
int m_urlRetries = 0;
|
||||
int m_urlInterval = 0;
|
||||
bool m_saveQueue = false;
|
||||
bool m_flushQueue = false;
|
||||
bool m_dupeCheck = false;
|
||||
CString m_controlIp;
|
||||
@@ -397,9 +374,8 @@ private:
|
||||
CString m_lockFile;
|
||||
CString m_daemonUsername;
|
||||
EOutputMode m_outputMode = omLoggable;
|
||||
bool m_reloadQueue = false;
|
||||
int m_urlConnections = 0;
|
||||
int m_logBufferSize = 0;
|
||||
int m_logBuffer = 0;
|
||||
EWriteLog m_writeLog = wlAppend;
|
||||
int m_rotateLog = 0;
|
||||
CString m_logFile;
|
||||
@@ -435,7 +411,6 @@ private:
|
||||
bool m_nzbCleanupDisk = false;
|
||||
int m_parTimeLimit = 0;
|
||||
int m_keepHistory = 0;
|
||||
bool m_accurateRate = false;
|
||||
bool m_unpack = false;
|
||||
bool m_directUnpack = false;
|
||||
bool m_unpackCleanupDisk = false;
|
||||
@@ -458,20 +433,12 @@ private:
|
||||
int m_dailyQuota = 0;
|
||||
bool m_reorderFiles = false;
|
||||
EFileNaming m_fileNaming = nfArticle;
|
||||
int m_downloadRate = 0;
|
||||
|
||||
// Current state
|
||||
// Application mode
|
||||
bool m_serverMode = false;
|
||||
bool m_daemonMode = false;
|
||||
bool m_remoteClientMode = false;
|
||||
bool m_pauseDownload = false;
|
||||
bool m_pausePostProcess = false;
|
||||
bool m_pauseScan = false;
|
||||
bool m_tempPauseDownload = true;
|
||||
bool m_tempPausePostprocess = true;
|
||||
int m_downloadRate = 0;
|
||||
time_t m_resumeTime = 0;
|
||||
int m_localTimeOffset = 0;
|
||||
bool m_quotaReached = false;
|
||||
|
||||
void Init(const char* exeName, const char* configFilename, bool noConfig,
|
||||
CmdOptList* commandLineOptions, bool noDiskAccess, Extender* extender);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "Scheduler.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Log.h"
|
||||
#include "NewsServer.h"
|
||||
#include "ServerPool.h"
|
||||
@@ -51,13 +52,33 @@ void Scheduler::FirstCheck()
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::ScheduleNextWork()
|
||||
{
|
||||
// Ideally we should calculate wait time until next scheduler task or until resume time.
|
||||
// The first isn't trivial and the second requires watching/reaction on changed scheduled resume time.
|
||||
// We do it simpler instead: check once per minute, when seconds are changing from 59 to 00.
|
||||
|
||||
time_t curTime = Util::CurrentTime();
|
||||
tm sched;
|
||||
gmtime_r(&curTime, &sched);
|
||||
sched.tm_min++;
|
||||
sched.tm_sec = 0;
|
||||
time_t nextMinute = Util::Timegm(&sched);
|
||||
|
||||
m_serviceInterval = nextMinute - curTime;
|
||||
}
|
||||
|
||||
void Scheduler::ServiceWork()
|
||||
{
|
||||
debug("Scheduler service work");
|
||||
|
||||
if (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Scheduler service work: doing work");
|
||||
|
||||
if (!m_firstChecked)
|
||||
{
|
||||
FirstCheck();
|
||||
@@ -68,6 +89,7 @@ void Scheduler::ServiceWork()
|
||||
m_executeProcess = true;
|
||||
CheckTasks();
|
||||
CheckScheduledResume();
|
||||
ScheduleNextWork();
|
||||
}
|
||||
|
||||
void Scheduler::CheckTasks()
|
||||
@@ -100,8 +122,8 @@ void Scheduler::CheckTasks()
|
||||
}
|
||||
}
|
||||
|
||||
time_t localCurrent = current + g_Options->GetLocalTimeOffset();
|
||||
time_t localLastCheck = m_lastCheck + g_Options->GetLocalTimeOffset();
|
||||
time_t localCurrent = current + g_WorkState->GetLocalTimeOffset();
|
||||
time_t localLastCheck = m_lastCheck + g_WorkState->GetLocalTimeOffset();
|
||||
|
||||
tm tmCurrent;
|
||||
gmtime_r(&localCurrent, &tmCurrent);
|
||||
@@ -159,10 +181,12 @@ void Scheduler::CheckTasks()
|
||||
|
||||
void Scheduler::ExecuteTask(Task* task)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const char* commandName[] = { "Pause", "Unpause", "Pause Post-processing", "Unpause Post-processing",
|
||||
"Set download rate", "Execute process", "Execute script",
|
||||
"Pause Scan", "Unpause Scan", "Enable Server", "Disable Server", "Fetch Feed" };
|
||||
debug("Executing scheduled command: %s", commandName[task->m_command]);
|
||||
#endif
|
||||
|
||||
bool executeProcess = m_executeProcess || task->m_hours == Task::STARTUP_TASK;
|
||||
|
||||
@@ -171,26 +195,26 @@ void Scheduler::ExecuteTask(Task* task)
|
||||
case scDownloadRate:
|
||||
if (!task->m_param.Empty())
|
||||
{
|
||||
g_Options->SetDownloadRate(atoi(task->m_param) * 1024);
|
||||
g_WorkState->SetSpeedLimit(atoi(task->m_param) * 1024);
|
||||
m_downloadRateChanged = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case scPauseDownload:
|
||||
case scUnpauseDownload:
|
||||
g_Options->SetPauseDownload(task->m_command == scPauseDownload);
|
||||
g_WorkState->SetPauseDownload(task->m_command == scPauseDownload);
|
||||
m_pauseDownloadChanged = true;
|
||||
break;
|
||||
|
||||
case scPausePostProcess:
|
||||
case scUnpausePostProcess:
|
||||
g_Options->SetPausePostProcess(task->m_command == scPausePostProcess);
|
||||
g_WorkState->SetPausePostProcess(task->m_command == scPausePostProcess);
|
||||
m_pausePostProcessChanged = true;
|
||||
break;
|
||||
|
||||
case scPauseScan:
|
||||
case scUnpauseScan:
|
||||
g_Options->SetPauseScan(task->m_command == scPauseScan);
|
||||
g_WorkState->SetPauseScan(task->m_command == scPauseScan);
|
||||
m_pauseScanChanged = true;
|
||||
break;
|
||||
|
||||
@@ -229,19 +253,19 @@ void Scheduler::PrintLog()
|
||||
{
|
||||
if (m_downloadRateChanged)
|
||||
{
|
||||
info("Scheduler: setting download rate to %i KB/s", g_Options->GetDownloadRate() / 1024);
|
||||
info("Scheduler: setting download rate to %i KB/s", g_WorkState->GetSpeedLimit() / 1024);
|
||||
}
|
||||
if (m_pauseDownloadChanged)
|
||||
{
|
||||
info("Scheduler: %s download", g_Options->GetPauseDownload() ? "pausing" : "unpausing");
|
||||
info("Scheduler: %s download", g_WorkState->GetPauseDownload() ? "pausing" : "unpausing");
|
||||
}
|
||||
if (m_pausePostProcessChanged)
|
||||
{
|
||||
info("Scheduler: %s post-processing", g_Options->GetPausePostProcess() ? "pausing" : "unpausing");
|
||||
info("Scheduler: %s post-processing", g_WorkState->GetPausePostProcess() ? "pausing" : "unpausing");
|
||||
}
|
||||
if (m_pauseScanChanged)
|
||||
{
|
||||
info("Scheduler: %s scan", g_Options->GetPauseScan() ? "pausing" : "unpausing");
|
||||
info("Scheduler: %s scan", g_WorkState->GetPauseScan() ? "pausing" : "unpausing");
|
||||
}
|
||||
if (m_serverChanged)
|
||||
{
|
||||
@@ -308,14 +332,14 @@ void Scheduler::FetchFeed(const char* feedList)
|
||||
|
||||
void Scheduler::CheckScheduledResume()
|
||||
{
|
||||
time_t resumeTime = g_Options->GetResumeTime();
|
||||
time_t resumeTime = g_WorkState->GetResumeTime();
|
||||
time_t currentTime = Util::CurrentTime();
|
||||
if (resumeTime > 0 && currentTime >= resumeTime)
|
||||
{
|
||||
info("Autoresume");
|
||||
g_Options->SetResumeTime(0);
|
||||
g_Options->SetPauseDownload(false);
|
||||
g_Options->SetPausePostProcess(false);
|
||||
g_Options->SetPauseScan(false);
|
||||
g_WorkState->SetResumeTime(0);
|
||||
g_WorkState->SetPauseDownload(false);
|
||||
g_WorkState->SetPausePostProcess(false);
|
||||
g_WorkState->SetPauseScan(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -66,7 +66,7 @@ public:
|
||||
void AddTask(std::unique_ptr<Task> task);
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 1000; }
|
||||
virtual int ServiceInterval() { return m_serviceInterval; }
|
||||
virtual void ServiceWork();
|
||||
|
||||
private:
|
||||
@@ -84,6 +84,7 @@ private:
|
||||
bool m_serverChanged;
|
||||
ServerStatusList m_serverStatusList;
|
||||
bool m_firstChecked = false;
|
||||
int m_serviceInterval = 1;
|
||||
|
||||
void ExecuteTask(Task* task);
|
||||
void CheckTasks();
|
||||
@@ -93,6 +94,7 @@ private:
|
||||
void FetchFeed(const char* feedList);
|
||||
void CheckScheduledResume();
|
||||
void FirstCheck();
|
||||
void ScheduleNextWork();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -282,7 +282,7 @@ public:
|
||||
void DoSegFault()
|
||||
{
|
||||
char* N = nullptr;
|
||||
strcpy(N, "");
|
||||
*N = '\0';
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
27
daemon/main/WorkState.cpp
Normal file
27
daemon/main/WorkState.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WorkState.h"
|
||||
|
||||
void WorkState::Changed()
|
||||
{
|
||||
Notify(nullptr);
|
||||
}
|
||||
76
daemon/main/WorkState.h
Normal file
76
daemon/main/WorkState.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WORKSTATE_H
|
||||
#define WORKSTATE_H
|
||||
|
||||
#include "Observer.h"
|
||||
|
||||
// WorkState is observable but notifications are not 100% reliable.
|
||||
// The changes via Set-methods and readings via Get-methods are not synchronized throughout the program.
|
||||
// As result race conditions may occur and some changes may go unnoticed.
|
||||
// When waiting for changes don't wait too long to avoid lock ups.
|
||||
|
||||
class WorkState : public Subject
|
||||
{
|
||||
public:
|
||||
void SetPauseDownload(bool pauseDownload) { m_pauseDownload = pauseDownload; Changed(); }
|
||||
bool GetPauseDownload() const { return m_pauseDownload; }
|
||||
void SetPausePostProcess(bool pausePostProcess) { m_pausePostProcess = pausePostProcess; Changed(); }
|
||||
bool GetPausePostProcess() const { return m_pausePostProcess; }
|
||||
void SetPauseScan(bool pauseScan) { m_pauseScan = pauseScan; Changed(); }
|
||||
bool GetPauseScan() const { return m_pauseScan; }
|
||||
void SetTempPauseDownload(bool tempPauseDownload) { m_tempPauseDownload = tempPauseDownload; Changed(); }
|
||||
bool GetTempPauseDownload() const { return m_tempPauseDownload; }
|
||||
void SetTempPausePostprocess(bool tempPausePostprocess) { m_tempPausePostprocess = tempPausePostprocess; Changed(); }
|
||||
bool GetTempPausePostprocess() const { return m_tempPausePostprocess; }
|
||||
void SetPauseFrontend(bool pauseFrontend) { m_pauseFrontend = pauseFrontend; Changed(); }
|
||||
bool GetPauseFrontend() const { return m_pauseFrontend; }
|
||||
void SetSpeedLimit(int speedLimit) { m_speedLimit = speedLimit; Changed(); }
|
||||
int GetSpeedLimit() const { return m_speedLimit; }
|
||||
void SetResumeTime(time_t resumeTime) { m_resumeTime = resumeTime; Changed(); }
|
||||
time_t GetResumeTime() const { return m_resumeTime; }
|
||||
void SetLocalTimeOffset(int localTimeOffset) { m_localTimeOffset = localTimeOffset; Changed(); }
|
||||
int GetLocalTimeOffset() { return m_localTimeOffset; }
|
||||
void SetQuotaReached(bool quotaReached) { m_quotaReached = quotaReached; Changed(); }
|
||||
bool GetQuotaReached() { return m_quotaReached; }
|
||||
void SetDownloading(bool downloading) { m_downloading = downloading; Changed(); }
|
||||
bool GetDownloading() { return m_downloading; }
|
||||
|
||||
private:
|
||||
bool m_pauseDownload = false;
|
||||
bool m_pausePostProcess = false;
|
||||
bool m_pauseScan = false;
|
||||
bool m_tempPauseDownload = true;
|
||||
bool m_tempPausePostprocess = true;
|
||||
bool m_pauseFrontend = false;
|
||||
int m_downloadRate = 0;
|
||||
time_t m_resumeTime = 0;
|
||||
int m_localTimeOffset = 0;
|
||||
bool m_quotaReached = false;
|
||||
int m_speedLimit = 0;
|
||||
bool m_downloading = false;
|
||||
|
||||
void Changed();
|
||||
};
|
||||
|
||||
extern WorkState* g_WorkState;
|
||||
|
||||
#endif
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "Log.h"
|
||||
#include "NzbFile.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "CommandLineParser.h"
|
||||
#include "ScriptConfig.h"
|
||||
#include "Thread.h"
|
||||
@@ -52,6 +53,7 @@
|
||||
#include "FileSystem.h"
|
||||
#include "StackTrace.h"
|
||||
#include "CommandScript.h"
|
||||
#include "YEncode.h"
|
||||
#ifdef WIN32
|
||||
#include "WinService.h"
|
||||
#include "WinConsole.h"
|
||||
@@ -70,6 +72,7 @@ void RunMain();
|
||||
// Globals
|
||||
Log* g_Log;
|
||||
Options* g_Options;
|
||||
WorkState* g_WorkState;
|
||||
ServerPool* g_ServerPool;
|
||||
QueueCoordinator* g_QueueCoordinator;
|
||||
UrlCoordinator* g_UrlCoordinator;
|
||||
@@ -112,6 +115,7 @@ int main(int argc, char *argv[], char *argp[])
|
||||
#endif
|
||||
|
||||
Util::Init();
|
||||
YEncode::init();
|
||||
|
||||
g_ArgumentCount = argc;
|
||||
g_Arguments = (char*(*)[])argv;
|
||||
@@ -191,6 +195,7 @@ private:
|
||||
// globals
|
||||
std::unique_ptr<Log> m_log;
|
||||
std::unique_ptr<Options> m_options;
|
||||
std::unique_ptr<WorkState> m_workState;
|
||||
std::unique_ptr<ServerPool> m_serverPool;
|
||||
std::unique_ptr<QueueCoordinator> m_queueCoordinator;
|
||||
std::unique_ptr<UrlCoordinator> m_urlCoordinator;
|
||||
@@ -221,13 +226,17 @@ private:
|
||||
|
||||
bool m_reloading = false;
|
||||
bool m_daemonized = false;
|
||||
bool m_stopped = false;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
|
||||
void Init();
|
||||
void Final();
|
||||
void BootConfig();
|
||||
void CreateGlobals();
|
||||
void Cleanup();
|
||||
void PrintOptions();
|
||||
bool ProcessDirect();
|
||||
void ProcessDirect();
|
||||
void ProcessClientRequest();
|
||||
void ProcessWebGet();
|
||||
void ProcessSigVerify();
|
||||
@@ -318,6 +327,17 @@ void NZBGet::Init()
|
||||
InstallErrorHandler();
|
||||
}
|
||||
|
||||
void NZBGet::Final()
|
||||
{
|
||||
if (!m_reloading)
|
||||
{
|
||||
#ifndef DISABLE_TLS
|
||||
TlsSocket::Final();
|
||||
#endif
|
||||
Connection::Final();
|
||||
}
|
||||
}
|
||||
|
||||
void NZBGet::CreateGlobals()
|
||||
{
|
||||
#ifdef WIN32
|
||||
@@ -325,6 +345,9 @@ void NZBGet::CreateGlobals()
|
||||
g_WinConsole = m_winConsole.get();
|
||||
#endif
|
||||
|
||||
m_workState = std::make_unique<WorkState>();
|
||||
g_WorkState = m_workState.get();
|
||||
|
||||
m_serviceCoordinator = std::make_unique<ServiceCoordinator>();
|
||||
g_ServiceCoordinator = m_serviceCoordinator.get();
|
||||
|
||||
@@ -398,7 +421,8 @@ void NZBGet::BootConfig()
|
||||
m_commandLineParser->GetNoConfig(), (Options::CmdOptList*)m_commandLineParser->GetOptionList(), this);
|
||||
m_options->SetRemoteClientMode(m_commandLineParser->GetRemoteClientMode());
|
||||
m_options->SetServerMode(m_commandLineParser->GetServerMode());
|
||||
m_options->SetPauseDownload(m_commandLineParser->GetPauseDownload());
|
||||
m_workState->SetPauseDownload(m_commandLineParser->GetPauseDownload());
|
||||
m_workState->SetSpeedLimit(g_Options->GetDownloadRate());
|
||||
|
||||
m_log->InitOptions();
|
||||
|
||||
@@ -410,9 +434,9 @@ void NZBGet::BootConfig()
|
||||
m_commandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
|
||||
{
|
||||
info("Pausing all activities due to errors in configuration");
|
||||
m_options->SetPauseDownload(true);
|
||||
m_options->SetPausePostProcess(true);
|
||||
m_options->SetPauseScan(true);
|
||||
m_workState->SetPauseDownload(true);
|
||||
m_workState->SetPausePostProcess(true);
|
||||
m_workState->SetPauseScan(true);
|
||||
}
|
||||
|
||||
m_serverPool->SetTimeout(m_options->GetArticleTimeout());
|
||||
@@ -451,41 +475,35 @@ void NZBGet::Cleanup()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool NZBGet::ProcessDirect()
|
||||
void NZBGet::ProcessDirect()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (m_commandLineParser->GetTestBacktrace())
|
||||
{
|
||||
TestSegFault();
|
||||
TestSegFault(); // never returns
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_commandLineParser->GetWebGet())
|
||||
{
|
||||
ProcessWebGet();
|
||||
return true;
|
||||
ProcessWebGet(); // never returns
|
||||
}
|
||||
|
||||
if (m_commandLineParser->GetSigVerify())
|
||||
{
|
||||
ProcessSigVerify();
|
||||
return true;
|
||||
ProcessSigVerify(); // never returns
|
||||
}
|
||||
|
||||
// client request
|
||||
if (m_commandLineParser->GetClientOperation() != CommandLineParser::opClientNoOperation)
|
||||
{
|
||||
ProcessClientRequest();
|
||||
return true;
|
||||
ProcessClientRequest(); // never returns
|
||||
}
|
||||
|
||||
if (m_commandLineParser->GetPrintOptions())
|
||||
{
|
||||
PrintOptions();
|
||||
return true;
|
||||
PrintOptions(); // never returns
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void NZBGet::StartRemoteServer()
|
||||
@@ -529,7 +547,26 @@ void NZBGet::StopRemoteServer()
|
||||
(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
|
||||
maxWaitMSec > 0)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
maxWaitMSec -= 100;
|
||||
}
|
||||
|
||||
if (m_remoteServer && m_remoteServer->IsRunning())
|
||||
{
|
||||
m_remoteServer->ForceStop();
|
||||
}
|
||||
|
||||
if (m_remoteSecureServer && m_remoteSecureServer->IsRunning())
|
||||
{
|
||||
m_remoteSecureServer->ForceStop();
|
||||
}
|
||||
|
||||
maxWaitMSec = 5000;
|
||||
while (((m_remoteServer && m_remoteServer->IsRunning()) ||
|
||||
(m_remoteSecureServer && m_remoteSecureServer->IsRunning())) &&
|
||||
maxWaitMSec > 0)
|
||||
{
|
||||
Util::Sleep(100);
|
||||
maxWaitMSec -= 100;
|
||||
}
|
||||
|
||||
@@ -587,7 +624,7 @@ void NZBGet::StopFrontend()
|
||||
}
|
||||
while (m_frontend->IsRunning())
|
||||
{
|
||||
usleep(50 * 1000);
|
||||
Util::Sleep(50);
|
||||
}
|
||||
debug("Frontend stopped");
|
||||
}
|
||||
@@ -667,7 +704,14 @@ void NZBGet::DoMainLoop()
|
||||
m_serviceCoordinator->Stop();
|
||||
}
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
|
||||
if (m_options->GetServerMode() && !m_stopped)
|
||||
{
|
||||
// wait for stop signal
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.Wait(m_waitMutex, [&]{ return m_stopped; });
|
||||
}
|
||||
}
|
||||
|
||||
debug("Main program loop terminated");
|
||||
@@ -679,10 +723,7 @@ void NZBGet::Run(bool reload)
|
||||
|
||||
Init();
|
||||
|
||||
if (ProcessDirect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
ProcessDirect();
|
||||
|
||||
StartRemoteServer();
|
||||
StartFrontend();
|
||||
@@ -701,6 +742,8 @@ void NZBGet::Run(bool reload)
|
||||
|
||||
StopRemoteServer();
|
||||
StopFrontend();
|
||||
|
||||
Final();
|
||||
}
|
||||
|
||||
void NZBGet::ProcessClientRequest()
|
||||
@@ -871,6 +914,11 @@ void NZBGet::Stop(bool reload)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// trigger stop/reload signal
|
||||
Guard guard(m_waitMutex);
|
||||
m_stopped = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void NZBGet::PrintOptions()
|
||||
@@ -879,6 +927,7 @@ void NZBGet::PrintOptions()
|
||||
{
|
||||
printf("%s = \"%s\"\n", optEntry.GetName(), optEntry.GetValue());
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
@@ -911,7 +960,12 @@ void NZBGet::Daemonize()
|
||||
error("Starting daemon failed: could not create lock-file %s", m_options->GetLockFile());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LOCKF
|
||||
if (lockf(lfp, F_TLOCK, 0) < 0)
|
||||
#else
|
||||
if (flock(lfp, LOCK_EX) < 0)
|
||||
#endif
|
||||
{
|
||||
error("Starting daemon failed: could not acquire lock on lock-file %s", m_options->GetLockFile());
|
||||
exit(1);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -59,9 +59,6 @@ compiled */
|
||||
/* Define to 1 if variadic macros are supported */
|
||||
#define HAVE_VARIADIC_MACROS
|
||||
|
||||
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
|
||||
#define HAVE_PAR2_CANCEL
|
||||
|
||||
/* Define to 1 if function GetAddrInfo is supported */
|
||||
#define HAVE_GETADDRINFO
|
||||
|
||||
@@ -95,6 +92,12 @@ compiled */
|
||||
#define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
|
||||
#ifdef _WIN64
|
||||
#define __amd64__
|
||||
#else
|
||||
#define __i686__
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
// detection of memory leaks
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
@@ -112,7 +115,7 @@ compiled */
|
||||
|
||||
// WINDOWS INCLUDES
|
||||
|
||||
// Using "WIN32_LEAN_AND_MEAN" to disable including on many unneeded headers
|
||||
// Using "WIN32_LEAN_AND_MEAN" to disable including of many unneeded headers
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
|
||||
@@ -166,21 +169,28 @@ using namespace MSXML;
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/file.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#include <pwd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#ifndef DISABLE_LIBXML2
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/entities.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ENDIAN_H
|
||||
#include <endian.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
@@ -199,6 +209,7 @@ using namespace MSXML;
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -206,12 +217,22 @@ using namespace MSXML;
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
|
||||
// NOTE: do not include <iostream> in "nzbget.h". <iostream> contains objects requiring
|
||||
// intialization, causing every unit in nzbget to have initialization routine. This in particular
|
||||
// is causing fatal problems in SIMD units which must not have static initialization because
|
||||
// they contain code with runtime CPU dispatching.
|
||||
//#include <iostream>
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef WIN32
|
||||
@@ -239,6 +260,7 @@ typedef int pid_t;
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/comp.h>
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
#ifdef HAVE_REGEX_H
|
||||
@@ -256,9 +278,6 @@ typedef int pid_t;
|
||||
#ifdef HAVE_MEMORY_H
|
||||
# include <memory.h>
|
||||
#endif
|
||||
#ifdef HAVE_INTTYPES_H
|
||||
# include <inttypes.h>
|
||||
#endif
|
||||
#endif /* NOT DISABLE_PARCHECK */
|
||||
|
||||
|
||||
@@ -285,7 +304,6 @@ typedef int pid_t;
|
||||
#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
|
||||
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
|
||||
#define S_DIRMODE nullptr
|
||||
#define usleep(usec) Sleep((usec) / 1000)
|
||||
#define socklen_t int
|
||||
#define SHUT_WR 0x01
|
||||
#define SHUT_RDWR 0x02
|
||||
@@ -307,6 +325,10 @@ typedef int pid_t;
|
||||
#define FOPEN_WB "wbN"
|
||||
#define FOPEN_AB "abN"
|
||||
|
||||
#define __SSE2__
|
||||
#define __SSSE3__
|
||||
#define __PCLMUL__
|
||||
|
||||
#ifdef DEBUG
|
||||
// redefine "exit" to avoid printing memory leaks report when terminated because of wrong command line switches
|
||||
#define exit(code) ExitProcess(code)
|
||||
@@ -362,8 +384,25 @@ typedef signed long long int64;
|
||||
typedef unsigned long long uint64;
|
||||
#endif
|
||||
|
||||
#ifndef PRId64
|
||||
#define PRId64 "lld"
|
||||
#endif
|
||||
#ifndef PRIi64
|
||||
#define PRIi64 "lli"
|
||||
#endif
|
||||
#ifndef PRIu64
|
||||
#define PRIu64 "llu"
|
||||
#endif
|
||||
|
||||
typedef unsigned char uchar;
|
||||
|
||||
// Assume little endian if byte order is not defined
|
||||
#ifndef __BYTE_ORDER
|
||||
#define __LITTLE_ENDIAN 1234
|
||||
#define __BIG_ENDIAN 4321
|
||||
#define __BYTE_ORDER __LITTLE_ENDIAN
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define PRINTF_SYNTAX(strindex) __attribute__ ((format (printf, strindex, strindex+1)))
|
||||
#define SCANF_SYNTAX(strindex) __attribute__ ((format (scanf, strindex, strindex+1)))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "ServerPool.h"
|
||||
#include "StatMeter.h"
|
||||
#include "Util.h"
|
||||
@@ -97,13 +98,13 @@ void ArticleDownloader::Run()
|
||||
while (!m_connection && !(IsStopped() || serverConfigGeneration != g_ServerPool->GetGeneration()))
|
||||
{
|
||||
m_connection = g_ServerPool->GetConnection(level, wantServer, &failedServers);
|
||||
usleep(5 * 1000);
|
||||
Util::Sleep(5);
|
||||
}
|
||||
SetLastUpdateTimeNow();
|
||||
SetStatus(adRunning);
|
||||
|
||||
if (IsStopped() || ((g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) && !force) ||
|
||||
(g_Options->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
|
||||
if (IsStopped() || ((g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) && !force) ||
|
||||
(g_WorkState->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
|
||||
serverConfigGeneration != g_ServerPool->GetGeneration())
|
||||
{
|
||||
status = adRetry;
|
||||
@@ -194,8 +195,8 @@ void ArticleDownloader::Run()
|
||||
break;
|
||||
}
|
||||
|
||||
if (IsStopped() || ((g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) && !force) ||
|
||||
(g_Options->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
|
||||
if (IsStopped() || ((g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) && !force) ||
|
||||
(g_WorkState->GetTempPauseDownload() && !m_fileInfo->GetExtraPriority()) ||
|
||||
serverConfigGeneration != g_ServerPool->GetGeneration())
|
||||
{
|
||||
status = adRetry;
|
||||
@@ -320,7 +321,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
}
|
||||
|
||||
// retrieve article
|
||||
response = m_connection->Request(BString<1024>("ARTICLE %s\r\n", m_articleInfo->GetMessageId()));
|
||||
response = m_connection->Request(BString<1024>("%s %s\r\n",
|
||||
g_Options->GetRawArticle() ? "ARTICLE" : "BODY", m_articleInfo->GetMessageId()));
|
||||
|
||||
status = CheckResponse(response, "could not fetch article");
|
||||
if (status != adFinished)
|
||||
@@ -328,47 +330,35 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
return status;
|
||||
}
|
||||
|
||||
if (g_Options->GetDecode())
|
||||
{
|
||||
m_yDecoder.Clear();
|
||||
m_yDecoder.SetCrcCheck(g_Options->GetCrcCheck());
|
||||
m_uDecoder.Clear();
|
||||
}
|
||||
m_decoder.Clear();
|
||||
m_decoder.SetCrcCheck(g_Options->GetCrcCheck());
|
||||
m_decoder.SetRawMode(g_Options->GetRawArticle());
|
||||
|
||||
bool body = false;
|
||||
bool end = false;
|
||||
CharBuffer lineBuf(1024*10);
|
||||
status = adRunning;
|
||||
CharBuffer lineBuf(1024*4);
|
||||
|
||||
while (!IsStopped())
|
||||
while (!IsStopped() && !m_decoder.GetEof())
|
||||
{
|
||||
time_t oldTime = m_lastUpdateTime;
|
||||
SetLastUpdateTimeNow();
|
||||
if (oldTime != m_lastUpdateTime)
|
||||
{
|
||||
AddServerData();
|
||||
}
|
||||
|
||||
// Throttle the bandwidth
|
||||
while (!IsStopped() && (g_Options->GetDownloadRate() > 0.0f) &&
|
||||
(g_StatMeter->CalcCurrentDownloadSpeed() > g_Options->GetDownloadRate() ||
|
||||
g_StatMeter->CalcMomentaryDownloadSpeed() > g_Options->GetDownloadRate()))
|
||||
// throttle the bandwidth
|
||||
while (!IsStopped() && (g_WorkState->GetSpeedLimit() > 0.0f) &&
|
||||
(g_StatMeter->CalcCurrentDownloadSpeed() > g_WorkState->GetSpeedLimit() ||
|
||||
g_StatMeter->CalcMomentaryDownloadSpeed() > g_WorkState->GetSpeedLimit()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(10 * 1000);
|
||||
Util::Sleep(10);
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
char* line = m_connection->ReadLine(lineBuf, lineBuf.Size(), &len);
|
||||
|
||||
g_StatMeter->AddSpeedReading(len);
|
||||
if (g_Options->GetAccurateRate())
|
||||
char* buffer;
|
||||
int len;
|
||||
m_connection->ReadBuffer(&buffer, &len);
|
||||
if (len == 0)
|
||||
{
|
||||
AddServerData();
|
||||
len = m_connection->TryRecv(lineBuf, lineBuf.Size());
|
||||
buffer = lineBuf;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
// have we encountered a timeout?
|
||||
if (len <= 0)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
@@ -378,67 +368,25 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
break;
|
||||
}
|
||||
|
||||
//detect end of article
|
||||
if (!strcmp(line, ".\r\n") || !strcmp(line, ".\n"))
|
||||
g_StatMeter->AddSpeedReading(len);
|
||||
time_t oldTime = m_lastUpdateTime;
|
||||
SetLastUpdateTimeNow();
|
||||
if (oldTime != m_lastUpdateTime)
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
AddServerData();
|
||||
}
|
||||
|
||||
//detect lines starting with "." (marked as "..")
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
len--;
|
||||
}
|
||||
|
||||
if (!body)
|
||||
{
|
||||
// detect body of article
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
body = true;
|
||||
}
|
||||
// check id of returned article
|
||||
else if (!strncmp(line, "Message-ID: ", 12))
|
||||
{
|
||||
char* p = line + 12;
|
||||
if (strncmp(p, m_articleInfo->GetMessageId(), strlen(m_articleInfo->GetMessageId())))
|
||||
{
|
||||
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
|
||||
detail("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", *m_infoName,
|
||||
*m_connectionName, m_articleInfo->GetMessageId(), p);
|
||||
status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_format == Decoder::efUnknown && g_Options->GetDecode())
|
||||
{
|
||||
m_format = Decoder::DetectFormat(line, len, body);
|
||||
if (m_format != Decoder::efUnknown)
|
||||
{
|
||||
// sometimes news servers misbehave and send article body without new line separator between headers and body
|
||||
// if we found decoder signature we know the body is already arrived
|
||||
body = true;
|
||||
}
|
||||
}
|
||||
// decode article data
|
||||
len = m_decoder.DecodeBuffer(buffer, len);
|
||||
|
||||
// write to output file
|
||||
if (((body && m_format != Decoder::efUnknown) || !g_Options->GetDecode()) && !Write(line, len))
|
||||
if (len > 0 && !Write(buffer, len))
|
||||
{
|
||||
status = adFatalError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!end && status == adRunning && !IsStopped())
|
||||
{
|
||||
detail("Article %s @ %s failed: article incomplete", *m_infoName, *m_connectionName);
|
||||
status = adFailed;
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
status = adFailed;
|
||||
@@ -497,57 +445,47 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* response
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Write(char* line, int len)
|
||||
bool ArticleDownloader::Write(char* buffer, int len)
|
||||
{
|
||||
const char* articleFilename = nullptr;
|
||||
int64 articleFileSize = 0;
|
||||
int64 articleOffset = 0;
|
||||
int articleSize = 0;
|
||||
|
||||
if (g_Options->GetDecode())
|
||||
if (!m_writingStarted)
|
||||
{
|
||||
if (m_format == Decoder::efYenc)
|
||||
if (!g_Options->GetRawArticle())
|
||||
{
|
||||
len = m_yDecoder.DecodeBuffer(line, len);
|
||||
articleFilename = m_yDecoder.GetArticleFilename();
|
||||
articleFileSize = m_yDecoder.GetSize();
|
||||
}
|
||||
else if (m_format == Decoder::efUx)
|
||||
{
|
||||
len = m_uDecoder.DecodeBuffer(line, len);
|
||||
articleFilename = m_uDecoder.GetArticleFilename();
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Decoding %s failed: unsupported encoding", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (len > 0 && m_format == Decoder::efYenc)
|
||||
{
|
||||
if (m_yDecoder.GetBegin() == 0 || m_yDecoder.GetEnd() == 0)
|
||||
articleFilename = m_decoder.GetArticleFilename();
|
||||
if (m_decoder.GetFormat() == Decoder::efYenc)
|
||||
{
|
||||
return false;
|
||||
if (m_decoder.GetBeginPos() == 0 || m_decoder.GetEndPos() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
articleFileSize = m_decoder.GetSize();
|
||||
articleOffset = m_decoder.GetBeginPos() - 1;
|
||||
articleSize = (int)(m_decoder.GetEndPos() - m_decoder.GetBeginPos() + 1);
|
||||
if (articleSize <= 0 || articleSize > 1024*1024*1024)
|
||||
{
|
||||
warn("Malformed article %s: size %i out of range", *m_infoName, articleSize);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
articleOffset = m_yDecoder.GetBegin() - 1;
|
||||
articleSize = (int)(m_yDecoder.GetEnd() - m_yDecoder.GetBegin() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_writingStarted && len > 0)
|
||||
{
|
||||
if (!m_articleWriter.Start(m_format, articleFilename, articleFileSize, articleOffset, articleSize))
|
||||
if (!m_articleWriter.Start(m_decoder.GetFormat(), articleFilename, articleFileSize, articleOffset, articleSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_writingStarted = true;
|
||||
}
|
||||
|
||||
bool ok = len == 0 || m_articleWriter.Write(line, len);
|
||||
bool ok = m_articleWriter.Write(buffer, len);
|
||||
|
||||
if (m_contentAnalyzer)
|
||||
{
|
||||
m_contentAnalyzer->Append(line, len);
|
||||
m_contentAnalyzer->Append(buffer, len);
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -555,36 +493,21 @@ bool ArticleDownloader::Write(char* line, int len)
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
{
|
||||
if (g_Options->GetDecode())
|
||||
if (!g_Options->GetRawArticle())
|
||||
{
|
||||
Decoder* decoder = nullptr;
|
||||
if (m_format == Decoder::efYenc)
|
||||
{
|
||||
decoder = &m_yDecoder;
|
||||
}
|
||||
else if (m_format == Decoder::efUx)
|
||||
{
|
||||
decoder = &m_uDecoder;
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Decoding %s failed: no binary data or unsupported encoding format", *m_infoName);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
Decoder::EStatus status = decoder->Check();
|
||||
Decoder::EStatus status = m_decoder.Check();
|
||||
|
||||
if (status == Decoder::dsFinished)
|
||||
{
|
||||
if (decoder->GetArticleFilename())
|
||||
if (m_decoder.GetArticleFilename())
|
||||
{
|
||||
m_articleFilename = decoder->GetArticleFilename();
|
||||
m_articleFilename = m_decoder.GetArticleFilename();
|
||||
}
|
||||
|
||||
if (m_format == Decoder::efYenc)
|
||||
if (m_decoder.GetFormat() == Decoder::efYenc)
|
||||
{
|
||||
m_articleInfo->SetCrc(g_Options->GetCrcCheck() ?
|
||||
m_yDecoder.GetCalculatedCrc() : m_yDecoder.GetExpectedCrc());
|
||||
m_decoder.GetCalculatedCrc() : m_decoder.GetExpectedCrc());
|
||||
}
|
||||
|
||||
return adFinished;
|
||||
@@ -645,22 +568,6 @@ void ArticleDownloader::Stop()
|
||||
debug("ArticleDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Terminate()
|
||||
{
|
||||
NntpConnection* connection = m_connection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && connection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
connection->SetSuppressErrors(true);
|
||||
connection->Cancel();
|
||||
connection->Disconnect();
|
||||
g_StatMeter->AddServerData(connection->FetchTotalBytesRead(), connection->GetNewsServer()->GetId());
|
||||
g_ServerPool->FreeConnection(connection, true);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void ArticleDownloader::FreeConnection(bool keepConnected)
|
||||
{
|
||||
if (m_connection)
|
||||
|
||||
@@ -66,7 +66,6 @@ public:
|
||||
ServerStatList* GetServerStats() { return &m_serverStats; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
bool Terminate();
|
||||
time_t GetLastUpdateTime() { return m_lastUpdateTime; }
|
||||
void SetLastUpdateTimeNow();
|
||||
const char* GetArticleFilename() { return m_articleFilename; }
|
||||
@@ -91,9 +90,7 @@ private:
|
||||
CString m_connectionName;
|
||||
CString m_articleFilename;
|
||||
time_t m_lastUpdateTime;
|
||||
Decoder::EFormat m_format = Decoder::efUnknown;
|
||||
YDecoder m_yDecoder;
|
||||
UDecoder m_uDecoder;
|
||||
Decoder m_decoder;
|
||||
ArticleWriter m_articleWriter;
|
||||
ServerStatList m_serverStats;
|
||||
bool m_writingStarted;
|
||||
@@ -105,7 +102,7 @@ private:
|
||||
void FreeConnection(bool keepConnected);
|
||||
EStatus CheckResponse(const char* response, const char* comment);
|
||||
void SetStatus(EStatus status) { m_status = status; }
|
||||
bool Write(char* line, int len);
|
||||
bool Write(char* buffer, int len);
|
||||
void AddServerData();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2014-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -106,14 +106,14 @@ bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 f
|
||||
}
|
||||
|
||||
// allocate cache buffer
|
||||
if (g_Options->GetArticleCache() > 0 && g_Options->GetDecode() &&
|
||||
if (g_Options->GetArticleCache() > 0 && !g_Options->GetRawArticle() &&
|
||||
(!g_Options->GetDirectWrite() || m_format == Decoder::efYenc))
|
||||
{
|
||||
m_articleData = g_ArticleCache->Alloc(m_articleSize);
|
||||
|
||||
while (!m_articleData.GetData() && g_ArticleCache->GetFlushing())
|
||||
{
|
||||
usleep(5 * 1000);
|
||||
Util::Sleep(5);
|
||||
m_articleData = g_ArticleCache->Alloc(m_articleSize);
|
||||
}
|
||||
|
||||
@@ -126,11 +126,11 @@ bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 f
|
||||
if (!m_articleData.GetData())
|
||||
{
|
||||
bool directWrite = (g_Options->GetDirectWrite() || m_fileInfo->GetForceDirectWrite()) && m_format == Decoder::efYenc;
|
||||
const char* filename = directWrite ? m_outputFilename : m_tempFilename;
|
||||
if (!m_outFile.Open(filename, directWrite ? DiskFile::omReadWrite : DiskFile::omWrite))
|
||||
const char* outFilename = directWrite ? m_outputFilename : m_tempFilename;
|
||||
if (!m_outFile.Open(outFilename, directWrite ? DiskFile::omReadWrite : DiskFile::omWrite))
|
||||
{
|
||||
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
|
||||
"Could not %s file %s: %s", directWrite ? "open" : "create", filename,
|
||||
"Could not %s file %s: %s", directWrite ? "open" : "create", outFilename,
|
||||
*FileSystem::GetLastErrorMessage());
|
||||
return false;
|
||||
}
|
||||
@@ -147,22 +147,31 @@ bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 f
|
||||
|
||||
bool ArticleWriter::Write(char* buffer, int len)
|
||||
{
|
||||
if (g_Options->GetDecode())
|
||||
if (!g_Options->GetRawArticle())
|
||||
{
|
||||
m_articlePtr += len;
|
||||
}
|
||||
|
||||
if (g_Options->GetDecode() && m_articleData.GetData())
|
||||
if (m_articlePtr > m_articleSize)
|
||||
{
|
||||
// An attempt to write beyond article border is detected.
|
||||
// That's an error condition (damaged article).
|
||||
// We return 'false' since this isn't a fatal disk error and
|
||||
// article size mismatch will be detected in decoder check anyway.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!g_Options->GetRawArticle() && m_articleData.GetData())
|
||||
{
|
||||
if (m_articlePtr > m_articleSize)
|
||||
{
|
||||
detail("Decoding %s failed: article size mismatch", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
memcpy(m_articleData.GetData() + m_articlePtr - len, buffer, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (g_Options->GetSkipWrite())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_outFile.Write(buffer, len) > 0;
|
||||
}
|
||||
|
||||
@@ -179,7 +188,7 @@ void ArticleWriter::Finish(bool success)
|
||||
|
||||
bool directWrite = (g_Options->GetDirectWrite() || m_fileInfo->GetForceDirectWrite()) && m_format == Decoder::efYenc;
|
||||
|
||||
if (g_Options->GetDecode())
|
||||
if (!g_Options->GetRawArticle())
|
||||
{
|
||||
if (!directWrite && !m_articleData.GetData())
|
||||
{
|
||||
@@ -189,10 +198,9 @@ void ArticleWriter::Finish(bool success)
|
||||
"Could not rename file %s to %s: %s", *m_tempFilename, m_resultFilename,
|
||||
*FileSystem::GetLastErrorMessage());
|
||||
}
|
||||
FileSystem::DeleteFile(m_tempFilename);
|
||||
}
|
||||
|
||||
FileSystem::DeleteFile(m_tempFilename);
|
||||
|
||||
if (m_articleData.GetData())
|
||||
{
|
||||
if (m_articleSize != m_articlePtr)
|
||||
@@ -224,19 +232,20 @@ void ArticleWriter::Finish(bool success)
|
||||
/* creates output file and subdirectores */
|
||||
bool ArticleWriter::CreateOutputFile(int64 size)
|
||||
{
|
||||
if (g_Options->GetDirectWrite() && FileSystem::FileExists(m_outputFilename) &&
|
||||
FileSystem::FileSize(m_outputFilename) == size)
|
||||
if (FileSystem::FileExists(m_outputFilename))
|
||||
{
|
||||
// keep existing old file from previous program session
|
||||
return true;
|
||||
if (FileSystem::FileSize(m_outputFilename) == size)
|
||||
{
|
||||
// keep existing old file from previous program session
|
||||
return true;
|
||||
}
|
||||
// delete existing old file from previous program session
|
||||
FileSystem::DeleteFile(m_outputFilename);
|
||||
}
|
||||
|
||||
// delete eventually existing old file from previous program session
|
||||
FileSystem::DeleteFile(m_outputFilename);
|
||||
|
||||
// ensure the directory exist
|
||||
BString<1024> destDir;
|
||||
destDir.Set(m_outputFilename, FileSystem::BaseFileName(m_outputFilename) - m_outputFilename);
|
||||
destDir.Set(m_outputFilename, (int)(FileSystem::BaseFileName(m_outputFilename) - m_outputFilename));
|
||||
CString errmsg;
|
||||
|
||||
if (!FileSystem::ForceDirectories(destDir, errmsg))
|
||||
@@ -305,7 +314,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
|
||||
bool cached = m_fileInfo->GetCachedArticles() > 0;
|
||||
|
||||
if (!g_Options->GetDecode())
|
||||
if (g_Options->GetRawArticle())
|
||||
{
|
||||
detail("Moving articles for %s", *infoFilename);
|
||||
}
|
||||
@@ -344,7 +353,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
DiskFile outfile;
|
||||
BString<1024> tmpdestfile("%s.tmp", *ofn);
|
||||
|
||||
if (g_Options->GetDecode() && !directWrite)
|
||||
if (!g_Options->GetRawArticle() && !directWrite)
|
||||
{
|
||||
FileSystem::DeleteFile(tmpdestfile);
|
||||
if (!outfile.Open(tmpdestfile, DiskFile::omWrite))
|
||||
@@ -364,7 +373,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
}
|
||||
tmpdestfile = *m_outputFilename;
|
||||
}
|
||||
else if (!g_Options->GetDecode())
|
||||
else if (g_Options->GetRawArticle())
|
||||
{
|
||||
FileSystem::DeleteFile(tmpdestfile);
|
||||
if (!FileSystem::CreateDirectory(ofn))
|
||||
@@ -392,7 +401,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
CharBuffer buffer;
|
||||
bool firstArticle = true;
|
||||
|
||||
if (g_Options->GetDecode() && !directWrite)
|
||||
if (!g_Options->GetRawArticle() && !directWrite)
|
||||
{
|
||||
buffer.Reserve(1024 * 64);
|
||||
}
|
||||
@@ -404,21 +413,27 @@ void ArticleWriter::CompleteFileParts()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (g_Options->GetDecode() && !directWrite && pa->GetSegmentOffset() > -1 &&
|
||||
if (!g_Options->GetRawArticle() && !directWrite && pa->GetSegmentOffset() > -1 &&
|
||||
pa->GetSegmentOffset() > outfile.Position() && outfile.Position() > -1)
|
||||
{
|
||||
memset(buffer, 0, buffer.Size());
|
||||
while (pa->GetSegmentOffset() > outfile.Position() && outfile.Position() > -1 &&
|
||||
outfile.Write(buffer, std::min((int)(pa->GetSegmentOffset() - outfile.Position()), buffer.Size())));
|
||||
if (!g_Options->GetSkipWrite())
|
||||
{
|
||||
while (pa->GetSegmentOffset() > outfile.Position() && outfile.Position() > -1 &&
|
||||
outfile.Write(buffer, std::min((int)(pa->GetSegmentOffset() - outfile.Position()), buffer.Size())));
|
||||
}
|
||||
}
|
||||
|
||||
if (pa->GetSegmentContent())
|
||||
{
|
||||
outfile.Seek(pa->GetSegmentOffset());
|
||||
outfile.Write(pa->GetSegmentContent(), pa->GetSegmentSize());
|
||||
if (!g_Options->GetSkipWrite())
|
||||
{
|
||||
outfile.Seek(pa->GetSegmentOffset());
|
||||
outfile.Write(pa->GetSegmentContent(), pa->GetSegmentSize());
|
||||
}
|
||||
pa->DiscardSegment();
|
||||
}
|
||||
else if (g_Options->GetDecode() && !directWrite)
|
||||
else if (!g_Options->GetRawArticle() && !directWrite && !g_Options->GetSkipWrite())
|
||||
{
|
||||
DiskFile infile;
|
||||
if (pa->GetResultFilename() && infile.Open(pa->GetResultFilename(), DiskFile::omRead))
|
||||
@@ -441,7 +456,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
(int)m_fileInfo->GetArticles()->size());
|
||||
}
|
||||
}
|
||||
else if (!g_Options->GetDecode())
|
||||
else if (g_Options->GetRawArticle())
|
||||
{
|
||||
BString<1024> dstFileName("%s%c%03i", *ofn, PATH_SEPARATOR, pa->GetPartNumber());
|
||||
if (!FileSystem::MoveFile(pa->GetResultFilename(), dstFileName))
|
||||
@@ -454,7 +469,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
|
||||
if (m_format == Decoder::efYenc)
|
||||
{
|
||||
crc = firstArticle ? pa->GetCrc() : Util::Crc32Combine(crc, pa->GetCrc(), pa->GetSegmentSize());
|
||||
crc = firstArticle ? pa->GetCrc() : Crc32::Combine(crc, pa->GetCrc(), pa->GetSegmentSize());
|
||||
firstArticle = false;
|
||||
}
|
||||
}
|
||||
@@ -490,7 +505,7 @@ void ArticleWriter::CompleteFileParts()
|
||||
{
|
||||
debug("Checking old dir for: %s", *m_outputFilename);
|
||||
BString<1024> oldDestDir;
|
||||
oldDestDir.Set(m_outputFilename, FileSystem::BaseFileName(m_outputFilename) - m_outputFilename);
|
||||
oldDestDir.Set(m_outputFilename, (int)(FileSystem::BaseFileName(m_outputFilename) - m_outputFilename));
|
||||
if (FileSystem::DirEmpty(oldDestDir))
|
||||
{
|
||||
debug("Deleting old dir: %s", *oldDestDir);
|
||||
@@ -520,18 +535,6 @@ void ArticleWriter::CompleteFileParts()
|
||||
"%i of %i article downloads failed for \"%s\"",
|
||||
m_fileInfo->GetMissedArticles() + m_fileInfo->GetFailedArticles(),
|
||||
m_fileInfo->GetTotalArticles(), *infoFilename);
|
||||
|
||||
if (g_Options->GetBrokenLog())
|
||||
{
|
||||
BString<1024> brokenLogName("%s%c_brokenlog.txt", *nzbDestDir, PATH_SEPARATOR);
|
||||
DiskFile file;
|
||||
if (file.Open(brokenLogName, DiskFile::omAppend))
|
||||
{
|
||||
file.Print("%s (%i/%i)%s", *filename, m_fileInfo->GetSuccessArticles(),
|
||||
m_fileInfo->GetTotalArticles(), LINE_ENDING);
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -579,10 +582,18 @@ void ArticleWriter::FlushCache()
|
||||
ArticleCache::FlushGuard flushGuard = g_ArticleCache->GuardFlush();
|
||||
|
||||
std::vector<ArticleInfo*> cachedArticles;
|
||||
cachedArticles.reserve(m_fileInfo->GetArticles()->size());
|
||||
|
||||
{
|
||||
Guard contentGuard = g_ArticleCache->GuardContent();
|
||||
|
||||
if (m_fileInfo->GetFlushLocked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_fileInfo->SetFlushLocked(true);
|
||||
|
||||
cachedArticles.reserve(m_fileInfo->GetArticles()->size());
|
||||
for (ArticleInfo* pa : m_fileInfo->GetArticles())
|
||||
{
|
||||
if (pa->GetSegmentContent())
|
||||
@@ -644,7 +655,10 @@ void ArticleWriter::FlushCache()
|
||||
outfile.Seek(pa->GetSegmentOffset());
|
||||
}
|
||||
|
||||
outfile.Write(pa->GetSegmentContent(), pa->GetSegmentSize());
|
||||
if (!g_Options->GetSkipWrite())
|
||||
{
|
||||
outfile.Write(pa->GetSegmentContent(), pa->GetSegmentSize());
|
||||
}
|
||||
|
||||
flushedSize += pa->GetSegmentSize();
|
||||
flushedArticles++;
|
||||
@@ -669,6 +683,7 @@ void ArticleWriter::FlushCache()
|
||||
{
|
||||
Guard contentGuard = g_ArticleCache->GuardContent();
|
||||
m_fileInfo->SetCachedArticles(m_fileInfo->GetCachedArticles() - flushedArticles);
|
||||
m_fileInfo->SetFlushLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -712,57 +727,6 @@ bool ArticleWriter::MoveCompletedFiles(NzbInfo* nzbInfo, const char* oldDestDir)
|
||||
}
|
||||
}
|
||||
|
||||
// move brokenlog.txt
|
||||
if (g_Options->GetBrokenLog())
|
||||
{
|
||||
BString<1024> oldBrokenLogName("%s%c_brokenlog.txt", oldDestDir, PATH_SEPARATOR);
|
||||
if (FileSystem::FileExists(oldBrokenLogName))
|
||||
{
|
||||
BString<1024> brokenLogName("%s%c_brokenlog.txt", nzbInfo->GetDestDir(), PATH_SEPARATOR);
|
||||
|
||||
detail("Moving file %s to %s", *oldBrokenLogName, *brokenLogName);
|
||||
if (FileSystem::FileExists(brokenLogName))
|
||||
{
|
||||
// copy content to existing new file, then delete old file
|
||||
DiskFile outfile;
|
||||
if (outfile.Open(brokenLogName, DiskFile::omAppend))
|
||||
{
|
||||
DiskFile infile;
|
||||
if (infile.Open(oldBrokenLogName, DiskFile::omRead))
|
||||
{
|
||||
CharBuffer buffer(1024 * 50);
|
||||
int cnt = buffer.Size();
|
||||
while (cnt == buffer.Size())
|
||||
{
|
||||
cnt = (int)infile.Read(buffer, buffer.Size());
|
||||
outfile.Write(buffer, cnt);
|
||||
}
|
||||
infile.Close();
|
||||
FileSystem::DeleteFile(oldBrokenLogName);
|
||||
}
|
||||
else
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkError, "Could not open file %s", *oldBrokenLogName);
|
||||
}
|
||||
outfile.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkError, "Could not open file %s", *brokenLogName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to new destination
|
||||
if (!FileSystem::MoveFile(oldBrokenLogName, brokenLogName))
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkError, "Could not move file %s to %s: %s",
|
||||
*oldBrokenLogName, *brokenLogName, *FileSystem::GetLastErrorMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete old directory (if empty)
|
||||
if (FileSystem::DirEmpty(oldDestDir))
|
||||
{
|
||||
@@ -807,10 +771,15 @@ CachedSegmentData ArticleCache::Alloc(int size)
|
||||
p = malloc(size);
|
||||
if (p)
|
||||
{
|
||||
if (!m_allocated && g_Options->GetSaveQueue() && g_Options->GetServerMode() && g_Options->GetContinuePartial())
|
||||
if (!m_allocated && g_Options->GetServerMode() && g_Options->GetContinuePartial())
|
||||
{
|
||||
g_DiskState->WriteCacheFlag();
|
||||
}
|
||||
if (!m_allocated)
|
||||
{
|
||||
// Resume Run(), the notification arrives later, after releasing m_allocMutex
|
||||
m_allocCond.NotifyAll();
|
||||
}
|
||||
m_allocated += size;
|
||||
}
|
||||
}
|
||||
@@ -827,6 +796,7 @@ bool ArticleCache::Realloc(CachedSegmentData* segment, int newSize)
|
||||
{
|
||||
m_allocated += newSize - segment->m_size;
|
||||
segment->m_size = newSize;
|
||||
segment->m_data = (char*)p;
|
||||
}
|
||||
|
||||
return p;
|
||||
@@ -840,7 +810,7 @@ void ArticleCache::Free(CachedSegmentData* segment)
|
||||
|
||||
Guard guard(m_allocMutex);
|
||||
m_allocated -= segment->m_size;
|
||||
if (!m_allocated && g_Options->GetSaveQueue() && g_Options->GetServerMode() && g_Options->GetContinuePartial())
|
||||
if (!m_allocated && g_Options->GetServerMode() && g_Options->GetContinuePartial())
|
||||
{
|
||||
g_DiskState->DeleteCacheFlag();
|
||||
}
|
||||
@@ -856,21 +826,36 @@ void ArticleCache::Run()
|
||||
bool justFlushed = false;
|
||||
while (!IsStopped() || m_allocated > 0)
|
||||
{
|
||||
if ((justFlushed || resetCounter >= 1000 || IsStopped() ||
|
||||
if ((justFlushed || resetCounter >= 1000 || IsStopped() ||
|
||||
(g_Options->GetDirectWrite() && m_allocated >= fillThreshold)) &&
|
||||
m_allocated > 0)
|
||||
{
|
||||
justFlushed = CheckFlush(m_allocated >= fillThreshold);
|
||||
resetCounter = 0;
|
||||
}
|
||||
else if (!m_allocated)
|
||||
{
|
||||
Guard guard(m_allocMutex);
|
||||
m_allocCond.Wait(m_allocMutex, [&]{ return IsStopped() || m_allocated > 0; });
|
||||
resetCounter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
usleep(5 * 1000);
|
||||
Util::Sleep(5);
|
||||
resetCounter += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleCache::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
// Resume Run() to exit it
|
||||
Guard guard(m_allocMutex);
|
||||
m_allocCond.NotifyAll();
|
||||
}
|
||||
|
||||
bool ArticleCache::CheckFlush(bool flushEverything)
|
||||
{
|
||||
debug("Checking cache, Allocated: %i, FlushEverything: %i", (int)m_allocated, (int)flushEverything);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2014-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -95,6 +95,7 @@ public:
|
||||
};
|
||||
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
CachedSegmentData Alloc(int size);
|
||||
bool Realloc(CachedSegmentData* segment, int newSize);
|
||||
void Free(CachedSegmentData* segment);
|
||||
@@ -111,6 +112,7 @@ private:
|
||||
Mutex m_flushMutex;
|
||||
Mutex m_contentMutex;
|
||||
FileInfo* m_fileInfo = nullptr;
|
||||
ConditionVar m_allocCond;
|
||||
|
||||
bool CheckFlush(bool flushEverything);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,22 +22,129 @@
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "YEncode.h"
|
||||
|
||||
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
|
||||
Decoder::Decoder()
|
||||
{
|
||||
debug("%s", YEncode::decode_simd ? "SIMD yEnc decoder can be used" : "SIMD yEnc decoder isn't available for this CPU");
|
||||
debug("%s", YEncode::crc_simd ? "SIMD Crc routine can be used" : "SIMD Crc routine isn't available for this CPU");
|
||||
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Decoder::Clear()
|
||||
{
|
||||
m_articleFilename.Clear();
|
||||
m_body = false;
|
||||
m_begin = false;
|
||||
m_part = false;
|
||||
m_end = false;
|
||||
m_crc = false;
|
||||
m_eof = false;
|
||||
m_expectedCRC = 0;
|
||||
m_crc32.Reset();
|
||||
m_beginPos = 0;
|
||||
m_endPos = 0;
|
||||
m_size = 0;
|
||||
m_endSize = 0;
|
||||
m_outSize = 0;
|
||||
m_state = 0;
|
||||
m_crcCheck = false;
|
||||
m_lineBuf.Reserve(1024*8);
|
||||
m_lineBuf.SetLength(0);
|
||||
}
|
||||
|
||||
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len, bool inBody)
|
||||
/* At the beginning of article the processing goes line by line to find '=ybegin'-marker.
|
||||
* Once the yEnc-data is started switches to blockwise processing.
|
||||
* At the end of yEnc-data switches back to line by line mode to
|
||||
* process '=yend'-marker and EOF-marker.
|
||||
* UU-encoded articles are processed completely in line by line mode.
|
||||
*/
|
||||
int Decoder::DecodeBuffer(char* buffer, int len)
|
||||
{
|
||||
if (m_rawMode)
|
||||
{
|
||||
ProcessRaw(buffer, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
int outlen = 0;
|
||||
|
||||
if (m_body && m_format == efYenc)
|
||||
{
|
||||
outlen = DecodeYenc(buffer, buffer, len);
|
||||
if (m_body)
|
||||
{
|
||||
return outlen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lineBuf.Append(buffer, len);
|
||||
}
|
||||
|
||||
char* line = (char*)m_lineBuf;
|
||||
while (char* end = strchr(line, '\n'))
|
||||
{
|
||||
int llen = (int)(end - line + 1);
|
||||
|
||||
if (line[0] == '.' && line[1] == '\r')
|
||||
{
|
||||
m_eof = true;
|
||||
m_lineBuf.SetLength(0);
|
||||
return outlen;
|
||||
}
|
||||
|
||||
if (m_format == efUnknown)
|
||||
{
|
||||
m_format = DetectFormat(line, llen);
|
||||
}
|
||||
|
||||
if (m_format == efYenc)
|
||||
{
|
||||
ProcessYenc(line, llen);
|
||||
if (m_body)
|
||||
{
|
||||
outlen = DecodeYenc(end + 1, buffer, m_lineBuf.Length() - (int)(end + 1 - m_lineBuf));
|
||||
if (m_body)
|
||||
{
|
||||
m_lineBuf.SetLength(0);
|
||||
return outlen;
|
||||
}
|
||||
line = (char*)m_lineBuf;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (m_format == efUx)
|
||||
{
|
||||
outlen += DecodeUx(line, llen);
|
||||
}
|
||||
|
||||
line = end + 1;
|
||||
}
|
||||
|
||||
if (*line)
|
||||
{
|
||||
len = m_lineBuf.Length() - (int)(line - m_lineBuf);
|
||||
memmove((char*)m_lineBuf, line, len);
|
||||
m_lineBuf.SetLength(len);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lineBuf.SetLength(0);
|
||||
}
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
|
||||
{
|
||||
if (!strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
return efYenc;
|
||||
}
|
||||
|
||||
if (inBody && (len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
{
|
||||
return efUx;
|
||||
}
|
||||
@@ -64,139 +171,122 @@ Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len, bool inBody)
|
||||
return efUnknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* YDecoder: fast implementation of yEnc-Decoder
|
||||
*/
|
||||
|
||||
YDecoder::YDecoder()
|
||||
void Decoder::ProcessYenc(char* buffer, int len)
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void YDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
m_body = false;
|
||||
m_begin = false;
|
||||
m_part = false;
|
||||
m_end = false;
|
||||
m_crc = false;
|
||||
m_expectedCRC = 0;
|
||||
m_calculatedCRC = 0xFFFFFFFF;
|
||||
m_beginPos = 0;
|
||||
m_endPos = 0;
|
||||
m_size = 0;
|
||||
m_endSize = 0;
|
||||
m_crcCheck = false;
|
||||
}
|
||||
|
||||
int YDecoder::DecodeBuffer(char* buffer, int len)
|
||||
{
|
||||
if (m_body && !m_end)
|
||||
if (!strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
if (!strncmp(buffer, "=yend ", 6))
|
||||
m_begin = true;
|
||||
char* pb = strstr(buffer, " name=");
|
||||
if (pb)
|
||||
{
|
||||
m_end = true;
|
||||
char* pb = strstr(buffer, m_part ? " pcrc32=" : " crc32=");
|
||||
if (pb)
|
||||
{
|
||||
m_crc = true;
|
||||
pb += 7 + (int)m_part; //=strlen(" crc32=") or strlen(" pcrc32=")
|
||||
m_expectedCRC = strtoul(pb, nullptr, 16);
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_endSize = (int64)atoll(pb);
|
||||
}
|
||||
return 0;
|
||||
pb += 6; //=strlen(" name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++);
|
||||
m_articleFilename = WebUtil::Latin1ToUtf8(CString(pb, (int)(pe - pb)));
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (true)
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_size = (int64)atoll(pb);
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
if (m_crcCheck)
|
||||
m_part = strstr(buffer, " part=");
|
||||
if (!m_part)
|
||||
{
|
||||
m_calculatedCRC = Util::Crc32m(m_calculatedCRC, (uchar *)buffer, (uint32)(optr - buffer));
|
||||
}
|
||||
return (int)(optr - buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_part && !strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
m_begin = true;
|
||||
char* pb = strstr(buffer, " name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
m_articleFilename = WebUtil::Latin1ToUtf8(CString(pb, (int)(pe - pb)));
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_size = (int64)atoll(pb);
|
||||
}
|
||||
m_part = strstr(buffer, " part=");
|
||||
if (!m_part)
|
||||
{
|
||||
m_body = true;
|
||||
m_beginPos = 1;
|
||||
m_endPos = m_size;
|
||||
}
|
||||
}
|
||||
else if (m_part && !strncmp(buffer, "=ypart ", 7))
|
||||
{
|
||||
m_part = true;
|
||||
m_body = true;
|
||||
char* pb = strstr(buffer, " begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 7; //=strlen(" begin=")
|
||||
m_beginPos = (int64)atoll(pb);
|
||||
}
|
||||
pb = strstr(buffer, " end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen(" end=")
|
||||
m_endPos = (int64)atoll(pb);
|
||||
}
|
||||
m_beginPos = 1;
|
||||
m_endPos = m_size;
|
||||
}
|
||||
}
|
||||
else if (!strncmp(buffer, "=ypart ", 7))
|
||||
{
|
||||
m_part = true;
|
||||
m_body = true;
|
||||
char* pb = strstr(buffer, " begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 7; //=strlen(" begin=")
|
||||
m_beginPos = (int64)atoll(pb);
|
||||
}
|
||||
pb = strstr(buffer, " end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen(" end=")
|
||||
m_endPos = (int64)atoll(pb);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(buffer, "=yend ", 6))
|
||||
{
|
||||
m_end = true;
|
||||
char* pb = strstr(buffer, m_part ? " pcrc32=" : " crc32=");
|
||||
if (pb)
|
||||
{
|
||||
m_crc = true;
|
||||
pb += 7 + (int)m_part; //=strlen(" crc32=") or strlen(" pcrc32=")
|
||||
m_expectedCRC = strtoul(pb, nullptr, 16);
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_endSize = (int64)atoll(pb);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Decoder::EStatus YDecoder::Check()
|
||||
int Decoder::DecodeYenc(char* buffer, char* outbuf, int len)
|
||||
{
|
||||
m_calculatedCRC ^= 0xFFFFFFFF;
|
||||
const unsigned char* src = (unsigned char*)buffer;
|
||||
unsigned char* dst = (unsigned char*)outbuf;
|
||||
|
||||
int endseq = YEncode::decode(&src, &dst, len, (YEncode::YencDecoderState*)&m_state);
|
||||
int outlen = (int)((char*)dst - outbuf);
|
||||
|
||||
// endseq:
|
||||
// 0: no end sequence found
|
||||
// 1: \r\n=y sequence found, src points to byte after 'y'
|
||||
// 2: \r\n.\r\n sequence found, src points to byte after last '\n'
|
||||
if (endseq != 0)
|
||||
{
|
||||
// switch back to line mode to process '=yend'- or eof- marker
|
||||
m_lineBuf.SetLength(0);
|
||||
m_lineBuf.Append(endseq == 1 ? "=y" : ".\r\n");
|
||||
int rem = len - (int)((const char*)src - buffer);
|
||||
if (rem > 0)
|
||||
{
|
||||
m_lineBuf.Append((const char*)src, rem);
|
||||
}
|
||||
m_body = false;
|
||||
}
|
||||
|
||||
if (m_crcCheck)
|
||||
{
|
||||
m_crc32.Append((uchar*)outbuf, (uint32)outlen);
|
||||
}
|
||||
|
||||
m_outSize += outlen;
|
||||
|
||||
return outlen;
|
||||
}
|
||||
|
||||
Decoder::EStatus Decoder::Check()
|
||||
{
|
||||
switch (m_format)
|
||||
{
|
||||
case efYenc:
|
||||
return CheckYenc();
|
||||
|
||||
case efUx:
|
||||
return CheckUx();
|
||||
|
||||
default:
|
||||
return dsUnknownError;
|
||||
}
|
||||
}
|
||||
|
||||
Decoder::EStatus Decoder::CheckYenc()
|
||||
{
|
||||
m_calculatedCRC = m_crc32.Finish();
|
||||
|
||||
debug("Expected crc32=%x", m_expectedCRC);
|
||||
debug("Calculated crc32=%x", m_calculatedCRC);
|
||||
@@ -209,7 +299,7 @@ Decoder::EStatus YDecoder::Check()
|
||||
{
|
||||
return dsArticleIncomplete;
|
||||
}
|
||||
else if (!m_part && m_size != m_endSize)
|
||||
else if ((!m_part && m_size != m_endSize) || (m_endSize != m_outSize))
|
||||
{
|
||||
return dsInvalidSize;
|
||||
}
|
||||
@@ -222,24 +312,7 @@ Decoder::EStatus YDecoder::Check()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UDecoder: supports UU encoding formats
|
||||
*/
|
||||
|
||||
UDecoder::UDecoder()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void UDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
m_body = false;
|
||||
m_end = false;
|
||||
}
|
||||
|
||||
/* DecodeBuffer-function uses portions of code from tool UUDECODE by Clem Dye
|
||||
/* DecodeUx-function uses portions of code from tool UUDECODE by Clem Dye
|
||||
* UUDECODE.c (http://www.bastet.com/uue.zip)
|
||||
* Copyright (C) 1998 Clem Dye
|
||||
*
|
||||
@@ -248,7 +321,7 @@ void UDecoder::Clear()
|
||||
|
||||
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
|
||||
|
||||
int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
int Decoder::DecodeUx(char* buffer, int len)
|
||||
{
|
||||
if (!m_body)
|
||||
{
|
||||
@@ -301,10 +374,7 @@ int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (effLen >= 1)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
}
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
if (effLen >= 2)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
|
||||
@@ -318,7 +388,7 @@ int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
Decoder::EStatus UDecoder::Check()
|
||||
Decoder::EStatus Decoder::CheckUx()
|
||||
{
|
||||
if (!m_body)
|
||||
{
|
||||
@@ -327,3 +397,50 @@ Decoder::EStatus UDecoder::Check()
|
||||
|
||||
return dsFinished;
|
||||
}
|
||||
|
||||
void Decoder::ProcessRaw(char* buffer, int len)
|
||||
{
|
||||
switch (m_state)
|
||||
{
|
||||
case 1:
|
||||
m_eof = len >= 4 && buffer[0] == '\n' &&
|
||||
buffer[1] == '.' && buffer[2] == '\r' && buffer[3] == '\n';
|
||||
break;
|
||||
|
||||
case 2:
|
||||
m_eof = len >= 3 && buffer[0] == '.' && buffer[1] == '\r' && buffer[2] == '\n';
|
||||
break;
|
||||
|
||||
case 3:
|
||||
m_eof = len >= 2 && buffer[0] == '\r' && buffer[1] == '\n';
|
||||
break;
|
||||
|
||||
case 4:
|
||||
m_eof = len >= 1 && buffer[0] == '\n';
|
||||
break;
|
||||
}
|
||||
|
||||
m_eof |= len >= 5 && strstr(buffer, "\r\n.\r\n");
|
||||
|
||||
if (len >= 4 && buffer[len-4] == '\r' && buffer[len-3] == '\n' &&
|
||||
buffer[len-2] == '.' && buffer[len-1] == '\r')
|
||||
{
|
||||
m_state = 4;
|
||||
}
|
||||
else if (len >= 3 && buffer[len-3] == '\r' && buffer[len-2] == '\n' && buffer[len-1] == '.')
|
||||
{
|
||||
m_state = 3;
|
||||
}
|
||||
else if (len >= 2 && buffer[len-2] == '\r' && buffer[len-1] == '\n')
|
||||
{
|
||||
m_state = 2;
|
||||
}
|
||||
else if (len >= 1 && buffer[len-1] == '\r')
|
||||
{
|
||||
m_state = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@
|
||||
#define DECODER_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Util.h"
|
||||
|
||||
class Decoder
|
||||
{
|
||||
@@ -43,37 +44,26 @@ public:
|
||||
efUx,
|
||||
};
|
||||
|
||||
static const char* FormatNames[];
|
||||
|
||||
virtual ~Decoder() {}
|
||||
virtual EStatus Check() = 0;
|
||||
virtual void Clear();
|
||||
virtual int DecodeBuffer(char* buffer, int len) = 0;
|
||||
const char* GetArticleFilename() { return m_articleFilename; }
|
||||
static EFormat DetectFormat(const char* buffer, int len, bool inBody);
|
||||
|
||||
protected:
|
||||
CString m_articleFilename;
|
||||
};
|
||||
|
||||
class YDecoder: public Decoder
|
||||
{
|
||||
public:
|
||||
YDecoder();
|
||||
virtual EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual int DecodeBuffer(char* buffer, int len);
|
||||
Decoder();
|
||||
EStatus Check();
|
||||
void Clear();
|
||||
int DecodeBuffer(char* buffer, int len);
|
||||
void SetCrcCheck(bool crcCheck) { m_crcCheck = crcCheck; }
|
||||
int64 GetBegin() { return m_beginPos; }
|
||||
int64 GetEnd() { return m_endPos; }
|
||||
void SetRawMode(bool rawMode) { m_rawMode = rawMode; }
|
||||
EFormat GetFormat() { return m_format; }
|
||||
int64 GetBeginPos() { return m_beginPos; }
|
||||
int64 GetEndPos() { return m_endPos; }
|
||||
int64 GetSize() { return m_size; }
|
||||
uint32 GetExpectedCrc() { return m_expectedCRC; }
|
||||
uint32 GetCalculatedCrc() { return m_calculatedCRC; }
|
||||
bool GetEof() { return m_eof; }
|
||||
const char* GetArticleFilename() { return m_articleFilename; }
|
||||
|
||||
private:
|
||||
private:
|
||||
EFormat m_format = efUnknown;
|
||||
bool m_begin;
|
||||
bool m_part;
|
||||
bool m_body;
|
||||
bool m_body;
|
||||
bool m_end;
|
||||
bool m_crc;
|
||||
uint32 m_expectedCRC;
|
||||
@@ -82,20 +72,22 @@ private:
|
||||
int64 m_endPos;
|
||||
int64 m_size;
|
||||
int64 m_endSize;
|
||||
int64 m_outSize;
|
||||
bool m_eof;
|
||||
bool m_crcCheck;
|
||||
};
|
||||
char m_state;
|
||||
bool m_rawMode = false;
|
||||
CString m_articleFilename;
|
||||
StringBuilder m_lineBuf;
|
||||
Crc32 m_crc32;
|
||||
|
||||
class UDecoder: public Decoder
|
||||
{
|
||||
public:
|
||||
UDecoder();
|
||||
virtual EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual int DecodeBuffer(char* buffer, int len);
|
||||
|
||||
private:
|
||||
bool m_body;
|
||||
bool m_end;
|
||||
EFormat DetectFormat(const char* buffer, int len);
|
||||
void ProcessYenc(char* buffer, int len);
|
||||
int DecodeYenc(char* buffer, char* outbuf, int len);
|
||||
EStatus CheckYenc();
|
||||
int DecodeUx(char* buffer, int len);
|
||||
EStatus CheckUx();
|
||||
void ProcessRaw(char* buffer, int len);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
NewsServer::NewsServer(int id, bool active, const char* name, const char* host, int port, int ipVersion,
|
||||
const char* user, const char* pass, bool joinGroup, bool tls, const char* cipher,
|
||||
int maxConnections, int retention, int level, int group, bool optional) :
|
||||
m_id(id), m_active(active), m_port(port), m_ipVersion(ipVersion), m_level(level), m_normLevel(level),
|
||||
m_group(group), m_maxConnections(maxConnections), m_joinGroup(joinGroup), m_tls(tls),
|
||||
m_name(name), m_host(host ? host : ""), m_user(user ? user : ""), m_password(pass ? pass : ""),
|
||||
m_cipher(cipher ? cipher : ""), m_retention(retention), m_optional(optional)
|
||||
m_id(id), m_active(active), m_name(name), m_host(host ? host : ""), m_port(port), m_ipVersion(ipVersion),
|
||||
m_user(user ? user : ""), m_password(pass ? pass : ""), m_joinGroup(joinGroup), m_tls(tls),
|
||||
m_cipher(cipher ? cipher : ""), m_maxConnections(maxConnections), m_retention(retention),
|
||||
m_level(level), m_normLevel(level), m_group(group), m_optional(optional)
|
||||
{
|
||||
if (m_name.Empty())
|
||||
{
|
||||
|
||||
@@ -65,19 +65,19 @@ private:
|
||||
int m_stateId = 0;
|
||||
bool m_active;
|
||||
CString m_name;
|
||||
int m_group;
|
||||
CString m_host;
|
||||
int m_port;
|
||||
int m_ipVersion;
|
||||
CString m_user;
|
||||
CString m_password;
|
||||
int m_maxConnections;
|
||||
int m_level;
|
||||
int m_normLevel;
|
||||
bool m_joinGroup;
|
||||
bool m_tls;
|
||||
CString m_cipher;
|
||||
int m_maxConnections;
|
||||
int m_retention;
|
||||
int m_level;
|
||||
int m_normLevel;
|
||||
int m_group;
|
||||
bool m_optional = false;
|
||||
time_t m_blockTime = 0;
|
||||
};
|
||||
|
||||
@@ -227,10 +227,7 @@ bool NntpConnection::Disconnect()
|
||||
{
|
||||
if (m_status == csConnected)
|
||||
{
|
||||
if (!m_broken)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
}
|
||||
Request("quit\r\n");
|
||||
m_activeGroup = nullptr;
|
||||
}
|
||||
return Connection::Disconnect();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2014-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2014-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "StatMeter.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "ServerPool.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
@@ -57,8 +58,8 @@ void ServerVolume::CalcSlots(time_t locCurTime)
|
||||
void ServerVolume::AddData(int bytes)
|
||||
{
|
||||
time_t curTime = Util::CurrentTime();
|
||||
time_t locCurTime = curTime + g_Options->GetLocalTimeOffset();
|
||||
time_t locDataTime = m_dataTime + g_Options->GetLocalTimeOffset();
|
||||
time_t locCurTime = curTime + g_WorkState->GetLocalTimeOffset();
|
||||
time_t locDataTime = m_dataTime + g_WorkState->GetLocalTimeOffset();
|
||||
|
||||
int lastMinSlot = m_minSlot;
|
||||
int lastHourSlot = m_hourSlot;
|
||||
@@ -138,28 +139,28 @@ void ServerVolume::LogDebugInfo()
|
||||
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
msg.AppendFmt("[%i]=%lli ", i, m_bytesPerSeconds[i]);
|
||||
msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerSeconds[i]);
|
||||
}
|
||||
info("Secs: %s", *msg);
|
||||
|
||||
msg.Clear();
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
msg.AppendFmt("[%i]=%lli ", i, m_bytesPerMinutes[i]);
|
||||
msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerMinutes[i]);
|
||||
}
|
||||
info("Mins: %s", *msg);
|
||||
|
||||
msg.Clear();
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
msg.AppendFmt("[%i]=%lli ", i, m_bytesPerHours[i]);
|
||||
msg.AppendFmt("[%i]=%" PRIi64 " ", i, m_bytesPerHours[i]);
|
||||
}
|
||||
info("Hours: %s", *msg);
|
||||
|
||||
msg.Clear();
|
||||
for (int i = 0; i < (int)m_bytesPerDays.size(); i++)
|
||||
{
|
||||
msg.AppendFmt("[%i]=%lli ", m_firstDay + i, m_bytesPerDays[i]);
|
||||
msg.AppendFmt("[%i]=%" PRIi64 " ", m_firstDay + i, m_bytesPerDays[i]);
|
||||
}
|
||||
info("Days: %s", *msg);
|
||||
}
|
||||
@@ -188,10 +189,10 @@ void StatMeter::AdjustTimeOffset()
|
||||
tmSplittedTime.tm_isdst = -1;
|
||||
time_t locTime = mktime(&tmSplittedTime);
|
||||
time_t localTimeDelta = utcTime - locTime;
|
||||
g_Options->SetLocalTimeOffset((int)localTimeDelta + g_Options->GetTimeCorrection());
|
||||
g_WorkState->SetLocalTimeOffset((int)localTimeDelta + g_Options->GetTimeCorrection());
|
||||
m_lastTimeOffset = utcTime;
|
||||
|
||||
debug("UTC delta: %i (%i+%i)", g_Options->GetLocalTimeOffset(), (int)localTimeDelta, g_Options->GetTimeCorrection());
|
||||
debug("UTC delta: %i (%i+%i)", g_WorkState->GetLocalTimeOffset(), (int)localTimeDelta, g_Options->GetTimeCorrection());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -308,8 +309,6 @@ void StatMeter::AddSpeedReading(int bytes)
|
||||
time_t curTime = Util::CurrentTime();
|
||||
int nowSlot = (int)curTime / SPEEDMETER_SLOTSIZE;
|
||||
|
||||
Guard guard(g_Options->GetAccurateRate() ? &m_speedMutex : nullptr);
|
||||
|
||||
if (curTime != m_curSecTime)
|
||||
{
|
||||
m_curSecTime = curTime;
|
||||
@@ -382,9 +381,9 @@ void StatMeter::LogDebugInfo()
|
||||
int timeDiff = (int)Util::CurrentTime() - m_speedStartTime * SPEEDMETER_SLOTSIZE;
|
||||
info(" Speed: %i", speed);
|
||||
info(" SpeedStartTime: %i", m_speedStartTime);
|
||||
info(" SpeedTotalBytes: %lli", m_speedTotalBytes);
|
||||
info(" SpeedTotalBytes: %" PRIi64, m_speedTotalBytes);
|
||||
info(" SpeedBytesIndex: %i", m_speedBytesIndex);
|
||||
info(" AllBytes: %lli", m_allBytes);
|
||||
info(" AllBytes: %" PRIi64, m_allBytes);
|
||||
info(" Time: %i", (int)Util::CurrentTime());
|
||||
info(" TimeDiff: %i", timeDiff);
|
||||
for (int i=0; i < SPEEDMETER_SLOTS; i++)
|
||||
@@ -448,7 +447,7 @@ bool StatMeter::Load(bool* perfectServerMatch)
|
||||
|
||||
for (ServerVolume& serverVolume : m_serverVolumes)
|
||||
{
|
||||
serverVolume.CalcSlots(serverVolume.GetDataTime() + g_Options->GetLocalTimeOffset());
|
||||
serverVolume.CalcSlots(serverVolume.GetDataTime() + g_WorkState->GetLocalTimeOffset());
|
||||
}
|
||||
|
||||
return ok;
|
||||
@@ -467,20 +466,20 @@ void StatMeter::CheckQuota()
|
||||
bool monthlyQuotaReached = g_Options->GetMonthlyQuota() > 0 && monthBytes >= (int64)g_Options->GetMonthlyQuota() * 1024 * 1024;
|
||||
bool dailyQuotaReached = g_Options->GetDailyQuota() > 0 && dayBytes >= (int64)g_Options->GetDailyQuota() * 1024 * 1024;
|
||||
|
||||
if (monthlyQuotaReached && !g_Options->GetQuotaReached())
|
||||
if (monthlyQuotaReached && !g_WorkState->GetQuotaReached())
|
||||
{
|
||||
warn("Monthly quota reached at %s", *Util::FormatSize(monthBytes));
|
||||
}
|
||||
else if (dailyQuotaReached && !g_Options->GetQuotaReached())
|
||||
else if (dailyQuotaReached && !g_WorkState->GetQuotaReached())
|
||||
{
|
||||
warn("Daily quota reached at %s", *Util::FormatSize(dayBytes));
|
||||
}
|
||||
else if (!monthlyQuotaReached && !dailyQuotaReached && g_Options->GetQuotaReached())
|
||||
else if (!monthlyQuotaReached && !dailyQuotaReached && g_WorkState->GetQuotaReached())
|
||||
{
|
||||
info("Quota lifted");
|
||||
}
|
||||
|
||||
g_Options->SetQuotaReached(monthlyQuotaReached || dailyQuotaReached);
|
||||
g_WorkState->SetQuotaReached(monthlyQuotaReached || dailyQuotaReached);
|
||||
}
|
||||
|
||||
void StatMeter::CalcQuotaUsage(int64& monthBytes, int64& dayBytes)
|
||||
@@ -489,7 +488,7 @@ void StatMeter::CalcQuotaUsage(int64& monthBytes, int64& dayBytes)
|
||||
|
||||
ServerVolume totalVolume = m_serverVolumes[0];
|
||||
|
||||
time_t locTime = Util::CurrentTime() + g_Options->GetLocalTimeOffset();
|
||||
time_t locTime = Util::CurrentTime() + g_WorkState->GetLocalTimeOffset();
|
||||
int daySlot = (int)(locTime / 86400) - totalVolume.GetFirstDay();
|
||||
|
||||
dayBytes = 0;
|
||||
@@ -517,7 +516,7 @@ int StatMeter::CalcMonthSlots(ServerVolume& volume)
|
||||
{
|
||||
int elapsedDays;
|
||||
|
||||
time_t locCurTime = Util::CurrentTime() + g_Options->GetLocalTimeOffset();
|
||||
time_t locCurTime = Util::CurrentTime() + g_WorkState->GetLocalTimeOffset();
|
||||
tm dayparts;
|
||||
gmtime_r(&locCurTime, &dayparts);
|
||||
|
||||
|
||||
@@ -84,7 +84,6 @@ public:
|
||||
void AddServerData(int bytes, int serverId);
|
||||
void CalcTotalStat(int* upTimeSec, int* dnTimeSec, int64* allBytes, bool* standBy);
|
||||
void CalcQuotaUsage(int64& monthBytes, int64& dayBytes);
|
||||
bool GetStandBy() { return m_standBy; }
|
||||
void IntervalCheck();
|
||||
void EnterLeaveStandBy(bool enter);
|
||||
GuardedServerVolumes GuardServerVolumes();
|
||||
@@ -106,7 +105,6 @@ private:
|
||||
int m_speedBytesIndex;
|
||||
int m_curSecBytes;
|
||||
time_t m_curSecTime;
|
||||
Mutex m_speedMutex;
|
||||
|
||||
// time
|
||||
int64 m_allBytes = 0;
|
||||
|
||||
@@ -34,7 +34,7 @@ void NServFrontend::Run()
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2016-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -44,6 +44,10 @@ struct NServOpts
|
||||
bool generateNzb;
|
||||
int segmentSize;
|
||||
bool quit;
|
||||
int latency;
|
||||
int speed;
|
||||
bool memCache;
|
||||
bool paramError;
|
||||
|
||||
NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts);
|
||||
};
|
||||
@@ -59,7 +63,7 @@ int NServMain(int argc, char* argv[])
|
||||
Options::CmdOptList cmdOpts;
|
||||
NServOpts opts(argc, argv, cmdOpts);
|
||||
|
||||
if (opts.dataDir.Empty())
|
||||
if (opts.dataDir.Empty() || opts.paramError)
|
||||
{
|
||||
NServPrintUsage(argv[0]);
|
||||
return 1;
|
||||
@@ -109,16 +113,18 @@ int NServMain(int argc, char* argv[])
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<NntpServer>> instances;
|
||||
NntpCache cache;
|
||||
|
||||
for (int i = 0; i < opts.instances; i++)
|
||||
{
|
||||
instances.emplace_back(std::make_unique<NntpServer>(i + 1, opts.bindAddress,
|
||||
opts.firstPort + i, opts.secureCert, opts.secureKey, opts.dataDir, opts.cacheDir));
|
||||
opts.firstPort + i, opts.secureCert, opts.secureKey, opts.dataDir, opts.cacheDir,
|
||||
opts.latency, opts.speed, opts.memCache ? &cache : nullptr));
|
||||
instances.back()->Start();
|
||||
}
|
||||
|
||||
info("Press Ctrl+C to quit");
|
||||
while (getchar()) usleep(1000*200);
|
||||
while (getchar()) Util::Sleep(200);
|
||||
|
||||
for (std::unique_ptr<NntpServer>& serv: instances)
|
||||
{
|
||||
@@ -134,7 +140,7 @@ int NServMain(int argc, char* argv[])
|
||||
{
|
||||
hasRunning |= serv->IsRunning();
|
||||
}
|
||||
usleep(50 * 1000);
|
||||
Util::Sleep(50);
|
||||
} while (hasRunning);
|
||||
|
||||
return 0;
|
||||
@@ -147,12 +153,15 @@ void NServPrintUsage(const char* com)
|
||||
" -d <data-dir> - directory whose files will be served\n"
|
||||
" Optional switches:\n"
|
||||
" -c <cache-dir> - directory to store encoded articles\n"
|
||||
" -m - in-memory cache (unlimited, use with care)\n"
|
||||
" -l <log-file> - write into log-file (disabled by default)\n"
|
||||
" -i <instances> - number of server instances (default is 1)\n"
|
||||
" -b <address> - ip address to bind to (default is 0.0.0.0)\n"
|
||||
" -p <port> - port number for the first instance (default is 6791)\n"
|
||||
" -s <cert> <key> - paths to SSL certificate and key files\n"
|
||||
" -v <verbose> - verbosity level 0..3 (default is 2)\n"
|
||||
" -w <msec> - response latency (in milliseconds)\n"
|
||||
" -r <KB/s> - speed throttling (in kilobytes per second)\n"
|
||||
" -z <seg-size> - generate nzbs for all files in data-dir (size in bytes)\n"
|
||||
" -q - quit after generating nzbs (in combination with -z)\n"
|
||||
, FileSystem::BaseFileName(com));
|
||||
@@ -166,9 +175,13 @@ NServOpts::NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts)
|
||||
generateNzb = false;
|
||||
segmentSize = 500000;
|
||||
quit = false;
|
||||
latency = 0;
|
||||
memCache = false;
|
||||
speed = 0;
|
||||
paramError = false;
|
||||
int verbosity = 2;
|
||||
|
||||
char short_options[] = "b:c:d:l:p:i:s:v:z:q";
|
||||
char short_options[] = "b:c:d:l:p:i:ms:v:w:r:z:q";
|
||||
|
||||
optind = 2;
|
||||
while (true)
|
||||
@@ -185,6 +198,10 @@ NServOpts::NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts)
|
||||
cacheDir = optind > argc ? nullptr : argv[optind - 1];
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
memCache = true;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
logFile = optind > argc ? nullptr : argv[optind - 1];
|
||||
break;
|
||||
@@ -211,6 +228,14 @@ NServOpts::NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts)
|
||||
verbosity = atoi(optind > argc ? "1" : argv[optind - 1]);
|
||||
break;
|
||||
|
||||
case 'w':
|
||||
latency = atoi(optind > argc ? "0" : argv[optind - 1]);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
speed = atoi(optind > argc ? "0" : argv[optind - 1]);
|
||||
break;
|
||||
|
||||
case 'z':
|
||||
generateNzb = true;
|
||||
segmentSize = atoi(optind > argc ? "500000" : argv[optind - 1]);
|
||||
@@ -222,6 +247,11 @@ NServOpts::NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts)
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc)
|
||||
{
|
||||
paramError = true;
|
||||
}
|
||||
|
||||
if (logFile.Empty())
|
||||
{
|
||||
cmdOpts.push_back("WriteLog=none");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2016-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -28,9 +28,10 @@ class NntpProcessor : public Thread
|
||||
{
|
||||
public:
|
||||
NntpProcessor(int id, int serverId, const char* dataDir, const char* cacheDir,
|
||||
const char* secureCert, const char* secureKey) :
|
||||
const char* secureCert, const char* secureKey, int latency, int speed, NntpCache* cache) :
|
||||
m_id(id), m_serverId(serverId), m_dataDir(dataDir), m_cacheDir(cacheDir),
|
||||
m_secureCert(secureCert), m_secureKey(secureKey) {}
|
||||
m_secureCert(secureCert), m_secureKey(secureKey), m_latency(latency),
|
||||
m_speed(speed), m_cache(cache) {}
|
||||
~NntpProcessor() { m_connection->Disconnect(); }
|
||||
virtual void Run();
|
||||
void SetConnection(std::unique_ptr<Connection>&& connection) { m_connection = std::move(connection); }
|
||||
@@ -43,24 +44,37 @@ private:
|
||||
const char* m_cacheDir;
|
||||
const char* m_secureCert;
|
||||
const char* m_secureKey;
|
||||
int m_latency;
|
||||
int m_speed;
|
||||
const char* m_messageid;
|
||||
CString m_filename;
|
||||
int m_part;
|
||||
int64 m_offset;
|
||||
int m_size;
|
||||
bool m_sendHeaders;
|
||||
int64 m_start;
|
||||
NntpCache* m_cache;
|
||||
|
||||
void ServArticle();
|
||||
void SendSegment();
|
||||
bool ServerInList(const char* servList);
|
||||
void SendData(const char* buffer, int size);
|
||||
};
|
||||
|
||||
|
||||
void NntpServer::Run()
|
||||
{
|
||||
debug("Entering NntpServer-loop");
|
||||
|
||||
info("Listening on port %i", m_port);
|
||||
|
||||
#ifdef WIN32
|
||||
if (m_speed > 0)
|
||||
{
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
#endif
|
||||
|
||||
int num = 1;
|
||||
|
||||
while (!IsStopped())
|
||||
@@ -89,12 +103,12 @@ void NntpServer::Run()
|
||||
break;
|
||||
}
|
||||
m_connection.reset();
|
||||
usleep(500 * 1000);
|
||||
Util::Sleep(500);
|
||||
continue;
|
||||
}
|
||||
|
||||
NntpProcessor* commandThread = new NntpProcessor(num++, m_id,
|
||||
m_dataDir, m_cacheDir, m_secureCert, m_secureKey);
|
||||
NntpProcessor* commandThread = new NntpProcessor(num++, m_id, m_dataDir,
|
||||
m_cacheDir, m_secureCert, m_secureKey, m_latency, m_speed, m_cache);
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetConnection(std::move(acceptedConnection));
|
||||
commandThread->Start();
|
||||
@@ -134,6 +148,7 @@ void NntpProcessor::Run()
|
||||
}
|
||||
#endif
|
||||
|
||||
info("[%i] Incoming connection from: %s", m_id, m_connection->GetHost() );
|
||||
m_connection->WriteLine("200 Welcome (NServ)\r\n");
|
||||
|
||||
CharBuffer buf(1024);
|
||||
@@ -157,7 +172,7 @@ void NntpProcessor::Run()
|
||||
}
|
||||
else if (!strncasecmp(line, "GROUP ", 6))
|
||||
{
|
||||
m_connection->WriteLine(CString::FormatStr("211 0 0 0 %s\r\n", line + 7));
|
||||
m_connection->WriteLine(CString::FormatStr("211 0 0 0 %s\r\n", line + 6));
|
||||
}
|
||||
else if (!strncasecmp(line, "AUTHINFO ", 9))
|
||||
{
|
||||
@@ -199,6 +214,11 @@ void NntpProcessor::ServArticle()
|
||||
{
|
||||
detail("[%i] Serving: %s", m_id, m_messageid);
|
||||
|
||||
if (m_latency)
|
||||
{
|
||||
Util::Sleep(m_latency);
|
||||
}
|
||||
|
||||
bool ok = false;
|
||||
|
||||
const char* from = strchr(m_messageid, '?');
|
||||
@@ -248,16 +268,34 @@ bool NntpProcessor::ServerInList(const char* servList)
|
||||
|
||||
void NntpProcessor::SendSegment()
|
||||
{
|
||||
detail("[%i] Sending segment %s (%i=%lli:%i)", m_id, *m_filename, m_part, (long long)m_offset, m_size);
|
||||
detail("[%i] Sending segment %s (%i=%" PRIi64 ":%i)", m_id, *m_filename, m_part, m_offset, m_size);
|
||||
|
||||
if (m_speed > 0)
|
||||
{
|
||||
m_start = Util::CurrentTicks();
|
||||
}
|
||||
|
||||
BString<1024> fullFilename("%s/%s", m_dataDir, *m_filename);
|
||||
BString<1024> cacheFileDir("%s/%s", m_cacheDir, *m_filename);
|
||||
BString<1024> cacheFileName("%i=%lli-%i", m_part, (long long)m_offset, m_size);
|
||||
BString<1024> cacheFileName("%i=%" PRIi64 "-%i", m_part, m_offset, m_size);
|
||||
BString<1024> cacheFullFilename("%s/%s", *cacheFileDir, *cacheFileName);
|
||||
BString<1024> cacheKey("%s/%s", *m_filename, *cacheFileName);
|
||||
|
||||
const char* cachedData = nullptr;
|
||||
int cachedSize;
|
||||
if (m_cache)
|
||||
{
|
||||
m_cache->Find(cacheKey, cachedData, cachedSize);
|
||||
}
|
||||
|
||||
DiskFile cacheFile;
|
||||
bool readCache = m_cacheDir && cacheFile.Open(cacheFullFilename, DiskFile::omRead);
|
||||
bool writeCache = m_cacheDir && !readCache;
|
||||
bool readCache = !cachedData && m_cacheDir && cacheFile.Open(cacheFullFilename, DiskFile::omRead);
|
||||
bool writeCache = !cachedData && m_cacheDir && !readCache;
|
||||
StringBuilder cacheMem;
|
||||
if (m_cache && !cachedData)
|
||||
{
|
||||
cacheMem.Reserve((int)(m_size * 1.1));
|
||||
}
|
||||
|
||||
CString errmsg;
|
||||
if (writeCache && !FileSystem::ForceDirectories(cacheFileDir, errmsg))
|
||||
@@ -270,23 +308,27 @@ void NntpProcessor::SendSegment()
|
||||
error("Could not create file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage());
|
||||
}
|
||||
|
||||
if (!readCache && !FileSystem::FileExists(fullFilename))
|
||||
if (!cachedData && !readCache && !FileSystem::FileExists(fullFilename))
|
||||
{
|
||||
m_connection->WriteLine(CString::FormatStr("430 Article not found\r\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
YEncoder encoder(fullFilename, m_part, m_offset, m_size,
|
||||
[con = m_connection.get(), writeCache, &cacheFile](const char* buf, int size)
|
||||
[proc = this, writeCache, &cacheFile, &cacheMem](const char* buf, int size)
|
||||
{
|
||||
if (proc->m_cache)
|
||||
{
|
||||
cacheMem.Append(buf);
|
||||
}
|
||||
if (writeCache)
|
||||
{
|
||||
cacheFile.Write(buf, size);
|
||||
}
|
||||
con->Send(buf, size);
|
||||
proc->SendData(buf, size);
|
||||
});
|
||||
|
||||
if (!readCache && !encoder.OpenFile(errmsg))
|
||||
if (!cachedData && !readCache && !encoder.OpenFile(errmsg))
|
||||
{
|
||||
m_connection->WriteLine(CString::FormatStr("403 %s\r\n", *errmsg));
|
||||
return;
|
||||
@@ -300,7 +342,11 @@ void NntpProcessor::SendSegment()
|
||||
m_connection->WriteLine("\r\n");
|
||||
}
|
||||
|
||||
if (readCache)
|
||||
if (cachedData)
|
||||
{
|
||||
SendData(cachedData, cachedSize);
|
||||
}
|
||||
else if (readCache)
|
||||
{
|
||||
cacheFile.Seek(0, DiskFile::soEnd);
|
||||
int size = (int)cacheFile.Position();
|
||||
@@ -310,12 +356,88 @@ void NntpProcessor::SendSegment()
|
||||
{
|
||||
error("Could not read file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage());
|
||||
}
|
||||
m_connection->Send(buf, size);
|
||||
if (m_cache)
|
||||
{
|
||||
cacheMem.Append(buf, size);
|
||||
}
|
||||
SendData(buf, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
encoder.WriteSegment();
|
||||
}
|
||||
|
||||
if (!cachedData && cacheMem.Length() > 0)
|
||||
{
|
||||
m_cache->Append(cacheKey, cacheMem, cacheMem.Length());
|
||||
}
|
||||
|
||||
m_connection->WriteLine(".\r\n");
|
||||
}
|
||||
|
||||
void NntpProcessor::SendData(const char* buffer, int size)
|
||||
{
|
||||
if (m_speed == 0)
|
||||
{
|
||||
m_connection->Send(buffer, size);
|
||||
return;
|
||||
}
|
||||
|
||||
int64 expectedTime = (int64)1000 * size / (m_speed * 1024) - (Util::CurrentTicks() - m_start) / 1000;
|
||||
|
||||
int chunkNum = 21;
|
||||
int chunkSize = size;
|
||||
int pause = 0;
|
||||
|
||||
while (pause < 1 && chunkNum > 1)
|
||||
{
|
||||
chunkNum--;
|
||||
chunkSize = size / chunkNum;
|
||||
pause = (int)(expectedTime / chunkNum);
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
for (int i = 0; i < chunkNum; i++)
|
||||
{
|
||||
int len = sent + chunkSize < size ? chunkSize : size - sent;
|
||||
|
||||
while (sent + len < size && *(buffer + sent + len) != '\r')
|
||||
{
|
||||
len++;
|
||||
}
|
||||
|
||||
m_connection->Send(buffer + sent, len);
|
||||
int64 now = Util::CurrentTicks();
|
||||
if (now + pause * 1000 < m_start + expectedTime * 1000)
|
||||
{
|
||||
Util::Sleep(pause);
|
||||
}
|
||||
sent += len;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NntpCache::Append(const char* key, const char* data, int len)
|
||||
{
|
||||
Guard guard(m_lock);
|
||||
if (!len)
|
||||
{
|
||||
len = strlen(data);
|
||||
}
|
||||
m_items.emplace(key, std::make_unique<CacheItem>(key, data, len));
|
||||
}
|
||||
|
||||
bool NntpCache::Find(const char* key, const char*& data, int& size)
|
||||
{
|
||||
Guard guard(m_lock);
|
||||
|
||||
CacheMap::iterator pos = m_items.find(key);
|
||||
if (pos != m_items.end())
|
||||
{
|
||||
data = (*pos).second->m_data;
|
||||
size = (*pos).second->m_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2016-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -23,14 +23,41 @@
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Connection.h"
|
||||
#include "Util.h"
|
||||
|
||||
class NntpCache
|
||||
{
|
||||
public:
|
||||
void Append(const char* key, const char* data, int len = 0);
|
||||
bool Find(const char* key, const char*& data, int& size);
|
||||
|
||||
private:
|
||||
class CacheItem
|
||||
{
|
||||
public:
|
||||
CacheItem(const char* key, const char* data, int size) :
|
||||
m_key(key), m_data(data), m_size(size) {}
|
||||
|
||||
CString m_key;
|
||||
CString m_data;
|
||||
int m_size = 0;
|
||||
};
|
||||
|
||||
typedef std::unordered_map<std::string, std::unique_ptr<CacheItem>> CacheMap;
|
||||
|
||||
CacheMap m_items;
|
||||
Mutex m_lock;
|
||||
};
|
||||
|
||||
class NntpServer : public Thread
|
||||
{
|
||||
public:
|
||||
NntpServer(int id, const char* host, int port, const char* secureCert,
|
||||
const char* secureKey, const char* dataDir, const char* cacheDir) :
|
||||
const char* secureKey, const char* dataDir, const char* cacheDir,
|
||||
int latency, int speed, NntpCache* cache) :
|
||||
m_id(id), m_host(host), m_port(port), m_secureCert(secureCert),
|
||||
m_secureKey(secureKey), m_dataDir(dataDir), m_cacheDir(cacheDir) {}
|
||||
m_secureKey(secureKey), m_dataDir(dataDir), m_cacheDir(cacheDir),
|
||||
m_latency(latency), m_speed(speed), m_cache(cache) {}
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
|
||||
@@ -38,11 +65,14 @@ private:
|
||||
int m_id;
|
||||
CString m_host;
|
||||
int m_port;
|
||||
CString m_dataDir;
|
||||
CString m_cacheDir;
|
||||
CString m_secureCert;
|
||||
CString m_secureKey;
|
||||
std::unique_ptr<Connection> m_connection;
|
||||
CString m_dataDir;
|
||||
CString m_cacheDir;
|
||||
int m_latency;
|
||||
int m_speed;
|
||||
NntpCache* m_cache;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -118,11 +118,11 @@ void NzbGenerator::AppendFile(DiskFile& outfile, const char* filename, const cha
|
||||
for (int segno = 1; segno <= segmentCount; segno++)
|
||||
{
|
||||
int segSize = (int)(segOffset + m_segmentSize < fileSize ? m_segmentSize : fileSize - segOffset);
|
||||
outfile.Print("<segment bytes=\"%i\" number=\"%i\">%s%s%s?%i=%lli:%i</segment>\n",
|
||||
m_segmentSize, segno,
|
||||
outfile.Print("<segment bytes=\"%i\" number=\"%i\">%s%s%s?%i=%" PRIi64 ":%i</segment>\n",
|
||||
m_segmentSize, segno,
|
||||
relativePath ? relativePath : "",
|
||||
relativePath ? "/" : "",
|
||||
FileSystem::BaseFileName(filename), segno, (long long)segOffset, (int)segSize);
|
||||
FileSystem::BaseFileName(filename), segno, segOffset, segSize);
|
||||
segOffset += segSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2016-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -64,10 +64,10 @@ void YEncoder::WriteSegment()
|
||||
StringBuilder outbuf;
|
||||
outbuf.Reserve(std::max(2048, std::min((int)(m_size * 1.1), 16 * 1024 * 1024)));
|
||||
|
||||
outbuf.Append(CString::FormatStr("=ybegin part=%i line=128 size=%lli name=%s\r\n", m_part, (long long)m_fileSize, FileSystem::BaseFileName(m_filename)));
|
||||
outbuf.Append(CString::FormatStr("=ypart begin=%lli end=%lli\r\n", (long long)(m_offset + 1), (long long)(m_offset + m_size)));
|
||||
outbuf.Append(CString::FormatStr("=ybegin part=%i line=128 size=%" PRIi64 " name=%s\r\n", m_part, m_fileSize, FileSystem::BaseFileName(m_filename)));
|
||||
outbuf.Append(CString::FormatStr("=ypart begin=%" PRIi64 " end=%" PRIi64 "\r\n", m_offset + 1, m_offset + m_size));
|
||||
|
||||
uint32 crc = 0xFFFFFFFF;
|
||||
Crc32 crc;
|
||||
CharBuffer inbuf(std::min(m_size, 16 * 1024 * 1024));
|
||||
int lnsz = 0;
|
||||
char* out = (char*)outbuf + outbuf.Length();
|
||||
@@ -82,7 +82,7 @@ void YEncoder::WriteSegment()
|
||||
return; // error;
|
||||
}
|
||||
|
||||
crc = Util::Crc32m(crc, (uchar*)(const char*)inbuf, (int)readBytes);
|
||||
crc.Append((uchar*)(const char*)inbuf, (int)readBytes);
|
||||
|
||||
char* in = inbuf;
|
||||
while (readBytes > 0)
|
||||
@@ -122,10 +122,8 @@ void YEncoder::WriteSegment()
|
||||
}
|
||||
}
|
||||
}
|
||||
crc ^= 0xFFFFFFFF;
|
||||
|
||||
m_diskfile.Close();
|
||||
|
||||
outbuf.Append(CString::FormatStr("=yend size=%i part=0 pcrc32=%08x\r\n", m_size, (unsigned int)crc));
|
||||
outbuf.Append(CString::FormatStr("=yend size=%i part=0 pcrc32=%08x\r\n", m_size, (unsigned int)crc.Finish()));
|
||||
m_writeFunc(outbuf, outbuf.Length());
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ void DirectUnpack::Run()
|
||||
m_destDir = nzbInfo->GetDestDir();
|
||||
m_finalDir = nzbInfo->BuildFinalDirName();
|
||||
|
||||
NzbParameter* parameter = nzbInfo->GetParameters()->Find("*Unpack:Password", false);
|
||||
NzbParameter* parameter = nzbInfo->GetParameters()->Find("*Unpack:Password");
|
||||
if (parameter)
|
||||
{
|
||||
m_password = parameter->GetValue();
|
||||
@@ -105,7 +105,7 @@ void DirectUnpack::Run()
|
||||
{
|
||||
break;
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ bool RarLister::FindLargestFile(DupeMatcher* owner, const char* directory,
|
||||
curTime + timeoutSec > Util::CurrentTime() &&
|
||||
curTime >= Util::CurrentTime()) // in a case clock was changed
|
||||
{
|
||||
usleep(200 * 1000);
|
||||
Util::Sleep(200);
|
||||
}
|
||||
|
||||
if (unrar.IsRunning())
|
||||
@@ -91,7 +91,7 @@ bool RarLister::FindLargestFile(DupeMatcher* owner, const char* directory,
|
||||
// wait until terminated or killed
|
||||
while (unrar.IsRunning())
|
||||
{
|
||||
usleep(200 * 1000);
|
||||
Util::Sleep(200);
|
||||
}
|
||||
|
||||
*maxSize = unrar.m_maxSize;
|
||||
@@ -161,7 +161,7 @@ bool DupeMatcher::Prepare()
|
||||
char filename[1024];
|
||||
FindLargestFile(m_destDir, filename, sizeof(filename), &m_maxSize, &m_compressed);
|
||||
bool sizeOK = SizeDiffOK(m_maxSize, m_expectedSize, 20);
|
||||
PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
|
||||
PrintMessage(Message::mkDetail, "Found main file %s with size %" PRIi64 " bytes%s",
|
||||
filename, m_maxSize, sizeOK ? "" : ", size mismatch");
|
||||
return sizeOK;
|
||||
}
|
||||
@@ -173,7 +173,7 @@ bool DupeMatcher::MatchDupeContent(const char* dupeDir)
|
||||
char filename[1024];
|
||||
FindLargestFile(dupeDir, filename, sizeof(filename), &dupeMaxSize, &dupeCompressed);
|
||||
bool ok = dupeMaxSize == m_maxSize && dupeCompressed == m_compressed;
|
||||
PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
|
||||
PrintMessage(Message::mkDetail, "Found main file %s with size %" PRIi64 " bytes%s",
|
||||
filename, m_maxSize, ok ? "" : ", size mismatch");
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -42,14 +42,6 @@ const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"internal error occurred",
|
||||
"out of memory" };
|
||||
|
||||
// Sleep interval for synchronisation (microseconds)
|
||||
#ifdef WIN32
|
||||
// Windows doesn't allow sleep intervals less than one millisecond
|
||||
#define SYNC_SLEEP_INTERVAL 1000
|
||||
#else
|
||||
#define SYNC_SLEEP_INTERVAL 100
|
||||
#endif
|
||||
|
||||
class RepairThread;
|
||||
|
||||
class Repairer : public Par2::Par2Repairer, public ParChecker::AbstractRepairer
|
||||
@@ -74,8 +66,8 @@ protected:
|
||||
private:
|
||||
typedef vector<Thread*> Threads;
|
||||
|
||||
Par2::CommandLine commandLine;
|
||||
ParChecker* m_owner;
|
||||
Par2::CommandLine commandLine;
|
||||
Threads m_threads;
|
||||
bool m_parallel;
|
||||
Mutex progresslock;
|
||||
@@ -83,6 +75,7 @@ private:
|
||||
virtual void BeginRepair();
|
||||
virtual void EndRepair();
|
||||
void RepairBlock(Par2::u32 inputindex, Par2::u32 outputindex, size_t blocklength);
|
||||
static void SyncSleep();
|
||||
|
||||
friend class ParChecker;
|
||||
friend class RepairThread;
|
||||
@@ -106,6 +99,11 @@ private:
|
||||
volatile bool m_working = false;
|
||||
};
|
||||
|
||||
class RepairCreatorPacket : public Par2::CreatorPacket
|
||||
{
|
||||
friend class ParChecker;
|
||||
};
|
||||
|
||||
Par2::Result Repairer::PreProcess(const char *parFilename)
|
||||
{
|
||||
BString<100> memParam("-m%i", g_Options->GetParBuffer());
|
||||
@@ -248,7 +246,7 @@ bool Repairer::RepairData(Par2::u32 inputindex, size_t blocklength)
|
||||
|
||||
if (!jobAdded)
|
||||
{
|
||||
usleep(SYNC_SLEEP_INTERVAL);
|
||||
SyncSleep();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +261,7 @@ bool Repairer::RepairData(Par2::u32 inputindex, size_t blocklength)
|
||||
if (repairThread->IsWorking())
|
||||
{
|
||||
working = true;
|
||||
usleep(SYNC_SLEEP_INTERVAL);
|
||||
SyncSleep();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -300,6 +298,17 @@ void Repairer::RepairBlock(Par2::u32 inputindex, Par2::u32 outputindex, size_t b
|
||||
}
|
||||
}
|
||||
|
||||
// Sleep for synchronisation
|
||||
void Repairer::SyncSleep()
|
||||
{
|
||||
#ifdef WIN32
|
||||
// Windows doesn't allow sleep intervals less than one millisecond
|
||||
Sleep(1);
|
||||
#else
|
||||
usleep(100);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RepairThread::Run()
|
||||
{
|
||||
while (!IsStopped())
|
||||
@@ -311,7 +320,7 @@ void RepairThread::Run()
|
||||
}
|
||||
else
|
||||
{
|
||||
usleep(SYNC_SLEEP_INTERVAL);
|
||||
Repairer::SyncSleep();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -368,6 +377,7 @@ int ParChecker::StreamBuf::overflow(int ch)
|
||||
|
||||
void ParChecker::Cleanup()
|
||||
{
|
||||
Guard guard(m_repairerMutex);
|
||||
m_repairer.reset();
|
||||
m_queuedParFiles.clear();
|
||||
m_processedFiles.clear();
|
||||
@@ -425,11 +435,6 @@ ParChecker::EStatus ParChecker::RunParCheckAll()
|
||||
{
|
||||
allStatus = status;
|
||||
}
|
||||
|
||||
if (g_Options->GetBrokenLog())
|
||||
{
|
||||
WriteBrokenLog(status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,6 +469,9 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* parFilename)
|
||||
return psFailed;
|
||||
}
|
||||
|
||||
CString creator = GetPacketCreator();
|
||||
info("Recovery files created by: %s", creator.Empty() ? "<unknown program>" : *creator);
|
||||
|
||||
m_stage = ptVerifyingSources;
|
||||
res = GetRepairer()->Process(false);
|
||||
|
||||
@@ -580,7 +588,8 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* parFilename)
|
||||
{
|
||||
m_errMsg = Par2CmdLineErrStr[res];
|
||||
}
|
||||
PrintMessage(Message::mkError, "Repair failed for %s: %s", *m_infoName, *m_errMsg);
|
||||
PrintMessage(Message::mkError, "Repair failed for %s: %s. Recovery files created by: %s",
|
||||
*m_infoName, *m_errMsg, creator.Empty() ? "<unknown program>" : *creator);
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
@@ -594,7 +603,10 @@ int ParChecker::PreProcessPar()
|
||||
{
|
||||
Cleanup();
|
||||
|
||||
m_repairer = std::make_unique<Repairer>(this);
|
||||
{
|
||||
Guard guard(m_repairerMutex);
|
||||
m_repairer = std::make_unique<Repairer>(this);
|
||||
}
|
||||
|
||||
res = GetRepairer()->PreProcess(m_parFilename);
|
||||
debug("ParChecker: PreProcess-result=%i", res);
|
||||
@@ -682,7 +694,7 @@ bool ParChecker::LoadMainParBak()
|
||||
Guard guard(m_queuedParFilesMutex);
|
||||
queuedParFilesChanged = m_queuedParFilesChanged;
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -748,7 +760,7 @@ int ParChecker::ProcessMorePars()
|
||||
Guard guard(m_queuedParFilesMutex);
|
||||
queuedParFilesChanged = m_queuedParFilesChanged;
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -814,29 +826,25 @@ bool ParChecker::AddSplittedFragments()
|
||||
DirBrowser dir(m_destDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (strcmp(filename, "_brokenlog.txt") && !IsParredFile(filename) && !IsProcessedFile(filename))
|
||||
if (!IsParredFile(filename) && !IsProcessedFile(filename))
|
||||
{
|
||||
for (Par2::Par2RepairerSourceFile *sourcefile : GetRepairer()->sourcefiles)
|
||||
{
|
||||
std::string target = sourcefile->TargetFileName();
|
||||
const char* filename2 = target.c_str();
|
||||
const char* basename2 = FileSystem::BaseFileName(filename2);
|
||||
int baseLen = strlen(basename2);
|
||||
const char* current = FileSystem::BaseFileName(target.c_str());
|
||||
|
||||
if (!strncasecmp(filename, basename2, baseLen))
|
||||
// if file was renamed by par-renamer we also check the original filename
|
||||
const char* original = FindFileOrigname(current);
|
||||
|
||||
if (MaybeSplittedFragement(filename, current) ||
|
||||
(!Util::EmptyStr(original) && strcasecmp(original, current) &&
|
||||
MaybeSplittedFragement(filename, original)))
|
||||
{
|
||||
const char* p = filename + baseLen;
|
||||
if (*p == '.')
|
||||
{
|
||||
for (p++; *p && strchr("0123456789", *p); p++) ;
|
||||
if (!*p)
|
||||
{
|
||||
debug("Found splitted fragment %s", filename);
|
||||
BString<1024> fullfilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
|
||||
Par2::CommandLine::ExtraFile extrafile(*fullfilename, FileSystem::FileSize(fullfilename));
|
||||
extrafiles.push_back(extrafile);
|
||||
}
|
||||
}
|
||||
detail("Found splitted fragment %s", filename);
|
||||
BString<1024> fullfilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
|
||||
Par2::CommandLine::ExtraFile extrafile(*fullfilename, FileSystem::FileSize(fullfilename));
|
||||
extrafiles.push_back(extrafile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -857,6 +865,40 @@ bool ParChecker::AddSplittedFragments()
|
||||
return fragmentsAdded;
|
||||
}
|
||||
|
||||
bool ParChecker::MaybeSplittedFragement(const char* filename1, const char* filename2)
|
||||
{
|
||||
// check if name is same but the first name has additional numerical extension
|
||||
int len = strlen(filename2);
|
||||
if (!strncasecmp(filename1, filename2, len))
|
||||
{
|
||||
const char* p = filename1 + len;
|
||||
if (*p == '.')
|
||||
{
|
||||
for (p++; *p && strchr("0123456789", *p); p++) ;
|
||||
if (!*p)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if same name (without extension) and extensions are numerical and exactly 3 characters long
|
||||
const char* ext1 = strrchr(filename1, '.');
|
||||
const char* ext2 = strrchr(filename2, '.');
|
||||
if (ext1 && ext2 && (strlen(ext1) == 4) && (strlen(ext2) == 4) &&
|
||||
!strncasecmp(filename1, filename2, ext1 - filename1))
|
||||
{
|
||||
for (ext1++; *ext1 && strchr("0123456789", *ext1); ext1++) ;
|
||||
for (ext2++; *ext2 && strchr("0123456789", *ext2); ext2++) ;
|
||||
if (!*ext1 && !*ext2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ParChecker::AddMissingFiles()
|
||||
{
|
||||
return AddExtraFiles(true, false, m_destDir);
|
||||
@@ -942,8 +984,7 @@ bool ParChecker::AddExtraFiles(bool onlyMissing, bool externalDir, const char* d
|
||||
DirBrowser dir(directory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (strcmp(filename, "_brokenlog.txt") &&
|
||||
(externalDir || (!IsParredFile(filename) && !IsProcessedFile(filename))))
|
||||
if (externalDir || (!IsParredFile(filename) && !IsProcessedFile(filename)))
|
||||
{
|
||||
BString<1024> fullfilename("%s%c%s", directory, PATH_SEPARATOR, filename);
|
||||
extrafiles.emplace_back(*fullfilename, FileSystem::FileSize(fullfilename));
|
||||
@@ -1186,49 +1227,14 @@ void ParChecker::CheckEmptyFiles()
|
||||
|
||||
void ParChecker::Cancel()
|
||||
{
|
||||
GetRepairer()->cancelled = true;
|
||||
QueueChanged();
|
||||
}
|
||||
|
||||
void ParChecker::WriteBrokenLog(EStatus status)
|
||||
{
|
||||
BString<1024> brokenLogName("%s%c_brokenlog.txt", *m_destDir, PATH_SEPARATOR);
|
||||
|
||||
if (status != psRepairNotNeeded || FileSystem::FileExists(brokenLogName))
|
||||
{
|
||||
DiskFile file;
|
||||
if (file.Open(brokenLogName, DiskFile::omAppend))
|
||||
Guard guard(m_repairerMutex);
|
||||
if (m_repairer)
|
||||
{
|
||||
if (status == psFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
file.Print("Repair cancelled for %s\n", *m_infoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
file.Print("Repair failed for %s: %s\n", *m_infoName, *m_errMsg);
|
||||
}
|
||||
}
|
||||
else if (status == psRepairPossible)
|
||||
{
|
||||
file.Print("Repair possible for %s\n", *m_infoName);
|
||||
}
|
||||
else if (status == psRepaired)
|
||||
{
|
||||
file.Print("Successfully repaired %s\n", *m_infoName);
|
||||
}
|
||||
else if (status == psRepairNotNeeded)
|
||||
{
|
||||
file.Print("Repair not needed for %s\n", *m_infoName);
|
||||
}
|
||||
file.Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not open file %s", *brokenLogName);
|
||||
m_repairer->GetRepairer()->cancelled = true;
|
||||
}
|
||||
}
|
||||
QueueChanged();
|
||||
}
|
||||
|
||||
void ParChecker::SaveSourceList()
|
||||
@@ -1399,7 +1405,7 @@ bool ParChecker::VerifySuccessDataFile(void* diskfile, void* sourcefile, uint32
|
||||
{
|
||||
const Par2::FILEVERIFICATIONENTRY* entry = packet->VerificationEntry(i);
|
||||
Par2::u32 blockCrc = entry->crc;
|
||||
parCrc = i == 0 ? blockCrc : Util::Crc32Combine(parCrc, blockCrc, (uint32)blocksize);
|
||||
parCrc = i == 0 ? blockCrc : Crc32::Combine(parCrc, blockCrc, (uint32)blocksize);
|
||||
}
|
||||
debug("Block-CRC: %x, filename: %s", parCrc, FileSystem::BaseFileName(sourceFile->GetTargetFile()->FileName().c_str()));
|
||||
|
||||
@@ -1423,7 +1429,7 @@ bool ParChecker::VerifyPartialDataFile(void* diskfile, void* sourcefile, Segment
|
||||
int64 blockEnd = blockStart + blocksize < fileSize - 1 ? blockStart + blocksize : fileSize - 1;
|
||||
bool blockOK = false;
|
||||
bool blockEndFound = false;
|
||||
Par2::u64 curOffset = 0;
|
||||
int64 curOffset = 0;
|
||||
for (Segment& segment : segments)
|
||||
{
|
||||
if (!blockOK && segment.GetSuccess() && segment.GetOffset() <= blockStart &&
|
||||
@@ -1477,7 +1483,7 @@ bool ParChecker::VerifyPartialDataFile(void* diskfile, void* sourcefile, Segment
|
||||
}
|
||||
const Par2::FILEVERIFICATIONENTRY* entry = packet->VerificationEntry(i);
|
||||
Par2::u32 blockCrc = entry->crc;
|
||||
parCrc = blockStart == i ? blockCrc : Util::Crc32Combine(parCrc, blockCrc, (uint32)blocksize);
|
||||
parCrc = blockStart == i ? blockCrc : Crc32::Combine(parCrc, blockCrc, (uint32)blocksize);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1537,7 +1543,7 @@ bool ParChecker::SmartCalcFileRangeCrc(DiskFile& file, int64 start, int64 end, S
|
||||
|
||||
if (segment.GetOffset() >= start && segment.GetOffset() + segment.GetSize() <= end)
|
||||
{
|
||||
downloadCrc = !started ? segment.GetCrc() : Util::Crc32Combine(downloadCrc, segment.GetCrc(), (uint32)segment.GetSize());
|
||||
downloadCrc = !started ? segment.GetCrc() : Crc32::Combine(downloadCrc, segment.GetCrc(), (uint32)segment.GetSize());
|
||||
started = true;
|
||||
}
|
||||
|
||||
@@ -1555,7 +1561,7 @@ bool ParChecker::SmartCalcFileRangeCrc(DiskFile& file, int64 start, int64 end, S
|
||||
return false;
|
||||
}
|
||||
|
||||
downloadCrc = Util::Crc32Combine(downloadCrc, (uint32)partialCrc, (uint32)(end - segment.GetOffset() + 1));
|
||||
downloadCrc = Crc32::Combine(downloadCrc, (uint32)partialCrc, (uint32)(end - segment.GetOffset() + 1));
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1576,21 +1582,37 @@ bool ParChecker::DumbCalcFileRangeCrc(DiskFile& file, int64 start, int64 end, ui
|
||||
}
|
||||
|
||||
CharBuffer buffer(1024 * 64);
|
||||
uint32 downloadCrc = 0xFFFFFFFF;
|
||||
Crc32 downloadCrc;
|
||||
|
||||
int cnt = buffer.Size();
|
||||
while (cnt == buffer.Size() && start < end)
|
||||
{
|
||||
int needBytes = end - start + 1 > buffer.Size() ? buffer.Size() : (int)(end - start + 1);
|
||||
cnt = (int)file.Read(buffer, needBytes);
|
||||
downloadCrc = Util::Crc32m(downloadCrc, (uchar*)(char*)buffer, cnt);
|
||||
downloadCrc.Append((uchar*)(char*)buffer, cnt);
|
||||
start += cnt;
|
||||
}
|
||||
|
||||
downloadCrc ^= 0xFFFFFFFF;
|
||||
|
||||
*downloadCrcOut = downloadCrc;
|
||||
*downloadCrcOut = downloadCrc.Finish();
|
||||
return true;
|
||||
}
|
||||
|
||||
CString ParChecker::GetPacketCreator()
|
||||
{
|
||||
Par2::CREATORPACKET* creatorpacket;
|
||||
if (GetRepairer()->creatorpacket &&
|
||||
(creatorpacket = (Par2::CREATORPACKET*)(((RepairCreatorPacket*)GetRepairer()->creatorpacket)->packetdata)))
|
||||
{
|
||||
int len = (int)(creatorpacket->header.length - sizeof(Par2::PACKET_HEADER));
|
||||
BString<1024> creator;
|
||||
if (len > 0)
|
||||
{
|
||||
creator.Set((const char*)creatorpacket->client, len);
|
||||
}
|
||||
return *creator;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -132,6 +132,7 @@ protected:
|
||||
virtual void RegisterParredFile(const char* filename) {}
|
||||
virtual bool IsParredFile(const char* filename) { return false; }
|
||||
virtual EFileStatus FindFileCrc(const char* filename, uint32* crc, SegmentList* segments) { return fsUnknown; }
|
||||
virtual const char* FindFileOrigname(const char* filename) { return nullptr; }
|
||||
virtual void RequestDupeSources(DupeSourceList* dupeSourceList) {}
|
||||
virtual void StatDupeSources(DupeSourceList* dupeSourceList) {}
|
||||
EStage GetStage() { return m_stage; }
|
||||
@@ -186,6 +187,7 @@ private:
|
||||
StreamBuf m_parErrStream{this, Message::mkError};
|
||||
std::ostream m_parCout{&m_parOutStream};
|
||||
std::ostream m_parCerr{&m_parErrStream};
|
||||
Mutex m_repairerMutex;
|
||||
|
||||
// "m_repairer" should be of type "Par2::Par2Repairer", however to prevent the
|
||||
// including of libpar2-headers into this header-file we use an empty abstract class.
|
||||
@@ -204,7 +206,6 @@ private:
|
||||
bool AddDupeFiles();
|
||||
bool AddExtraFiles(bool onlyMissing, bool externalDir, const char* directory);
|
||||
bool IsProcessedFile(const char* filename);
|
||||
void WriteBrokenLog(EStatus status);
|
||||
void SaveSourceList();
|
||||
void DeleteLeftovers();
|
||||
void signal_filename(std::string str);
|
||||
@@ -220,6 +221,8 @@ private:
|
||||
uint32* downloadCrc);
|
||||
bool DumbCalcFileRangeCrc(DiskFile& file, int64 start, int64 end, uint32* downloadCrc);
|
||||
void CheckEmptyFiles();
|
||||
CString GetPacketCreator();
|
||||
bool MaybeSplittedFragement(const char* filename1, const char* filename2);
|
||||
|
||||
friend class Repairer;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Log.h"
|
||||
#include "HistoryCoordinator.h"
|
||||
#include "DupeCoordinator.h"
|
||||
@@ -49,23 +50,35 @@ void PrePostProcessor::Run()
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
usleep(20 * 1000);
|
||||
Util::Sleep(20);
|
||||
}
|
||||
|
||||
if (g_Options->GetServerMode() && g_Options->GetSaveQueue() && g_Options->GetReloadQueue())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
SanitisePostQueue();
|
||||
}
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!g_Options->GetTempPausePostprocess() && m_queuedJobs)
|
||||
if (g_WorkState->GetTempPausePostprocess())
|
||||
{
|
||||
// Postprocess is paused: just wait and loop
|
||||
Util::Sleep(200);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_queuedJobs)
|
||||
{
|
||||
// check post-queue every 200 msec
|
||||
CheckPostQueue();
|
||||
Util::Sleep(200);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Wait until we get the stop signal or more jobs in the queue
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.Wait(m_waitMutex, [&]{ return m_queuedJobs || IsStopped(); });
|
||||
}
|
||||
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
WaitJobs();
|
||||
@@ -89,7 +102,7 @@ void PrePostProcessor::WaitJobs()
|
||||
}
|
||||
}
|
||||
CheckPostQueue();
|
||||
usleep(200 * 1000);
|
||||
Util::Sleep(200);
|
||||
}
|
||||
|
||||
// kill remaining post-processing jobs; not safe but we can't wait any longer
|
||||
@@ -124,7 +137,7 @@ void PrePostProcessor::WaitJobs()
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(200 * 1000);
|
||||
Util::Sleep(200);
|
||||
}
|
||||
|
||||
// disconnect remaining direct unpack jobs
|
||||
@@ -142,23 +155,30 @@ void PrePostProcessor::WaitJobs()
|
||||
void PrePostProcessor::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
|
||||
for (NzbInfo* postJob : m_activeJobs)
|
||||
{
|
||||
if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
|
||||
for (NzbInfo* postJob : m_activeJobs)
|
||||
{
|
||||
postJob->GetPostInfo()->GetPostThread()->Stop();
|
||||
if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
|
||||
{
|
||||
postJob->GetPostInfo()->GetPostThread()->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
if (nzbInfo->GetUnpackThread())
|
||||
{
|
||||
((DirectUnpack*)nzbInfo->GetUnpackThread())->Stop(downloadQueue, nzbInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
if (nzbInfo->GetUnpackThread())
|
||||
{
|
||||
((DirectUnpack*)nzbInfo->GetUnpackThread())->Stop(downloadQueue, nzbInfo);
|
||||
}
|
||||
}
|
||||
// Resume Run() to exit it
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,11 +217,13 @@ void PrePostProcessor::DownloadQueueUpdate(void* aspect)
|
||||
}
|
||||
|
||||
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
|
||||
if (queueAspect->action == DownloadQueue::eaNzbFound)
|
||||
if (queueAspect->action == DownloadQueue::eaNzbFound ||
|
||||
queueAspect->action == DownloadQueue::eaUrlFound)
|
||||
{
|
||||
NzbFound(queueAspect->downloadQueue, queueAspect->nzbInfo);
|
||||
}
|
||||
else if (queueAspect->action == DownloadQueue::eaNzbAdded)
|
||||
else if (queueAspect->action == DownloadQueue::eaNzbAdded ||
|
||||
queueAspect->action == DownloadQueue::eaUrlAdded)
|
||||
{
|
||||
NzbAdded(queueAspect->downloadQueue, queueAspect->nzbInfo);
|
||||
}
|
||||
@@ -220,6 +242,14 @@ void PrePostProcessor::DownloadQueueUpdate(void* aspect)
|
||||
"Collection %s deleted from queue", queueAspect->nzbInfo->GetName());
|
||||
NzbDeleted(queueAspect->downloadQueue, queueAspect->nzbInfo);
|
||||
}
|
||||
else if (queueAspect->action == DownloadQueue::eaUrlDeleted)
|
||||
{
|
||||
NzbDeleted(queueAspect->downloadQueue, queueAspect->nzbInfo);
|
||||
}
|
||||
else if (queueAspect->action == DownloadQueue::eaUrlFailed)
|
||||
{
|
||||
NzbCompleted(queueAspect->downloadQueue, queueAspect->nzbInfo, true);
|
||||
}
|
||||
else if ((queueAspect->action == DownloadQueue::eaFileCompleted ||
|
||||
queueAspect->action == DownloadQueue::eaFileDeleted))
|
||||
{
|
||||
@@ -309,12 +339,11 @@ void PrePostProcessor::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbI
|
||||
g_QueueScriptCoordinator->EnqueueScript(nzbInfo, QueueScriptCoordinator::qeNzbDeleted);
|
||||
}
|
||||
|
||||
if (!nzbInfo->GetPostInfo() && g_Options->GetDecode())
|
||||
if (!nzbInfo->GetPostInfo() && !g_Options->GetRawArticle() && !g_Options->GetSkipWrite())
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkInfo, "Queueing %s for post-processing", nzbInfo->GetName());
|
||||
|
||||
nzbInfo->EnterPostProcess();
|
||||
m_queuedJobs++;
|
||||
|
||||
if (nzbInfo->GetParStatus() == NzbInfo::psNone &&
|
||||
g_Options->GetParCheck() != Options::pcAlways &&
|
||||
@@ -330,7 +359,13 @@ void PrePostProcessor::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbI
|
||||
((DirectUnpack*)nzbInfo->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbInfo);
|
||||
}
|
||||
|
||||
downloadQueue->Save();
|
||||
nzbInfo->SetChanged(true);
|
||||
downloadQueue->SaveChanged();
|
||||
|
||||
// We have more jobs in the queue, notify Run()
|
||||
Guard guard(m_waitMutex);
|
||||
m_queuedJobs++;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -371,6 +406,7 @@ void PrePostProcessor::NzbDeleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo
|
||||
|
||||
void PrePostProcessor::NzbCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, bool saveQueue)
|
||||
{
|
||||
bool downloadDupe = nzbInfo->GetDupeHint() == NzbInfo::dhRedownloadAuto;
|
||||
bool addToHistory = g_Options->GetKeepHistory() > 0 && !nzbInfo->GetAvoidHistory();
|
||||
if (addToHistory)
|
||||
{
|
||||
@@ -384,7 +420,8 @@ void PrePostProcessor::NzbCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbIn
|
||||
(nzbInfo->GetDeleteStatus() == NzbInfo::dsNone ||
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsHealth ||
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsBad ||
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsScan))
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsScan ||
|
||||
(nzbInfo->GetDeleteStatus() == NzbInfo::dsCopy && downloadDupe)))
|
||||
{
|
||||
g_DupeCoordinator->NzbCompleted(downloadQueue, nzbInfo);
|
||||
needSave = true;
|
||||
@@ -426,12 +463,12 @@ void PrePostProcessor::DeleteCleanup(NzbInfo* nzbInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// delete .out.tmp-files and _brokenlog.txt
|
||||
// delete .out.tmp-files
|
||||
DirBrowser dir(nzbInfo->GetDestDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int len = strlen(filename);
|
||||
if ((len > 8 && !strcmp(filename + len - 8, ".out.tmp")) || !strcmp(filename, "_brokenlog.txt"))
|
||||
if (len > 8 && !strcmp(filename + len - 8, ".out.tmp"))
|
||||
{
|
||||
BString<1024> fullFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, filename);
|
||||
detail("Deleting file %s", filename);
|
||||
@@ -574,7 +611,7 @@ NzbInfo* PrePostProcessor::PickNextJob(DownloadQueue* downloadQueue, bool allowP
|
||||
!g_QueueScriptCoordinator->HasJob(nzbInfo1->GetId(), nullptr) &&
|
||||
nzbInfo1->GetDirectUnpackStatus() != NzbInfo::nsRunning &&
|
||||
(!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()) &&
|
||||
(!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
|
||||
(!g_WorkState->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
|
||||
(allowPar || !nzbInfo1->GetPostInfo()->GetNeedParCheck()) &&
|
||||
(std::find(m_activeJobs.begin(), m_activeJobs.end(), nzbInfo1) == m_activeJobs.end()) &&
|
||||
nzbInfo1->IsDownloadCompleted(true))
|
||||
@@ -608,7 +645,7 @@ void PrePostProcessor::CheckPostQueue()
|
||||
|
||||
PostInfo* postInfo = postJob->GetPostInfo();
|
||||
if (postInfo->GetStage() == PostInfo::ptQueued &&
|
||||
(!g_Options->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
|
||||
(!g_WorkState->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
|
||||
{
|
||||
StartJob(downloadQueue, postInfo, allowPar);
|
||||
CheckRequestPar(downloadQueue);
|
||||
@@ -628,6 +665,8 @@ void PrePostProcessor::CheckPostQueue()
|
||||
|
||||
void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo, bool allowPar)
|
||||
{
|
||||
NzbInfo* nzbInfo = postInfo->GetNzbInfo();
|
||||
|
||||
if (!postInfo->GetStartTime())
|
||||
{
|
||||
postInfo->SetStartTime(Util::CurrentTime());
|
||||
@@ -637,8 +676,8 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
|
||||
postInfo->SetFileProgress(0);
|
||||
postInfo->SetProgressLabel("");
|
||||
|
||||
if (postInfo->GetNzbInfo()->GetParRenameStatus() == NzbInfo::rsNone &&
|
||||
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
if (nzbInfo->GetParRenameStatus() == NzbInfo::rsNone &&
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
g_Options->GetParRename())
|
||||
{
|
||||
EnterStage(downloadQueue, postInfo, PostInfo::ptParRenaming);
|
||||
@@ -647,10 +686,10 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone &&
|
||||
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone)
|
||||
if (nzbInfo->GetParStatus() == NzbInfo::psNone &&
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsNone)
|
||||
{
|
||||
if (ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
|
||||
if (ParParser::FindMainPars(nzbInfo->GetDestDir(), nullptr))
|
||||
{
|
||||
if (!allowPar)
|
||||
{
|
||||
@@ -664,54 +703,54 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
|
||||
}
|
||||
else
|
||||
{
|
||||
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
|
||||
"Nothing to par-check for %s", postInfo->GetNzbInfo()->GetName());
|
||||
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psSkipped);
|
||||
nzbInfo->PrintMessage(Message::mkInfo,
|
||||
"Nothing to par-check for %s", nzbInfo->GetName());
|
||||
nzbInfo->SetParStatus(NzbInfo::psSkipped);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
|
||||
if (nzbInfo->GetParStatus() == NzbInfo::psSkipped &&
|
||||
((g_Options->GetParScan() != Options::psDupe &&
|
||||
postInfo->GetNzbInfo()->CalcHealth() < postInfo->GetNzbInfo()->CalcCriticalHealth(false) &&
|
||||
postInfo->GetNzbInfo()->CalcCriticalHealth(false) < 1000) ||
|
||||
postInfo->GetNzbInfo()->CalcHealth() == 0) &&
|
||||
ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
|
||||
nzbInfo->CalcHealth() < nzbInfo->CalcCriticalHealth(false) &&
|
||||
nzbInfo->CalcCriticalHealth(false) < 1000) ||
|
||||
nzbInfo->CalcHealth() == 0) &&
|
||||
ParParser::FindMainPars(nzbInfo->GetDestDir(), nullptr))
|
||||
{
|
||||
if (postInfo->GetNzbInfo()->CalcHealth() == 0)
|
||||
if (nzbInfo->CalcHealth() == 0)
|
||||
{
|
||||
postInfo->GetNzbInfo()->PrintMessage(Message::mkWarning,
|
||||
"Skipping par-check for %s due to health 0%%", postInfo->GetNzbInfo()->GetName());
|
||||
nzbInfo->PrintMessage(Message::mkWarning,
|
||||
"Skipping par-check for %s due to health 0%%", nzbInfo->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
postInfo->GetNzbInfo()->PrintMessage(Message::mkWarning,
|
||||
nzbInfo->PrintMessage(Message::mkWarning,
|
||||
"Skipping par-check for %s due to health %.1f%% below critical %.1f%%",
|
||||
postInfo->GetNzbInfo()->GetName(),
|
||||
postInfo->GetNzbInfo()->CalcHealth() / 10.0, postInfo->GetNzbInfo()->CalcCriticalHealth(false) / 10.0);
|
||||
nzbInfo->GetName(),
|
||||
nzbInfo->CalcHealth() / 10.0, nzbInfo->CalcCriticalHealth(false) / 10.0);
|
||||
}
|
||||
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psFailure);
|
||||
nzbInfo->SetParStatus(NzbInfo::psFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
|
||||
postInfo->GetNzbInfo()->GetFailedSize() - postInfo->GetNzbInfo()->GetParFailedSize() > 0 &&
|
||||
ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
|
||||
if (nzbInfo->GetParStatus() == NzbInfo::psSkipped &&
|
||||
nzbInfo->GetFailedSize() - nzbInfo->GetParFailedSize() > 0 &&
|
||||
ParParser::FindMainPars(nzbInfo->GetDestDir(), nullptr))
|
||||
{
|
||||
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
|
||||
nzbInfo->PrintMessage(Message::mkInfo,
|
||||
"Collection %s with health %.1f%% needs par-check",
|
||||
postInfo->GetNzbInfo()->GetName(), postInfo->GetNzbInfo()->CalcHealth() / 10.0);
|
||||
nzbInfo->GetName(), nzbInfo->CalcHealth() / 10.0);
|
||||
postInfo->SetRequestParCheck(true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzbParameter* unpackParameter = postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:", false);
|
||||
NzbParameter* unpackParameter = nzbInfo->GetParameters()->Find("*Unpack:");
|
||||
bool wantUnpack = !(unpackParameter && !strcasecmp(unpackParameter->GetValue(), "no"));
|
||||
bool unpack = wantUnpack && postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone &&
|
||||
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone;
|
||||
bool unpack = wantUnpack && nzbInfo->GetUnpackStatus() == NzbInfo::usNone &&
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsNone;
|
||||
|
||||
if (postInfo->GetNzbInfo()->GetRarRenameStatus() == NzbInfo::rsNone &&
|
||||
if (nzbInfo->GetRarRenameStatus() == NzbInfo::rsNone &&
|
||||
unpack && g_Options->GetRarRename())
|
||||
{
|
||||
EnterStage(downloadQueue, postInfo, PostInfo::ptRarRenaming);
|
||||
@@ -719,43 +758,63 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
|
||||
return;
|
||||
}
|
||||
|
||||
bool parFailed = postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psFailure ||
|
||||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psRepairPossible ||
|
||||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psManual;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (nzbInfo->GetParStatus() == NzbInfo::psSkipped &&
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
g_Options->GetParCheck() == Options::pcAuto &&
|
||||
!UnpackController::HasCompletedArchiveFiles(nzbInfo) &&
|
||||
ParParser::FindMainPars(nzbInfo->GetDestDir(), nullptr))
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkInfo,
|
||||
"Requesting par-check for collection %s without archive files",
|
||||
nzbInfo->GetName());
|
||||
postInfo->SetRequestParCheck(true);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool parFailed = nzbInfo->GetParStatus() == NzbInfo::psFailure ||
|
||||
nzbInfo->GetParStatus() == NzbInfo::psRepairPossible ||
|
||||
nzbInfo->GetParStatus() == NzbInfo::psManual;
|
||||
|
||||
bool cleanup = !unpack && wantUnpack &&
|
||||
postInfo->GetNzbInfo()->GetCleanupStatus() == NzbInfo::csNone &&
|
||||
nzbInfo->GetCleanupStatus() == NzbInfo::csNone &&
|
||||
!Util::EmptyStr(g_Options->GetExtCleanupDisk()) &&
|
||||
((postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSuccess &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usFailure &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usSpace &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usPassword) ||
|
||||
(postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usSuccess &&
|
||||
postInfo->GetNzbInfo()->GetParStatus() != NzbInfo::psFailure) ||
|
||||
((postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone ||
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usSkipped) &&
|
||||
(postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone ||
|
||||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped) &&
|
||||
postInfo->GetNzbInfo()->CalcHealth() == 1000));
|
||||
((nzbInfo->GetParStatus() == NzbInfo::psSuccess &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usFailure &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usSpace &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usPassword) ||
|
||||
(nzbInfo->GetUnpackStatus() == NzbInfo::usSuccess &&
|
||||
nzbInfo->GetParStatus() != NzbInfo::psFailure) ||
|
||||
((nzbInfo->GetUnpackStatus() == NzbInfo::usNone ||
|
||||
nzbInfo->GetUnpackStatus() == NzbInfo::usSkipped) &&
|
||||
(nzbInfo->GetParStatus() == NzbInfo::psNone ||
|
||||
nzbInfo->GetParStatus() == NzbInfo::psSkipped) &&
|
||||
nzbInfo->CalcHealth() == 1000));
|
||||
|
||||
bool moveInter = !unpack &&
|
||||
postInfo->GetNzbInfo()->GetMoveStatus() == NzbInfo::msNone &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usFailure &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usSpace &&
|
||||
postInfo->GetNzbInfo()->GetUnpackStatus() != NzbInfo::usPassword &&
|
||||
postInfo->GetNzbInfo()->GetParStatus() != NzbInfo::psFailure &&
|
||||
postInfo->GetNzbInfo()->GetParStatus() != NzbInfo::psManual &&
|
||||
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
nzbInfo->GetMoveStatus() == NzbInfo::msNone &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usFailure &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usSpace &&
|
||||
nzbInfo->GetUnpackStatus() != NzbInfo::usPassword &&
|
||||
nzbInfo->GetParStatus() != NzbInfo::psFailure &&
|
||||
nzbInfo->GetParStatus() != NzbInfo::psManual &&
|
||||
nzbInfo->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
!(((nzbInfo->GetUnpackStatus() == NzbInfo::usNone ||
|
||||
nzbInfo->GetUnpackStatus() == NzbInfo::usSkipped) &&
|
||||
(nzbInfo->GetParStatus() == NzbInfo::psNone ||
|
||||
nzbInfo->GetParStatus() == NzbInfo::psSkipped) &&
|
||||
nzbInfo->CalcHealth() < 1000)) &&
|
||||
!Util::EmptyStr(g_Options->GetInterDir()) &&
|
||||
!strncmp(postInfo->GetNzbInfo()->GetDestDir(), g_Options->GetInterDir(), strlen(g_Options->GetInterDir())) &&
|
||||
postInfo->GetNzbInfo()->GetDestDir()[strlen(g_Options->GetInterDir())] == PATH_SEPARATOR;
|
||||
!strncmp(nzbInfo->GetDestDir(), g_Options->GetInterDir(), strlen(g_Options->GetInterDir())) &&
|
||||
nzbInfo->GetDestDir()[strlen(g_Options->GetInterDir())] == PATH_SEPARATOR;
|
||||
|
||||
if (unpack && parFailed)
|
||||
{
|
||||
postInfo->GetNzbInfo()->PrintMessage(Message::mkWarning,
|
||||
"Skipping unpack for %s due to %s", postInfo->GetNzbInfo()->GetName(),
|
||||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psManual ? "required par-repair" : "par-failure");
|
||||
postInfo->GetNzbInfo()->SetUnpackStatus(NzbInfo::usSkipped);
|
||||
nzbInfo->PrintMessage(Message::mkWarning,
|
||||
"Skipping unpack for %s due to %s", nzbInfo->GetName(),
|
||||
nzbInfo->GetParStatus() == NzbInfo::psManual ? "required par-repair" : "par-failure");
|
||||
nzbInfo->SetUnpackStatus(NzbInfo::usSkipped);
|
||||
unpack = false;
|
||||
}
|
||||
|
||||
@@ -798,6 +857,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* downloadQueue, PostInfo* post
|
||||
NzbCompleted(downloadQueue, nzbInfo, false);
|
||||
}
|
||||
|
||||
Guard guard(m_waitMutex);
|
||||
m_queuedJobs--;
|
||||
}
|
||||
|
||||
@@ -833,16 +893,16 @@ void PrePostProcessor::UpdatePauseState()
|
||||
}
|
||||
}
|
||||
|
||||
if (needPause && !g_Options->GetTempPauseDownload())
|
||||
if (needPause && !g_WorkState->GetTempPauseDownload())
|
||||
{
|
||||
info("Pausing download before post-processing");
|
||||
}
|
||||
else if (!needPause && g_Options->GetTempPauseDownload())
|
||||
else if (!needPause && g_WorkState->GetTempPauseDownload())
|
||||
{
|
||||
info("Unpausing download after post-processing");
|
||||
}
|
||||
|
||||
g_Options->SetTempPauseDownload(needPause);
|
||||
g_WorkState->SetTempPauseDownload(needPause);
|
||||
}
|
||||
|
||||
bool PrePostProcessor::EditList(DownloadQueue* downloadQueue, IdList* idList,
|
||||
@@ -927,7 +987,7 @@ void PrePostProcessor::FileDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzb
|
||||
g_QueueScriptCoordinator->EnqueueScript(nzbInfo, QueueScriptCoordinator::qeFileDownloaded);
|
||||
}
|
||||
|
||||
if (g_Options->GetDirectUnpack() && g_Options->GetDecode())
|
||||
if (g_Options->GetDirectUnpack() && !g_Options->GetRawArticle() && !g_Options->GetSkipWrite())
|
||||
{
|
||||
bool allowPar;
|
||||
if (nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsNone &&
|
||||
@@ -935,7 +995,7 @@ void PrePostProcessor::FileDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzb
|
||||
DirectUnpack::IsArchiveFilename(fileInfo->GetFilename()) &&
|
||||
CanRunMoreJobs(&allowPar))
|
||||
{
|
||||
NzbParameter* unpackParameter = nzbInfo->GetParameters()->Find("*Unpack:", false);
|
||||
NzbParameter* unpackParameter = nzbInfo->GetParameters()->Find("*Unpack:");
|
||||
bool wantUnpack = !(unpackParameter && !strcasecmp(unpackParameter->GetValue(), "no"));
|
||||
if (wantUnpack && nzbInfo->GetFailedArticles() == 0)
|
||||
{
|
||||
|
||||
@@ -44,6 +44,8 @@ protected:
|
||||
private:
|
||||
int m_queuedJobs = 0;
|
||||
RawNzbList m_activeJobs;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
|
||||
void CheckPostQueue();
|
||||
void CheckRequestPar(DownloadQueue* downloadQueue);
|
||||
|
||||
@@ -173,7 +173,7 @@ bool RarVolume::Skip(DiskFile& file, RarBlock* block, int64 size)
|
||||
uint8 buf[256];
|
||||
while (size > 0)
|
||||
{
|
||||
int64 len = size <= sizeof(buf) ? size : sizeof(buf);
|
||||
int64 len = size <= (int64)sizeof(buf) ? size : (int64)sizeof(buf);
|
||||
if (!Read(file, block, buf, len)) return false;
|
||||
size -= len;
|
||||
}
|
||||
@@ -289,8 +289,10 @@ RarVolume::RarBlock RarVolume::ReadRar3Block(DiskFile& file)
|
||||
block.trailsize = blocksize - sizeof(buf) - 4;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static int num = 0;
|
||||
debug("%i) %llu, %i, %i, %i, %u, %llu", ++num, (long long)block.crc, (int)block.type, (int)block.flags, (int)size, (int)block.addsize, (long long)block.trailsize);
|
||||
debug("%i) %u, %i, %i, %i, %" PRIu64 ", %" PRIu64, ++num, block.crc, block.type, block.flags, size, block.addsize, block.trailsize);
|
||||
#endif
|
||||
|
||||
return block;
|
||||
}
|
||||
@@ -450,8 +452,10 @@ RarVolume::RarBlock RarVolume::ReadRar5Block(DiskFile& file)
|
||||
if ((block.flags & RAR5_BLOCK_DATAAREA) && !ReadV(file, &block, &datasize)) return {0};
|
||||
block.trailsize += datasize;
|
||||
|
||||
#ifdef DEBUG
|
||||
static int num = 0;
|
||||
debug("%i) %llu, %i, %i, %i, %u, %llu", ++num, (long long)block.crc, (int)block.type, (int)block.flags, (int)size, (int)block.addsize, (long long)block.trailsize);
|
||||
debug("%i) %u, %i, %i, %i, %" PRIu64 ", %" PRIu64, ++num, block.crc, block.type, block.flags, size, block.addsize, block.trailsize);
|
||||
#endif
|
||||
|
||||
return block;
|
||||
}
|
||||
@@ -527,23 +531,25 @@ bool RarVolume::ReadRar5File(DiskFile& file, RarBlock& block, RarFile& innerFile
|
||||
}
|
||||
}
|
||||
|
||||
debug("%llu, %i, %s", (long long)block.trailsize, (int)namelen, (const char*)name);
|
||||
debug("%" PRIu64 ", %" PRIu64 ", %s", block.trailsize, namelen, (const char*)name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RarVolume::LogDebugInfo()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
debug("Volume: version:%i, multi:%i, vol-no:%i, new-naming:%i, has-next:%i, encrypted:%i, file-count:%i, [%s]",
|
||||
(int)m_version, (int)m_multiVolume, m_volumeNo, (int)m_newNaming, (int)m_hasNextVolume,
|
||||
(int)m_encrypted, (int)m_files.size(), FileSystem::BaseFileName(m_filename));
|
||||
|
||||
for (RarFile& file : m_files)
|
||||
{
|
||||
debug(" time:%i, size:%lli, attr:%i, split-before:%i, split-after:%i, [%s]",
|
||||
(int)file.m_time, (long long)file.m_size, (int)file.m_attr,
|
||||
(int)file.m_splitBefore, (int)file.m_splitAfter, *file.m_filename);
|
||||
debug(" time:%i, size:%" PRIi64 ", attr:%i, split-before:%i, split-after:%i, [%s]",
|
||||
file.m_time, file.m_size, file.m_attr,
|
||||
file.m_splitBefore, file.m_splitAfter, *file.m_filename);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RarVolume::DecryptRar3Prepare(const uint8 salt[8])
|
||||
|
||||
@@ -202,7 +202,8 @@ void RarRenamer::MakeSets()
|
||||
// find first volumes and create initial incomplete sets
|
||||
for (RarVolume& volume : m_volumes)
|
||||
{
|
||||
if (!volume.GetFiles()->empty() && volume.GetVolumeNo() == 0)
|
||||
if (!volume.GetFiles()->empty() && volume.GetVolumeNo() == 0 &&
|
||||
!volume.GetFiles()->front().GetSplitBefore())
|
||||
{
|
||||
m_sets.push_back({&volume});
|
||||
}
|
||||
@@ -272,6 +273,7 @@ void RarRenamer::MakeSets()
|
||||
}),
|
||||
m_sets.end());
|
||||
|
||||
#ifdef DEBUG
|
||||
// debug log
|
||||
for (RarVolumeSet& set : m_sets)
|
||||
{
|
||||
@@ -281,6 +283,7 @@ void RarRenamer::MakeSets()
|
||||
debug(" %s", FileSystem::BaseFileName(volume->GetFilename()));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool RarRenamer::SameArchiveName(const char* filename1, const char* filename2, bool newNaming)
|
||||
@@ -305,8 +308,8 @@ bool RarRenamer::SameArchiveName(const char* filename1, const char* filename2, b
|
||||
return false;
|
||||
}
|
||||
BString<1024> name1, name2;
|
||||
name1.Set(filename1, ext1 - filename1);
|
||||
name2.Set(filename2, ext2 - filename2);
|
||||
name1.Set(filename1, (int)(ext1 - filename1));
|
||||
name2.Set(filename2, (int)(ext2 - filename2));
|
||||
ext1 = strrchr(name1, '.');
|
||||
ext2 = strrchr(name2, '.');
|
||||
return ext1 && ext2 && strlen(ext1) == strlen(ext2) &&
|
||||
@@ -345,7 +348,7 @@ bool RarRenamer::IsSetProperlyNamed(RarVolumeSet& set)
|
||||
{
|
||||
setPartLen = partNo.Length();
|
||||
}
|
||||
bool ok = atoi(partNo) == volume->GetVolumeNo() + 1 &&
|
||||
bool ok = (uint32)atoi(partNo) == volume->GetVolumeNo() + 1 &&
|
||||
partNo.Length() == setPartLen &&
|
||||
!strncmp(setBasename, filename, regExPart.GetMatchStart(1));
|
||||
if (!ok)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2016-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -137,7 +137,7 @@ void RenameController::ExecRename(const char* destDir, const char* finalDir, con
|
||||
m_rarRenamer.SetInfoName(nzbName);
|
||||
m_rarRenamer.SetIgnoreExt(g_Options->GetUnpackIgnoreExt());
|
||||
|
||||
NzbParameter* parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:Password", false);
|
||||
NzbParameter* parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:Password");
|
||||
if (parameter)
|
||||
{
|
||||
m_rarRenamer.SetPassword(parameter->GetValue());
|
||||
@@ -213,6 +213,10 @@ void RenameController::RegisterRenamedFile(const char* oldFilename, const char*
|
||||
{
|
||||
if (!strcasecmp(completedFile.GetFilename(), oldFilename))
|
||||
{
|
||||
if (Util::EmptyStr(completedFile.GetOrigname()))
|
||||
{
|
||||
completedFile.SetOrigname(completedFile.GetFilename());
|
||||
}
|
||||
completedFile.SetFilename(newFilename);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "DupeCoordinator.h"
|
||||
#include "ParParser.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
@@ -114,6 +115,19 @@ ParChecker::EFileStatus RepairController::PostParChecker::FindFileCrc(const char
|
||||
ParChecker::fsUnknown;
|
||||
}
|
||||
|
||||
const char* RepairController::PostParChecker::FindFileOrigname(const char* filename)
|
||||
{
|
||||
for (CompletedFile& completedFile : m_postInfo->GetNzbInfo()->GetCompletedFiles())
|
||||
{
|
||||
if (!strcasecmp(completedFile.GetFilename(), filename))
|
||||
{
|
||||
return completedFile.GetOrigname();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RepairController::PostParChecker::RequestDupeSources(DupeSourceList* dupeSourceList)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
@@ -519,7 +533,7 @@ void RepairController::UpdateParCheckProgress()
|
||||
|
||||
void RepairController::CheckPauseState(PostInfo* postInfo)
|
||||
{
|
||||
if (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority())
|
||||
if (g_WorkState->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority())
|
||||
{
|
||||
time_t stageTime = postInfo->GetStageTime();
|
||||
time_t startTime = postInfo->GetStartTime();
|
||||
@@ -528,9 +542,9 @@ void RepairController::CheckPauseState(PostInfo* postInfo)
|
||||
time_t waitTime = Util::CurrentTime();
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
while (g_WorkState->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
{
|
||||
usleep(50 * 1000);
|
||||
Util::Sleep(50);
|
||||
|
||||
// update time stamps
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -68,6 +68,7 @@ private:
|
||||
virtual void RegisterParredFile(const char* filename);
|
||||
virtual bool IsParredFile(const char* filename);
|
||||
virtual EFileStatus FindFileCrc(const char* filename, uint32* crc, SegmentList* segments);
|
||||
virtual const char* FindFileOrigname(const char* filename);
|
||||
virtual void RequestDupeSources(DupeSourceList* dupeSourceList);
|
||||
virtual void StatDupeSources(DupeSourceList* dupeSourceList);
|
||||
private:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2013-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -60,10 +60,10 @@ void UnpackController::Run()
|
||||
m_finalDir = m_postInfo->GetNzbInfo()->GetFinalDir();
|
||||
m_name = m_postInfo->GetNzbInfo()->GetName();
|
||||
|
||||
NzbParameter* parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:", false);
|
||||
NzbParameter* parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:");
|
||||
unpack = !(parameter && !strcasecmp(parameter->GetValue(), "no"));
|
||||
|
||||
parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:Password", false);
|
||||
parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:Password");
|
||||
if (parameter)
|
||||
{
|
||||
m_password = parameter->GetValue();
|
||||
@@ -874,8 +874,10 @@ void UnpackController::AddMessage(Message::EKind kind, const char* text)
|
||||
|
||||
if (m_unpacker == upUnrar && !strncmp(text, "Unrar: Extracting from ", 23))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const char *filename = text + 23;
|
||||
debug("Filename: %s", filename);
|
||||
#endif
|
||||
SetProgressLabel(text + 7);
|
||||
}
|
||||
|
||||
@@ -886,7 +888,8 @@ void UnpackController::AddMessage(Message::EKind kind, const char* text)
|
||||
m_unpackDecryptError = true;
|
||||
}
|
||||
|
||||
if (m_unpacker == upUnrar && !strncmp(text, "Unrar: The specified password is incorrect.", 43))
|
||||
if (m_unpacker == upUnrar && (!strncmp(text, "Unrar: The specified password is incorrect.", 43) ||
|
||||
!strncmp(text, "Unrar: Incorrect password for", 29)))
|
||||
{
|
||||
m_unpackPasswordError = true;
|
||||
}
|
||||
@@ -929,3 +932,23 @@ void UnpackController::SetProgressLabel(const char* progressLabel)
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->SetProgressLabel(progressLabel);
|
||||
}
|
||||
|
||||
bool UnpackController::HasCompletedArchiveFiles(NzbInfo* nzbInfo)
|
||||
{
|
||||
RegEx regExRar(".*\\.rar$");
|
||||
RegEx regExSevenZip(".*\\.7z$");
|
||||
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
|
||||
|
||||
for (CompletedFile& completedFile: nzbInfo->GetCompletedFiles())
|
||||
{
|
||||
const char* filename = completedFile.GetFilename();
|
||||
if (regExRar.Match(filename) ||
|
||||
regExSevenZip.Match(filename) ||
|
||||
regExSevenZipMulti.Match(filename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2013-2018 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartJob(PostInfo* postInfo);
|
||||
static bool HasCompletedArchiveFiles(NzbInfo* nzbInfo);
|
||||
|
||||
protected:
|
||||
virtual bool ReadLine(char* buf, int bufSize, FILE* stream);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2017-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -208,7 +208,8 @@ void DirectRenamer::ArticleDownloaded(DownloadQueue* downloadQueue, FileInfo* fi
|
||||
fileInfo->SetPaused(false);
|
||||
}
|
||||
|
||||
downloadQueue->Save();
|
||||
nzbInfo->SetChanged(true);
|
||||
downloadQueue->SaveChanged();
|
||||
}
|
||||
|
||||
if (fileInfo->GetParFile())
|
||||
@@ -258,7 +259,8 @@ void DirectRenamer::CheckState(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
|
||||
// all first articles downloaded
|
||||
UnpausePars(nzbInfo);
|
||||
nzbInfo->SetWaitingPar(true);
|
||||
downloadQueue->Save();
|
||||
nzbInfo->SetChanged(true);
|
||||
downloadQueue->SaveChanged();
|
||||
}
|
||||
|
||||
if (nzbInfo->GetWaitingPar() && !nzbInfo->GetLoadingPar())
|
||||
@@ -376,12 +378,20 @@ void DirectRenamer::RenameFiles(DownloadQueue* downloadQueue, NzbInfo* nzbInfo,
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkInfo, "Renaming in-progress file %s to %s",
|
||||
fileInfo->GetFilename(), *newName);
|
||||
if (Util::EmptyStr(fileInfo->GetOrigname()))
|
||||
{
|
||||
fileInfo->SetOrigname(fileInfo->GetFilename());
|
||||
}
|
||||
fileInfo->SetFilename(newName);
|
||||
fileInfo->SetFilenameConfirmed(true);
|
||||
renamedCount++;
|
||||
}
|
||||
else if (RenameCompletedFile(nzbInfo, fileInfo->GetFilename(), newName))
|
||||
{
|
||||
if (Util::EmptyStr(fileInfo->GetOrigname()))
|
||||
{
|
||||
fileInfo->SetOrigname(fileInfo->GetFilename());
|
||||
}
|
||||
fileInfo->SetFilename(newName);
|
||||
fileInfo->SetFilenameConfirmed(true);
|
||||
renamedCount++;
|
||||
@@ -404,6 +414,10 @@ void DirectRenamer::RenameFiles(DownloadQueue* downloadQueue, NzbInfo* nzbInfo,
|
||||
|
||||
if (newName && RenameCompletedFile(nzbInfo, completedFile.GetFilename(), newName))
|
||||
{
|
||||
if (Util::EmptyStr(completedFile.GetOrigname()))
|
||||
{
|
||||
completedFile.SetOrigname(completedFile.GetFilename());
|
||||
}
|
||||
completedFile.SetFilename(newName);
|
||||
renamedCount++;
|
||||
}
|
||||
@@ -523,12 +537,12 @@ void RenameContentAnalyzer::Reset()
|
||||
void RenameContentAnalyzer::Append(const void* buffer, int len)
|
||||
{
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (m_dataSize < sizeof(m_signature))
|
||||
if ((size_t)m_dataSize < sizeof(m_signature))
|
||||
{
|
||||
memcpy(m_signature + m_dataSize, buffer, std::min((size_t)len, sizeof(m_signature) - m_dataSize));
|
||||
}
|
||||
|
||||
if (m_dataSize >= sizeof(m_signature) && (*(Par2::MAGIC*)m_signature) == Par2::packet_magic)
|
||||
if ((size_t)m_dataSize >= sizeof(m_signature) && (*(Par2::MAGIC*)m_signature) == Par2::packet_magic)
|
||||
{
|
||||
m_parFile = true;
|
||||
m_parSetId = ((Par2::PACKET_HEADER*)m_signature)->setid.print().c_str();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -27,8 +27,8 @@
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version ";
|
||||
const int DISKSTATE_QUEUE_VERSION = 60;
|
||||
const int DISKSTATE_FILE_VERSION = 5;
|
||||
const int DISKSTATE_QUEUE_VERSION = 62;
|
||||
const int DISKSTATE_FILE_VERSION = 6;
|
||||
const int DISKSTATE_STATS_VERSION = 3;
|
||||
const int DISKSTATE_FEEDS_VERSION = 3;
|
||||
|
||||
@@ -45,12 +45,10 @@ int64 StateDiskFile::PrintLine(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
CString str;
|
||||
str.FormatV(format, ap);
|
||||
BString<1024> str;
|
||||
int len = str.FormatV(format, ap);
|
||||
va_end(ap);
|
||||
|
||||
int len = str.Length();
|
||||
|
||||
// replacing terminating <NULL> with <LF>
|
||||
str[len++] = '\n';
|
||||
|
||||
@@ -117,8 +115,8 @@ public:
|
||||
private:
|
||||
BString<1024> m_destFilename;
|
||||
BString<1024> m_tempFilename;
|
||||
bool m_transactional;
|
||||
int m_formatVersion;
|
||||
bool m_transactional;
|
||||
int m_fileVersion;
|
||||
StateDiskFile m_file;
|
||||
|
||||
@@ -313,6 +311,10 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory
|
||||
}
|
||||
}
|
||||
|
||||
// progress-file isn't needed after saving of full queue data
|
||||
StateFile progressStateFile("progress", DISKSTATE_QUEUE_VERSION, true);
|
||||
progressStateFile.Discard();
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -335,7 +337,12 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers
|
||||
|
||||
formatVersion = stateFile.GetFileVersion();
|
||||
|
||||
if (formatVersion < 47)
|
||||
if (formatVersion <= 0)
|
||||
{
|
||||
error("Failed to read queue: diskstate file is corrupted");
|
||||
goto error;
|
||||
}
|
||||
else if (formatVersion < 47)
|
||||
{
|
||||
error("Failed to read queue and history data. Only queue and history from NZBGet v13 or newer can be converted by this NZBGet version. "
|
||||
"Old queue and history data still can be converted using NZBGet v16 as an intermediate version.");
|
||||
@@ -351,6 +358,26 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
StateFile stateFile("progress", DISKSTATE_QUEUE_VERSION, true);
|
||||
if (stateFile.FileExists())
|
||||
{
|
||||
StateDiskFile* infile = stateFile.BeginRead();
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateFile.GetFileVersion() <= 0)
|
||||
{
|
||||
error("Failed to read queue: diskstate file is corrupted");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!LoadProgress(downloadQueue->GetQueue(), servers, *infile, stateFile.GetFileVersion())) goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (formatVersion == 0 || formatVersion >= 57)
|
||||
{
|
||||
StateFile stateFile("history", DISKSTATE_QUEUE_VERSION, true);
|
||||
@@ -361,6 +388,13 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stateFile.GetFileVersion() <= 0)
|
||||
{
|
||||
error("Failed to read queue: diskstate file is corrupted");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!LoadHistory(downloadQueue->GetHistory(), servers, *infile, stateFile.GetFileVersion())) goto error;
|
||||
}
|
||||
}
|
||||
@@ -388,6 +422,42 @@ error:
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool DiskState::SaveDownloadProgress(DownloadQueue* downloadQueue)
|
||||
{
|
||||
int count = 0;
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
count += nzbInfo->GetChanged() ? 1 : 0;
|
||||
}
|
||||
|
||||
debug("Saving queue progress to disk");
|
||||
|
||||
bool ok = true;
|
||||
|
||||
{
|
||||
StateFile stateFile("progress", DISKSTATE_QUEUE_VERSION, true);
|
||||
if (count > 0)
|
||||
{
|
||||
StateDiskFile* outfile = stateFile.BeginWrite();
|
||||
if (!outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SaveProgress(downloadQueue->GetQueue(), *outfile, count);
|
||||
|
||||
// now rename to dest file name
|
||||
ok = stateFile.FinishWrite();
|
||||
}
|
||||
else
|
||||
{
|
||||
stateFile.Discard();
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void DiskState::SaveQueue(NzbList* queue, StateDiskFile& outfile)
|
||||
{
|
||||
debug("Saving nzb list to disk");
|
||||
@@ -420,6 +490,50 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiskState::SaveProgress(NzbList* queue, StateDiskFile& outfile, int changedCount)
|
||||
{
|
||||
debug("Saving nzb progress to disk");
|
||||
|
||||
outfile.PrintLine("%i", changedCount);
|
||||
for (NzbInfo* nzbInfo : queue)
|
||||
{
|
||||
if (nzbInfo->GetChanged())
|
||||
{
|
||||
outfile.PrintLine("%i", nzbInfo->GetId());
|
||||
SaveNzbInfo(nzbInfo, outfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DiskState::LoadProgress(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion)
|
||||
{
|
||||
debug("Loading nzb progress from disk");
|
||||
|
||||
// load nzb-infos
|
||||
int size;
|
||||
if (infile.ScanLine("%i", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int id;
|
||||
if (infile.ScanLine("%i", &id) != 1) goto error;
|
||||
|
||||
NzbInfo* nzbInfo = queue->Find(id);
|
||||
if (!nzbInfo)
|
||||
{
|
||||
error("NZB with id %i could not be found", id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!LoadNzbInfo(nzbInfo, servers, infile, formatVersion)) goto error;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
error("Error reading nzb progress from disk");
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiskState::SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile)
|
||||
{
|
||||
outfile.PrintLine("%i", nzbInfo->GetId());
|
||||
@@ -464,7 +578,7 @@ void DiskState::SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile)
|
||||
outfile.PrintLine("%i,%i,%i", nzbInfo->GetTotalArticles(), nzbInfo->GetSuccessArticles(), nzbInfo->GetFailedArticles());
|
||||
|
||||
outfile.PrintLine("%s", nzbInfo->GetDupeKey());
|
||||
outfile.PrintLine("%i,%i", (int)nzbInfo->GetDupeMode(), nzbInfo->GetDupeScore());
|
||||
outfile.PrintLine("%i,%i,%i", (int)nzbInfo->GetDupeMode(), nzbInfo->GetDupeScore(), (int)nzbInfo->GetDupeHint());
|
||||
|
||||
Util::SplitInt64(nzbInfo->GetDownloadedSize(), &High1, &Low1);
|
||||
outfile.PrintLine("%u,%u,%i,%i,%i,%i,%i", High1, Low1, nzbInfo->GetDownloadSec(), nzbInfo->GetPostTotalSec(),
|
||||
@@ -473,11 +587,12 @@ void DiskState::SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile)
|
||||
outfile.PrintLine("%i", (int)nzbInfo->GetCompletedFiles()->size());
|
||||
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
|
||||
{
|
||||
outfile.PrintLine("%i,%i,%u,%i,%s,%s,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
|
||||
outfile.PrintLine("%i,%i,%u,%i,%s,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
|
||||
completedFile.GetCrc(), (int)completedFile.GetParFile(),
|
||||
completedFile.GetHash16k() ? completedFile.GetHash16k() : "",
|
||||
completedFile.GetParSetId() ? completedFile.GetParSetId() : "",
|
||||
completedFile.GetFilename());
|
||||
completedFile.GetParSetId() ? completedFile.GetParSetId() : "");
|
||||
outfile.PrintLine("%s", completedFile.GetFilename());
|
||||
outfile.PrintLine("%s", completedFile.GetOrigname() ? completedFile.GetOrigname() : "");
|
||||
}
|
||||
|
||||
outfile.PrintLine("%i", (int)nzbInfo->GetParameters()->size());
|
||||
@@ -690,10 +805,19 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
|
||||
nzbInfo->SetDupeKey(buf);
|
||||
|
||||
int dupeMode, dupeScore;
|
||||
if (infile.ScanLine("%i,%i", &dupeMode, &dupeScore) != 2) goto error;
|
||||
int dupeMode, dupeScore, dupeHint;
|
||||
dupeHint = 0; //clang requires initialization in a separate line (due to goto statements)
|
||||
if (formatVersion >= 61)
|
||||
{
|
||||
if (infile.ScanLine("%i,%i,%i", &dupeMode, &dupeScore, &dupeHint) != 3) goto error;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (infile.ScanLine("%i,%i", &dupeMode, &dupeScore) != 2) goto error;
|
||||
}
|
||||
nzbInfo->SetDupeMode((EDupeMode)dupeMode);
|
||||
nzbInfo->SetDupeScore(dupeScore);
|
||||
nzbInfo->SetDupeMode((EDupeMode)dupeHint);
|
||||
|
||||
if (formatVersion >= 48)
|
||||
{
|
||||
@@ -707,6 +831,7 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
nzbInfo->SetUnpackSec(unpackSec);
|
||||
}
|
||||
|
||||
nzbInfo->GetCompletedFiles()->clear();
|
||||
if (infile.ScanLine("%i", &fileCount) != 1) goto error;
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
@@ -719,6 +844,8 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
int parFile = 0;
|
||||
char* hash16k = nullptr;
|
||||
char* parSetId = nullptr;
|
||||
char filenameBuf[1024];
|
||||
char origName[1024];
|
||||
|
||||
if (formatVersion >= 49)
|
||||
{
|
||||
@@ -756,14 +883,22 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
{
|
||||
fileName++;
|
||||
}
|
||||
if (formatVersion >= 62)
|
||||
{
|
||||
if (!infile.ReadLine(filenameBuf, sizeof(filenameBuf))) goto error;
|
||||
fileName = filenameBuf;
|
||||
if (!infile.ReadLine(origName, sizeof(origName))) goto error;
|
||||
}
|
||||
}
|
||||
|
||||
nzbInfo->GetCompletedFiles()->emplace_back(id, fileName,
|
||||
Util::EmptyStr(origName) ? nullptr : origName,
|
||||
(CompletedFile::EStatus)status, crc, (bool)parFile,
|
||||
Util::EmptyStr(hash16k) ? nullptr : hash16k,
|
||||
Util::EmptyStr(parSetId) ? nullptr : parSetId);
|
||||
}
|
||||
|
||||
nzbInfo->GetParameters()->clear();
|
||||
int parameterCount;
|
||||
if (infile.ScanLine("%i", ¶meterCount) != 1) goto error;
|
||||
for (int i = 0; i < parameterCount; i++)
|
||||
@@ -779,6 +914,7 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
}
|
||||
}
|
||||
|
||||
nzbInfo->GetScriptStatuses()->clear();
|
||||
int scriptCount;
|
||||
if (infile.ScanLine("%i", &scriptCount) != 1) goto error;
|
||||
for (int i = 0; i < scriptCount; i++)
|
||||
@@ -808,6 +944,7 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
|
||||
}
|
||||
}
|
||||
|
||||
nzbInfo->GetFileList()->clear();
|
||||
if (infile.ScanLine("%i", &fileCount) != 1) goto error;
|
||||
for (int i = 0; i < fileCount; i++)
|
||||
{
|
||||
@@ -900,6 +1037,7 @@ bool DiskState::SaveFileInfo(FileInfo* fileInfo, StateDiskFile& outfile, bool ar
|
||||
{
|
||||
outfile.PrintLine("%s", fileInfo->GetSubject());
|
||||
outfile.PrintLine("%s", fileInfo->GetFilename());
|
||||
outfile.PrintLine("%s", fileInfo->GetOrigname() ? fileInfo->GetOrigname() : "");
|
||||
|
||||
outfile.PrintLine("%i,%i", (int)fileInfo->GetFilenameConfirmed(), (int)fileInfo->GetTime());
|
||||
|
||||
@@ -963,6 +1101,12 @@ bool DiskState::LoadFileInfo(FileInfo* fileInfo, StateDiskFile& infile, int form
|
||||
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
|
||||
if (fileSummary) fileInfo->SetFilename(buf);
|
||||
|
||||
if (formatVersion >= 6)
|
||||
{
|
||||
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
|
||||
if (fileSummary) fileInfo->SetOrigname(Util::EmptyStr(buf) ? nullptr : buf);
|
||||
}
|
||||
|
||||
if (formatVersion >= 5)
|
||||
{
|
||||
int time, filenameConfirmed;
|
||||
@@ -1055,7 +1199,8 @@ bool DiskState::SaveFileState(FileInfo* fileInfo, StateDiskFile& outfile, bool c
|
||||
outfile.PrintLine("%u,%u,%u,%u,%u,%u", High1, Low1, High2, Low2, High3, Low3);
|
||||
|
||||
outfile.PrintLine("%s", fileInfo->GetFilename());
|
||||
outfile.PrintLine("%s", fileInfo->GetHash16k());
|
||||
outfile.PrintLine("%s", fileInfo->GetHash16k() ? fileInfo->GetHash16k() : "");
|
||||
outfile.PrintLine("%s", fileInfo->GetParSetId() ? fileInfo->GetParSetId() : "");
|
||||
outfile.PrintLine("%i", (int)fileInfo->GetParFile());
|
||||
|
||||
SaveServerStats(fileInfo->GetServerStats(), outfile);
|
||||
@@ -1114,6 +1259,11 @@ bool DiskState::LoadFileState(FileInfo* fileInfo, Servers* servers, StateDiskFil
|
||||
{
|
||||
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
|
||||
fileInfo->SetHash16k(*buf ? buf : nullptr);
|
||||
if (formatVersion >= 6)
|
||||
{
|
||||
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
|
||||
fileInfo->SetParSetId(*buf ? buf : nullptr);
|
||||
}
|
||||
int parFile = 0;
|
||||
if (infile.ScanLine("%i", &parFile) != 1) goto error;
|
||||
fileInfo->SetParFile((bool)parFile);
|
||||
@@ -1451,7 +1601,8 @@ void DiskState::CleanupQueueDir(DownloadQueue* downloadQueue)
|
||||
|
||||
for (HistoryInfo* historyInfo : downloadQueue->GetHistory())
|
||||
{
|
||||
if (historyInfo->GetKind() == HistoryInfo::hkNzb)
|
||||
if (historyInfo->GetKind() == HistoryInfo::hkNzb ||
|
||||
historyInfo->GetKind() == HistoryInfo::hkUrl)
|
||||
{
|
||||
NzbInfo* nzbInfo = historyInfo->GetNzbInfo();
|
||||
nzbIdList.push_back(nzbInfo->GetId());
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
bool DownloadQueueExists();
|
||||
bool SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory);
|
||||
bool LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers);
|
||||
bool SaveDownloadProgress(DownloadQueue* downloadQueue);
|
||||
bool SaveFile(FileInfo* fileInfo);
|
||||
bool LoadFile(FileInfo* fileInfo, bool fileSummary, bool articles);
|
||||
bool SaveAllFileInfos(DownloadQueue* downloadQueue);
|
||||
@@ -63,6 +64,8 @@ private:
|
||||
bool LoadFileState(FileInfo* fileInfo, Servers* servers, StateDiskFile& infile, int formatVersion, bool completed);
|
||||
void SaveQueue(NzbList* queue, StateDiskFile& outfile);
|
||||
bool LoadQueue(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion);
|
||||
void SaveProgress(NzbList* queue, StateDiskFile& outfile, int changedCount);
|
||||
bool LoadProgress(NzbList* queue, Servers* servers, StateDiskFile& infile, int formatVersion);
|
||||
void SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile);
|
||||
bool LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& infile, int formatVersion);
|
||||
void SaveDupInfo(DupInfo* dupInfo, StateDiskFile& outfile);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -40,7 +40,7 @@ void NzbParameterList::SetParameter(const char* name, const char* value)
|
||||
iterator pos = std::find_if(begin(), end(),
|
||||
[name](NzbParameter& parameter)
|
||||
{
|
||||
return !strcmp(parameter.GetName(), name);
|
||||
return !strcasecmp(parameter.GetName(), name);
|
||||
});
|
||||
|
||||
if (emptyVal && pos != end())
|
||||
@@ -57,12 +57,11 @@ void NzbParameterList::SetParameter(const char* name, const char* value)
|
||||
}
|
||||
}
|
||||
|
||||
NzbParameter* NzbParameterList::Find(const char* name, bool caseSensitive)
|
||||
NzbParameter* NzbParameterList::Find(const char* name)
|
||||
{
|
||||
for (NzbParameter& parameter : this)
|
||||
{
|
||||
if ((caseSensitive && !strcmp(parameter.GetName(), name)) ||
|
||||
(!caseSensitive && !strcasecmp(parameter.GetName(), name)))
|
||||
if (!strcasecmp(parameter.GetName(), name))
|
||||
{
|
||||
return ¶meter;
|
||||
}
|
||||
@@ -347,10 +346,12 @@ void NzbInfo::UpdateMinMaxTime()
|
||||
}
|
||||
}
|
||||
|
||||
void NzbInfo::AddMessage(Message::EKind kind, const char * text)
|
||||
void NzbInfo::AddMessage(Message::EKind kind, const char * text, bool print)
|
||||
{
|
||||
switch (kind)
|
||||
if (print)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case Message::mkDetail:
|
||||
detail("%s", text);
|
||||
break;
|
||||
@@ -370,19 +371,20 @@ void NzbInfo::AddMessage(Message::EKind kind, const char * text)
|
||||
case Message::mkDebug:
|
||||
debug("%s", text);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Guard guard(m_logMutex);
|
||||
|
||||
m_messages.emplace_back(++m_idMessageGen, kind, Util::CurrentTime(), text);
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() && g_Options->GetNzbLog())
|
||||
if (g_Options->GetServerMode() && g_Options->GetNzbLog())
|
||||
{
|
||||
g_DiskState->AppendNzbMessage(m_id, kind, text);
|
||||
m_messageCount++;
|
||||
}
|
||||
|
||||
while (m_messages.size() > (uint32)g_Options->GetLogBufferSize())
|
||||
while (m_messages.size() > (uint32)g_Options->GetLogBuffer())
|
||||
{
|
||||
m_messages.pop_front();
|
||||
}
|
||||
@@ -497,6 +499,9 @@ bool NzbInfo::IsDupeSuccess()
|
||||
m_parStatus == NzbInfo::psFailure ||
|
||||
m_unpackStatus == NzbInfo::usFailure ||
|
||||
m_unpackStatus == NzbInfo::usPassword ||
|
||||
m_urlStatus == NzbInfo::lsFailed ||
|
||||
m_urlStatus == NzbInfo::lsScanSkipped ||
|
||||
m_urlStatus == NzbInfo::lsScanFailed ||
|
||||
(m_parStatus == NzbInfo::psSkipped &&
|
||||
m_unpackStatus == NzbInfo::usSkipped &&
|
||||
CalcHealth() < CalcCriticalHealth(true)));
|
||||
@@ -629,6 +634,10 @@ const char* NzbInfo::MakeTextStatus(bool ignoreScriptStatus)
|
||||
{
|
||||
status = "DELETED/DUPE";
|
||||
}
|
||||
else if (m_deleteStatus == NzbInfo::dsGood)
|
||||
{
|
||||
status = "DELETED/GOOD";
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* urlStatusName[] = { "FAILURE/INTERNAL_ERROR", "FAILURE/INTERNAL_ERROR", "FAILURE/INTERNAL_ERROR",
|
||||
@@ -829,10 +838,10 @@ void FileInfo::SetActiveDownloads(int activeDownloads)
|
||||
}
|
||||
|
||||
|
||||
CompletedFile::CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
|
||||
bool parFile, const char* hash16k, const char* parSetId) :
|
||||
m_id(id), m_filename(filename), m_status(status), m_crc(crc), m_parFile(parFile),
|
||||
m_hash16k(hash16k), m_parSetId(parSetId)
|
||||
CompletedFile::CompletedFile(int id, const char* filename, const char* origname, EStatus status,
|
||||
uint32 crc, bool parFile, const char* hash16k, const char* parSetId) :
|
||||
m_id(id), m_filename(filename), m_origname(origname), m_status(status),
|
||||
m_crc(crc), m_parFile(parFile), m_hash16k(hash16k), m_parSetId(parSetId)
|
||||
{
|
||||
if (FileInfo::m_idMax < m_id)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -140,6 +140,8 @@ public:
|
||||
void SetSubject(const char* subject) { m_subject = subject; }
|
||||
const char* GetFilename() { return m_filename; }
|
||||
void SetFilename(const char* filename) { m_filename = filename; }
|
||||
void SetOrigname(const char* origname) { m_origname = origname; }
|
||||
const char* GetOrigname() { return m_origname; }
|
||||
void MakeValidFilename();
|
||||
bool GetFilenameConfirmed() { return m_filenameConfirmed; }
|
||||
void SetFilenameConfirmed(bool filenameConfirmed) { m_filenameConfirmed = filenameConfirmed; }
|
||||
@@ -196,6 +198,8 @@ public:
|
||||
void SetHash16k(const char* hash16k) { m_hash16k = hash16k; }
|
||||
const char* GetParSetId() { return m_parSetId; }
|
||||
void SetParSetId(const char* parSetId) { m_parSetId = parSetId; }
|
||||
bool GetFlushLocked() { return m_flushLocked; }
|
||||
void SetFlushLocked(bool flushLocked) { m_flushLocked = flushLocked; }
|
||||
|
||||
ServerStatList* GetServerStats() { return &m_serverStats; }
|
||||
|
||||
@@ -207,6 +211,7 @@ private:
|
||||
ServerStatList m_serverStats;
|
||||
CString m_subject;
|
||||
CString m_filename;
|
||||
CString m_origname;
|
||||
int64 m_size = 0;
|
||||
int64 m_remainingSize = 0;
|
||||
int64 m_successSize = 0;
|
||||
@@ -235,6 +240,7 @@ private:
|
||||
uint32 m_crc = 0;
|
||||
CString m_hash16k;
|
||||
CString m_parSetId;
|
||||
bool m_flushLocked = false;
|
||||
|
||||
static int m_idGen;
|
||||
static int m_idMax;
|
||||
@@ -256,11 +262,13 @@ public:
|
||||
cfFailure
|
||||
};
|
||||
|
||||
CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
|
||||
bool parFile, const char* hash16k, const char* parSetId);
|
||||
CompletedFile(int id, const char* filename, const char* oldname, EStatus status,
|
||||
uint32 crc, bool parFile, const char* hash16k, const char* parSetId);
|
||||
int GetId() { return m_id; }
|
||||
void SetFilename(const char* filename) { m_filename = filename; }
|
||||
const char* GetFilename() { return m_filename; }
|
||||
void SetOrigname(const char* origname) { m_origname = origname; }
|
||||
const char* GetOrigname() { return m_origname; }
|
||||
bool GetParFile() { return m_parFile; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
uint32 GetCrc() { return m_crc; }
|
||||
@@ -272,6 +280,7 @@ public:
|
||||
private:
|
||||
int m_id;
|
||||
CString m_filename;
|
||||
CString m_origname;
|
||||
EStatus m_status;
|
||||
uint32 m_crc;
|
||||
bool m_parFile;
|
||||
@@ -304,7 +313,7 @@ class NzbParameterList : public NzbParameterListBase
|
||||
{
|
||||
public:
|
||||
void SetParameter(const char* name, const char* value);
|
||||
NzbParameter* Find(const char* name, bool caseSensitive);
|
||||
NzbParameter* Find(const char* name);
|
||||
void CopyFrom(NzbParameterList* sourceParameters);
|
||||
};
|
||||
|
||||
@@ -443,6 +452,13 @@ public:
|
||||
nkUrl
|
||||
};
|
||||
|
||||
enum EDupeHint
|
||||
{
|
||||
dhNone,
|
||||
dhRedownloadManual,
|
||||
dhRedownloadAuto
|
||||
};
|
||||
|
||||
int GetId() { return m_id; }
|
||||
void SetId(int id);
|
||||
static void ResetGenId(bool max);
|
||||
@@ -577,6 +593,8 @@ public:
|
||||
void SetDupeScore(int dupeScore) { m_dupeScore = dupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_dupeMode; }
|
||||
void SetDupeMode(EDupeMode dupeMode) { m_dupeMode = dupeMode; }
|
||||
EDupeHint GetDupeHint() { return m_dupeHint; }
|
||||
void SetDupeHint(EDupeHint dupeHint) { m_dupeHint = dupeHint; }
|
||||
uint32 GetFullContentHash() { return m_fullContentHash; }
|
||||
void SetFullContentHash(uint32 fullContentHash) { m_fullContentHash = fullContentHash; }
|
||||
uint32 GetFilteredContentHash() { return m_filteredContentHash; }
|
||||
@@ -612,7 +630,7 @@ public:
|
||||
void LeavePostProcess();
|
||||
bool IsDupeSuccess();
|
||||
const char* MakeTextStatus(bool ignoreScriptStatus);
|
||||
void AddMessage(Message::EKind kind, const char* text);
|
||||
void AddMessage(Message::EKind kind, const char* text, bool print = true);
|
||||
void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3);
|
||||
int GetMessageCount() { return m_messageCount; }
|
||||
void SetMessageCount(int messageCount) { m_messageCount = messageCount; }
|
||||
@@ -695,6 +713,7 @@ private:
|
||||
CString m_dupeKey = "";
|
||||
int m_dupeScore = 0;
|
||||
EDupeMode m_dupeMode = dmScore;
|
||||
EDupeHint m_dupeHint = dhNone;
|
||||
uint32 m_fullContentHash = 0;
|
||||
uint32 m_filteredContentHash = 0;
|
||||
FileList m_fileList;
|
||||
@@ -882,7 +901,7 @@ public:
|
||||
};
|
||||
|
||||
HistoryInfo(std::unique_ptr<NzbInfo> nzbInfo) : m_info(nzbInfo.release()),
|
||||
m_kind(nzbInfo->GetKind() == NzbInfo::nkNzb ? hkNzb : hkUrl) {}
|
||||
m_kind(GetNzbInfo()->GetKind() == NzbInfo::nkNzb ? hkNzb : hkUrl) {}
|
||||
HistoryInfo(std::unique_ptr<DupInfo> dupInfo) : m_info(dupInfo.release()), m_kind(hkDup) {}
|
||||
~HistoryInfo();
|
||||
EKind GetKind() { return m_kind; }
|
||||
@@ -895,8 +914,8 @@ public:
|
||||
const char* GetName();
|
||||
|
||||
private:
|
||||
EKind m_kind;
|
||||
void* m_info;
|
||||
EKind m_kind;
|
||||
time_t m_time = 0;
|
||||
};
|
||||
|
||||
@@ -913,9 +932,15 @@ public:
|
||||
eaNzbAdded,
|
||||
eaNzbDeleted,
|
||||
eaNzbNamed,
|
||||
eaNzbReturned,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted,
|
||||
eaUrlCompleted
|
||||
eaUrlFound,
|
||||
eaUrlAdded,
|
||||
eaUrlDeleted,
|
||||
eaUrlCompleted,
|
||||
eaUrlFailed,
|
||||
eaUrlReturned
|
||||
};
|
||||
|
||||
struct Aspect
|
||||
@@ -996,6 +1021,7 @@ public:
|
||||
virtual bool EditList(IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, const char* args) = 0;
|
||||
virtual void HistoryChanged() = 0;
|
||||
virtual void Save() = 0;
|
||||
virtual void SaveChanged() = 0;
|
||||
void CalcRemainingSize(int64* remaining, int64* remainingForced);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -31,8 +31,8 @@ bool DupeCoordinator::SameNameOrKey(const char* name1, const char* dupeKey1,
|
||||
const char* name2, const char* dupeKey2)
|
||||
{
|
||||
bool hasDupeKeys = !Util::EmptyStr(dupeKey1) && !Util::EmptyStr(dupeKey2);
|
||||
return (hasDupeKeys && !strcmp(dupeKey1, dupeKey2)) ||
|
||||
(!hasDupeKeys && !strcmp(name1, name2));
|
||||
return (hasDupeKeys && !strcasecmp(dupeKey1, dupeKey2)) ||
|
||||
(!hasDupeKeys && !strcasecmp(name1, name2));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,12 +202,19 @@ void DupeCoordinator::NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if (!sameContent && nzbInfo->GetDupeHint() != NzbInfo::dhNone)
|
||||
{
|
||||
// dupe check when "download again" URLs: checking same content only
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sameContent && !good && nzbInfo->GetDupeMode() == dmScore)
|
||||
{
|
||||
// nzb-files having success-duplicates in recent history (with different content) are added to history for backup
|
||||
for (HistoryInfo* historyInfo : downloadQueue->GetHistory())
|
||||
{
|
||||
if (historyInfo->GetKind() == HistoryInfo::hkNzb &&
|
||||
if ((historyInfo->GetKind() == HistoryInfo::hkNzb ||
|
||||
historyInfo->GetKind() == HistoryInfo::hkUrl) &&
|
||||
historyInfo->GetNzbInfo()->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(historyInfo->GetNzbInfo()->GetName(), historyInfo->GetNzbInfo()->GetDupeKey(),
|
||||
nzbInfo->GetName(), nzbInfo->GetDupeKey()) &&
|
||||
@@ -237,7 +244,7 @@ void DupeCoordinator::NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
|
||||
sameContent ? "exactly same content" : good ? "good status" : "success status");
|
||||
}
|
||||
|
||||
if (nzbInfo->GetFeedId())
|
||||
if (nzbInfo->GetFeedId() && nzbInfo->GetDupeHint() == NzbInfo::dhNone)
|
||||
{
|
||||
warn("%s", *message);
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
@@ -263,7 +270,9 @@ void DupeCoordinator::NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
|
||||
{
|
||||
NzbInfo* queuedNzbInfo = (*it++).get();
|
||||
if (queuedNzbInfo != nzbInfo &&
|
||||
queuedNzbInfo->GetKind() == NzbInfo::nkNzb &&
|
||||
queuedNzbInfo->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
(queuedNzbInfo->GetKind() == NzbInfo::nkNzb ||
|
||||
(queuedNzbInfo->GetKind() == NzbInfo::nkUrl && nzbInfo->GetKind() == NzbInfo::nkUrl)) &&
|
||||
queuedNzbInfo->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(queuedNzbInfo->GetName(), queuedNzbInfo->GetDupeKey(),
|
||||
nzbInfo->GetName(), nzbInfo->GetDupeKey()))
|
||||
@@ -286,9 +295,13 @@ void DupeCoordinator::NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
|
||||
// the existing queue item is moved to history as dupe-backup
|
||||
info("Moving collection %s with lower duplicate score to history", queuedNzbInfo->GetName());
|
||||
queuedNzbInfo->SetDeleteStatus(NzbInfo::dsDupe);
|
||||
int oldSize = downloadQueue->GetQueue()->size();
|
||||
downloadQueue->EditEntry(queuedNzbInfo->GetId(),
|
||||
DownloadQueue::eaGroupDelete, nullptr);
|
||||
int newSize = downloadQueue->GetQueue()->size();
|
||||
index += oldSize == newSize ? 1 : 0;
|
||||
it = downloadQueue->GetQueue()->begin() + index;
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -377,7 +390,8 @@ void DupeCoordinator::ReturnBestDupe(DownloadQueue* downloadQueue, NzbInfo* nzbI
|
||||
HistoryInfo* historyDupe = nullptr;
|
||||
for (HistoryInfo* historyInfo : downloadQueue->GetHistory())
|
||||
{
|
||||
if (historyInfo->GetKind() == HistoryInfo::hkNzb &&
|
||||
if ((historyInfo->GetKind() == HistoryInfo::hkNzb ||
|
||||
historyInfo->GetKind() == HistoryInfo::hkUrl) &&
|
||||
historyInfo->GetNzbInfo()->GetDupeMode() != dmForce &&
|
||||
historyInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsDupe &&
|
||||
historyInfo->GetNzbInfo()->CalcHealth() >= historyInfo->GetNzbInfo()->CalcCriticalHealth(true) &&
|
||||
@@ -395,6 +409,7 @@ void DupeCoordinator::ReturnBestDupe(DownloadQueue* downloadQueue, NzbInfo* nzbI
|
||||
if (historyDupe)
|
||||
{
|
||||
info("Found duplicate %s for %s", historyDupe->GetNzbInfo()->GetName(), nzbName);
|
||||
historyDupe->GetNzbInfo()->SetDupeHint(NzbInfo::dhRedownloadAuto);
|
||||
g_HistoryCoordinator->Redownload(downloadQueue, historyDupe);
|
||||
}
|
||||
}
|
||||
@@ -465,7 +480,8 @@ void DupeCoordinator::HistoryCleanup(DownloadQueue* downloadQueue, HistoryInfo*
|
||||
{
|
||||
HistoryInfo* historyInfo = (*it).get();
|
||||
|
||||
if (historyInfo->GetKind() == HistoryInfo::hkNzb &&
|
||||
if ((historyInfo->GetKind() == HistoryInfo::hkNzb ||
|
||||
historyInfo->GetKind() == HistoryInfo::hkUrl) &&
|
||||
historyInfo->GetNzbInfo()->GetDupeMode() != dmForce &&
|
||||
historyInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsDupe &&
|
||||
historyInfo != markHistoryInfo &&
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -87,7 +87,7 @@ void HistoryCoordinator::ServiceWork()
|
||||
|
||||
void HistoryCoordinator::DeleteDiskFiles(NzbInfo* nzbInfo)
|
||||
{
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
// delete parked files
|
||||
g_DiskState->DiscardFiles(nzbInfo);
|
||||
@@ -131,7 +131,8 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
|
||||
{
|
||||
nzbInfo->UpdateCompletedStats(fileInfo);
|
||||
nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(), fileInfo->GetFilename(),
|
||||
CompletedFile::cfNone, 0, fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
|
||||
fileInfo->GetOrigname(), CompletedFile::cfNone, 0, fileInfo->GetParFile(),
|
||||
fileInfo->GetHash16k(), fileInfo->GetParSetId());
|
||||
}
|
||||
|
||||
// Cleaning up parked files if par-check was successful or unpack was successful or
|
||||
@@ -183,6 +184,8 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
|
||||
nzbInfo->SetDirectRenameStatus(NzbInfo::tsFailure);
|
||||
}
|
||||
|
||||
nzbInfo->SetDupeHint(NzbInfo::dhNone);
|
||||
|
||||
nzbInfo->PrintMessage(Message::mkInfo, "Collection %s added to history", nzbInfo->GetName());
|
||||
}
|
||||
|
||||
@@ -421,6 +424,8 @@ void HistoryCoordinator::MoveToQueue(DownloadQueue* downloadQueue, HistoryList::
|
||||
// start postprocessing
|
||||
debug("Restarting postprocessing for %s", *nicename);
|
||||
g_PrePostProcessor->NzbDownloaded(downloadQueue, nzbInfo);
|
||||
DownloadQueue::Aspect aspect = {DownloadQueue::eaNzbReturned, downloadQueue, nzbInfo, nullptr};
|
||||
downloadQueue->Notify(&aspect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,8 +438,13 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* downloadQueue, History
|
||||
historyInfo->DiscardNzbInfo();
|
||||
nzbInfo->SetUrlStatus(NzbInfo::lsNone);
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsNone);
|
||||
nzbInfo->SetDupeHint(nzbInfo->GetDupeHint() == NzbInfo::dhNone ? NzbInfo::dhRedownloadManual : nzbInfo->GetDupeHint());
|
||||
downloadQueue->GetQueue()->Add(std::unique_ptr<NzbInfo>(nzbInfo), true);
|
||||
downloadQueue->GetHistory()->erase(itHistory);
|
||||
|
||||
DownloadQueue::Aspect aspect = {DownloadQueue::eaUrlReturned, downloadQueue, nzbInfo, nullptr};
|
||||
downloadQueue->Notify(&aspect);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -522,6 +532,9 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* downloadQueue, History
|
||||
MoveToQueue(downloadQueue, itHistory, historyInfo, false);
|
||||
|
||||
g_PrePostProcessor->NzbAdded(downloadQueue, nzbInfo);
|
||||
|
||||
DownloadQueue::Aspect aspect = {DownloadQueue::eaNzbReturned, downloadQueue, nzbInfo, nullptr};
|
||||
downloadQueue->Notify(&aspect);
|
||||
}
|
||||
|
||||
void HistoryCoordinator::HistoryReturn(DownloadQueue* downloadQueue, HistoryList::iterator itHistory,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -34,7 +34,7 @@ public:
|
||||
void Redownload(DownloadQueue* downloadQueue, HistoryInfo* historyInfo);
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 600000; }
|
||||
virtual int ServiceInterval() { return 60 * 60; }
|
||||
virtual void ServiceWork();
|
||||
|
||||
private:
|
||||
|
||||
@@ -117,6 +117,12 @@ void NzbFile::ParseSubject(FileInfo* fileInfo, bool TryQuotes)
|
||||
{
|
||||
// Example subject: some garbage "title" yEnc (10/99)
|
||||
|
||||
if (!fileInfo->GetSubject())
|
||||
{
|
||||
// Malformed file element without subject. We generate subject using internal element id.
|
||||
fileInfo->SetSubject(CString::FormatStr("%d", fileInfo->GetId()));
|
||||
}
|
||||
|
||||
// strip the "yEnc (10/99)"-suffix
|
||||
BString<1024> subject = fileInfo->GetSubject();
|
||||
char* end = subject + strlen(subject) - 1;
|
||||
@@ -374,7 +380,7 @@ void NzbFile::ProcessFiles()
|
||||
|
||||
CalcHashes();
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
for (FileInfo* fileInfo : m_nzbInfo->GetFileList())
|
||||
{
|
||||
@@ -595,6 +601,10 @@ bool NzbFile::ParseNzb(IUnknown* nzb)
|
||||
|
||||
bool NzbFile::Parse()
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
error("Could not parse rss feed, program was compiled without libxml2 support");
|
||||
return false;
|
||||
#else
|
||||
xmlSAXHandler SAX_handler = {0};
|
||||
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
|
||||
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
|
||||
@@ -623,6 +633,7 @@ bool NzbFile::Parse()
|
||||
ProcessFiles();
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void NzbFile::Parse_StartElement(const char *name, const char **atts)
|
||||
@@ -805,7 +816,11 @@ void NzbFile::SAX_characters(NzbFile* file, const char * xmlstr, int len)
|
||||
|
||||
void* NzbFile::SAX_getEntity(NzbFile* file, const char * name)
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
void* e = nullptr;
|
||||
#else
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
#endif
|
||||
if (!e)
|
||||
{
|
||||
file->m_nzbInfo->AddMessage(Message::mkWarning, "entity not found");
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "ArticleWriter.h"
|
||||
@@ -58,7 +59,7 @@ void QueueCoordinator::CoordinatorDownloadQueue::Save()
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->SaveDownloadQueue(this, m_historyChanged);
|
||||
m_stateChanged = true;
|
||||
@@ -71,6 +72,18 @@ void QueueCoordinator::CoordinatorDownloadQueue::Save()
|
||||
|
||||
m_wantSave = false;
|
||||
m_historyChanged = false;
|
||||
|
||||
// queue has changed, time to wake up if in standby
|
||||
m_owner->WakeUp();
|
||||
}
|
||||
|
||||
void QueueCoordinator::CoordinatorDownloadQueue::SaveChanged()
|
||||
{
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->SaveDownloadProgress(this);
|
||||
m_stateChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
@@ -78,6 +91,7 @@ QueueCoordinator::QueueCoordinator()
|
||||
debug("Creating QueueCoordinator");
|
||||
|
||||
CoordinatorDownloadQueue::Init(&m_downloadQueue);
|
||||
g_WorkState->Attach(this);
|
||||
}
|
||||
|
||||
QueueCoordinator::~QueueCoordinator()
|
||||
@@ -103,11 +117,11 @@ void QueueCoordinator::Load()
|
||||
bool perfectServerMatch = true;
|
||||
bool queueLoaded = false;
|
||||
|
||||
if (g_Options->GetServerMode() && g_Options->GetSaveQueue())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
statLoaded = g_StatMeter->Load(&perfectServerMatch);
|
||||
|
||||
if (g_Options->GetReloadQueue() && g_DiskState->DownloadQueueExists())
|
||||
if (g_DiskState->DownloadQueueExists())
|
||||
{
|
||||
queueLoaded = g_DiskState->LoadDownloadQueue(downloadQueue, g_ServerPool->GetServers());
|
||||
}
|
||||
@@ -134,7 +148,7 @@ void QueueCoordinator::Load()
|
||||
downloadQueue->Save();
|
||||
|
||||
// re-save file states into diskstate to update server ids
|
||||
if (g_Options->GetServerMode() && g_Options->GetSaveQueue())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
@@ -177,8 +191,9 @@ void QueueCoordinator::Run()
|
||||
AdjustDownloadsLimit();
|
||||
bool wasStandBy = true;
|
||||
bool articeDownloadsRunning = false;
|
||||
int resetCounter = 0;
|
||||
time_t lastReset = 0;
|
||||
g_StatMeter->IntervalCheck();
|
||||
int waitInterval = 100;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
@@ -199,7 +214,7 @@ void QueueCoordinator::Run()
|
||||
downloadsChecked = true;
|
||||
m_hasMoreJobs = hasMoreArticles || articeDownloadsRunning;
|
||||
if (hasMoreArticles && !IsStopped() && (int)m_activeDownloads.size() < m_downloadsLimit &&
|
||||
(!g_Options->GetTempPauseDownload() || fileInfo->GetExtraPriority()))
|
||||
(!g_WorkState->GetTempPauseDownload() || fileInfo->GetExtraPriority()))
|
||||
{
|
||||
StartArticleDownload(fileInfo, articleInfo, connection);
|
||||
articeDownloadsRunning = true;
|
||||
@@ -227,6 +242,7 @@ void QueueCoordinator::Run()
|
||||
if (standBy != wasStandBy)
|
||||
{
|
||||
g_StatMeter->EnterLeaveStandBy(standBy);
|
||||
g_WorkState->SetDownloading(!standBy);
|
||||
wasStandBy = standBy;
|
||||
if (standBy)
|
||||
{
|
||||
@@ -235,18 +251,23 @@ void QueueCoordinator::Run()
|
||||
}
|
||||
|
||||
// sleep longer in StandBy
|
||||
int sleepInterval = downloadStarted ? 0 : standBy ? 100 : 5;
|
||||
usleep(sleepInterval * 1000);
|
||||
|
||||
if (!standBy)
|
||||
if (standBy)
|
||||
{
|
||||
Guard guard(m_waitMutex);
|
||||
// sleeping max. 2 seconds; can't sleep much longer because we can't rely on
|
||||
// notifications from 'WorkState' and we also have periodical work to do here
|
||||
waitInterval = std::min(waitInterval * 2, 2000);
|
||||
m_waitCond.WaitFor(m_waitMutex, waitInterval, [&]{ return m_hasMoreJobs || IsStopped(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
int sleepInterval = downloadStarted ? 0 : 5;
|
||||
Util::Sleep(sleepInterval);
|
||||
g_StatMeter->AddSpeedReading(0);
|
||||
waitInterval = 100;
|
||||
}
|
||||
|
||||
Util::SetStandByMode(standBy);
|
||||
|
||||
resetCounter += sleepInterval;
|
||||
if (resetCounter >= 1000)
|
||||
if (lastReset != Util::CurrentTime())
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
g_ServerPool->CloseUnusedConnections();
|
||||
@@ -255,19 +276,31 @@ void QueueCoordinator::Run()
|
||||
{
|
||||
SaveAllPartialState();
|
||||
}
|
||||
resetCounter = 0;
|
||||
g_StatMeter->IntervalCheck();
|
||||
g_Log->IntervalCheck();
|
||||
AdjustDownloadsLimit();
|
||||
Util::SetStandByMode(standBy);
|
||||
lastReset = Util::CurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
WaitJobs();
|
||||
SaveAllPartialState();
|
||||
SaveQueueIfChanged();
|
||||
SaveAllFileState();
|
||||
|
||||
debug("Exiting QueueCoordinator-loop");
|
||||
}
|
||||
|
||||
void QueueCoordinator::WakeUp()
|
||||
{
|
||||
debug("Waking up QueueCoordinator");
|
||||
// Resume Run()
|
||||
Guard guard(m_waitMutex);
|
||||
m_hasMoreJobs = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void QueueCoordinator::WaitJobs()
|
||||
{
|
||||
// waiting for downloads
|
||||
@@ -282,7 +315,7 @@ void QueueCoordinator::WaitJobs()
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
|
||||
@@ -333,7 +366,7 @@ NzbInfo* QueueCoordinator::AddNzbFileToQueue(std::unique_ptr<NzbInfo> nzbInfo, N
|
||||
for (FileInfo* fileInfo: nzbInfo->GetFileList())
|
||||
{
|
||||
allPaused &= fileInfo->GetPaused();
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->DiscardFile(fileInfo->GetId(), true, false, false);
|
||||
}
|
||||
@@ -400,11 +433,6 @@ void QueueCoordinator::CheckDupeFileInfos(NzbInfo* nzbInfo)
|
||||
{
|
||||
debug("CheckDupeFileInfos");
|
||||
|
||||
if (!g_Options->GetDupeCheck() || nzbInfo->GetDupeMode() == dmForce)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RawFileList dupeList;
|
||||
|
||||
int index1 = 0;
|
||||
@@ -421,9 +449,19 @@ void QueueCoordinator::CheckDupeFileInfos(NzbInfo* nzbInfo)
|
||||
(fileInfo->GetSize() < fileInfo2->GetSize() ||
|
||||
(fileInfo->GetSize() == fileInfo2->GetSize() && index2 < index1)))
|
||||
{
|
||||
warn("File \"%s\" appears twice in collection, adding only the biggest file", fileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
// If more than two files have same filename we don't filter them out since that
|
||||
// naming might be intentional and correct filenames must be read from article bodies.
|
||||
int dupeCount = (int)std::count_if(nzbInfo->GetFileList()->begin(), nzbInfo->GetFileList()->end(),
|
||||
[fileInfo2](std::unique_ptr<FileInfo>& fileInfo3)
|
||||
{
|
||||
return !strcmp(fileInfo3->GetFilename(), fileInfo2->GetFilename());
|
||||
});
|
||||
if (dupeCount == 2)
|
||||
{
|
||||
warn("File \"%s\" appears twice in collection, adding only the biggest file", fileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dupe)
|
||||
@@ -437,7 +475,7 @@ void QueueCoordinator::CheckDupeFileInfos(NzbInfo* nzbInfo)
|
||||
{
|
||||
nzbInfo->UpdateDeletedStats(fileInfo);
|
||||
nzbInfo->GetFileList()->Remove(fileInfo);
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->DiscardFile(fileInfo->GetId(), true, false, false);
|
||||
}
|
||||
@@ -449,12 +487,18 @@ void QueueCoordinator::Stop()
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping ArticleDownloads");
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (ArticleDownloader* articleDownloader : m_activeDownloads)
|
||||
{
|
||||
articleDownloader->Stop();
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (ArticleDownloader* articleDownloader : m_activeDownloads)
|
||||
{
|
||||
articleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
debug("ArticleDownloads are notified");
|
||||
|
||||
// Resume Run() to exit it
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -489,7 +533,7 @@ bool QueueCoordinator::GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &f
|
||||
bool nzbPaused = nzbInfo->GetFileList()->size() - nzbInfo->GetPausedFileCount() <= 0;
|
||||
|
||||
if ((!fileInfo || nzbHigherPriority) && !nzbPaused &&
|
||||
(!(g_Options->GetPauseDownload() || g_Options->GetQuotaReached()) || nzbInfo->GetForcePriority()))
|
||||
(!(g_WorkState->GetPauseDownload() || g_WorkState->GetQuotaReached()) || nzbInfo->GetForcePriority()))
|
||||
{
|
||||
for (FileInfo* fileInfo1 : nzbInfo->GetFileList())
|
||||
{
|
||||
@@ -527,7 +571,7 @@ bool QueueCoordinator::GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &f
|
||||
return true;
|
||||
}
|
||||
|
||||
if (fileInfo->GetArticles()->empty() && g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (fileInfo->GetArticles()->empty() && g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->LoadArticles(fileInfo);
|
||||
LoadPartialState(fileInfo);
|
||||
@@ -561,7 +605,7 @@ bool QueueCoordinator::GetNextFirstArticle(NzbInfo* nzbInfo, FileInfo* &fileInfo
|
||||
{
|
||||
if (!fileInfo1->GetFilenameConfirmed())
|
||||
{
|
||||
if (fileInfo1->GetArticles()->empty() && g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (fileInfo1->GetArticles()->empty() && g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->LoadArticles(fileInfo1);
|
||||
LoadPartialState(fileInfo1);
|
||||
@@ -597,7 +641,7 @@ void QueueCoordinator::StartArticleDownload(FileInfo* fileInfo, ArticleInfo* art
|
||||
articleDownloader->SetArticleInfo(articleInfo);
|
||||
articleDownloader->SetConnection(connection);
|
||||
|
||||
if (articleInfo->GetPartNumber() == 1 && g_Options->GetDirectRename() && g_Options->GetDecode())
|
||||
if (articleInfo->GetPartNumber() == 1 && g_Options->GetDirectRename() && !g_Options->GetRawArticle())
|
||||
{
|
||||
articleDownloader->SetContentAnalyzer(m_directRenamer.MakeArticleContentAnalyzer());
|
||||
}
|
||||
@@ -613,11 +657,18 @@ void QueueCoordinator::StartArticleDownload(FileInfo* fileInfo, ArticleInfo* art
|
||||
articleDownloader->Start();
|
||||
}
|
||||
|
||||
void QueueCoordinator::Update(Subject* Caller, void* Aspect)
|
||||
void QueueCoordinator::Update(Subject* caller, void* aspect)
|
||||
{
|
||||
if (caller == g_WorkState)
|
||||
{
|
||||
debug("Notification from WorkState received");
|
||||
WakeUp();
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Notification from ArticleDownloader received");
|
||||
|
||||
ArticleDownloader* articleDownloader = (ArticleDownloader*)Caller;
|
||||
ArticleDownloader* articleDownloader = (ArticleDownloader*)caller;
|
||||
if ((articleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
|
||||
(articleDownloader->GetStatus() == ArticleDownloader::adFailed) ||
|
||||
(articleDownloader->GetStatus() == ArticleDownloader::adRetry))
|
||||
@@ -762,7 +813,8 @@ void QueueCoordinator::DeleteDownloader(DownloadQueue* downloadQueue,
|
||||
if (deleteFileObj)
|
||||
{
|
||||
DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
|
||||
downloadQueue->Save();
|
||||
nzbInfo->SetChanged(true);
|
||||
downloadQueue->SaveChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,7 +822,7 @@ void QueueCoordinator::DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fi
|
||||
{
|
||||
while (g_ArticleCache->FileBusy(fileInfo))
|
||||
{
|
||||
usleep(5*1000);
|
||||
Util::Sleep(5);
|
||||
}
|
||||
|
||||
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
|
||||
@@ -794,7 +846,7 @@ void QueueCoordinator::DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fi
|
||||
fileInfo->GetSuccessArticles() > 0 || fileInfo->GetFailedArticles() > 0 ? CompletedFile::cfPartial :
|
||||
CompletedFile::cfNone;
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->DiscardFile(fileInfo->GetId(), fileStatus == CompletedFile::cfSuccess || (fileDeleted && !parking), true, false);
|
||||
if (fileStatus == CompletedFile::cfPartial && (completed || parking))
|
||||
@@ -814,7 +866,7 @@ void QueueCoordinator::DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fi
|
||||
fileInfo->GetId(),
|
||||
completed && fileInfo->GetOutputFilename() ?
|
||||
FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(),
|
||||
fileStatus,
|
||||
fileInfo->GetOrigname(), fileStatus,
|
||||
fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0,
|
||||
fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
|
||||
}
|
||||
@@ -860,33 +912,45 @@ void QueueCoordinator::DiscardTempFiles(FileInfo* fileInfo)
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::SaveAllPartialState()
|
||||
void QueueCoordinator::SaveQueueIfChanged()
|
||||
{
|
||||
if (!(g_Options->GetServerMode() && g_Options->GetSaveQueue()))
|
||||
if (!g_Options->GetServerMode())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasUnsavedData = false;
|
||||
bool hasChanges = false;
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
if (g_Options->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo* fileInfo : nzbInfo->GetFileList())
|
||||
{
|
||||
SavePartialState(fileInfo);
|
||||
}
|
||||
}
|
||||
hasUnsavedData |= nzbInfo->GetChanged();
|
||||
hasChanges |= nzbInfo->GetChanged();
|
||||
}
|
||||
|
||||
if (hasUnsavedData)
|
||||
if (hasChanges)
|
||||
{
|
||||
downloadQueue->Save();
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::SaveAllPartialState()
|
||||
{
|
||||
if (!g_Options->GetServerMode() || !g_Options->GetContinuePartial())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
|
||||
{
|
||||
for (FileInfo* fileInfo : nzbInfo->GetFileList())
|
||||
{
|
||||
SavePartialState(fileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
downloadQueue->SaveChanged();
|
||||
}
|
||||
|
||||
void QueueCoordinator::SavePartialState(FileInfo* fileInfo)
|
||||
{
|
||||
if (fileInfo->GetPartialChanged())
|
||||
@@ -923,7 +987,7 @@ void QueueCoordinator::LoadPartialState(FileInfo* fileInfo)
|
||||
|
||||
void QueueCoordinator::SaveAllFileState()
|
||||
{
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() && m_downloadQueue.m_stateChanged)
|
||||
if (g_Options->GetServerMode() && m_downloadQueue.m_stateChanged)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
g_DiskState->SaveAllFileInfos(downloadQueue);
|
||||
@@ -972,9 +1036,9 @@ void QueueCoordinator::LogDebugInfo()
|
||||
downloadQueue->CalcRemainingSize(&remaining, &remainingForced);
|
||||
info(" Remaining: %.1f MB, Forced: %.1f MB", remaining / 1024.0 / 1024.0, remainingForced / 1024.0 / 1024.0);
|
||||
info(" Download: %s, Post-process: %s, Scan: %s",
|
||||
(g_Options->GetPauseDownload() ? "paused" : g_Options->GetTempPauseDownload() ? "temp-paused" : "active"),
|
||||
(g_Options->GetPausePostProcess() ? "paused" : "active"),
|
||||
(g_Options->GetPauseScan() ? "paused" : "active"));
|
||||
(g_WorkState->GetPauseDownload() ? "paused" : g_WorkState->GetTempPauseDownload() ? "temp-paused" : "active"),
|
||||
(g_WorkState->GetPausePostProcess() ? "paused" : "active"),
|
||||
(g_WorkState->GetPauseScan() ? "paused" : "active"));
|
||||
|
||||
info(" ---------- QueueCoordinator");
|
||||
info(" Active Downloads: %i, Limit: %i", (int)m_activeDownloads.size(), m_downloadsLimit);
|
||||
@@ -986,7 +1050,7 @@ void QueueCoordinator::LogDebugInfo()
|
||||
|
||||
void QueueCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
if (g_Options->GetTerminateTimeout() == 0 && g_Options->GetArticleTimeout() == 0)
|
||||
if (g_Options->GetArticleTimeout() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -994,46 +1058,16 @@ void QueueCoordinator::ResetHangingDownloads()
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
time_t tm = Util::CurrentTime();
|
||||
|
||||
m_activeDownloads.erase(std::remove_if(m_activeDownloads.begin(), m_activeDownloads.end(),
|
||||
[tm](ArticleDownloader* articleDownloader)
|
||||
for (ArticleDownloader* articleDownloader : m_activeDownloads)
|
||||
{
|
||||
if (tm - articleDownloader->GetLastUpdateTime() > g_Options->GetArticleTimeout() + 1 &&
|
||||
articleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
if (tm - articleDownloader->GetLastUpdateTime() > g_Options->GetArticleTimeout() + 1 &&
|
||||
articleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
error("Cancelling hanging download %s @ %s", articleDownloader->GetInfoName(),
|
||||
articleDownloader->GetConnectionName());
|
||||
articleDownloader->Stop();
|
||||
}
|
||||
|
||||
if (tm - articleDownloader->GetLastUpdateTime() > g_Options->GetTerminateTimeout() &&
|
||||
articleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
ArticleInfo* articleInfo = articleDownloader->GetArticleInfo();
|
||||
debug("Terminating hanging download %s", articleDownloader->GetInfoName());
|
||||
if (articleDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s @ %s", articleDownloader->GetInfoName(),
|
||||
articleDownloader->GetConnectionName());
|
||||
articleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s @ %s", articleDownloader->GetInfoName(),
|
||||
articleDownloader->GetConnectionName());
|
||||
}
|
||||
|
||||
articleDownloader->GetFileInfo()->SetActiveDownloads(articleDownloader->GetFileInfo()->GetActiveDownloads() - 1);
|
||||
articleDownloader->GetFileInfo()->GetNzbInfo()->SetActiveDownloads(articleDownloader->GetFileInfo()->GetNzbInfo()->GetActiveDownloads() - 1);
|
||||
articleDownloader->GetFileInfo()->GetNzbInfo()->SetDownloadedSize(articleDownloader->GetFileInfo()->GetNzbInfo()->GetDownloadedSize() + articleDownloader->GetDownloadedSize());
|
||||
|
||||
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
|
||||
delete articleDownloader;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_activeDownloads.end());
|
||||
error("Cancelling hanging download %s @ %s", articleDownloader->GetInfoName(),
|
||||
articleDownloader->GetConnectionName());
|
||||
articleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1316,7 +1350,7 @@ void QueueCoordinator::DirectRenameCompleted(DownloadQueue* downloadQueue, NzbIn
|
||||
{
|
||||
for (FileInfo* fileInfo : nzbInfo->GetFileList())
|
||||
{
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() && !fileInfo->GetArticles()->empty())
|
||||
if (g_Options->GetServerMode() && !fileInfo->GetArticles()->empty())
|
||||
{
|
||||
// save new file name into disk state file
|
||||
g_DiskState->SaveFile(fileInfo);
|
||||
@@ -1339,7 +1373,8 @@ void QueueCoordinator::DirectRenameCompleted(DownloadQueue* downloadQueue, NzbIn
|
||||
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupSortFiles, nullptr);
|
||||
}
|
||||
|
||||
downloadQueue->Save();
|
||||
nzbInfo->SetChanged(true);
|
||||
downloadQueue->SaveChanged();
|
||||
|
||||
DownloadQueue::Aspect namedAspect = { DownloadQueue::eaNzbNamed, downloadQueue, nzbInfo, nullptr };
|
||||
downloadQueue->Notify(&namedAspect);
|
||||
@@ -1352,63 +1387,36 @@ void QueueCoordinator::DiscardDirectRename(DownloadQueue* downloadQueue, NzbInfo
|
||||
|
||||
for (FileInfo* fileInfo : nzbInfo->GetFileList())
|
||||
{
|
||||
if (fileInfo->GetParFile() && fileInfo->GetCompletedArticles() == 1 && fileInfo->GetActiveDownloads() == 0)
|
||||
if (fileInfo->GetParFile() && fileInfo->GetCompletedArticles() == 1 &&
|
||||
fileInfo->GetActiveDownloads() == 0)
|
||||
{
|
||||
// discard downloaded articles from partially downloaded par-files
|
||||
discardedSize += fileInfo->GetSuccessSize();
|
||||
discardedCount++;
|
||||
|
||||
nzbInfo->SetRemainingSize(nzbInfo->GetRemainingSize() + fileInfo->GetSuccessSize() + fileInfo->GetFailedSize());
|
||||
if (fileInfo->GetPaused())
|
||||
bool locked = false;
|
||||
{
|
||||
nzbInfo->SetPausedSize(nzbInfo->GetPausedSize() + fileInfo->GetSuccessSize() + fileInfo->GetFailedSize());
|
||||
}
|
||||
nzbInfo->GetCurrentServerStats()->ListOp(fileInfo->GetServerStats(), ServerStatList::soSubtract);
|
||||
fileInfo->GetServerStats()->clear();
|
||||
|
||||
nzbInfo->SetCurrentSuccessArticles(nzbInfo->GetCurrentSuccessArticles() - fileInfo->GetSuccessArticles());
|
||||
nzbInfo->SetCurrentSuccessSize(nzbInfo->GetCurrentSuccessSize() - fileInfo->GetSuccessSize());
|
||||
nzbInfo->SetParCurrentSuccessSize(nzbInfo->GetParCurrentSuccessSize() - fileInfo->GetSuccessSize());
|
||||
fileInfo->SetSuccessSize(0);
|
||||
fileInfo->SetSuccessArticles(0);
|
||||
|
||||
nzbInfo->SetCurrentFailedArticles(nzbInfo->GetCurrentFailedArticles() - fileInfo->GetFailedArticles());
|
||||
nzbInfo->SetCurrentFailedSize(nzbInfo->GetCurrentFailedSize() - fileInfo->GetFailedSize());
|
||||
nzbInfo->SetParCurrentFailedSize(nzbInfo->GetParCurrentFailedSize() - fileInfo->GetFailedSize());
|
||||
fileInfo->SetFailedSize(0);
|
||||
fileInfo->SetFailedArticles(0);
|
||||
|
||||
fileInfo->SetCompletedArticles(0);
|
||||
fileInfo->SetRemainingSize(fileInfo->GetSize() - fileInfo->GetMissedSize());
|
||||
|
||||
// discard temporary files
|
||||
DiscardTempFiles(fileInfo);
|
||||
g_DiskState->DiscardFile(fileInfo->GetId(), false, true, false);
|
||||
|
||||
fileInfo->SetOutputFilename(nullptr);
|
||||
fileInfo->SetOutputInitialized(false);
|
||||
fileInfo->SetCachedArticles(0);
|
||||
fileInfo->SetPartialChanged(false);
|
||||
fileInfo->SetPartialState(FileInfo::psNone);
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
|
||||
{
|
||||
// free up memory used by articles if possible
|
||||
fileInfo->GetArticles()->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset article states if discarding isn't possible
|
||||
for (ArticleInfo* articleInfo : fileInfo->GetArticles())
|
||||
Guard contentGuard = g_ArticleCache->GuardContent();
|
||||
locked = fileInfo->GetFlushLocked();
|
||||
if (!locked)
|
||||
{
|
||||
articleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
articleInfo->SetResultFilename(nullptr);
|
||||
articleInfo->DiscardSegment();
|
||||
fileInfo->SetFlushLocked(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
{
|
||||
// discard downloaded articles from partially downloaded par-files
|
||||
discardedSize += fileInfo->GetSuccessSize();
|
||||
discardedCount++;
|
||||
|
||||
DiscardDownloadedArticles(nzbInfo, fileInfo);
|
||||
}
|
||||
|
||||
if (!locked)
|
||||
{
|
||||
Guard contentGuard = g_ArticleCache->GuardContent();
|
||||
fileInfo->SetFlushLocked(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() &&
|
||||
if (g_Options->GetServerMode() &&
|
||||
!fileInfo->GetArticles()->empty() && g_Options->GetContinuePartial() &&
|
||||
fileInfo->GetActiveDownloads() == 0 && fileInfo->GetCachedArticles() == 0)
|
||||
{
|
||||
@@ -1426,3 +1434,55 @@ void QueueCoordinator::DiscardDirectRename(DownloadQueue* downloadQueue, NzbInfo
|
||||
*Util::FormatSize(discardedSize), discardedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::DiscardDownloadedArticles(NzbInfo* nzbInfo, FileInfo* fileInfo)
|
||||
{
|
||||
nzbInfo->SetRemainingSize(nzbInfo->GetRemainingSize() + fileInfo->GetSuccessSize() + fileInfo->GetFailedSize());
|
||||
if (fileInfo->GetPaused())
|
||||
{
|
||||
nzbInfo->SetPausedSize(nzbInfo->GetPausedSize() + fileInfo->GetSuccessSize() + fileInfo->GetFailedSize());
|
||||
}
|
||||
nzbInfo->GetCurrentServerStats()->ListOp(fileInfo->GetServerStats(), ServerStatList::soSubtract);
|
||||
fileInfo->GetServerStats()->clear();
|
||||
|
||||
nzbInfo->SetCurrentSuccessArticles(nzbInfo->GetCurrentSuccessArticles() - fileInfo->GetSuccessArticles());
|
||||
nzbInfo->SetCurrentSuccessSize(nzbInfo->GetCurrentSuccessSize() - fileInfo->GetSuccessSize());
|
||||
nzbInfo->SetParCurrentSuccessSize(nzbInfo->GetParCurrentSuccessSize() - fileInfo->GetSuccessSize());
|
||||
fileInfo->SetSuccessSize(0);
|
||||
fileInfo->SetSuccessArticles(0);
|
||||
|
||||
nzbInfo->SetCurrentFailedArticles(nzbInfo->GetCurrentFailedArticles() - fileInfo->GetFailedArticles());
|
||||
nzbInfo->SetCurrentFailedSize(nzbInfo->GetCurrentFailedSize() - fileInfo->GetFailedSize());
|
||||
nzbInfo->SetParCurrentFailedSize(nzbInfo->GetParCurrentFailedSize() - fileInfo->GetFailedSize());
|
||||
fileInfo->SetFailedSize(0);
|
||||
fileInfo->SetFailedArticles(0);
|
||||
|
||||
fileInfo->SetCompletedArticles(0);
|
||||
fileInfo->SetRemainingSize(fileInfo->GetSize() - fileInfo->GetMissedSize());
|
||||
|
||||
// discard temporary files
|
||||
DiscardTempFiles(fileInfo);
|
||||
g_DiskState->DiscardFile(fileInfo->GetId(), false, true, false);
|
||||
|
||||
fileInfo->SetOutputFilename(nullptr);
|
||||
fileInfo->SetOutputInitialized(false);
|
||||
fileInfo->SetCachedArticles(0);
|
||||
fileInfo->SetPartialChanged(false);
|
||||
fileInfo->SetPartialState(FileInfo::psNone);
|
||||
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
// free up memory used by articles if possible
|
||||
fileInfo->GetArticles()->clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reset article states if discarding isn't possible
|
||||
for (ArticleInfo* articleInfo : fileInfo->GetArticles())
|
||||
{
|
||||
articleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
articleInfo->SetResultFilename(nullptr);
|
||||
articleInfo->DiscardSegment();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
void Update(Subject* caller, void* aspect);
|
||||
|
||||
// editing queue
|
||||
NzbInfo* AddNzbFileToQueue(std::unique_ptr<NzbInfo> nzbInfo, NzbInfo* urlInfo, bool addFirst);
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
EEditAction action, const char* args);
|
||||
virtual void HistoryChanged() { m_historyChanged = true; }
|
||||
virtual void Save();
|
||||
virtual void SaveChanged();
|
||||
private:
|
||||
QueueCoordinator* m_owner;
|
||||
bool m_massEdit = false;
|
||||
@@ -94,6 +95,8 @@ private:
|
||||
bool m_hasMoreJobs = true;
|
||||
int m_downloadsLimit;
|
||||
int m_serverConfigGeneration = 0;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
|
||||
bool GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
|
||||
bool GetNextFirstArticle(NzbInfo* nzbInfo, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
|
||||
@@ -103,15 +106,18 @@ private:
|
||||
void DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fileInfo, bool completed);
|
||||
void DirectRenameCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
|
||||
void DiscardDirectRename(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
|
||||
void DiscardDownloadedArticles(NzbInfo* nzbInfo, FileInfo* fileInfo);
|
||||
void CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileInfo);
|
||||
void ResetHangingDownloads();
|
||||
void AdjustDownloadsLimit();
|
||||
void Load();
|
||||
void SaveQueueIfChanged();
|
||||
void SaveAllPartialState();
|
||||
void SavePartialState(FileInfo* fileInfo);
|
||||
void LoadPartialState(FileInfo* fileInfo);
|
||||
void SaveAllFileState();
|
||||
void WaitJobs();
|
||||
void WakeUp();
|
||||
};
|
||||
|
||||
extern QueueCoordinator* g_QueueCoordinator;
|
||||
|
||||
@@ -33,9 +33,9 @@ private:
|
||||
class EditItem
|
||||
{
|
||||
public:
|
||||
int m_offset;
|
||||
FileInfo* m_fileInfo;
|
||||
NzbInfo* m_nzbInfo;
|
||||
int m_offset;
|
||||
|
||||
EditItem(FileInfo* fileInfo, NzbInfo* nzbInfo, int offset) :
|
||||
m_fileInfo(fileInfo), m_nzbInfo(nzbInfo), m_offset(offset) {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "HistoryCoordinator.h"
|
||||
@@ -73,60 +74,72 @@ void Scanner::QueueData::SetNzbId(int nzbId)
|
||||
|
||||
void Scanner::InitOptions()
|
||||
{
|
||||
m_nzbDirInterval = g_Options->GetNzbDirInterval() * 1000;
|
||||
m_nzbDirInterval = 1;
|
||||
m_scanScript = ScanScriptController::HasScripts();
|
||||
}
|
||||
|
||||
int Scanner::ServiceInterval()
|
||||
{
|
||||
return m_requestedNzbDirScan ? Service::Now :
|
||||
g_Options->GetNzbDirInterval() <= 0 ? Service::Sleep :
|
||||
// g_Options->GetPauseScan() ? Service::Sleep : // for that to work we need to react on changing of pause-state
|
||||
m_nzbDirInterval;
|
||||
}
|
||||
|
||||
void Scanner::ServiceWork()
|
||||
{
|
||||
debug("Scanner service work");
|
||||
|
||||
if (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_nzbDirInterval = g_Options->GetNzbDirInterval();
|
||||
|
||||
if (g_WorkState->GetPauseScan() && !m_requestedNzbDirScan)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Scanner service work: doing work");
|
||||
|
||||
Guard guard(m_scanMutex);
|
||||
|
||||
if (m_requestedNzbDirScan ||
|
||||
(!g_Options->GetPauseScan() && g_Options->GetNzbDirInterval() > 0 &&
|
||||
m_nzbDirInterval >= g_Options->GetNzbDirInterval() * 1000))
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
|
||||
bool checkStat = !m_requestedNzbDirScan;
|
||||
m_requestedNzbDirScan = false;
|
||||
m_scanning = true;
|
||||
CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
|
||||
if (!checkStat && m_scanScript)
|
||||
{
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
|
||||
bool checkStat = !m_requestedNzbDirScan;
|
||||
m_requestedNzbDirScan = false;
|
||||
m_scanning = true;
|
||||
// if immediate scan requested, we need second scan to process files extracted by scan-scripts
|
||||
CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
|
||||
if (!checkStat && m_scanScript)
|
||||
{
|
||||
// if immediate scan requested, we need second scan to process files extracted by scan-scripts
|
||||
CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
|
||||
}
|
||||
m_scanning = false;
|
||||
m_nzbDirInterval = 0;
|
||||
|
||||
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
|
||||
// is set for rare scans like once per hour) we make 4 scans:
|
||||
// - one additional scan is neccessary to check sizes of detected files;
|
||||
// - another scan is required to check files which were extracted by scan-scripts;
|
||||
// - third scan is needed to check sizes of extracted files.
|
||||
if (g_Options->GetNzbDirInterval() > 0 && g_Options->GetNzbDirFileAge() < g_Options->GetNzbDirInterval())
|
||||
{
|
||||
int maxPass = m_scanScript ? 3 : 1;
|
||||
if (m_pass < maxPass)
|
||||
{
|
||||
// scheduling another scan of incoming directory in NzbDirFileAge seconds.
|
||||
m_nzbDirInterval = (g_Options->GetNzbDirInterval() - g_Options->GetNzbDirFileAge()) * 1000;
|
||||
m_pass++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pass = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DropOldFiles();
|
||||
m_queueList.clear();
|
||||
}
|
||||
m_nzbDirInterval += 200;
|
||||
m_scanning = false;
|
||||
|
||||
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
|
||||
// is set for rare scans like once per hour) we make 4 scans:
|
||||
// - one additional scan is neccessary to check sizes of detected files;
|
||||
// - another scan is required to check files which were extracted by scan-scripts;
|
||||
// - third scan is needed to check sizes of extracted files.
|
||||
if (g_Options->GetNzbDirInterval() > 0 && g_Options->GetNzbDirFileAge() < g_Options->GetNzbDirInterval())
|
||||
{
|
||||
int maxPass = m_scanScript ? 3 : 1;
|
||||
if (m_pass < maxPass)
|
||||
{
|
||||
// scheduling another scan of incoming directory in NzbDirFileAge seconds.
|
||||
m_nzbDirInterval = g_Options->GetNzbDirFileAge();
|
||||
m_pass++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pass = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DropOldFiles();
|
||||
m_queueList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +384,10 @@ void Scanner::InitPPParameters(const char* category, NzbParameterList* parameter
|
||||
}
|
||||
}
|
||||
|
||||
parameters->SetParameter("*Unpack:", unpack ? "yes" : "no");
|
||||
if (!parameters->Find("*Unpack:"))
|
||||
{
|
||||
parameters->SetParameter("*Unpack:", unpack ? "yes" : "no");
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(extensions))
|
||||
{
|
||||
@@ -381,10 +397,12 @@ void Scanner::InitPPParameters(const char* category, NzbParameterList* parameter
|
||||
{
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
BString<1024> paramName("%s:", scriptName);
|
||||
if ((script.GetPostScript() || script.GetQueueScript()) &&
|
||||
!parameters->Find(paramName) &&
|
||||
FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
parameters->SetParameter(BString<1024>("%s:", scriptName), "yes");
|
||||
parameters->SetParameter(paramName, "yes");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -433,6 +451,7 @@ bool Scanner::AddFileToQueue(const char* filename, const char* nzbName, const ch
|
||||
nzbInfo->SetUrl(urlInfo->GetUrl());
|
||||
nzbInfo->SetUrlStatus(urlInfo->GetUrlStatus());
|
||||
nzbInfo->SetFeedId(urlInfo->GetFeedId());
|
||||
nzbInfo->SetDupeHint(urlInfo->GetDupeHint());
|
||||
}
|
||||
|
||||
if (nzbFile.GetPassword())
|
||||
@@ -453,7 +472,14 @@ bool Scanner::AddFileToQueue(const char* filename, const char* nzbName, const ch
|
||||
{
|
||||
addedNzb = g_QueueCoordinator->AddNzbFileToQueue(std::move(nzbInfo), std::move(urlInfo), addTop);
|
||||
}
|
||||
else if (!urlInfo)
|
||||
else if (urlInfo)
|
||||
{
|
||||
for (Message& message : nzbInfo->GuardCachedMessages())
|
||||
{
|
||||
urlInfo->AddMessage(message.GetKind(), message.GetText(), false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsScan);
|
||||
addedNzb = g_QueueCoordinator->AddNzbFileToQueue(std::move(nzbInfo), std::move(urlInfo), addTop);
|
||||
@@ -473,16 +499,17 @@ void Scanner::ScanNzbDir(bool syncMode)
|
||||
Guard guard(m_scanMutex);
|
||||
m_scanning = true;
|
||||
m_requestedNzbDirScan = true;
|
||||
WakeUp();
|
||||
}
|
||||
|
||||
while (syncMode && (m_scanning || m_requestedNzbDirScan))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
Scanner::EAddStatus Scanner::AddExternalFile(const char* nzbName, const char* category,
|
||||
int priority, const char* dupeKey, int dupeScore, EDupeMode dupeMode,
|
||||
int priority, const char* dupeKey, int dupeScore, EDupeMode dupeMode,
|
||||
NzbParameterList* parameters, bool addTop, bool addPaused, NzbInfo* urlInfo,
|
||||
const char* fileName, const char* buffer, int bufSize, int* nzbId)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
void InitPPParameters(const char* category, NzbParameterList* parameters, bool reset);
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 200; }
|
||||
virtual int ServiceInterval();
|
||||
virtual void ServiceWork();
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
@@ -60,6 +61,12 @@ void UrlDownloader::ProcessHeader(const char* line)
|
||||
}
|
||||
}
|
||||
|
||||
UrlCoordinator::UrlCoordinator()
|
||||
{
|
||||
m_downloadQueueObserver.m_owner = this;
|
||||
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
|
||||
}
|
||||
|
||||
UrlCoordinator::~UrlCoordinator()
|
||||
{
|
||||
debug("Destroying UrlCoordinator");
|
||||
@@ -79,41 +86,45 @@ void UrlCoordinator::Run()
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
usleep(20 * 1000);
|
||||
Util::Sleep(20);
|
||||
}
|
||||
|
||||
int resetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
time_t lastReset = 0;
|
||||
bool downloadStarted = false;
|
||||
if (!g_Options->GetPauseDownload() || g_Options->GetUrlForce())
|
||||
|
||||
{
|
||||
// start download for next URL
|
||||
NzbInfo* nzbInfo = nullptr;
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
|
||||
{
|
||||
NzbInfo* nzbInfo = GetNextUrl(downloadQueue);
|
||||
bool hasMoreUrls = nzbInfo != nullptr;
|
||||
bool urlDownloadsRunning = !m_activeDownloads.empty();
|
||||
m_hasMoreJobs = hasMoreUrls || urlDownloadsRunning;
|
||||
if (hasMoreUrls && !IsStopped())
|
||||
nzbInfo = GetNextUrl(downloadQueue);
|
||||
if (nzbInfo && (!g_WorkState->GetPauseDownload() || g_Options->GetUrlForce()))
|
||||
{
|
||||
StartUrlDownload(nzbInfo);
|
||||
downloadStarted = true;
|
||||
}
|
||||
}
|
||||
m_hasMoreJobs = !m_activeDownloads.empty() || nzbInfo;
|
||||
}
|
||||
|
||||
int sleepInterval = downloadStarted ? 0 : 100;
|
||||
usleep(sleepInterval * 1000);
|
||||
|
||||
resetCounter += sleepInterval;
|
||||
if (resetCounter >= 1000)
|
||||
if (lastReset != Util::CurrentTime())
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
ResetHangingDownloads();
|
||||
resetCounter = 0;
|
||||
lastReset = Util::CurrentTime();
|
||||
}
|
||||
|
||||
if (!m_hasMoreJobs && !IsStopped())
|
||||
{
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.Wait(m_waitMutex, [&] { return m_hasMoreJobs || IsStopped(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
int sleepInterval = downloadStarted ? 0 : 100;
|
||||
Util::Sleep(sleepInterval);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +147,7 @@ void UrlCoordinator::WaitJobs()
|
||||
break;
|
||||
}
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
|
||||
@@ -148,18 +159,38 @@ void UrlCoordinator::Stop()
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (UrlDownloader* urlDownloader : m_activeDownloads)
|
||||
{
|
||||
urlDownloader->Stop();
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (UrlDownloader* urlDownloader : m_activeDownloads)
|
||||
{
|
||||
urlDownloader->Stop();
|
||||
}
|
||||
}
|
||||
debug("UrlDownloads are notified");
|
||||
|
||||
// Resume Run() to exit it
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void UrlCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from download queue received");
|
||||
|
||||
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
|
||||
if (queueAspect->action == DownloadQueue::eaUrlAdded ||
|
||||
queueAspect->action == DownloadQueue::eaUrlReturned)
|
||||
{
|
||||
// Resume Run()
|
||||
Guard guard(m_waitMutex);
|
||||
m_hasMoreJobs = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int timeout = g_Options->GetTerminateTimeout();
|
||||
if (timeout == 0)
|
||||
if (g_Options->GetUrlTimeout() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -167,32 +198,15 @@ void UrlCoordinator::ResetHangingDownloads()
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
time_t tm = Util::CurrentTime();
|
||||
|
||||
m_activeDownloads.erase(std::remove_if(m_activeDownloads.begin(), m_activeDownloads.end(),
|
||||
[timeout, tm](UrlDownloader* urlDownloader)
|
||||
for (UrlDownloader* urlDownloader: m_activeDownloads)
|
||||
{
|
||||
if (tm - urlDownloader->GetLastUpdateTime() > g_Options->GetUrlTimeout() + 10 &&
|
||||
urlDownloader->GetStatus() == UrlDownloader::adRunning)
|
||||
{
|
||||
if (tm - urlDownloader->GetLastUpdateTime() > timeout &&
|
||||
urlDownloader->GetStatus() == UrlDownloader::adRunning)
|
||||
{
|
||||
NzbInfo* nzbInfo = urlDownloader->GetNzbInfo();
|
||||
debug("Terminating hanging download %s", urlDownloader->GetInfoName());
|
||||
if (urlDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", urlDownloader->GetInfoName());
|
||||
nzbInfo->SetUrlStatus(NzbInfo::lsNone);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", urlDownloader->GetInfoName());
|
||||
}
|
||||
|
||||
// it's not safe to destroy urlDownloader, because the state of object is unknown
|
||||
delete urlDownloader;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_activeDownloads.end());
|
||||
error("Cancelling hanging url download %s", urlDownloader->GetInfoName());
|
||||
urlDownloader->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::LogDebugInfo()
|
||||
@@ -212,8 +226,6 @@ void UrlCoordinator::LogDebugInfo()
|
||||
*/
|
||||
NzbInfo* UrlCoordinator::GetNextUrl(DownloadQueue* downloadQueue)
|
||||
{
|
||||
bool pauseDownload = g_Options->GetPauseDownload();
|
||||
|
||||
NzbInfo* nzbInfo = nullptr;
|
||||
|
||||
for (NzbInfo* nzbInfo1 : downloadQueue->GetQueue())
|
||||
@@ -221,7 +233,6 @@ NzbInfo* UrlCoordinator::GetNextUrl(DownloadQueue* downloadQueue)
|
||||
if (nzbInfo1->GetKind() == NzbInfo::nkUrl &&
|
||||
nzbInfo1->GetUrlStatus() == NzbInfo::lsNone &&
|
||||
nzbInfo1->GetDeleteStatus() == NzbInfo::dsNone &&
|
||||
(!pauseDownload || g_Options->GetUrlForce()) &&
|
||||
(!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()))
|
||||
{
|
||||
nzbInfo = nzbInfo1;
|
||||
@@ -295,13 +306,11 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* urlDownloader)
|
||||
// remove downloader from downloader list
|
||||
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), urlDownloader));
|
||||
|
||||
nzbInfo->SetActiveDownloads(0);
|
||||
|
||||
retry = urlDownloader->GetStatus() == WebDownloader::adRetry && !nzbInfo->GetDeleting();
|
||||
|
||||
if (nzbInfo->GetDeleting())
|
||||
{
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsManual);
|
||||
nzbInfo->SetDeleteStatus(nzbInfo->GetDeleteStatus() == NzbInfo::dsNone ? NzbInfo::dsManual : nzbInfo->GetDeleteStatus());
|
||||
nzbInfo->SetUrlStatus(NzbInfo::lsNone);
|
||||
nzbInfo->SetDeleting(false);
|
||||
}
|
||||
@@ -327,6 +336,7 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* urlDownloader)
|
||||
|
||||
if (retry)
|
||||
{
|
||||
nzbInfo->SetActiveDownloads(0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -342,7 +352,7 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* urlDownloader)
|
||||
|
||||
if (addStatus == Scanner::asSuccess)
|
||||
{
|
||||
// if scanner has successfully added nzb-file to queue, our pNZBInfo is
|
||||
// if scanner has successfully added nzb-file to queue, our nzbInfo is
|
||||
// already removed from queue and destroyed
|
||||
return;
|
||||
}
|
||||
@@ -354,31 +364,13 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* urlDownloader)
|
||||
|
||||
g_QueueScriptCoordinator->EnqueueScript(nzbInfo, QueueScriptCoordinator::qeUrlCompleted);
|
||||
|
||||
std::unique_ptr<NzbInfo> oldNzbInfo;
|
||||
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
|
||||
// delete URL from queue
|
||||
oldNzbInfo = downloadQueue->GetQueue()->Remove(nzbInfo);
|
||||
nzbInfo->SetActiveDownloads(0);
|
||||
|
||||
// add failed URL to history
|
||||
if (g_Options->GetKeepHistory() > 0 &&
|
||||
nzbInfo->GetUrlStatus() != NzbInfo::lsFinished &&
|
||||
!nzbInfo->GetAvoidHistory())
|
||||
{
|
||||
std::unique_ptr<HistoryInfo> historyInfo = std::make_unique<HistoryInfo>(std::move(oldNzbInfo));
|
||||
historyInfo->SetTime(Util::CurrentTime());
|
||||
downloadQueue->GetHistory()->Add(std::move(historyInfo), true);
|
||||
downloadQueue->HistoryChanged();
|
||||
}
|
||||
|
||||
downloadQueue->Save();
|
||||
}
|
||||
|
||||
if (oldNzbInfo)
|
||||
{
|
||||
g_DiskState->DiscardFiles(oldNzbInfo.get());
|
||||
DownloadQueue::Aspect aspect = {DownloadQueue::eaUrlFailed, downloadQueue, nzbInfo, nullptr};
|
||||
downloadQueue->Notify(&aspect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,26 +390,40 @@ bool UrlCoordinator::DeleteQueueEntry(DownloadQueue* downloadQueue, NzbInfo* nzb
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
info("Deleting URL %s", nzbInfo->GetName());
|
||||
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsManual);
|
||||
nzbInfo->SetDeleteStatus(nzbInfo->GetDeleteStatus() == NzbInfo::dsNone ? NzbInfo::dsManual : nzbInfo->GetDeleteStatus());
|
||||
nzbInfo->SetUrlStatus(NzbInfo::lsNone);
|
||||
|
||||
std::unique_ptr<NzbInfo> oldNzbInfo = downloadQueue->GetQueue()->Remove(nzbInfo);
|
||||
|
||||
if (g_Options->GetKeepHistory() > 0 && !avoidHistory)
|
||||
{
|
||||
std::unique_ptr<HistoryInfo> historyInfo = std::make_unique<HistoryInfo>(std::move(oldNzbInfo));
|
||||
historyInfo->SetTime(Util::CurrentTime());
|
||||
downloadQueue->GetHistory()->Add(std::move(historyInfo), true);
|
||||
downloadQueue->HistoryChanged();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_DiskState->DiscardFiles(oldNzbInfo.get());
|
||||
}
|
||||
DownloadQueue::Aspect deletedAspect = {DownloadQueue::eaUrlDeleted, downloadQueue, nzbInfo, nullptr};
|
||||
downloadQueue->Notify(&deletedAspect);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UrlCoordinator::AddUrlToQueue(std::unique_ptr<NzbInfo> nzbInfo, bool addFirst)
|
||||
{
|
||||
debug("Adding URL to queue");
|
||||
|
||||
NzbInfo* addedNzb = nzbInfo.get();
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
|
||||
DownloadQueue::Aspect foundAspect = {DownloadQueue::eaUrlFound, downloadQueue, addedNzb, nullptr};
|
||||
downloadQueue->Notify(&foundAspect);
|
||||
|
||||
if (addedNzb->GetDeleteStatus() != NzbInfo::dsManual)
|
||||
{
|
||||
downloadQueue->GetQueue()->Add(std::move(nzbInfo), addFirst);
|
||||
|
||||
DownloadQueue::Aspect addedAspect = {DownloadQueue::eaUrlAdded, downloadQueue, addedNzb, nullptr};
|
||||
downloadQueue->Notify(&addedAspect);
|
||||
}
|
||||
|
||||
downloadQueue->Save();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -33,12 +33,14 @@ class UrlDownloader;
|
||||
class UrlCoordinator : public Thread, public Observer, public Debuggable
|
||||
{
|
||||
public:
|
||||
UrlCoordinator();
|
||||
virtual ~UrlCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* caller, void* aspect);
|
||||
|
||||
// Editing the queue
|
||||
void AddUrlToQueue(std::unique_ptr<NzbInfo> nzbInfo, bool addFirst);
|
||||
bool HasMoreJobs() { return m_hasMoreJobs; }
|
||||
bool DeleteQueueEntry(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, bool avoidHistory);
|
||||
|
||||
@@ -48,15 +50,26 @@ protected:
|
||||
private:
|
||||
typedef std::list<UrlDownloader*> ActiveDownloads;
|
||||
|
||||
class DownloadQueueObserver: public Observer
|
||||
{
|
||||
public:
|
||||
UrlCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
ActiveDownloads m_activeDownloads;
|
||||
bool m_hasMoreJobs = true;
|
||||
bool m_force;
|
||||
Mutex m_waitMutex;
|
||||
ConditionVar m_waitCond;
|
||||
DownloadQueueObserver m_downloadQueueObserver;
|
||||
|
||||
NzbInfo* GetNextUrl(DownloadQueue* downloadQueue);
|
||||
void StartUrlDownload(NzbInfo* nzbInfo);
|
||||
void UrlCompleted(UrlDownloader* urlDownloader);
|
||||
void ResetHangingDownloads();
|
||||
void WaitJobs();
|
||||
void DownloadQueueUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
extern UrlCoordinator* g_UrlCoordinator;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -23,12 +23,14 @@
|
||||
#include "BinRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Scanner.h"
|
||||
#include "StatMeter.h"
|
||||
#include "UrlCoordinator.h"
|
||||
|
||||
extern void ExitProc();
|
||||
extern void Reload();
|
||||
@@ -312,20 +314,20 @@ void PauseUnpauseBinCommand::Execute()
|
||||
return;
|
||||
}
|
||||
|
||||
g_Options->SetResumeTime(0);
|
||||
g_WorkState->SetResumeTime(0);
|
||||
|
||||
switch (ntohl(PauseUnpauseRequest.m_action))
|
||||
{
|
||||
case rpDownload:
|
||||
g_Options->SetPauseDownload(ntohl(PauseUnpauseRequest.m_pause));
|
||||
g_WorkState->SetPauseDownload(ntohl(PauseUnpauseRequest.m_pause));
|
||||
break;
|
||||
|
||||
case rpPostProcess:
|
||||
g_Options->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_pause));
|
||||
g_WorkState->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_pause));
|
||||
break;
|
||||
|
||||
case rpScan:
|
||||
g_Options->SetPauseScan(ntohl(PauseUnpauseRequest.m_pause));
|
||||
g_WorkState->SetPauseScan(ntohl(PauseUnpauseRequest.m_pause));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -340,7 +342,7 @@ void SetDownloadRateBinCommand::Execute()
|
||||
return;
|
||||
}
|
||||
|
||||
g_Options->SetDownloadRate(ntohl(SetDownloadRequest.m_downloadRate));
|
||||
g_WorkState->SetSpeedLimit(ntohl(SetDownloadRequest.m_downloadRate));
|
||||
SendBoolResponse(true, "Rate-Command completed successfully");
|
||||
}
|
||||
|
||||
@@ -430,9 +432,7 @@ void DownloadBinCommand::Execute()
|
||||
nzbInfo->SetDupeScore(dupeScore);
|
||||
nzbInfo->SetDupeMode((EDupeMode)dupeMode);
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
downloadQueue->GetQueue()->Add(std::move(nzbInfo), addTop);
|
||||
downloadQueue->Save();
|
||||
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), addTop);
|
||||
|
||||
ok = true;
|
||||
}
|
||||
@@ -675,10 +675,10 @@ void ListBinCommand::Execute()
|
||||
Util::SplitInt64(remainingSize, &sizeHi, &sizeLo);
|
||||
ListResponse.m_remainingSizeHi = htonl(sizeHi);
|
||||
ListResponse.m_remainingSizeLo = htonl(sizeLo);
|
||||
ListResponse.m_downloadLimit = htonl(g_Options->GetDownloadRate());
|
||||
ListResponse.m_downloadPaused = htonl(g_Options->GetPauseDownload());
|
||||
ListResponse.m_postPaused = htonl(g_Options->GetPausePostProcess());
|
||||
ListResponse.m_scanPaused = htonl(g_Options->GetPauseScan());
|
||||
ListResponse.m_downloadLimit = htonl(g_WorkState->GetSpeedLimit());
|
||||
ListResponse.m_downloadPaused = htonl(g_WorkState->GetPauseDownload());
|
||||
ListResponse.m_postPaused = htonl(g_WorkState->GetPausePostProcess());
|
||||
ListResponse.m_scanPaused = htonl(g_WorkState->GetPauseScan());
|
||||
ListResponse.m_threadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
|
||||
ListResponse.m_postJobCount = htonl(postJobCount);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -497,7 +497,7 @@ bool RemoteClient::RequestServerList(bool files, bool groups, const char* patter
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Groups: %i\n", downloadQueue->GetQueue()->size());
|
||||
printf("Groups: %i\n", (int)downloadQueue->GetQueue()->size());
|
||||
if (pattern)
|
||||
{
|
||||
printf("Matches: %i\n", matches);
|
||||
@@ -797,7 +797,7 @@ bool RemoteClient::RequestServerEditQueue(DownloadQueue::EEditAction action, int
|
||||
|
||||
if (textLen > 0)
|
||||
{
|
||||
strcpy(trailingData, text);
|
||||
strncpy(trailingData, text, textLen);
|
||||
}
|
||||
|
||||
int32* ids = (int32*)(trailingData + textLen);
|
||||
@@ -812,7 +812,8 @@ bool RemoteClient::RequestServerEditQueue(DownloadQueue::EEditAction action, int
|
||||
char *names = trailingData + textLen + idLength;
|
||||
for (CString& name : nameList)
|
||||
{
|
||||
int len = strlen(name);
|
||||
// "len" must be less or equal than: "buffer size" - "already used buffer" - "ending \0"
|
||||
size_t len = strnlen(name, length - (names - trailingData) - 1);
|
||||
strncpy(names, name, len + 1);
|
||||
names += len + 1;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ void RemoteServer::Run()
|
||||
break;
|
||||
}
|
||||
m_connection.reset();
|
||||
usleep(500 * 1000);
|
||||
Util::Sleep(500);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ void RemoteServer::Run()
|
||||
Guard guard(m_processorsMutex);
|
||||
completed = m_activeProcessors.size() == 0;
|
||||
}
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
debug("RemoteServer: request processor are completed");
|
||||
|
||||
@@ -125,6 +125,7 @@ void RemoteServer::Stop()
|
||||
if (m_connection)
|
||||
{
|
||||
m_connection->SetSuppressErrors(true);
|
||||
m_connection->SetForceClose(true);
|
||||
m_connection->Cancel();
|
||||
|
||||
debug("Stopping RequestProcessors");
|
||||
@@ -138,6 +139,18 @@ void RemoteServer::Stop()
|
||||
debug("RemoteServer stop end");
|
||||
}
|
||||
|
||||
void RemoteServer::ForceStop()
|
||||
{
|
||||
debug("Killing RequestProcessors");
|
||||
Guard guard(m_processorsMutex);
|
||||
for (RequestProcessor* requestProcessor : m_activeProcessors)
|
||||
{
|
||||
requestProcessor->Kill();
|
||||
}
|
||||
m_activeProcessors.clear();
|
||||
debug("RequestProcessors are killed");
|
||||
}
|
||||
|
||||
void RemoteServer::Update(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from RequestProcessor received");
|
||||
@@ -164,6 +177,9 @@ void RequestProcessor::Run()
|
||||
void RequestProcessor::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
#ifdef WIN32
|
||||
m_connection->SetForceClose(true);
|
||||
#endif
|
||||
m_connection->Cancel();
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ public:
|
||||
RemoteServer(bool tls) : m_tls(tls) {}
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void ForceStop();
|
||||
void Update(Subject* caller, void* aspect);
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,7 @@ void WebProcessor::Init()
|
||||
|
||||
for (int j = uaControl; j <= uaAdd; j++)
|
||||
{
|
||||
for (int i = 0; i < sizeof(m_serverAuthToken[j]) - 1; i++)
|
||||
for (size_t i = 0; i < sizeof(m_serverAuthToken[j]) - 1; i++)
|
||||
{
|
||||
int ch = rand() % (10 + 26 + 26);
|
||||
if (0 <= ch && ch < 10)
|
||||
@@ -222,18 +222,11 @@ void WebProcessor::ParseUrl()
|
||||
if (pauth1 && pauth1 < pauth2)
|
||||
{
|
||||
char* pstart = m_url + 1;
|
||||
int len = 0;
|
||||
char* pend = strchr(pstart + 1, '/');
|
||||
if (pend)
|
||||
{
|
||||
len = (int)(pend - pstart < (int)sizeof(m_authInfo) - 1 ? pend - pstart : (int)sizeof(m_authInfo) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlen(pstart);
|
||||
}
|
||||
char* pend = pauth2;
|
||||
int len = std::min((int)(pend - pstart), (int)sizeof(m_authInfo) - 1);
|
||||
strncpy(m_authInfo, pstart, len);
|
||||
m_authInfo[len] = '\0';
|
||||
WebUtil::UrlDecode(m_authInfo);
|
||||
m_url = CString(pend);
|
||||
}
|
||||
|
||||
@@ -283,10 +276,10 @@ bool WebProcessor::CheckCredentials()
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Request received on port %i from %s%s, but username or password invalid (%s:%s)",
|
||||
warn("Request received on port %i from %s%s, but username (%s) or password invalid",
|
||||
g_Options->GetControlPort(), m_connection->GetRemoteAddr(),
|
||||
!m_forwardedFor.Empty() ? (char*)BString<1024>(" (forwarded for: %s)", *m_forwardedFor) : "",
|
||||
m_authInfo, pw);
|
||||
m_authInfo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -301,9 +294,10 @@ bool WebProcessor::IsAuthorizedIp(const char* remoteAddr)
|
||||
// split option AuthorizedIP into tokens and check each token
|
||||
bool authorized = false;
|
||||
Tokenizer tok(g_Options->GetAuthorizedIp(), ",;");
|
||||
while (const char* iP = tok.Next())
|
||||
while (const char* ip = tok.Next())
|
||||
{
|
||||
if (!strcmp(iP, remoteIp))
|
||||
WildMask mask(ip);
|
||||
if (mask.Match(remoteIp))
|
||||
{
|
||||
authorized = true;
|
||||
break;
|
||||
@@ -465,8 +459,8 @@ void WebProcessor::SendBodyResponse(const char* body, int bodyLen, const char* c
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"Access-Control-Max-Age: 86400\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Set-Cookie: Auth-Type=%s\r\n"
|
||||
"Set-Cookie: Auth-Token=%s\r\n"
|
||||
"Set-Cookie: Auth-Type=%s; SameSite=Lax\r\n"
|
||||
"Set-Cookie: Auth-Token=%s; HttpOnly; SameSite=Lax\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"%s" // Content-Type: xxx
|
||||
"%s" // Content-Encoding: gzip
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "XmlRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Scanner.h"
|
||||
#include "FeedCoordinator.h"
|
||||
#include "ServerPool.h"
|
||||
@@ -34,6 +35,7 @@
|
||||
#include "ScriptConfig.h"
|
||||
#include "QueueScript.h"
|
||||
#include "CommandScript.h"
|
||||
#include "UrlCoordinator.h"
|
||||
|
||||
extern void ExitProc();
|
||||
extern void Reload();
|
||||
@@ -403,12 +405,12 @@ void XmlRpcProcessor::Dispatch()
|
||||
int valueLen = 0;
|
||||
if (const char* methodPtr = WebUtil::JsonFindField(m_request, "method", &valueLen))
|
||||
{
|
||||
valueLen = valueLen >= sizeof(methodName) ? sizeof(methodName) - 1 : valueLen;
|
||||
valueLen = valueLen >= (int)sizeof(methodName) ? (int)sizeof(methodName) - 1 : valueLen;
|
||||
methodName.Set(methodPtr + 1, valueLen - 2);
|
||||
}
|
||||
if (const char* requestIdPtr = WebUtil::JsonFindField(m_request, "id", &valueLen))
|
||||
{
|
||||
valueLen = valueLen >= sizeof(requestId) ? sizeof(requestId) - 1 : valueLen;
|
||||
valueLen = valueLen >= (int)sizeof(requestId) ? (int)sizeof(requestId) - 1 : valueLen;
|
||||
requestId.Set(requestIdPtr, valueLen);
|
||||
}
|
||||
}
|
||||
@@ -559,9 +561,7 @@ void XmlRpcProcessor::BuildResponse(const char* response, const char* callbackFu
|
||||
void XmlRpcProcessor::BuildErrorResponse(int errCode, const char* errText)
|
||||
{
|
||||
ErrorXmlCommand command(errCode, errText);
|
||||
command.SetRequest(m_request);
|
||||
command.SetProtocol(m_protocol);
|
||||
command.PrepareParams();
|
||||
command.Execute();
|
||||
BuildResponse(command.GetResponse(), "", command.GetFault(), nullptr);
|
||||
}
|
||||
@@ -892,7 +892,7 @@ bool XmlCommand::NextParamAsInt(int* value)
|
||||
}
|
||||
*value = atoi(param + 1);
|
||||
m_requestPtr = param + 1;
|
||||
while (strchr("-+0123456789&", *m_requestPtr))
|
||||
while (*m_requestPtr && strchr("-+0123456789&", *m_requestPtr))
|
||||
{
|
||||
m_requestPtr++;
|
||||
}
|
||||
@@ -1101,20 +1101,20 @@ void PauseUnpauseXmlCommand::Execute()
|
||||
{
|
||||
bool ok = true;
|
||||
|
||||
g_Options->SetResumeTime(0);
|
||||
g_WorkState->SetResumeTime(0);
|
||||
|
||||
switch (m_pauseAction)
|
||||
{
|
||||
case paDownload:
|
||||
g_Options->SetPauseDownload(m_pause);
|
||||
g_WorkState->SetPauseDownload(m_pause);
|
||||
break;
|
||||
|
||||
case paPostProcess:
|
||||
g_Options->SetPausePostProcess(m_pause);
|
||||
g_WorkState->SetPausePostProcess(m_pause);
|
||||
break;
|
||||
|
||||
case paScan:
|
||||
g_Options->SetPauseScan(m_pause);
|
||||
g_WorkState->SetPauseScan(m_pause);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1136,7 +1136,7 @@ void ScheduleResumeXmlCommand::Execute()
|
||||
|
||||
time_t curTime = Util::CurrentTime();
|
||||
|
||||
g_Options->SetResumeTime(curTime + seconds);
|
||||
g_WorkState->SetResumeTime(curTime + seconds);
|
||||
|
||||
BuildBoolResponse(true);
|
||||
}
|
||||
@@ -1178,7 +1178,7 @@ void SetDownloadRateXmlCommand::Execute()
|
||||
return;
|
||||
}
|
||||
|
||||
g_Options->SetDownloadRate(rate * 1024);
|
||||
g_WorkState->SetSpeedLimit(rate * 1024);
|
||||
BuildBoolResponse(true);
|
||||
}
|
||||
|
||||
@@ -1321,11 +1321,11 @@ void StatusXmlCommand::Execute()
|
||||
int articleCacheMBytes = (int)(articleCache / 1024 / 1024);
|
||||
|
||||
int downloadRate = (int)(g_StatMeter->CalcCurrentDownloadSpeed());
|
||||
int downloadLimit = (int)(g_Options->GetDownloadRate());
|
||||
bool downloadPaused = g_Options->GetPauseDownload();
|
||||
bool postPaused = g_Options->GetPausePostProcess();
|
||||
bool scanPaused = g_Options->GetPauseScan();
|
||||
bool quotaReached = g_Options->GetQuotaReached();
|
||||
int downloadLimit = (int)(g_WorkState->GetSpeedLimit());
|
||||
bool downloadPaused = g_WorkState->GetPauseDownload();
|
||||
bool postPaused = g_WorkState->GetPausePostProcess();
|
||||
bool scanPaused = g_WorkState->GetPauseScan();
|
||||
bool quotaReached = g_WorkState->GetQuotaReached();
|
||||
int threadCount = Thread::GetThreadCount() - 1; // not counting itself
|
||||
|
||||
uint32 downloadedSizeHi, downloadedSizeLo;
|
||||
@@ -1353,7 +1353,7 @@ void StatusXmlCommand::Execute()
|
||||
int freeDiskSpaceMB = (int)(freeDiskSpace / 1024 / 1024);
|
||||
|
||||
int serverTime = (int)Util::CurrentTime();
|
||||
int resumeTime = (int)g_Options->GetResumeTime();
|
||||
int resumeTime = (int)g_WorkState->GetResumeTime();
|
||||
bool feedActive = g_FeedCoordinator->HasActiveDownloads();
|
||||
int queuedScripts = g_QueueScriptCoordinator->GetQueueSize();
|
||||
|
||||
@@ -1443,7 +1443,7 @@ void LogXmlCommand::Execute()
|
||||
|
||||
AppendCondResponse(",\n", IsJson() && index++ > 0);
|
||||
AppendFmtResponse(IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM,
|
||||
message.GetId(), messageType[message.GetKind()], message.GetTime(),
|
||||
message.GetId(), messageType[message.GetKind()], (int)message.GetTime(),
|
||||
*EncodeStr(message.GetText()));
|
||||
}
|
||||
|
||||
@@ -1550,7 +1550,7 @@ void ListFilesXmlCommand::Execute()
|
||||
AppendCondResponse(",\n", IsJson() && index++ > 0);
|
||||
AppendFmtResponse(IsJson() ? JSON_LIST_ITEM : XML_LIST_ITEM,
|
||||
fileInfo->GetId(), fileSizeLo, fileSizeHi, remainingSizeLo, remainingSizeHi,
|
||||
fileInfo->GetTime(), BoolToStr(fileInfo->GetFilenameConfirmed()),
|
||||
(int)fileInfo->GetTime(), BoolToStr(fileInfo->GetFilenameConfirmed()),
|
||||
BoolToStr(fileInfo->GetPaused()), fileInfo->GetNzbInfo()->GetId(),
|
||||
*xmlNzbNicename, *xmlNzbNicename, *EncodeStr(fileInfo->GetNzbInfo()->GetFilename()),
|
||||
*EncodeStr(fileInfo->GetSubject()), *EncodeStr(fileInfo->GetFilename()),
|
||||
@@ -1748,14 +1748,14 @@ void NzbInfoXmlCommand::AppendNzbInfoFields(NzbInfo* nzbInfo)
|
||||
deleteStatusName[nzbInfo->GetDeleteStatus()], markStatusName[nzbInfo->GetMarkStatus()],
|
||||
urlStatusName[nzbInfo->GetUrlStatus()],
|
||||
fileSizeLo, fileSizeHi, fileSizeMB, nzbInfo->GetFileCount(),
|
||||
nzbInfo->GetMinTime(), nzbInfo->GetMaxTime(),
|
||||
(int)nzbInfo->GetMinTime(), (int)nzbInfo->GetMaxTime(),
|
||||
nzbInfo->GetTotalArticles(), nzbInfo->GetCurrentSuccessArticles(), nzbInfo->GetCurrentFailedArticles(),
|
||||
nzbInfo->CalcHealth(), nzbInfo->CalcCriticalHealth(false),
|
||||
*EncodeStr(nzbInfo->GetDupeKey()), nzbInfo->GetDupeScore(), dupeModeName[nzbInfo->GetDupeMode()],
|
||||
BoolToStr(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone),
|
||||
downloadedSizeLo, downloadedSizeHi, downloadedSizeMB, nzbInfo->GetDownloadSec(),
|
||||
nzbInfo->GetPostTotalSec() + (nzbInfo->GetPostInfo() && nzbInfo->GetPostInfo()->GetStartTime() ?
|
||||
Util::CurrentTime() - nzbInfo->GetPostInfo()->GetStartTime() : 0),
|
||||
(int)(nzbInfo->GetPostTotalSec() + (nzbInfo->GetPostInfo() && nzbInfo->GetPostInfo()->GetStartTime() ?
|
||||
Util::CurrentTime() - nzbInfo->GetPostInfo()->GetStartTime() : 0)),
|
||||
nzbInfo->GetParSec(), nzbInfo->GetRepairSec(), nzbInfo->GetUnpackSec(), messageCount, nzbInfo->GetExtraParBlocks());
|
||||
|
||||
// Post-processing parameters
|
||||
@@ -1856,8 +1856,8 @@ void NzbInfoXmlCommand::AppendPostInfoFields(PostInfo* postInfo, int logEntries,
|
||||
|
||||
AppendFmtResponse(itemStart, *EncodeStr(postInfo->GetProgressLabel()),
|
||||
postInfo->GetStageProgress(),
|
||||
postInfo->GetStageTime() ? curTime - postInfo->GetStageTime() : 0,
|
||||
postInfo->GetStartTime() ? curTime - postInfo->GetStartTime() : 0);
|
||||
(int)(postInfo->GetStageTime() ? curTime - postInfo->GetStageTime() : 0),
|
||||
(int)(postInfo->GetStartTime() ? curTime - postInfo->GetStartTime() : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1884,7 +1884,7 @@ void NzbInfoXmlCommand::AppendPostInfoFields(PostInfo* postInfo, int logEntries,
|
||||
|
||||
AppendCondResponse(",\n", IsJson() && index++ > 0);
|
||||
AppendFmtResponse(IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM,
|
||||
message.GetId(), messageType[message.GetKind()], message.GetTime(),
|
||||
message.GetId(), messageType[message.GetKind()], (int)message.GetTime(),
|
||||
*EncodeStr(message.GetText()));
|
||||
}
|
||||
}
|
||||
@@ -2238,7 +2238,7 @@ void DownloadXmlCommand::Execute()
|
||||
}
|
||||
}
|
||||
|
||||
if (!strncasecmp(nzbContent, "http://", 6) || !strncasecmp(nzbContent, "https://", 7))
|
||||
if (!strncasecmp(nzbContent, "http://", 7) || !strncasecmp(nzbContent, "https://", 8))
|
||||
{
|
||||
// add url
|
||||
std::unique_ptr<NzbInfo> nzbInfo = std::make_unique<NzbInfo>();
|
||||
@@ -2256,11 +2256,7 @@ void DownloadXmlCommand::Execute()
|
||||
|
||||
info("Queue %s", *nzbInfo->MakeNiceUrlName(nzbContent, nzbFilename));
|
||||
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
downloadQueue->GetQueue()->Add(std::move(nzbInfo), addTop);
|
||||
downloadQueue->Save();
|
||||
}
|
||||
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), addTop);
|
||||
|
||||
if (v13)
|
||||
{
|
||||
@@ -2468,8 +2464,8 @@ void HistoryXmlCommand::Execute()
|
||||
"\"Kind\" : \"%s\",\n"
|
||||
"\"Name\" : \"%s\",\n"
|
||||
"\"HistoryTime\" : %i,\n"
|
||||
"\"FileSizeLo\" : %i,\n"
|
||||
"\"FileSizeHi\" : %i,\n"
|
||||
"\"FileSizeLo\" : %u,\n"
|
||||
"\"FileSizeHi\" : %u,\n"
|
||||
"\"FileSizeMB\" : %i,\n"
|
||||
"\"DupeKey\" : \"%s\",\n"
|
||||
"\"DupeScore\" : %i,\n"
|
||||
@@ -2506,7 +2502,7 @@ void HistoryXmlCommand::Execute()
|
||||
|
||||
AppendFmtResponse(IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
|
||||
historyInfo->GetId(), *EncodeStr(historyInfo->GetName()), nzbInfo->GetParkedFileCount(),
|
||||
BoolToStr(nzbInfo->GetCompletedFiles()->size()), historyInfo->GetTime(), status);
|
||||
BoolToStr(nzbInfo->GetCompletedFiles()->size()), (int)historyInfo->GetTime(), status);
|
||||
}
|
||||
else if (historyInfo->GetKind() == HistoryInfo::hkDup)
|
||||
{
|
||||
@@ -2518,7 +2514,7 @@ void HistoryXmlCommand::Execute()
|
||||
|
||||
AppendFmtResponse(IsJson() ? JSON_HISTORY_DUP_ITEM : XML_HISTORY_DUP_ITEM,
|
||||
historyInfo->GetId(), historyInfo->GetId(), "DUP", *EncodeStr(historyInfo->GetName()),
|
||||
historyInfo->GetTime(), fileSizeLo, fileSizeHi, fileSizeMB,
|
||||
(int)historyInfo->GetTime(), fileSizeLo, fileSizeHi, fileSizeMB,
|
||||
*EncodeStr(dupInfo->GetDupeKey()), dupInfo->GetDupeScore(),
|
||||
dupeModeName[dupInfo->GetDupeMode()], dupStatusName[dupInfo->GetStatus()],
|
||||
status);
|
||||
@@ -2840,8 +2836,8 @@ void ViewFeedXmlCommand::Execute()
|
||||
"<member><name>Title</name><value><string>%s</string></value></member>\n"
|
||||
"<member><name>Filename</name><value><string>%s</string></value></member>\n"
|
||||
"<member><name>URL</name><value><string>%s</string></value></member>\n"
|
||||
"<member><name>SizeLo</name><value><i4>%i</i4></value></member>\n"
|
||||
"<member><name>SizeHi</name><value><i4>%i</i4></value></member>\n"
|
||||
"<member><name>SizeLo</name><value><i4>%u</i4></value></member>\n"
|
||||
"<member><name>SizeHi</name><value><i4>%u</i4></value></member>\n"
|
||||
"<member><name>SizeMB</name><value><i4>%i</i4></value></member>\n"
|
||||
"<member><name>Category</name><value><string>%s</string></value></member>\n"
|
||||
"<member><name>AddCategory</name><value><string>%s</string></value></member>\n"
|
||||
@@ -2861,8 +2857,8 @@ void ViewFeedXmlCommand::Execute()
|
||||
"\"Title\" : \"%s\",\n"
|
||||
"\"Filename\" : \"%s\",\n"
|
||||
"\"URL\" : \"%s\",\n"
|
||||
"\"SizeLo\" : %i,\n"
|
||||
"\"SizeHi\" : %i,\n"
|
||||
"\"SizeLo\" : %u,\n"
|
||||
"\"SizeHi\" : %u,\n"
|
||||
"\"SizeMB\" : %i,\n"
|
||||
"\"Category\" : \"%s\",\n"
|
||||
"\"AddCategory\" : \"%s\",\n"
|
||||
@@ -2897,7 +2893,7 @@ void ViewFeedXmlCommand::Execute()
|
||||
*EncodeStr(feedItemInfo.GetTitle()), *EncodeStr(feedItemInfo.GetFilename()),
|
||||
*EncodeStr(feedItemInfo.GetUrl()), sizeLo, sizeHi, sizeMB,
|
||||
*EncodeStr(feedItemInfo.GetCategory()), *EncodeStr(feedItemInfo.GetAddCategory()),
|
||||
BoolToStr(feedItemInfo.GetPauseNzb()), feedItemInfo.GetPriority(), feedItemInfo.GetTime(),
|
||||
BoolToStr(feedItemInfo.GetPauseNzb()), feedItemInfo.GetPriority(), (int)feedItemInfo.GetTime(),
|
||||
matchStatusType[feedItemInfo.GetMatchStatus()], feedItemInfo.GetMatchRule(),
|
||||
*EncodeStr(feedItemInfo.GetDupeKey()), feedItemInfo.GetDupeScore(),
|
||||
dupeModeType[feedItemInfo.GetDupeMode()], statusType[feedItemInfo.GetStatus()]);
|
||||
@@ -3123,11 +3119,11 @@ void ServerVolumesXmlCommand::Execute()
|
||||
"\"ServerID\" : %i,\n"
|
||||
"\"DataTime\" : %i,\n"
|
||||
"\"FirstDay\" : %i,\n"
|
||||
"\"TotalSizeLo\" : %i,\n"
|
||||
"\"TotalSizeHi\" : %i,\n"
|
||||
"\"TotalSizeLo\" : %u,\n"
|
||||
"\"TotalSizeHi\" : %u,\n"
|
||||
"\"TotalSizeMB\" : %i,\n"
|
||||
"\"CustomSizeLo\" : %i,\n"
|
||||
"\"CustomSizeHi\" : %i,\n"
|
||||
"\"CustomSizeLo\" : %u,\n"
|
||||
"\"CustomSizeHi\" : %u,\n"
|
||||
"\"CustomSizeMB\" : %i,\n"
|
||||
"\"CustomTime\" : %i,\n"
|
||||
"\"SecSlot\" : %i,\n"
|
||||
@@ -3317,8 +3313,14 @@ void TestServerXmlCommand::Execute()
|
||||
bool ok = connection.Connect();
|
||||
if (ok)
|
||||
{
|
||||
const char* response = connection.Request("ARTICLE <connection-test>\r\n");
|
||||
ok = response && *response == '4';
|
||||
// generate a unique non-existent message-id since we don't want a real article to be returned
|
||||
BString<1024> id;
|
||||
while (id.Length() < 30)
|
||||
{
|
||||
id.AppendFmt("%i", rand());
|
||||
}
|
||||
const char* response = connection.Request(BString<1024>("ARTICLE <%s@nzbget.net>\r\n", *id));
|
||||
ok = response && (*response == '4' || *response == '2');
|
||||
}
|
||||
|
||||
BString<1024> content(IsJson() ? JSON_RESPONSE_STR_BODY : XML_RESPONSE_STR_BODY,
|
||||
|
||||
@@ -304,10 +304,29 @@ bool FileSystem::AllocateFile(const char* filename, int64 size, bool sparse, CSt
|
||||
errmsg = GetLastErrorMessage();
|
||||
return false;
|
||||
}
|
||||
char c = '0';
|
||||
fwrite(&c, 1, size, file);
|
||||
|
||||
// write zeros in 16K chunks
|
||||
CharBuffer zeros(16 * 1024);
|
||||
memset(zeros, 0, zeros.Size());
|
||||
for (int64 remaining = size; remaining > 0;)
|
||||
{
|
||||
int64 needbytes = std::min(remaining, (int64)zeros.Size());
|
||||
int64 written = fwrite(zeros, 1, needbytes, file);
|
||||
if (written != needbytes)
|
||||
{
|
||||
errmsg = GetLastErrorMessage();
|
||||
fclose(file);
|
||||
return false;
|
||||
}
|
||||
remaining -= written;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
ok = FileSize(filename) == size;
|
||||
if (!ok)
|
||||
{
|
||||
errmsg = "created file has wrong size";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return ok;
|
||||
@@ -1020,52 +1039,9 @@ CString FileSystem::WidePathToUtfPath(const wchar_t* wpath)
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
DirBrowser::DirBrowser(const char* path)
|
||||
{
|
||||
BString<1024> mask("%s%c*.*", path, PATH_SEPARATOR);
|
||||
m_file = FindFirstFileW(FileSystem::UtfPathToWidePath(mask), &m_findData);
|
||||
m_first = true;
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
FindClose(m_file);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::InternNext()
|
||||
{
|
||||
bool ok = false;
|
||||
if (m_first)
|
||||
{
|
||||
ok = m_file != INVALID_HANDLE_VALUE;
|
||||
m_first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = FindNextFileW(m_file, &m_findData) != 0;
|
||||
}
|
||||
if (ok)
|
||||
{
|
||||
m_filename = FileSystem::WidePathToUtfPath(m_findData.cFileName);
|
||||
return m_filename;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
DirBrowser::DirBrowser(const char* path, bool snapshot) :
|
||||
m_snapshot(snapshot)
|
||||
#else
|
||||
DirBrowser::DirBrowser(const char* path)
|
||||
#endif
|
||||
{
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
if (m_snapshot)
|
||||
{
|
||||
DirBrowser dir(path, false);
|
||||
@@ -1076,35 +1052,57 @@ DirBrowser::DirBrowser(const char* path)
|
||||
m_snapshotIter = m_snapshotFiles.begin();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef WIN32
|
||||
BString<1024> mask("%s%c*.*", path, PATH_SEPARATOR);
|
||||
m_file = FindFirstFileW(FileSystem::UtfPathToWidePath(mask), &m_findData);
|
||||
m_first = true;
|
||||
#else
|
||||
m_dir = opendir(path);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
if (!m_snapshot)
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
if (m_file != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (m_dir)
|
||||
{
|
||||
closedir(m_dir);
|
||||
}
|
||||
FindClose(m_file);
|
||||
}
|
||||
#else
|
||||
if (m_dir)
|
||||
{
|
||||
closedir(m_dir);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* DirBrowser::InternNext()
|
||||
{
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
if (m_snapshot)
|
||||
{
|
||||
return m_snapshotIter == m_snapshotFiles.end() ? nullptr : **m_snapshotIter++;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool ok = false;
|
||||
if (m_first)
|
||||
{
|
||||
ok = m_file != INVALID_HANDLE_VALUE;
|
||||
m_first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = FindNextFileW(m_file, &m_findData) != 0;
|
||||
}
|
||||
if (ok)
|
||||
{
|
||||
m_filename = FileSystem::WidePathToUtfPath(m_findData.cFileName);
|
||||
return m_filename;
|
||||
}
|
||||
#else
|
||||
if (m_dir)
|
||||
{
|
||||
m_findData = readdir(m_dir);
|
||||
@@ -1113,10 +1111,10 @@ const char* DirBrowser::InternNext()
|
||||
return m_findData->d_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
|
||||
@@ -84,18 +84,14 @@ public:
|
||||
class DirBrowser
|
||||
{
|
||||
public:
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
DirBrowser(const char* path, bool snapshot = true);
|
||||
#else
|
||||
DirBrowser(const char* path);
|
||||
#endif
|
||||
~DirBrowser();
|
||||
const char* Next();
|
||||
|
||||
private:
|
||||
#ifdef WIN32
|
||||
WIN32_FIND_DATAW m_findData;
|
||||
HANDLE m_file;
|
||||
HANDLE m_file = INVALID_HANDLE_VALUE;
|
||||
bool m_first;
|
||||
CString m_filename;
|
||||
#else
|
||||
@@ -103,12 +99,10 @@ private:
|
||||
struct dirent* m_findData;
|
||||
#endif
|
||||
|
||||
#ifdef DIRBROWSER_SNAPSHOT
|
||||
bool m_snapshot;
|
||||
typedef std::deque<CString> FileList;
|
||||
FileList m_snapshotFiles;
|
||||
FileList::iterator m_snapshotIter;
|
||||
#endif
|
||||
|
||||
const char* InternNext();
|
||||
};
|
||||
|
||||
@@ -75,31 +75,55 @@ void Log::Filelog(const char* msg, ...)
|
||||
|
||||
if ((int)rawtime/86400 != (int)m_lastWritten/86400 && g_Options->GetWriteLog() == Options::wlRotate)
|
||||
{
|
||||
if (m_logFile)
|
||||
{
|
||||
m_logFile.reset();
|
||||
}
|
||||
RotateLog();
|
||||
}
|
||||
|
||||
m_lastWritten = rawtime;
|
||||
|
||||
DiskFile file;
|
||||
if (file.Open(m_logFilename, DiskFile::omAppend))
|
||||
if (!m_logFile)
|
||||
{
|
||||
#ifdef WIN32
|
||||
uint64 processId = GetCurrentProcessId();
|
||||
uint64 threadId = GetCurrentThreadId();
|
||||
#else
|
||||
uint64 processId = (uint64)getpid();
|
||||
uint64 threadId = (uint64)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
file.Print("%s\t%llu\t%llu\t%s%s", time, processId, threadId, tmp2, LINE_ENDING);
|
||||
#else
|
||||
file.Print("%s\t%s%s", time, tmp2, LINE_ENDING);
|
||||
#endif
|
||||
file.Close();
|
||||
m_logFile = std::make_unique<DiskFile>();
|
||||
if (!m_logFile->Open(m_logFilename, DiskFile::omAppend))
|
||||
{
|
||||
perror(m_logFilename);
|
||||
m_logFile.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
m_logFile->Seek(0, DiskFile::soEnd);
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef WIN32
|
||||
uint64 processId = GetCurrentProcessId();
|
||||
uint64 threadId = GetCurrentThreadId();
|
||||
#else
|
||||
uint64 processId = (uint64)getpid();
|
||||
uint64 threadId = (uint64)pthread_self();
|
||||
#endif
|
||||
m_logFile->Print("%s\t%" PRIu64 "\t%" PRIu64 "\t%s%s", time, processId, threadId, tmp2, LINE_ENDING);
|
||||
#else
|
||||
m_logFile->Print("%s\t%s%s", time, tmp2, LINE_ENDING);
|
||||
#endif
|
||||
|
||||
m_logFile->Flush();
|
||||
}
|
||||
|
||||
void Log::IntervalCheck()
|
||||
{
|
||||
// Close log-file on idle (if last write into log was more than a second ago)
|
||||
if (m_logFile)
|
||||
{
|
||||
perror(m_logFilename);
|
||||
time_t curTime = Util::CurrentTime() + g_Options->GetTimeCorrection();
|
||||
if (std::abs(curTime - m_lastWritten) > 1)
|
||||
{
|
||||
Guard guard(m_logMutex);
|
||||
m_logFile.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -262,7 +286,7 @@ void Log::AddMessage(Message::EKind kind, const char * text)
|
||||
|
||||
if (m_optInit && g_Options)
|
||||
{
|
||||
while (m_messages.size() > (uint32)g_Options->GetLogBufferSize())
|
||||
while (m_messages.size() > (uint32)g_Options->GetLogBuffer())
|
||||
{
|
||||
m_messages.pop_front();
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ typedef std::deque<Message> MessageList;
|
||||
typedef GuardedPtr<MessageList> GuardedMessageList;
|
||||
|
||||
class Debuggable;
|
||||
class DiskFile;
|
||||
|
||||
class Log
|
||||
{
|
||||
@@ -83,6 +84,7 @@ public:
|
||||
void RegisterDebuggable(Debuggable* debuggable);
|
||||
void UnregisterDebuggable(Debuggable* debuggable);
|
||||
void LogDebugInfo();
|
||||
void IntervalCheck();
|
||||
|
||||
private:
|
||||
typedef std::list<Debuggable*> Debuggables;
|
||||
@@ -92,6 +94,7 @@ private:
|
||||
Debuggables m_debuggables;
|
||||
Mutex m_debugMutex;
|
||||
CString m_logFilename;
|
||||
std::unique_ptr<DiskFile> m_logFile;
|
||||
uint32 m_idGen = 0;
|
||||
time_t m_lastWritten = 0;
|
||||
bool m_optInit = false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -76,18 +76,21 @@ void BString<size>::AppendFmtV(const char* format, va_list ap)
|
||||
}
|
||||
|
||||
template <int size>
|
||||
void BString<size>::Format(const char* format, ...)
|
||||
int BString<size>::Format(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
FormatV(format, ap);
|
||||
int len = FormatV(format, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
template <int size>
|
||||
void BString<size>::FormatV(const char* format, va_list ap)
|
||||
int BString<size>::FormatV(const char* format, va_list ap)
|
||||
{
|
||||
vsnprintf(m_data, size, format, ap);
|
||||
// ensure result isn't negative (in case of bad argument)
|
||||
int len = std::max(vsnprintf(m_data, size, format, ap), 0);
|
||||
return len;
|
||||
}
|
||||
|
||||
bool CString::operator==(const CString& other)
|
||||
@@ -159,26 +162,28 @@ void CString::AppendFmtV(const char* format, va_list ap)
|
||||
va_end(ap2);
|
||||
}
|
||||
|
||||
void CString::Format(const char* format, ...)
|
||||
int CString::Format(const char* format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
FormatV(format, ap);
|
||||
int len = FormatV(format, ap);
|
||||
va_end(ap);
|
||||
return len;
|
||||
}
|
||||
|
||||
void CString::FormatV(const char* format, va_list ap)
|
||||
int CString::FormatV(const char* format, va_list ap)
|
||||
{
|
||||
va_list ap2;
|
||||
va_copy(ap2, ap);
|
||||
|
||||
int newLen = vsnprintf(nullptr, 0, format, ap);
|
||||
if (newLen < 0) return; // bad argument
|
||||
// "std::max" to ensure result isn't negative (in case of bad argument)
|
||||
int newLen = std::max(vsnprintf(nullptr, 0, format, ap), 0);
|
||||
|
||||
m_data = (char*)realloc(m_data, newLen + 1);
|
||||
vsnprintf(m_data, newLen + 1, format, ap2);
|
||||
newLen = vsnprintf(m_data, newLen + 1, format, ap2);
|
||||
|
||||
va_end(ap2);
|
||||
return newLen;
|
||||
}
|
||||
|
||||
int CString::Find(const char* str, int pos)
|
||||
@@ -281,7 +286,7 @@ WString::WString(const char* utfstr)
|
||||
m_data = (wchar_t*)malloc((strlen(utfstr) * 2 + 1) * sizeof(wchar_t));
|
||||
|
||||
wchar_t* out = m_data;
|
||||
unsigned int codepoint;
|
||||
unsigned int codepoint = 0;
|
||||
while (*utfstr != 0)
|
||||
{
|
||||
unsigned char ch = (unsigned char)*utfstr;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -46,8 +46,8 @@ public:
|
||||
void Append(const char* str, int len = 0);
|
||||
void AppendFmt(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
void AppendFmtV(const char* format, va_list ap);
|
||||
void Format(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
void FormatV(const char* format, va_list ap);
|
||||
int Format(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
int FormatV(const char* format, va_list ap);
|
||||
|
||||
protected:
|
||||
char m_data[size];
|
||||
@@ -83,8 +83,8 @@ public:
|
||||
void Append(const char* str, int len = 0);
|
||||
void AppendFmt(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
void AppendFmtV(const char* format, va_list ap);
|
||||
void Format(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
void FormatV(const char* format, va_list ap);
|
||||
int Format(const char* format, ...) PRINTF_SYNTAX(2);
|
||||
int FormatV(const char* format, va_list ap);
|
||||
int Find(const char* str, int pos = 0);
|
||||
void Replace(int pos, int len, const char* str, int strLen = 0);
|
||||
void Replace(const char* from, const char* to);
|
||||
@@ -150,6 +150,7 @@ public:
|
||||
CharBuffer(int size) : m_size(size) { m_data = (char*)malloc(size); }
|
||||
CharBuffer(CharBuffer& other) : m_data(other.m_data), m_size(other.m_size) { other.m_data = nullptr; other.m_size = 0; }
|
||||
~CharBuffer() { free(m_data); }
|
||||
CharBuffer& operator=(CharBuffer&& other) = delete;
|
||||
int Size() { return m_size; }
|
||||
void Reserve(int size) { m_data = (char*)realloc(m_data, size); m_size = size; }
|
||||
void Clear() { free(m_data); m_data = nullptr; m_size = 0; }
|
||||
|
||||
@@ -66,7 +66,7 @@ void ChildWatchDog::Run()
|
||||
time_t start = Util::CurrentTime();
|
||||
while (!IsStopped() && (Util::CurrentTime() - start) < WAIT_SECONDS)
|
||||
{
|
||||
usleep(10 * 1000);
|
||||
Util::Sleep(10);
|
||||
}
|
||||
|
||||
if (!IsStopped())
|
||||
@@ -394,7 +394,7 @@ int ScriptController::Execute()
|
||||
}
|
||||
while (watchDog.IsRunning())
|
||||
{
|
||||
usleep(5 * 1000);
|
||||
Util::Sleep(5);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -584,11 +584,13 @@ void ScriptController::StartProcess(int* pipein, int* pipeout)
|
||||
args.emplace_back(nullptr);
|
||||
char* const* argdata = (char* const*)args.data();
|
||||
|
||||
#ifdef DEBUG
|
||||
debug("Starting process: %s", script);
|
||||
for (const char* arg : m_args)
|
||||
{
|
||||
debug("arg: %s", arg);
|
||||
}
|
||||
#endif
|
||||
|
||||
debug("forking");
|
||||
pid_t pid = fork();
|
||||
@@ -739,7 +741,7 @@ void ScriptController::TerminateAll()
|
||||
time_t curtime = Util::CurrentTime();
|
||||
while (!script->m_completed && std::abs(curtime - Util::CurrentTime()) <= 10)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
Util::Sleep(100);
|
||||
}
|
||||
}
|
||||
script->Terminate();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2015-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* 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
|
||||
@@ -30,6 +30,11 @@ Service::Service()
|
||||
g_ServiceCoordinator->RegisterService(this);
|
||||
}
|
||||
|
||||
void Service::WakeUp()
|
||||
{
|
||||
g_ServiceCoordinator->WakeUp();
|
||||
};
|
||||
|
||||
|
||||
ServiceCoordinator::ServiceCoordinator()
|
||||
{
|
||||
@@ -46,29 +51,65 @@ void ServiceCoordinator::Run()
|
||||
{
|
||||
debug("Entering ServiceCoordinator-loop");
|
||||
|
||||
const int stepMSec = 100;
|
||||
int curTick = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
// perform service work
|
||||
for (Service* service : m_services)
|
||||
{
|
||||
if (curTick >= service->m_lastTick + service->ServiceInterval() || // interval expired
|
||||
curTick == 0 || // first start
|
||||
curTick + 10000 < service->m_lastTick) // int overflow
|
||||
int serviceInterval = service->ServiceInterval();
|
||||
if (serviceInterval >= Service::Now &&
|
||||
abs(Util::CurrentTime() - service->m_lastWork) >= serviceInterval)
|
||||
{
|
||||
service->ServiceWork();
|
||||
service->m_lastTick = curTick;
|
||||
service->m_lastWork = Util::CurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
curTick += stepMSec;
|
||||
usleep(stepMSec * 1000);
|
||||
// calculate wait interval
|
||||
int waitInterval = 60 * 60 * 24; // something very large
|
||||
time_t curTime = Util::CurrentTime();
|
||||
for (Service* service : m_services)
|
||||
{
|
||||
int serviceInterval = service->ServiceInterval();
|
||||
int remaining = Service::Sleep;
|
||||
if (serviceInterval >= Service::Now)
|
||||
{
|
||||
remaining = serviceInterval - (curTime - service->m_lastWork);
|
||||
waitInterval = std::min(waitInterval, remaining);
|
||||
}
|
||||
debug("serviceInterval: %i, remaining: %i", serviceInterval, remaining);
|
||||
}
|
||||
|
||||
debug("Waiting in ServiceCoordinator: %i", waitInterval);
|
||||
if (waitInterval > 0)
|
||||
{
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.WaitFor(m_waitMutex, waitInterval * 1000, [&] { return m_workenUp || IsStopped(); });
|
||||
m_workenUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Exiting ServiceCoordinator-loop");
|
||||
}
|
||||
|
||||
void ServiceCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
// Resume Run() to exit it
|
||||
Guard guard(m_waitMutex);
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void ServiceCoordinator::WakeUp()
|
||||
{
|
||||
debug("Waking up ServiceCoordinator");
|
||||
// Resume Run()
|
||||
Guard guard(m_waitMutex);
|
||||
m_workenUp = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void ServiceCoordinator::RegisterService(Service* service)
|
||||
{
|
||||
m_services.push_back(service);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user