Compare commits

...

115 Commits
v17.1 ... v18.1

Author SHA1 Message Date
Andrey Prygunkov
f7930b56a6 updated version string to "18.1" 2017-04-08 17:37:57 +02:00
Andrey Prygunkov
b2bf488d59 fixed #338: "undefined" in reorder extension scripts
When editing option “ScriptOrder” in web-interface.
2017-04-08 17:28:41 +02:00
Andrey Prygunkov
8e4b75b21e fixed #347: possible crash at the end of post-processing 2017-04-08 17:27:51 +02:00
Andrey Prygunkov
5b17bebbd6 fixed #348: queue was not saved after deleting of queued post-jobs 2017-04-08 17:27:22 +02:00
Andrey Prygunkov
bda1eaa192 fixed #350: sleep mode no longer works on Windows 2017-04-08 17:26:15 +02:00
Andrey Prygunkov
4bcdbbeb09 fixed #356: crash during download caused by a race condition 2017-04-08 17:25:29 +02:00
Andrey Prygunkov
69d40c11fd Merge branch 'develop' for v18.0 2017-02-12 17:20:07 +01:00
Andrey Prygunkov
58893710d8 updated version string to "18.0" 2017-02-12 17:05:21 +01:00
Andrey Prygunkov
6c6f781510 updated ChangeLog for v18.0 2017-02-12 17:03:04 +01:00
Andrey Prygunkov
569ec22ee8 updated copyright string in Windows and OS X apps 2017-02-12 17:02:56 +01:00
Andrey Prygunkov
b06d3eca86 #327: printing only base file name in warnings 2017-02-12 14:35:44 +01:00
Andrey Prygunkov
dcf63b4db7 #277: allow control of what tab is shown when opening web-interface
Add “#downloads”, “#history”, “#messages” or “#settings” to the URL,
for example “http://localhost:6789/#history” or
“http://localhost:6789/index.html#history”
2017-01-27 20:10:57 +01:00
Andrey Prygunkov
06e7573572 #329: improved selecting of par2-file for repair 2017-01-19 21:48:35 +01:00
Andrey Prygunkov
639e0b6bfb #327: reverted non-strict par2-filename matching
To handle article subjects with non-parseable filenames. Also created a
functional test for this case.
2017-01-17 22:18:34 +01:00
Andrey Prygunkov
f2073ff920 #327: better handing of damaged par2-files in par-renamer
If par-renamer can’t load par2-file another par2-file is downloaded and
par-renamer tries again.
2017-01-17 00:23:29 +01:00
Sander
4ba2e07d94 #328: NServ: option -b to specify on which address to listen 2017-01-08 19:37:43 +01:00
Sander
91515b20e5 #324, #325: Nserv: Check if specified data-dir is an existing directory 2017-01-06 21:11:42 +01:00
Andrey Prygunkov
7226e1c186 #319: fixed automatic changing of pp-scripts when changing category in web-interface 2016-12-30 22:29:14 +01:00
Andrey Prygunkov
98c2dd46b7 #291: fixed "PostTotalTimeSec" in API-method "listgroups" 2016-12-27 17:21:06 +01:00
Andrey Prygunkov
f0b08dbd63 #315: reset feed search box 2016-12-22 20:24:00 +01:00
Sander
25ddfd5659 #320, #321: newlines added to 281 and 205 server answers 2016-12-21 19:46:55 +01:00
Andrey Prygunkov
7450b97871 #319: scheduler scripts in option "Extensions"
Scheduler scripts can now be selected in option “Extensions” if the
scripts provide default time definition.
2016-12-20 22:22:09 +01:00
Andrey Prygunkov
9f7e0ee972 #304: safer termination of scripts 2016-12-19 19:33:38 +01:00
Andrey Prygunkov
ae7719e948 #319: auto migration of old script settings 2016-12-16 21:03:59 +01:00
Andrey Prygunkov
9a92896678 #319: unified extension scripts settings
- new option “Extension” as replacement for options “PostScript”,
“QueueScript”, “ScanScript”, “FeedScript”;
- renamed “CategoryX.PostScript” to “CategoryX.Extensions”;
- renamed “FeedX.PostScript” to “FeedX.Extensions”.
2016-12-14 20:34:14 +01:00
Andrey Prygunkov
ab0723adda #288: rar-rename only if unpack is enabled 2016-12-14 18:50:14 +01:00
Andrey Prygunkov
13b98fca83 fixed incompatibility with macOS El Capitan and XCode 7/8 2016-12-13 21:52:32 +01:00
Andrey Prygunkov
5901998cb5 #242: fixed enter-key in feed filter dialog 2016-12-10 20:13:19 +01:00
Andrey Prygunkov
9ddd73368e #282: 0d67e322a3: fixed confirmation dialog 2016-12-07 21:02:55 +01:00
Andrey Prygunkov
12daa683e5 #313: 5f71c4a5a7: fixed test failure on Linux 2016-12-06 19:18:06 +01:00
Andrey Prygunkov
5f71c4a5a7 #313: better handling of renamed par-files 2016-12-06 18:49:12 +01:00
Andrey Prygunkov
13db817395 #313: better handling of obfuscated par-files in par-renamer
Avoid false detection of missing files.
2016-12-04 19:04:39 +01:00
Andrey Prygunkov
84f4cf2f33 #312: extended description of option "KeepHistory" 2016-12-03 12:54:49 +01:00
Andrey Prygunkov
0d67e322a3 #282: extra warning when deleting from history
An extra warning is shown when deleting 50 or more records from history
and there are selected records on other pages.
2016-11-29 18:56:00 +01:00
Andrey Prygunkov
374f706354 #282: visual indication of records selected on other pages 2016-11-22 21:36:20 +01:00
Andrey Prygunkov
f7cefadf33 use default SDK when building mac app 2016-11-20 19:31:06 +01:00
Andrey Prygunkov
c111a114b9 #242: keyboard support in web-interface 2016-11-20 14:24:27 +01:00
Andrey Prygunkov
2cd3f0fc68 #309: allow sending e-mails to multiple recipients
in the example pp-script “EMail.py”.
2016-11-20 13:12:50 +01:00
Andrey Prygunkov
92db59116e #301: showing drag grip
and extended the drag grip area to the full cell containing check box.
2016-11-18 19:59:33 +01:00
Andrey Prygunkov
760aed68fb #288: fixed incorrect renaming
of archives containing directory entries
2016-11-16 21:41:34 +01:00
Andrey Prygunkov
e612257c28 #304: graceful termination of scheduler scripts
If a script is running when the program must shutdown, the script
receives signal SIGINT (CTRL+BREAK on Windows) and has 10 seconds to
gracefully terminate until it is killed.
2016-11-15 19:22:52 +01:00
Andrey Prygunkov
31208a5816 #304: scheduler tasks can be started at program launch
Using asterisk as TaskX.Time.
2016-11-15 19:00:49 +01:00
Andrey Prygunkov
a03e7cd550 #295: disabled SSLv3 in built-in web-server 2016-11-15 18:47:30 +01:00
Andrey Prygunkov
02b33fb559 #306: splitted options "Retries" and "RetryInterval"
into four options: ArticleRetries, ArticleInterval, UrlRetries, and
UrlInterval.
2016-11-13 13:05:37 +01:00
Andrey Prygunkov
a9ed53daa8 fixed #303: rar tests fail with TLS disabled 2016-11-12 18:37:48 +01:00
Andrey Prygunkov
a3c460ed40 #301: showing row content while dragging
Implemented alternative UI for drag and drop.
2016-11-12 14:23:19 +01:00
Andrey Prygunkov
df11d1acb4 #301: functional tests for api-method "editqueue"
And a small fix in edit-commands GroupMoveAfter and GroupMoveBefore.
2016-11-08 21:02:36 +01:00
Andrey Prygunkov
e49d4c59af #301: auto scrolling during dragging 2016-11-06 21:16:19 +01:00
Andrey Prygunkov
5dc9c07a58 #301: moved drag-n-drop code into module fasttable.js
Ready for reuse on other places.
2016-11-04 22:02:26 +01:00
Andrey Prygunkov
3bb0751c86 #301: drag-n-drop for touch screens
; also no more dragging via priority-icon but only via check mark.
2016-11-03 19:20:03 +01:00
Andrey Prygunkov
a3b3921bea #301: API extension for drag-n-drop support
New actions “GroupMoveBefore” and “GroupMoveAfter” in API-method
“editqueue”; parameter “args” contains id of the target item to move
before/after.
2016-11-01 12:27:42 +01:00
Andrey Prygunkov
b19d26aee8 #301: drag and drop handling in web-interface 2016-11-01 12:23:33 +01:00
Andrey Prygunkov
7fae337360 fixed #300: sorting of selected items may give wrong results 2016-10-31 19:05:15 +01:00
Andrey Prygunkov
80debf521a #299: removed parameter "offset" from api-method "editqueue"
When needed the “offset” is now passed within parameter “Args” as
string.
2016-10-31 15:58:02 +01:00
Andrey Prygunkov
528133482e #298: added compatibility with openssl 1.1.0 2016-10-31 10:11:23 +01:00
Andrey Prygunkov
a98bbd7d0d #279: cache support in built-in nntp server 2016-10-29 16:55:12 +02:00
Andrey Prygunkov
2681fe187b #288: sooner detection of damaged rar-files 2016-10-22 15:05:19 +02:00
Andrey Prygunkov
e16cab67fb #288: ed4761db37: reverted std-input handling on Windows 2016-10-22 15:04:11 +02:00
Andrey Prygunkov
50e2e0cf37 #291: refactor: handling of shutdown/reload 2016-10-18 21:53:48 +02:00
Andrey Prygunkov
d2d20f29c1 #291: better handling of shutdown/reload
for post-processing jobs
2016-10-18 21:52:30 +02:00
Andrey Prygunkov
53f4992eeb #279: corrected generation of test files 2016-10-18 21:51:22 +02:00
Andrey Prygunkov
3ae1be1ca4 #294: handling of incomplete obfuscated rar-archives
and special file extensions; new option “UnpackIgnoreExt” honored by
rar-renamer and unpacker.
2016-10-16 13:21:58 +02:00
Andrey Prygunkov
ce1e1b61e7 #291: fixed status in api-method "listgroups" 2016-10-15 16:12:48 +02:00
Andrey Prygunkov
cac25ed290 #291: distinguishing pp-stages par-renaming and rar-renaming
to better handling temporary pause.
2016-10-15 13:37:34 +02:00
Andrey Prygunkov
8ed0b70df5 #291: eacfacc5a2: fixed compilation error on Windows 2016-10-15 13:11:54 +02:00
Andrey Prygunkov
d72071c8ed #291: temporary pause during post-processing
Options ParPauseQueue, UnpackPauseQueue, PostPauseQueue work properly.
2016-10-15 13:00:52 +02:00
Andrey Prygunkov
ba05dfc202 #291: tweaking balanced pp-startegy
Other job can be run only during repair stage but not during
verification stage.
2016-10-14 11:20:57 +02:00
Andrey Prygunkov
f3adab5690 #293: split section "Download Queue"
Many options moved into new section “Connection”.
2016-10-14 00:19:14 +02:00
Andrey Prygunkov
b1b5405809 #291: new option "PostStrategy" to configure multi-post-processing
instead of option “ParExclusive”.
2016-10-14 00:07:13 +02:00
Andrey Prygunkov
99fcd164ac #291: smoother transition between pp-stages
without intermediate “pp-queued” state shown in web-interface
2016-10-13 00:07:44 +02:00
Andrey Prygunkov
eacfacc5a2 #291: non-global cout/cerr in par2-module
Fixed global cout/cerr in par2-module to prevent crashes when executing
multiple par2-instances.
2016-10-12 16:00:08 +02:00
Andrey Prygunkov
3404b544de #291: removed not used unit of par2-module 2016-10-12 15:58:08 +02:00
Andrey Prygunkov
3604534850 #291: functional tests for multi-processing 2016-10-12 00:30:23 +02:00
Andrey Prygunkov
2e18021e08 #291: new option "ParExclusive"
to configure simultaneous post-processing of a second item if currently
performing par-check/repair for the first item in pp-queue
2016-10-12 00:22:34 +02:00
Andrey Prygunkov
2e19382ccc #291: multi post-processing
During par-check/repair another pp-item can be post-processed at the
same time, as long as it doesn’t require par-check/repair.
2016-10-12 00:13:50 +02:00
Andrey Prygunkov
a6dc20dc8e #291: refactor: made par-checker non-global
and more similar to other post-processing stages.
2016-10-11 20:43:11 +02:00
Andrey Prygunkov
a1bef9146e #286: corrected html in priority column 2016-10-08 15:33:04 +02:00
Andrey Prygunkov
61f18f81b7 #29, #278: small UI tweak 2016-10-08 15:32:26 +02:00
Andrey Prygunkov
129125faa1 #286: priority column and sorting
Also removed horizontal lines in tables, for better visuals.
2016-10-07 23:46:21 +02:00
Andrey Prygunkov
b97c987f66 #274: fixed error on history page
Fixed javascript error on history page when showing hidden items.
2016-10-04 22:11:32 +02:00
Andrey Prygunkov
53e504d974 fixed failing functional tests 2016-10-03 23:39:34 +02:00
Andrey Prygunkov
5885258c35 #288: reading encrypted archives using nettle library
when compiled with GnuTLS instead of OpenSSL.
2016-10-03 22:38:48 +02:00
Andrey Prygunkov
2034ea97d2 #288: fixed compilation error with GnuTLS
and added unit test for rar3 encrypted files
2016-10-03 15:05:29 +02:00
Andrey Prygunkov
2cc38a85df #288: fixed incompatibility with older compilers 2016-10-03 01:35:46 +02:00
Andrey Prygunkov
ccd509b70c #288: reading rar3 encrypted archives
Rar-renamer now supports renaming of rar3 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-10-02 23:10:31 +02:00
Andrey Prygunkov
bab43d8d30 #288: reading rar5 encrypted archives
Rar-renamer now supports renaming of rar5 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-09-30 21:39:35 +02:00
Andrey Prygunkov
ed4761db37 #288: unit and functional tests for encrypted rar files 2016-09-30 19:32:55 +02:00
Andrey Prygunkov
db7bc4314f #288: rar-rename as a separate post-processing stage
Made rar-rename a separate post-processing stage which runs after
par-repair, in order to restore archive file names after possible
repair stage (which could rename files back to obfuscated names if
rar-rename were run before par-repair).
2016-09-28 22:22:18 +02:00
Andrey Prygunkov
8a21626bf6 #288: sophisticated renaming for rar-files 2016-09-27 23:34:42 +02:00
Andrey Prygunkov
804f8ab085 #288: integrated rar-renamer into post-processing
also implemented initial simple renaming for rar-files
2016-09-26 21:55:41 +02:00
Andrey Prygunkov
3593990dd6 #288: new module "Rename" to handle par-rename and rar-rename 2016-09-26 21:16:27 +02:00
Andrey Prygunkov
3a38a2c7c6 #288: refactor: moved parts of module "RarRenamer" into new module "RarReader"
Also added new unit test for module “RarReader”.
2016-09-26 21:01:15 +02:00
Andrey Prygunkov
16aef2e7a8 #288: functional tests for par-renamer and rar-renamer
Rar-renamer tests are currently failing as the feature isn’t completed
yet.
2016-09-25 21:08:45 +02:00
Andrey Prygunkov
bf992195e8 #288: fixed unit test for rar-renamer 2016-09-25 21:07:23 +02:00
Andrey Prygunkov
2fb0fd1113 #288: reading rar5-files 2016-09-24 22:43:20 +02:00
Andrey Prygunkov
709c9856c9 #288: reading rar3-files 2016-09-24 22:39:45 +02:00
Andrey Prygunkov
9e9ef3120f #288: added module "RarRenamer" with unit tests
The tests are failing at the moment as the module is not fully
implemented yet.
2016-09-24 18:37:02 +02:00
guaguasi
a7525ae6f9 #274, #285: password-badge for nzbs with passwords 2016-09-23 21:28:12 +02:00
Andrey Prygunkov
5e0e91f671 #249: do not cleanup if unpack disabled
If parameter "unpack" is disabled for an nzb-file the cleanup isn't
performed for it.
2016-09-19 23:40:00 +02:00
Andrey Prygunkov
09f863f3af #275: nZEDb attributes in rss feeds
Added support for nZEDb attributes in rss feeds.
2016-09-19 23:37:51 +02:00
Andrey Prygunkov
87e8d479d8 #281: improved error reporting on feed parse errors 2016-09-19 22:22:29 +02:00
Andrey Prygunkov
fb8c6b9cd3 #279: size of test files
can now be adjusted via pytest.ini; reduced default sizes to speed up
tests.
2016-09-19 22:04:49 +02:00
Andrey Prygunkov
bc98b0582c #279: corrected nntp error reporting 2016-09-19 21:44:38 +02:00
guaguasi
84388785e7 #276 , #280: highlight selected rows with alternative colors 2016-09-19 06:43:07 +02:00
Andrey Prygunkov
4c519cf646 #279: fixed compilation error on Windows 2016-09-17 23:45:03 +02:00
Andrey Prygunkov
f28c049c12 fixed #284: API-method "append" and dupe check
method “append” returns nzb-id also for items added straight to history.
2016-09-17 20:38:57 +02:00
Andrey Prygunkov
33dbbcb0b5 Merge branch '279-nserv' into develop 2016-09-17 13:49:45 +02:00
Andrey Prygunkov
7f6cbd137f #279: updated .gitignore 2016-09-17 13:49:09 +02:00
Andrey Prygunkov
facd8cd41f #279: fixed file permissions 2016-09-17 13:37:56 +02:00
guaguasi
e269d8f062 #29, #278: additional options in "custom pause dialog" 2016-09-16 06:40:55 +02:00
Andrey Prygunkov
dd141e4cf5 #279: initial collection of functional tests 2016-09-13 22:05:10 +02:00
Andrey Prygunkov
f18ee92a23 #279: built-in nntp server for functional testing
- implemented nserv - built-in simple nntp server to be used for
functional testing;
- nserv is part of nzbget executable and can be easily used on all
platforms;
- to start nzbget in nserv mode use command “nzbget --nserv”.
2016-09-13 21:31:20 +02:00
Andrey Prygunkov
b25a88683b #271: do not close files on daemonizing 2016-09-09 20:12:10 +02:00
guaguasi
9dc2b8c71b #49, #260: fields containing passwords are now password fields
- fields containing passwords are now password fields with option to view;
- excluding post-processing/archive password.
2016-09-09 19:43:46 +02:00
Andrey Prygunkov
2ef393531a updated version string to 18.0-testing 2016-09-09 19:45:46 +02:00
165 changed files with 8623 additions and 2917 deletions

3
.gitignore vendored Executable file → Normal file
View File

@@ -61,3 +61,6 @@ ipch/
# NZBGet specific
nzbget
code_revision.cpp
*.temp
*.pyc
pytest.ini

View File

@@ -1,3 +1,93 @@
nzbget-18.1:
- fixed: crash during download caused by a race condition;
- fixed: sleep mode did not work on Windows;
- fixed: queue was not saved after deleting of queued post-jobs;
- fixed: possible crash at the end of post-processing;
- fixed: "undefined" in reorder extension scripts.
nzbget-18.0:
- automatic deobfuscation of rar-archives without par-files:
- obfuscated downloads not having par-files can now be successfully
unpacked;
- also helps with downloads where rar-files were obfuscated before
creating par-files;
- new options "RarRename" and "UnpackIgnoreExt";
- multi post-processing:
- in addition to classic post-processing strategy where items are
processed one after another it is now possible to post-process
multiple items at the same time;
- new option "PostStrategy" to choose from four: sequential, balanced,
aggressive, rocket;
- in "balanced" strategy downloads needing repair do not block other
items which are processed sequentially but simultaneously with
repairing item;
- in "aggressive" mode up to three items are post-processed at the same
time and in "rocket" mode up to six items (including up to two repair
tasks);
- unified extension scripts settings:
- options "PostScript", "QueueScript", "ScanScript" and "FeedScript"
were replaced with one option "Extensions";
- users don't need to know the technical details os extension scripts as
all scripts are now can be selected at one place;
- easier activation of complex extension scripts which previously needed
to be selected in multiple options for their proper work;
- reordering download queue with drag and drop in web-interface:
- new actions "GroupMoveBefore" and "GroupMoveAfter" in API-method
"editqueue";
- priorities are now displayed as a column instead of badge; that makes it
possible to manually sort on priority;
- removed vertical lines in tables; looks better in combination with new
priority column;
- keyboard shortcuts in web-interface;
- improved UI to prevent accidental deletion of many items:
- visual indication of records selected on other pages;
- extra warning when deleting many records from history;
- additional options in "custom pause dialog";
- better handing of damaged par2-files in par-renamer:
- if par-renamer can't load a (damaged) par2-file then another par2-file
is downloaded and par-renamer tries again;
- reverted non-strict par2-filename matching to handle article subjects
with non-parseable filenames;
- better handling of obfuscated par-files;
- splitted option "Retries" into "ArticleRetries" and "UrlRetries"; option
"RetryInterval" into "ArticleInterval" and "UrlInterval";
- scheduler tasks can be started at program launch:
- use asterisk as TaskX.Time;
- graceful termination of scheduler scripts:
- scripts receive signal SIGINT (CTRL+BREAK on Windows) before
termination;
- added support for nZEDb attributes in rss feeds;
- better cleanup handling: if parameter "unpack" is disabled for an nzb-file
the cleanup isn't performed for it;
- fields containing passwords are now displayed as protected fields;
- showing password-badge for nzbs with passwords;
- allow control of what tab is shown when opening web-interface:
add "#downloads", "#history", "#messages" or "#settings" to the URL,
for example "http://localhost:6789/#history" or
"http://localhost:6789/index.html#history";
- functional testing to ensure program quality:
- implemented built-in simple nntp server to be used for functional
testing;
- created a number of tests;
- new features come with additional tests;
- improved API-method "append" in combination with duplicate check; method
returns nzb-id also for items added straight to history;
- removed parameter "offset" from api-method "editqueue":
- when needed the "offset" is now passed within parameter "Args" as
string;
- old method signature is supported for compatibility;
- improved error reporting on feed parse errors;
- highlighting selected rows with alternative colors;
- improved selecting of par2-file for repair;
- splitted config section "Download Queue" and moved many options into new
section "Connection";
- disabled SSLv3 in built-in web-server;
- multiple recipients in the example pp-script "EMail.py";
- added compatibility with openssl 1.1.0;
- fixed TLS handshake error when using GnuTLS;
- fixed: sorting of selected items may give wrong results;
- fixed: search box filter in feed view were not reset.
nzbget-17.1:
- adjustments and fixes for "Retry failed articles" function, better handling
of certain corner cases;

View File

@@ -90,14 +90,20 @@ nzbget_SOURCES = \
daemon/postprocess/DupeMatcher.h \
daemon/postprocess/ParChecker.cpp \
daemon/postprocess/ParChecker.h \
daemon/postprocess/ParCoordinator.cpp \
daemon/postprocess/ParCoordinator.h \
daemon/postprocess/ParParser.cpp \
daemon/postprocess/ParParser.h \
daemon/postprocess/ParRenamer.cpp \
daemon/postprocess/ParRenamer.h \
daemon/postprocess/PrePostProcessor.cpp \
daemon/postprocess/PrePostProcessor.h \
daemon/postprocess/RarRenamer.cpp \
daemon/postprocess/RarRenamer.h \
daemon/postprocess/RarReader.cpp \
daemon/postprocess/RarReader.h \
daemon/postprocess/Rename.cpp \
daemon/postprocess/Rename.h \
daemon/postprocess/Repair.cpp \
daemon/postprocess/Repair.h \
daemon/postprocess/Unpack.cpp \
daemon/postprocess/Unpack.h \
daemon/queue/DiskState.cpp \
@@ -146,6 +152,16 @@ nzbget_SOURCES = \
daemon/util/FileSystem.h \
daemon/util/Util.cpp \
daemon/util/Util.h \
daemon/nserv/NServMain.h \
daemon/nserv/NServMain.cpp \
daemon/nserv/NServFrontend.h \
daemon/nserv/NServFrontend.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
if WITH_PAR2
@@ -174,8 +190,6 @@ nzbget_SOURCES += \
lib/par2/md5.cpp \
lib/par2/md5.h \
lib/par2/par2cmdline.h \
lib/par2/par2creatorsourcefile.cpp \
lib/par2/par2creatorsourcefile.h \
lib/par2/par2fileformat.cpp \
lib/par2/par2fileformat.h \
lib/par2/par2repairer.cpp \
@@ -205,6 +219,7 @@ AM_CPPFLAGS = \
-I$(srcdir)/daemon/queue \
-I$(srcdir)/daemon/remote \
-I$(srcdir)/daemon/util \
-I$(srcdir)/daemon/nserv \
-I$(srcdir)/lib/par2
if WITH_TESTS
@@ -218,6 +233,8 @@ nzbget_SOURCES += \
tests/main/OptionsTest.cpp \
tests/feed/FeedFilterTest.cpp \
tests/postprocess/DupeMatcherTest.cpp \
tests/postprocess/RarRenamerTest.cpp \
tests/postprocess/RarReaderTest.cpp \
tests/queue/NzbFileTest.cpp \
tests/nntp/ServerPoolTest.cpp \
tests/util/FileSystemTest.cpp \
@@ -366,7 +383,28 @@ testdata_FILES = \
tests/testdata/parchecker/testfile.par2 \
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
tests/testdata/parchecker/testfile.vol03+3.PAR2
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
tests/testdata/rarrenamer/testfile3.part01.rar \
tests/testdata/rarrenamer/testfile3.part02.rar \
tests/testdata/rarrenamer/testfile3.part03.rar \
tests/testdata/rarrenamer/testfile5.part01.rar \
tests/testdata/rarrenamer/testfile5.part02.rar \
tests/testdata/rarrenamer/testfile5.part03.rar \
tests/testdata/rarrenamer/testfile3oldnam.rar \
tests/testdata/rarrenamer/testfile3oldnam.r00 \
tests/testdata/rarrenamer/testfile3oldnam.r01 \
tests/testdata/rarrenamer/testfile3encdata.part01.rar \
tests/testdata/rarrenamer/testfile3encdata.part02.rar \
tests/testdata/rarrenamer/testfile3encdata.part03.rar \
tests/testdata/rarrenamer/testfile3encnam.part01.rar \
tests/testdata/rarrenamer/testfile3encnam.part02.rar \
tests/testdata/rarrenamer/testfile3encnam.part03.rar \
tests/testdata/rarrenamer/testfile5encdata.part01.rar \
tests/testdata/rarrenamer/testfile5encdata.part02.rar \
tests/testdata/rarrenamer/testfile5encdata.part03.rar \
tests/testdata/rarrenamer/testfile5encnam.part01.rar \
tests/testdata/rarrenamer/testfile5encnam.part02.rar \
tests/testdata/rarrenamer/testfile5encnam.part03.rar
# Install
dist_doc_DATA = $(doc_FILES)

397
Makefile.in vendored
View File

@@ -84,8 +84,6 @@ bin_PROGRAMS = nzbget$(EXEEXT)
@WITH_PAR2_TRUE@ lib/par2/md5.cpp \
@WITH_PAR2_TRUE@ lib/par2/md5.h \
@WITH_PAR2_TRUE@ lib/par2/par2cmdline.h \
@WITH_PAR2_TRUE@ lib/par2/par2creatorsourcefile.cpp \
@WITH_PAR2_TRUE@ lib/par2/par2creatorsourcefile.h \
@WITH_PAR2_TRUE@ lib/par2/par2fileformat.cpp \
@WITH_PAR2_TRUE@ lib/par2/par2fileformat.h \
@WITH_PAR2_TRUE@ lib/par2/par2repairer.cpp \
@@ -112,16 +110,20 @@ bin_PROGRAMS = nzbget$(EXEEXT)
@WITH_TESTS_TRUE@ tests/main/CommandLineParserTest.cpp \
@WITH_TESTS_TRUE@ tests/main/OptionsTest.cpp \
@WITH_TESTS_TRUE@ tests/feed/FeedFilterTest.cpp \
@WITH_TESTS_TRUE@ tests/postprocess/ParCheckerTest.cpp \
@WITH_TESTS_TRUE@ tests/postprocess/ParRenamerTest.cpp \
@WITH_TESTS_TRUE@ tests/postprocess/DupeMatcherTest.cpp \
@WITH_TESTS_TRUE@ tests/postprocess/RarRenamerTest.cpp \
@WITH_TESTS_TRUE@ tests/postprocess/RarReaderTest.cpp \
@WITH_TESTS_TRUE@ tests/queue/NzbFileTest.cpp \
@WITH_TESTS_TRUE@ tests/nntp/ServerPoolTest.cpp \
@WITH_TESTS_TRUE@ tests/util/FileSystemTest.cpp \
@WITH_TESTS_TRUE@ tests/util/NStringTest.cpp \
@WITH_TESTS_TRUE@ tests/util/UtilTest.cpp
@WITH_TESTS_TRUE@am__append_3 = \
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@am__append_3 = \
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@ tests/postprocess/ParCheckerTest.cpp \
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@ tests/postprocess/ParRenamerTest.cpp
@WITH_TESTS_TRUE@am__append_4 = \
@WITH_TESTS_TRUE@ -I$(srcdir)/lib/catch \
@WITH_TESTS_TRUE@ -I$(srcdir)/tests/suite
@@ -191,18 +193,21 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
daemon/postprocess/DupeMatcher.h \
daemon/postprocess/ParChecker.cpp \
daemon/postprocess/ParChecker.h \
daemon/postprocess/ParCoordinator.cpp \
daemon/postprocess/ParCoordinator.h \
daemon/postprocess/ParParser.cpp \
daemon/postprocess/ParParser.h \
daemon/postprocess/ParRenamer.cpp \
daemon/postprocess/ParRenamer.h \
daemon/postprocess/PrePostProcessor.cpp \
daemon/postprocess/PrePostProcessor.h \
daemon/postprocess/Unpack.cpp daemon/postprocess/Unpack.h \
daemon/queue/DiskState.cpp daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \
daemon/queue/DupeCoordinator.cpp \
daemon/postprocess/RarRenamer.cpp \
daemon/postprocess/RarRenamer.h \
daemon/postprocess/RarReader.cpp \
daemon/postprocess/RarReader.h daemon/postprocess/Rename.cpp \
daemon/postprocess/Rename.h daemon/postprocess/Repair.cpp \
daemon/postprocess/Repair.h daemon/postprocess/Unpack.cpp \
daemon/postprocess/Unpack.h daemon/queue/DiskState.cpp \
daemon/queue/DiskState.h daemon/queue/DownloadInfo.cpp \
daemon/queue/DownloadInfo.h daemon/queue/DupeCoordinator.cpp \
daemon/queue/DupeCoordinator.h \
daemon/queue/HistoryCoordinator.cpp \
daemon/queue/HistoryCoordinator.h daemon/queue/NzbFile.cpp \
@@ -223,21 +228,26 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
daemon/util/Thread.cpp daemon/util/Thread.h \
daemon/util/Service.cpp daemon/util/Service.h \
daemon/util/FileSystem.cpp daemon/util/FileSystem.h \
daemon/util/Util.cpp daemon/util/Util.h code_revision.cpp \
lib/par2/commandline.cpp lib/par2/commandline.h \
lib/par2/crc.cpp lib/par2/crc.h lib/par2/creatorpacket.cpp \
lib/par2/creatorpacket.h lib/par2/criticalpacket.cpp \
lib/par2/criticalpacket.h lib/par2/datablock.cpp \
lib/par2/datablock.h lib/par2/descriptionpacket.cpp \
lib/par2/descriptionpacket.h lib/par2/diskfile.cpp \
lib/par2/diskfile.h lib/par2/filechecksummer.cpp \
lib/par2/filechecksummer.h lib/par2/galois.cpp \
lib/par2/galois.h lib/par2/letype.h lib/par2/mainpacket.cpp \
lib/par2/mainpacket.h lib/par2/md5.cpp lib/par2/md5.h \
lib/par2/par2cmdline.h lib/par2/par2creatorsourcefile.cpp \
lib/par2/par2creatorsourcefile.h lib/par2/par2fileformat.cpp \
lib/par2/par2fileformat.h lib/par2/par2repairer.cpp \
lib/par2/par2repairer.h lib/par2/par2repairersourcefile.cpp \
daemon/util/Util.cpp daemon/util/Util.h \
daemon/nserv/NServMain.h daemon/nserv/NServMain.cpp \
daemon/nserv/NServFrontend.h daemon/nserv/NServFrontend.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 lib/par2/commandline.cpp \
lib/par2/commandline.h lib/par2/crc.cpp lib/par2/crc.h \
lib/par2/creatorpacket.cpp lib/par2/creatorpacket.h \
lib/par2/criticalpacket.cpp lib/par2/criticalpacket.h \
lib/par2/datablock.cpp lib/par2/datablock.h \
lib/par2/descriptionpacket.cpp lib/par2/descriptionpacket.h \
lib/par2/diskfile.cpp lib/par2/diskfile.h \
lib/par2/filechecksummer.cpp lib/par2/filechecksummer.h \
lib/par2/galois.cpp lib/par2/galois.h lib/par2/letype.h \
lib/par2/mainpacket.cpp lib/par2/mainpacket.h lib/par2/md5.cpp \
lib/par2/md5.h lib/par2/par2cmdline.h \
lib/par2/par2fileformat.cpp lib/par2/par2fileformat.h \
lib/par2/par2repairer.cpp lib/par2/par2repairer.h \
lib/par2/par2repairersourcefile.cpp \
lib/par2/par2repairersourcefile.h lib/par2/parheaders.cpp \
lib/par2/parheaders.h lib/par2/recoverypacket.cpp \
lib/par2/recoverypacket.h lib/par2/reedsolomon.cpp \
@@ -248,19 +258,19 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
tests/suite/TestMain.h tests/suite/TestUtil.cpp \
tests/suite/TestUtil.h tests/main/CommandLineParserTest.cpp \
tests/main/OptionsTest.cpp tests/feed/FeedFilterTest.cpp \
tests/postprocess/ParCheckerTest.cpp \
tests/postprocess/ParRenamerTest.cpp \
tests/postprocess/DupeMatcherTest.cpp \
tests/postprocess/RarRenamerTest.cpp \
tests/postprocess/RarReaderTest.cpp \
tests/queue/NzbFileTest.cpp tests/nntp/ServerPoolTest.cpp \
tests/util/FileSystemTest.cpp tests/util/NStringTest.cpp \
tests/util/UtilTest.cpp
tests/util/UtilTest.cpp tests/postprocess/ParCheckerTest.cpp \
tests/postprocess/ParRenamerTest.cpp
@WITH_PAR2_TRUE@am__objects_1 = commandline.$(OBJEXT) crc.$(OBJEXT) \
@WITH_PAR2_TRUE@ creatorpacket.$(OBJEXT) \
@WITH_PAR2_TRUE@ criticalpacket.$(OBJEXT) datablock.$(OBJEXT) \
@WITH_PAR2_TRUE@ descriptionpacket.$(OBJEXT) diskfile.$(OBJEXT) \
@WITH_PAR2_TRUE@ filechecksummer.$(OBJEXT) galois.$(OBJEXT) \
@WITH_PAR2_TRUE@ mainpacket.$(OBJEXT) md5.$(OBJEXT) \
@WITH_PAR2_TRUE@ par2creatorsourcefile.$(OBJEXT) \
@WITH_PAR2_TRUE@ par2fileformat.$(OBJEXT) \
@WITH_PAR2_TRUE@ par2repairer.$(OBJEXT) \
@WITH_PAR2_TRUE@ par2repairersourcefile.$(OBJEXT) \
@@ -272,13 +282,15 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
@WITH_TESTS_TRUE@ CommandLineParserTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ OptionsTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ FeedFilterTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ ParCheckerTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ ParRenamerTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ DupeMatcherTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ NzbFileTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ RarRenamerTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ RarReaderTest.$(OBJEXT) NzbFileTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ ServerPoolTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ FileSystemTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ NStringTest.$(OBJEXT) UtilTest.$(OBJEXT)
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@am__objects_3 = \
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@ ParCheckerTest.$(OBJEXT) \
@WITH_PAR2_TRUE@@WITH_TESTS_TRUE@ ParRenamerTest.$(OBJEXT)
am_nzbget_OBJECTS = Connection.$(OBJEXT) TlsSocket.$(OBJEXT) \
WebDownloader.$(OBJEXT) FeedScript.$(OBJEXT) \
NzbScript.$(OBJEXT) PostScript.$(OBJEXT) QueueScript.$(OBJEXT) \
@@ -294,10 +306,10 @@ am_nzbget_OBJECTS = Connection.$(OBJEXT) TlsSocket.$(OBJEXT) \
Decoder.$(OBJEXT) NewsServer.$(OBJEXT) \
NntpConnection.$(OBJEXT) ServerPool.$(OBJEXT) \
StatMeter.$(OBJEXT) Cleanup.$(OBJEXT) DupeMatcher.$(OBJEXT) \
ParChecker.$(OBJEXT) ParCoordinator.$(OBJEXT) \
ParParser.$(OBJEXT) ParRenamer.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) Unpack.$(OBJEXT) \
DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
ParChecker.$(OBJEXT) ParParser.$(OBJEXT) ParRenamer.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) RarRenamer.$(OBJEXT) \
RarReader.$(OBJEXT) Rename.$(OBJEXT) Repair.$(OBJEXT) \
Unpack.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
DupeCoordinator.$(OBJEXT) HistoryCoordinator.$(OBJEXT) \
NzbFile.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) Scanner.$(OBJEXT) \
@@ -306,8 +318,10 @@ am_nzbget_OBJECTS = Connection.$(OBJEXT) TlsSocket.$(OBJEXT) \
WebServer.$(OBJEXT) XmlRpc.$(OBJEXT) Log.$(OBJEXT) \
NString.$(OBJEXT) Observer.$(OBJEXT) Script.$(OBJEXT) \
Thread.$(OBJEXT) Service.$(OBJEXT) FileSystem.$(OBJEXT) \
Util.$(OBJEXT) code_revision.$(OBJEXT) $(am__objects_1) \
$(am__objects_2)
Util.$(OBJEXT) NServMain.$(OBJEXT) NServFrontend.$(OBJEXT) \
NntpServer.$(OBJEXT) NzbGenerator.$(OBJEXT) YEncoder.$(OBJEXT) \
code_revision.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
$(am__objects_3)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -443,6 +457,8 @@ mandir = @mandir@
mkdir_p = @mkdir_p@
ncurses_CFLAGS = @ncurses_CFLAGS@
ncurses_LIBS = @ncurses_LIBS@
nettle_CFLAGS = @nettle_CFLAGS@
nettle_LIBS = @nettle_LIBS@
oldincludedir = @oldincludedir@
openssl_CFLAGS = @openssl_CFLAGS@
openssl_LIBS = @openssl_LIBS@
@@ -504,18 +520,21 @@ nzbget_SOURCES = daemon/connect/Connection.cpp \
daemon/postprocess/DupeMatcher.h \
daemon/postprocess/ParChecker.cpp \
daemon/postprocess/ParChecker.h \
daemon/postprocess/ParCoordinator.cpp \
daemon/postprocess/ParCoordinator.h \
daemon/postprocess/ParParser.cpp \
daemon/postprocess/ParParser.h \
daemon/postprocess/ParRenamer.cpp \
daemon/postprocess/ParRenamer.h \
daemon/postprocess/PrePostProcessor.cpp \
daemon/postprocess/PrePostProcessor.h \
daemon/postprocess/Unpack.cpp daemon/postprocess/Unpack.h \
daemon/queue/DiskState.cpp daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \
daemon/queue/DupeCoordinator.cpp \
daemon/postprocess/RarRenamer.cpp \
daemon/postprocess/RarRenamer.h \
daemon/postprocess/RarReader.cpp \
daemon/postprocess/RarReader.h daemon/postprocess/Rename.cpp \
daemon/postprocess/Rename.h daemon/postprocess/Repair.cpp \
daemon/postprocess/Repair.h daemon/postprocess/Unpack.cpp \
daemon/postprocess/Unpack.h daemon/queue/DiskState.cpp \
daemon/queue/DiskState.h daemon/queue/DownloadInfo.cpp \
daemon/queue/DownloadInfo.h daemon/queue/DupeCoordinator.cpp \
daemon/queue/DupeCoordinator.h \
daemon/queue/HistoryCoordinator.cpp \
daemon/queue/HistoryCoordinator.h daemon/queue/NzbFile.cpp \
@@ -536,14 +555,20 @@ nzbget_SOURCES = daemon/connect/Connection.cpp \
daemon/util/Thread.cpp daemon/util/Thread.h \
daemon/util/Service.cpp daemon/util/Service.h \
daemon/util/FileSystem.cpp daemon/util/FileSystem.h \
daemon/util/Util.cpp daemon/util/Util.h code_revision.cpp \
$(am__append_1) $(am__append_2)
daemon/util/Util.cpp daemon/util/Util.h \
daemon/nserv/NServMain.h daemon/nserv/NServMain.cpp \
daemon/nserv/NServFrontend.h daemon/nserv/NServFrontend.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)
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)/lib/par2 $(am__append_3)
-I$(srcdir)/daemon/nserv -I$(srcdir)/lib/par2 $(am__append_4)
EXTRA_DIST = \
$(windows_FILES) \
$(osx_FILES) \
@@ -675,7 +700,28 @@ testdata_FILES = \
tests/testdata/parchecker/testfile.par2 \
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
tests/testdata/parchecker/testfile.vol03+3.PAR2
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
tests/testdata/rarrenamer/testfile3.part01.rar \
tests/testdata/rarrenamer/testfile3.part02.rar \
tests/testdata/rarrenamer/testfile3.part03.rar \
tests/testdata/rarrenamer/testfile5.part01.rar \
tests/testdata/rarrenamer/testfile5.part02.rar \
tests/testdata/rarrenamer/testfile5.part03.rar \
tests/testdata/rarrenamer/testfile3oldnam.rar \
tests/testdata/rarrenamer/testfile3oldnam.r00 \
tests/testdata/rarrenamer/testfile3oldnam.r01 \
tests/testdata/rarrenamer/testfile3encdata.part01.rar \
tests/testdata/rarrenamer/testfile3encdata.part02.rar \
tests/testdata/rarrenamer/testfile3encdata.part03.rar \
tests/testdata/rarrenamer/testfile3encnam.part01.rar \
tests/testdata/rarrenamer/testfile3encnam.part02.rar \
tests/testdata/rarrenamer/testfile3encnam.part03.rar \
tests/testdata/rarrenamer/testfile5encdata.part01.rar \
tests/testdata/rarrenamer/testfile5encdata.part02.rar \
tests/testdata/rarrenamer/testfile5encdata.part03.rar \
tests/testdata/rarrenamer/testfile5encnam.part01.rar \
tests/testdata/rarrenamer/testfile5encnam.part02.rar \
tests/testdata/rarrenamer/testfile5encnam.part03.rar
# Install
@@ -834,19 +880,22 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoggableFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Maintenance.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NServFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NServMain.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NString.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NStringTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NewsServer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NntpConnection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NntpServer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NzbFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NzbFileTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NzbGenerator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NzbScript.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/OptionsTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParCheckerTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParParser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamerTest.Po@am__quote@
@@ -855,8 +904,14 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueScript.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarReader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarReaderTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarRenamer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RarRenamerTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteClient.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Rename.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Repair.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScanScript.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scanner.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scheduler.Po@am__quote@
@@ -879,6 +934,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebServer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/YEncoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/code_revision.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/commandline.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crc.Po@am__quote@
@@ -892,7 +948,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainpacket.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/md5.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nzbget.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2creatorsourcefile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2fileformat.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2repairer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/par2repairersourcefile.Po@am__quote@
@@ -1406,20 +1461,6 @@ ParChecker.obj: daemon/postprocess/ParChecker.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParChecker.obj `if test -f 'daemon/postprocess/ParChecker.cpp'; then $(CYGPATH_W) 'daemon/postprocess/ParChecker.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/ParChecker.cpp'; fi`
ParCoordinator.o: daemon/postprocess/ParCoordinator.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCoordinator.o -MD -MP -MF "$(DEPDIR)/ParCoordinator.Tpo" -c -o ParCoordinator.o `test -f 'daemon/postprocess/ParCoordinator.cpp' || echo '$(srcdir)/'`daemon/postprocess/ParCoordinator.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCoordinator.Tpo" "$(DEPDIR)/ParCoordinator.Po"; else rm -f "$(DEPDIR)/ParCoordinator.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/ParCoordinator.cpp' object='ParCoordinator.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCoordinator.o `test -f 'daemon/postprocess/ParCoordinator.cpp' || echo '$(srcdir)/'`daemon/postprocess/ParCoordinator.cpp
ParCoordinator.obj: daemon/postprocess/ParCoordinator.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCoordinator.obj -MD -MP -MF "$(DEPDIR)/ParCoordinator.Tpo" -c -o ParCoordinator.obj `if test -f 'daemon/postprocess/ParCoordinator.cpp'; then $(CYGPATH_W) 'daemon/postprocess/ParCoordinator.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/ParCoordinator.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCoordinator.Tpo" "$(DEPDIR)/ParCoordinator.Po"; else rm -f "$(DEPDIR)/ParCoordinator.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/ParCoordinator.cpp' object='ParCoordinator.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCoordinator.obj `if test -f 'daemon/postprocess/ParCoordinator.cpp'; then $(CYGPATH_W) 'daemon/postprocess/ParCoordinator.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/ParCoordinator.cpp'; fi`
ParParser.o: daemon/postprocess/ParParser.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParParser.o -MD -MP -MF "$(DEPDIR)/ParParser.Tpo" -c -o ParParser.o `test -f 'daemon/postprocess/ParParser.cpp' || echo '$(srcdir)/'`daemon/postprocess/ParParser.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParParser.Tpo" "$(DEPDIR)/ParParser.Po"; else rm -f "$(DEPDIR)/ParParser.Tpo"; exit 1; fi
@@ -1462,6 +1503,62 @@ PrePostProcessor.obj: daemon/postprocess/PrePostProcessor.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o PrePostProcessor.obj `if test -f 'daemon/postprocess/PrePostProcessor.cpp'; then $(CYGPATH_W) 'daemon/postprocess/PrePostProcessor.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/PrePostProcessor.cpp'; fi`
RarRenamer.o: daemon/postprocess/RarRenamer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarRenamer.o -MD -MP -MF "$(DEPDIR)/RarRenamer.Tpo" -c -o RarRenamer.o `test -f 'daemon/postprocess/RarRenamer.cpp' || echo '$(srcdir)/'`daemon/postprocess/RarRenamer.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarRenamer.Tpo" "$(DEPDIR)/RarRenamer.Po"; else rm -f "$(DEPDIR)/RarRenamer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/RarRenamer.cpp' object='RarRenamer.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarRenamer.o `test -f 'daemon/postprocess/RarRenamer.cpp' || echo '$(srcdir)/'`daemon/postprocess/RarRenamer.cpp
RarRenamer.obj: daemon/postprocess/RarRenamer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarRenamer.obj -MD -MP -MF "$(DEPDIR)/RarRenamer.Tpo" -c -o RarRenamer.obj `if test -f 'daemon/postprocess/RarRenamer.cpp'; then $(CYGPATH_W) 'daemon/postprocess/RarRenamer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/RarRenamer.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarRenamer.Tpo" "$(DEPDIR)/RarRenamer.Po"; else rm -f "$(DEPDIR)/RarRenamer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/RarRenamer.cpp' object='RarRenamer.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarRenamer.obj `if test -f 'daemon/postprocess/RarRenamer.cpp'; then $(CYGPATH_W) 'daemon/postprocess/RarRenamer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/RarRenamer.cpp'; fi`
RarReader.o: daemon/postprocess/RarReader.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarReader.o -MD -MP -MF "$(DEPDIR)/RarReader.Tpo" -c -o RarReader.o `test -f 'daemon/postprocess/RarReader.cpp' || echo '$(srcdir)/'`daemon/postprocess/RarReader.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarReader.Tpo" "$(DEPDIR)/RarReader.Po"; else rm -f "$(DEPDIR)/RarReader.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/RarReader.cpp' object='RarReader.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarReader.o `test -f 'daemon/postprocess/RarReader.cpp' || echo '$(srcdir)/'`daemon/postprocess/RarReader.cpp
RarReader.obj: daemon/postprocess/RarReader.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarReader.obj -MD -MP -MF "$(DEPDIR)/RarReader.Tpo" -c -o RarReader.obj `if test -f 'daemon/postprocess/RarReader.cpp'; then $(CYGPATH_W) 'daemon/postprocess/RarReader.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/RarReader.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarReader.Tpo" "$(DEPDIR)/RarReader.Po"; else rm -f "$(DEPDIR)/RarReader.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/RarReader.cpp' object='RarReader.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarReader.obj `if test -f 'daemon/postprocess/RarReader.cpp'; then $(CYGPATH_W) 'daemon/postprocess/RarReader.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/RarReader.cpp'; fi`
Rename.o: daemon/postprocess/Rename.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Rename.o -MD -MP -MF "$(DEPDIR)/Rename.Tpo" -c -o Rename.o `test -f 'daemon/postprocess/Rename.cpp' || echo '$(srcdir)/'`daemon/postprocess/Rename.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/Rename.Tpo" "$(DEPDIR)/Rename.Po"; else rm -f "$(DEPDIR)/Rename.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/Rename.cpp' object='Rename.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Rename.o `test -f 'daemon/postprocess/Rename.cpp' || echo '$(srcdir)/'`daemon/postprocess/Rename.cpp
Rename.obj: daemon/postprocess/Rename.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Rename.obj -MD -MP -MF "$(DEPDIR)/Rename.Tpo" -c -o Rename.obj `if test -f 'daemon/postprocess/Rename.cpp'; then $(CYGPATH_W) 'daemon/postprocess/Rename.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/Rename.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/Rename.Tpo" "$(DEPDIR)/Rename.Po"; else rm -f "$(DEPDIR)/Rename.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/Rename.cpp' object='Rename.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Rename.obj `if test -f 'daemon/postprocess/Rename.cpp'; then $(CYGPATH_W) 'daemon/postprocess/Rename.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/Rename.cpp'; fi`
Repair.o: daemon/postprocess/Repair.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Repair.o -MD -MP -MF "$(DEPDIR)/Repair.Tpo" -c -o Repair.o `test -f 'daemon/postprocess/Repair.cpp' || echo '$(srcdir)/'`daemon/postprocess/Repair.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/Repair.Tpo" "$(DEPDIR)/Repair.Po"; else rm -f "$(DEPDIR)/Repair.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/Repair.cpp' object='Repair.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Repair.o `test -f 'daemon/postprocess/Repair.cpp' || echo '$(srcdir)/'`daemon/postprocess/Repair.cpp
Repair.obj: daemon/postprocess/Repair.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Repair.obj -MD -MP -MF "$(DEPDIR)/Repair.Tpo" -c -o Repair.obj `if test -f 'daemon/postprocess/Repair.cpp'; then $(CYGPATH_W) 'daemon/postprocess/Repair.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/Repair.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/Repair.Tpo" "$(DEPDIR)/Repair.Po"; else rm -f "$(DEPDIR)/Repair.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/Repair.cpp' object='Repair.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Repair.obj `if test -f 'daemon/postprocess/Repair.cpp'; then $(CYGPATH_W) 'daemon/postprocess/Repair.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/Repair.cpp'; fi`
Unpack.o: daemon/postprocess/Unpack.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT Unpack.o -MD -MP -MF "$(DEPDIR)/Unpack.Tpo" -c -o Unpack.o `test -f 'daemon/postprocess/Unpack.cpp' || echo '$(srcdir)/'`daemon/postprocess/Unpack.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/Unpack.Tpo" "$(DEPDIR)/Unpack.Po"; else rm -f "$(DEPDIR)/Unpack.Tpo"; exit 1; fi
@@ -1784,6 +1881,76 @@ Util.obj: daemon/util/Util.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o Util.obj `if test -f 'daemon/util/Util.cpp'; then $(CYGPATH_W) 'daemon/util/Util.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/util/Util.cpp'; fi`
NServMain.o: daemon/nserv/NServMain.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NServMain.o -MD -MP -MF "$(DEPDIR)/NServMain.Tpo" -c -o NServMain.o `test -f 'daemon/nserv/NServMain.cpp' || echo '$(srcdir)/'`daemon/nserv/NServMain.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NServMain.Tpo" "$(DEPDIR)/NServMain.Po"; else rm -f "$(DEPDIR)/NServMain.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NServMain.cpp' object='NServMain.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NServMain.o `test -f 'daemon/nserv/NServMain.cpp' || echo '$(srcdir)/'`daemon/nserv/NServMain.cpp
NServMain.obj: daemon/nserv/NServMain.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NServMain.obj -MD -MP -MF "$(DEPDIR)/NServMain.Tpo" -c -o NServMain.obj `if test -f 'daemon/nserv/NServMain.cpp'; then $(CYGPATH_W) 'daemon/nserv/NServMain.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NServMain.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NServMain.Tpo" "$(DEPDIR)/NServMain.Po"; else rm -f "$(DEPDIR)/NServMain.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NServMain.cpp' object='NServMain.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NServMain.obj `if test -f 'daemon/nserv/NServMain.cpp'; then $(CYGPATH_W) 'daemon/nserv/NServMain.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NServMain.cpp'; fi`
NServFrontend.o: daemon/nserv/NServFrontend.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NServFrontend.o -MD -MP -MF "$(DEPDIR)/NServFrontend.Tpo" -c -o NServFrontend.o `test -f 'daemon/nserv/NServFrontend.cpp' || echo '$(srcdir)/'`daemon/nserv/NServFrontend.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NServFrontend.Tpo" "$(DEPDIR)/NServFrontend.Po"; else rm -f "$(DEPDIR)/NServFrontend.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NServFrontend.cpp' object='NServFrontend.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NServFrontend.o `test -f 'daemon/nserv/NServFrontend.cpp' || echo '$(srcdir)/'`daemon/nserv/NServFrontend.cpp
NServFrontend.obj: daemon/nserv/NServFrontend.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NServFrontend.obj -MD -MP -MF "$(DEPDIR)/NServFrontend.Tpo" -c -o NServFrontend.obj `if test -f 'daemon/nserv/NServFrontend.cpp'; then $(CYGPATH_W) 'daemon/nserv/NServFrontend.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NServFrontend.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NServFrontend.Tpo" "$(DEPDIR)/NServFrontend.Po"; else rm -f "$(DEPDIR)/NServFrontend.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NServFrontend.cpp' object='NServFrontend.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NServFrontend.obj `if test -f 'daemon/nserv/NServFrontend.cpp'; then $(CYGPATH_W) 'daemon/nserv/NServFrontend.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NServFrontend.cpp'; fi`
NntpServer.o: daemon/nserv/NntpServer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NntpServer.o -MD -MP -MF "$(DEPDIR)/NntpServer.Tpo" -c -o NntpServer.o `test -f 'daemon/nserv/NntpServer.cpp' || echo '$(srcdir)/'`daemon/nserv/NntpServer.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NntpServer.Tpo" "$(DEPDIR)/NntpServer.Po"; else rm -f "$(DEPDIR)/NntpServer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NntpServer.cpp' object='NntpServer.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NntpServer.o `test -f 'daemon/nserv/NntpServer.cpp' || echo '$(srcdir)/'`daemon/nserv/NntpServer.cpp
NntpServer.obj: daemon/nserv/NntpServer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NntpServer.obj -MD -MP -MF "$(DEPDIR)/NntpServer.Tpo" -c -o NntpServer.obj `if test -f 'daemon/nserv/NntpServer.cpp'; then $(CYGPATH_W) 'daemon/nserv/NntpServer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NntpServer.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NntpServer.Tpo" "$(DEPDIR)/NntpServer.Po"; else rm -f "$(DEPDIR)/NntpServer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NntpServer.cpp' object='NntpServer.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NntpServer.obj `if test -f 'daemon/nserv/NntpServer.cpp'; then $(CYGPATH_W) 'daemon/nserv/NntpServer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NntpServer.cpp'; fi`
NzbGenerator.o: daemon/nserv/NzbGenerator.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NzbGenerator.o -MD -MP -MF "$(DEPDIR)/NzbGenerator.Tpo" -c -o NzbGenerator.o `test -f 'daemon/nserv/NzbGenerator.cpp' || echo '$(srcdir)/'`daemon/nserv/NzbGenerator.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NzbGenerator.Tpo" "$(DEPDIR)/NzbGenerator.Po"; else rm -f "$(DEPDIR)/NzbGenerator.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NzbGenerator.cpp' object='NzbGenerator.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NzbGenerator.o `test -f 'daemon/nserv/NzbGenerator.cpp' || echo '$(srcdir)/'`daemon/nserv/NzbGenerator.cpp
NzbGenerator.obj: daemon/nserv/NzbGenerator.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NzbGenerator.obj -MD -MP -MF "$(DEPDIR)/NzbGenerator.Tpo" -c -o NzbGenerator.obj `if test -f 'daemon/nserv/NzbGenerator.cpp'; then $(CYGPATH_W) 'daemon/nserv/NzbGenerator.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NzbGenerator.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NzbGenerator.Tpo" "$(DEPDIR)/NzbGenerator.Po"; else rm -f "$(DEPDIR)/NzbGenerator.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/NzbGenerator.cpp' object='NzbGenerator.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o NzbGenerator.obj `if test -f 'daemon/nserv/NzbGenerator.cpp'; then $(CYGPATH_W) 'daemon/nserv/NzbGenerator.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/NzbGenerator.cpp'; fi`
YEncoder.o: daemon/nserv/YEncoder.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT YEncoder.o -MD -MP -MF "$(DEPDIR)/YEncoder.Tpo" -c -o YEncoder.o `test -f 'daemon/nserv/YEncoder.cpp' || echo '$(srcdir)/'`daemon/nserv/YEncoder.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/YEncoder.Tpo" "$(DEPDIR)/YEncoder.Po"; else rm -f "$(DEPDIR)/YEncoder.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/YEncoder.cpp' object='YEncoder.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o YEncoder.o `test -f 'daemon/nserv/YEncoder.cpp' || echo '$(srcdir)/'`daemon/nserv/YEncoder.cpp
YEncoder.obj: daemon/nserv/YEncoder.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT YEncoder.obj -MD -MP -MF "$(DEPDIR)/YEncoder.Tpo" -c -o YEncoder.obj `if test -f 'daemon/nserv/YEncoder.cpp'; then $(CYGPATH_W) 'daemon/nserv/YEncoder.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/YEncoder.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/YEncoder.Tpo" "$(DEPDIR)/YEncoder.Po"; else rm -f "$(DEPDIR)/YEncoder.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/nserv/YEncoder.cpp' object='YEncoder.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o YEncoder.obj `if test -f 'daemon/nserv/YEncoder.cpp'; then $(CYGPATH_W) 'daemon/nserv/YEncoder.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/nserv/YEncoder.cpp'; fi`
commandline.o: lib/par2/commandline.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT commandline.o -MD -MP -MF "$(DEPDIR)/commandline.Tpo" -c -o commandline.o `test -f 'lib/par2/commandline.cpp' || echo '$(srcdir)/'`lib/par2/commandline.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/commandline.Tpo" "$(DEPDIR)/commandline.Po"; else rm -f "$(DEPDIR)/commandline.Tpo"; exit 1; fi
@@ -1938,20 +2105,6 @@ md5.obj: lib/par2/md5.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o md5.obj `if test -f 'lib/par2/md5.cpp'; then $(CYGPATH_W) 'lib/par2/md5.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/md5.cpp'; fi`
par2creatorsourcefile.o: lib/par2/par2creatorsourcefile.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2creatorsourcefile.o -MD -MP -MF "$(DEPDIR)/par2creatorsourcefile.Tpo" -c -o par2creatorsourcefile.o `test -f 'lib/par2/par2creatorsourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2creatorsourcefile.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2creatorsourcefile.Tpo" "$(DEPDIR)/par2creatorsourcefile.Po"; else rm -f "$(DEPDIR)/par2creatorsourcefile.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2creatorsourcefile.cpp' object='par2creatorsourcefile.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2creatorsourcefile.o `test -f 'lib/par2/par2creatorsourcefile.cpp' || echo '$(srcdir)/'`lib/par2/par2creatorsourcefile.cpp
par2creatorsourcefile.obj: lib/par2/par2creatorsourcefile.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2creatorsourcefile.obj -MD -MP -MF "$(DEPDIR)/par2creatorsourcefile.Tpo" -c -o par2creatorsourcefile.obj `if test -f 'lib/par2/par2creatorsourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2creatorsourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2creatorsourcefile.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2creatorsourcefile.Tpo" "$(DEPDIR)/par2creatorsourcefile.Po"; else rm -f "$(DEPDIR)/par2creatorsourcefile.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='lib/par2/par2creatorsourcefile.cpp' object='par2creatorsourcefile.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o par2creatorsourcefile.obj `if test -f 'lib/par2/par2creatorsourcefile.cpp'; then $(CYGPATH_W) 'lib/par2/par2creatorsourcefile.cpp'; else $(CYGPATH_W) '$(srcdir)/lib/par2/par2creatorsourcefile.cpp'; fi`
par2fileformat.o: lib/par2/par2fileformat.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT par2fileformat.o -MD -MP -MF "$(DEPDIR)/par2fileformat.Tpo" -c -o par2fileformat.o `test -f 'lib/par2/par2fileformat.cpp' || echo '$(srcdir)/'`lib/par2/par2fileformat.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/par2fileformat.Tpo" "$(DEPDIR)/par2fileformat.Po"; else rm -f "$(DEPDIR)/par2fileformat.Tpo"; exit 1; fi
@@ -2134,34 +2287,6 @@ FeedFilterTest.obj: tests/feed/FeedFilterTest.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o FeedFilterTest.obj `if test -f 'tests/feed/FeedFilterTest.cpp'; then $(CYGPATH_W) 'tests/feed/FeedFilterTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/feed/FeedFilterTest.cpp'; fi`
ParCheckerTest.o: tests/postprocess/ParCheckerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCheckerTest.o -MD -MP -MF "$(DEPDIR)/ParCheckerTest.Tpo" -c -o ParCheckerTest.o `test -f 'tests/postprocess/ParCheckerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParCheckerTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCheckerTest.Tpo" "$(DEPDIR)/ParCheckerTest.Po"; else rm -f "$(DEPDIR)/ParCheckerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParCheckerTest.cpp' object='ParCheckerTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCheckerTest.o `test -f 'tests/postprocess/ParCheckerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParCheckerTest.cpp
ParCheckerTest.obj: tests/postprocess/ParCheckerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCheckerTest.obj -MD -MP -MF "$(DEPDIR)/ParCheckerTest.Tpo" -c -o ParCheckerTest.obj `if test -f 'tests/postprocess/ParCheckerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParCheckerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParCheckerTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCheckerTest.Tpo" "$(DEPDIR)/ParCheckerTest.Po"; else rm -f "$(DEPDIR)/ParCheckerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParCheckerTest.cpp' object='ParCheckerTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCheckerTest.obj `if test -f 'tests/postprocess/ParCheckerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParCheckerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParCheckerTest.cpp'; fi`
ParRenamerTest.o: tests/postprocess/ParRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParRenamerTest.o -MD -MP -MF "$(DEPDIR)/ParRenamerTest.Tpo" -c -o ParRenamerTest.o `test -f 'tests/postprocess/ParRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParRenamerTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParRenamerTest.Tpo" "$(DEPDIR)/ParRenamerTest.Po"; else rm -f "$(DEPDIR)/ParRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParRenamerTest.cpp' object='ParRenamerTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParRenamerTest.o `test -f 'tests/postprocess/ParRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParRenamerTest.cpp
ParRenamerTest.obj: tests/postprocess/ParRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParRenamerTest.obj -MD -MP -MF "$(DEPDIR)/ParRenamerTest.Tpo" -c -o ParRenamerTest.obj `if test -f 'tests/postprocess/ParRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParRenamerTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParRenamerTest.Tpo" "$(DEPDIR)/ParRenamerTest.Po"; else rm -f "$(DEPDIR)/ParRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParRenamerTest.cpp' object='ParRenamerTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParRenamerTest.obj `if test -f 'tests/postprocess/ParRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParRenamerTest.cpp'; fi`
DupeMatcherTest.o: tests/postprocess/DupeMatcherTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DupeMatcherTest.o -MD -MP -MF "$(DEPDIR)/DupeMatcherTest.Tpo" -c -o DupeMatcherTest.o `test -f 'tests/postprocess/DupeMatcherTest.cpp' || echo '$(srcdir)/'`tests/postprocess/DupeMatcherTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DupeMatcherTest.Tpo" "$(DEPDIR)/DupeMatcherTest.Po"; else rm -f "$(DEPDIR)/DupeMatcherTest.Tpo"; exit 1; fi
@@ -2176,6 +2301,34 @@ DupeMatcherTest.obj: tests/postprocess/DupeMatcherTest.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o DupeMatcherTest.obj `if test -f 'tests/postprocess/DupeMatcherTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/DupeMatcherTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/DupeMatcherTest.cpp'; fi`
RarRenamerTest.o: tests/postprocess/RarRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarRenamerTest.o -MD -MP -MF "$(DEPDIR)/RarRenamerTest.Tpo" -c -o RarRenamerTest.o `test -f 'tests/postprocess/RarRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/RarRenamerTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarRenamerTest.Tpo" "$(DEPDIR)/RarRenamerTest.Po"; else rm -f "$(DEPDIR)/RarRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/RarRenamerTest.cpp' object='RarRenamerTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarRenamerTest.o `test -f 'tests/postprocess/RarRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/RarRenamerTest.cpp
RarRenamerTest.obj: tests/postprocess/RarRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarRenamerTest.obj -MD -MP -MF "$(DEPDIR)/RarRenamerTest.Tpo" -c -o RarRenamerTest.obj `if test -f 'tests/postprocess/RarRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/RarRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/RarRenamerTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarRenamerTest.Tpo" "$(DEPDIR)/RarRenamerTest.Po"; else rm -f "$(DEPDIR)/RarRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/RarRenamerTest.cpp' object='RarRenamerTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarRenamerTest.obj `if test -f 'tests/postprocess/RarRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/RarRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/RarRenamerTest.cpp'; fi`
RarReaderTest.o: tests/postprocess/RarReaderTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarReaderTest.o -MD -MP -MF "$(DEPDIR)/RarReaderTest.Tpo" -c -o RarReaderTest.o `test -f 'tests/postprocess/RarReaderTest.cpp' || echo '$(srcdir)/'`tests/postprocess/RarReaderTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarReaderTest.Tpo" "$(DEPDIR)/RarReaderTest.Po"; else rm -f "$(DEPDIR)/RarReaderTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/RarReaderTest.cpp' object='RarReaderTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarReaderTest.o `test -f 'tests/postprocess/RarReaderTest.cpp' || echo '$(srcdir)/'`tests/postprocess/RarReaderTest.cpp
RarReaderTest.obj: tests/postprocess/RarReaderTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT RarReaderTest.obj -MD -MP -MF "$(DEPDIR)/RarReaderTest.Tpo" -c -o RarReaderTest.obj `if test -f 'tests/postprocess/RarReaderTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/RarReaderTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/RarReaderTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/RarReaderTest.Tpo" "$(DEPDIR)/RarReaderTest.Po"; else rm -f "$(DEPDIR)/RarReaderTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/RarReaderTest.cpp' object='RarReaderTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o RarReaderTest.obj `if test -f 'tests/postprocess/RarReaderTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/RarReaderTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/RarReaderTest.cpp'; fi`
NzbFileTest.o: tests/queue/NzbFileTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NzbFileTest.o -MD -MP -MF "$(DEPDIR)/NzbFileTest.Tpo" -c -o NzbFileTest.o `test -f 'tests/queue/NzbFileTest.cpp' || echo '$(srcdir)/'`tests/queue/NzbFileTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NzbFileTest.Tpo" "$(DEPDIR)/NzbFileTest.Po"; else rm -f "$(DEPDIR)/NzbFileTest.Tpo"; exit 1; fi
@@ -2245,6 +2398,34 @@ UtilTest.obj: tests/util/UtilTest.cpp
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/util/UtilTest.cpp' object='UtilTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o UtilTest.obj `if test -f 'tests/util/UtilTest.cpp'; then $(CYGPATH_W) 'tests/util/UtilTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/util/UtilTest.cpp'; fi`
ParCheckerTest.o: tests/postprocess/ParCheckerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCheckerTest.o -MD -MP -MF "$(DEPDIR)/ParCheckerTest.Tpo" -c -o ParCheckerTest.o `test -f 'tests/postprocess/ParCheckerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParCheckerTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCheckerTest.Tpo" "$(DEPDIR)/ParCheckerTest.Po"; else rm -f "$(DEPDIR)/ParCheckerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParCheckerTest.cpp' object='ParCheckerTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCheckerTest.o `test -f 'tests/postprocess/ParCheckerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParCheckerTest.cpp
ParCheckerTest.obj: tests/postprocess/ParCheckerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParCheckerTest.obj -MD -MP -MF "$(DEPDIR)/ParCheckerTest.Tpo" -c -o ParCheckerTest.obj `if test -f 'tests/postprocess/ParCheckerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParCheckerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParCheckerTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParCheckerTest.Tpo" "$(DEPDIR)/ParCheckerTest.Po"; else rm -f "$(DEPDIR)/ParCheckerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParCheckerTest.cpp' object='ParCheckerTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParCheckerTest.obj `if test -f 'tests/postprocess/ParCheckerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParCheckerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParCheckerTest.cpp'; fi`
ParRenamerTest.o: tests/postprocess/ParRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParRenamerTest.o -MD -MP -MF "$(DEPDIR)/ParRenamerTest.Tpo" -c -o ParRenamerTest.o `test -f 'tests/postprocess/ParRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParRenamerTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParRenamerTest.Tpo" "$(DEPDIR)/ParRenamerTest.Po"; else rm -f "$(DEPDIR)/ParRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParRenamerTest.cpp' object='ParRenamerTest.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParRenamerTest.o `test -f 'tests/postprocess/ParRenamerTest.cpp' || echo '$(srcdir)/'`tests/postprocess/ParRenamerTest.cpp
ParRenamerTest.obj: tests/postprocess/ParRenamerTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT ParRenamerTest.obj -MD -MP -MF "$(DEPDIR)/ParRenamerTest.Tpo" -c -o ParRenamerTest.obj `if test -f 'tests/postprocess/ParRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParRenamerTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/ParRenamerTest.Tpo" "$(DEPDIR)/ParRenamerTest.Po"; else rm -f "$(DEPDIR)/ParRenamerTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/ParRenamerTest.cpp' object='ParRenamerTest.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o ParRenamerTest.obj `if test -f 'tests/postprocess/ParRenamerTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/ParRenamerTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/ParRenamerTest.cpp'; fi`
uninstall-info-am:
install-dist_docDATA: $(dist_doc_DATA)
@$(NORMAL_INSTALL)
@@ -2351,7 +2532,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/daemon/windows $(distdir)/lib/par2 $(distdir)/linux $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/posix $(distdir)/scripts $(distdir)/tests/testdata/dupematcher1 $(distdir)/tests/testdata/dupematcher2 $(distdir)/tests/testdata/nzbfile $(distdir)/tests/testdata/parchecker $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib $(distdir)/windows $(distdir)/windows/resources $(distdir)/windows/setup
$(mkdir_p) $(distdir)/daemon/windows $(distdir)/lib/par2 $(distdir)/linux $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/posix $(distdir)/scripts $(distdir)/tests/testdata/dupematcher1 $(distdir)/tests/testdata/dupematcher2 $(distdir)/tests/testdata/nzbfile $(distdir)/tests/testdata/parchecker $(distdir)/tests/testdata/rarrenamer $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib $(distdir)/windows $(distdir)/windows/resources $(distdir)/windows/setup
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \

View File

@@ -92,7 +92,10 @@
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
/* Define to 1 to use Nettle library for decryption. */
#undef HAVE_NETTLE
/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
#undef HAVE_OPENSSL
/* Define to 1 if you have the <regex.h> header file. */

457
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 17.1.
# Generated by GNU Autoconf 2.61 for nzbget 18.1.
#
# Report bugs to <hugbug@users.sourceforge.net>.
#
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='nzbget'
PACKAGE_TARNAME='nzbget'
PACKAGE_VERSION='17.1'
PACKAGE_STRING='nzbget 17.1'
PACKAGE_VERSION='18.1'
PACKAGE_STRING='nzbget 18.1'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="daemon/main/nzbget.cpp"
@@ -721,6 +721,8 @@ openssl_CFLAGS
openssl_LIBS
gnutls_CFLAGS
gnutls_LIBS
nettle_CFLAGS
nettle_LIBS
zlib_CFLAGS
zlib_LIBS
WITH_TESTS_TRUE
@@ -747,6 +749,8 @@ openssl_CFLAGS
openssl_LIBS
gnutls_CFLAGS
gnutls_LIBS
nettle_CFLAGS
nettle_LIBS
zlib_CFLAGS
zlib_LIBS'
@@ -1251,7 +1255,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 17.1 to adapt to many kinds of systems.
\`configure' configures nzbget 18.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1322,7 +1326,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 17.1:";;
short | recursive ) echo "Configuration of nzbget 18.1:";;
esac
cat <<\_ACEOF
@@ -1369,6 +1373,10 @@ Optional Packages:
GnuTLS include directory
--with-libgnutls-libraries=DIR
GnuTLS library directory
--with-libnettle-includes=DIR
Nettle include directory
--with-libnettle-libraries=DIR
Nettle library directory
--with-zlib-includes=DIR
zlib include directory
--with-zlib-libraries=DIR
@@ -1399,6 +1407,9 @@ Some influential environment variables:
gnutls_CFLAGS
C compiler flags for gnutls, overriding pkg-config
gnutls_LIBS linker flags for gnutls, overriding pkg-config
nettle_CFLAGS
C compiler flags for nettle, overriding pkg-config
nettle_LIBS linker flags for nettle, overriding pkg-config
zlib_CFLAGS C compiler flags for zlib, overriding pkg-config
zlib_LIBS linker flags for zlib, overriding pkg-config
@@ -1466,7 +1477,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 17.1
nzbget configure 18.1
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1480,7 +1491,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 17.1, which was
It was created by nzbget $as_me 18.1, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2276,7 +2287,7 @@ fi
# Define the identity of the package.
PACKAGE='nzbget'
VERSION='17.1'
VERSION='18.1'
cat >>confdefs.h <<_ACEOF
@@ -9797,9 +9808,9 @@ echo "$as_me: error: Couldn't find OpenSSL headers (ssl.h)" >&2;}
{ (exit 1); exit 1; }; }
fi
if test "$FOUND" = "yes"; then
{ echo "$as_me:$LINENO: checking for library containing CRYPTO_set_locking_callback" >&5
echo $ECHO_N "checking for library containing CRYPTO_set_locking_callback... $ECHO_C" >&6; }
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
{ echo "$as_me:$LINENO: checking for library containing ASN1_OBJECT_free" >&5
echo $ECHO_N "checking for library containing ASN1_OBJECT_free... $ECHO_C" >&6; }
if test "${ac_cv_search_ASN1_OBJECT_free+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -9816,11 +9827,11 @@ cat >>conftest.$ac_ext <<_ACEOF
#ifdef __cplusplus
extern "C"
#endif
char CRYPTO_set_locking_callback ();
char ASN1_OBJECT_free ();
int
main ()
{
return CRYPTO_set_locking_callback ();
return ASN1_OBJECT_free ();
;
return 0;
}
@@ -9850,7 +9861,7 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_search_CRYPTO_set_locking_callback=$ac_res
ac_cv_search_ASN1_OBJECT_free=$ac_res
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
@@ -9860,26 +9871,26 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
if test "${ac_cv_search_ASN1_OBJECT_free+set}" = set; then
break
fi
done
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
if test "${ac_cv_search_ASN1_OBJECT_free+set}" = set; then
:
else
ac_cv_search_CRYPTO_set_locking_callback=no
ac_cv_search_ASN1_OBJECT_free=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_CRYPTO_set_locking_callback" >&5
echo "${ECHO_T}$ac_cv_search_CRYPTO_set_locking_callback" >&6; }
ac_res=$ac_cv_search_CRYPTO_set_locking_callback
{ echo "$as_me:$LINENO: result: $ac_cv_search_ASN1_OBJECT_free" >&5
echo "${ECHO_T}$ac_cv_search_ASN1_OBJECT_free" >&6; }
ac_res=$ac_cv_search_ASN1_OBJECT_free
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
{ echo "$as_me:$LINENO: checking for library containing SSL_library_init" >&5
echo $ECHO_N "checking for library containing SSL_library_init... $ECHO_C" >&6; }
if test "${ac_cv_search_SSL_library_init+set}" = set; then
{ echo "$as_me:$LINENO: checking for library containing SSL_CTX_new" >&5
echo $ECHO_N "checking for library containing SSL_CTX_new... $ECHO_C" >&6; }
if test "${ac_cv_search_SSL_CTX_new+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -9896,11 +9907,11 @@ cat >>conftest.$ac_ext <<_ACEOF
#ifdef __cplusplus
extern "C"
#endif
char SSL_library_init ();
char SSL_CTX_new ();
int
main ()
{
return SSL_library_init ();
return SSL_CTX_new ();
;
return 0;
}
@@ -9930,7 +9941,7 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_search_SSL_library_init=$ac_res
ac_cv_search_SSL_CTX_new=$ac_res
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
@@ -9940,21 +9951,21 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext
if test "${ac_cv_search_SSL_library_init+set}" = set; then
if test "${ac_cv_search_SSL_CTX_new+set}" = set; then
break
fi
done
if test "${ac_cv_search_SSL_library_init+set}" = set; then
if test "${ac_cv_search_SSL_CTX_new+set}" = set; then
:
else
ac_cv_search_SSL_library_init=no
ac_cv_search_SSL_CTX_new=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_SSL_library_init" >&5
echo "${ECHO_T}$ac_cv_search_SSL_library_init" >&6; }
ac_res=$ac_cv_search_SSL_library_init
{ echo "$as_me:$LINENO: result: $ac_cv_search_SSL_CTX_new" >&5
echo "${ECHO_T}$ac_cv_search_SSL_CTX_new" >&6; }
ac_res=$ac_cv_search_SSL_CTX_new
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
FOUND=yes
@@ -10630,6 +10641,380 @@ echo "$as_me: error: Couldn't find GnuTLS library" >&2;}
cat >>confdefs.h <<\_ACEOF
#define HAVE_LIBGNUTLS 1
_ACEOF
fi
fi
if test "$TLSLIB" = "GnuTLS"; then
# Check whether --with-libnettle_includes was given.
if test "${with_libnettle_includes+set}" = set; then
withval=$with_libnettle_includes; CPPFLAGS="${CPPFLAGS} -I${withval}"
INCVAL="yes"
else
INCVAL="no"
fi
# Check whether --with-libnettle_libraries was given.
if test "${with_libnettle_libraries+set}" = set; then
withval=$with_libnettle_libraries; LDFLAGS="${LDFLAGS} -L${withval}"
LIBVAL="yes"
else
LIBVAL="no"
fi
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
pkg_failed=no
{ echo "$as_me:$LINENO: checking for nettle" >&5
echo $ECHO_N "checking for nettle... $ECHO_C" >&6; }
if test -n "$PKG_CONFIG"; then
if test -n "$nettle_CFLAGS"; then
pkg_cv_nettle_CFLAGS="$nettle_CFLAGS"
else
if test -n "$PKG_CONFIG" && \
{ (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"nettle\"") >&5
($PKG_CONFIG --exists --print-errors "nettle") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_nettle_CFLAGS=`$PKG_CONFIG --cflags "nettle" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test -n "$PKG_CONFIG"; then
if test -n "$nettle_LIBS"; then
pkg_cv_nettle_LIBS="$nettle_LIBS"
else
if test -n "$PKG_CONFIG" && \
{ (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"nettle\"") >&5
($PKG_CONFIG --exists --print-errors "nettle") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_nettle_LIBS=`$PKG_CONFIG --libs "nettle" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
nettle_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "nettle"`
else
nettle_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "nettle"`
fi
# Put the nasty error message in config.log where it belongs
echo "$nettle_PKG_ERRORS" >&5
{ { echo "$as_me:$LINENO: error: Package requirements (nettle) were not met:
$nettle_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables nettle_CFLAGS
and nettle_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
" >&5
echo "$as_me: error: Package requirements (nettle) were not met:
$nettle_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables nettle_CFLAGS
and nettle_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
" >&2;}
{ (exit 1); exit 1; }; }
elif test $pkg_failed = untried; then
{ { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
Alternatively, you may set the environment variables nettle_CFLAGS
and nettle_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See \`config.log' for more details." >&5
echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
Alternatively, you may set the environment variables nettle_CFLAGS
and nettle_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See \`config.log' for more details." >&2;}
{ (exit 1); exit 1; }; }
else
nettle_CFLAGS=$pkg_cv_nettle_CFLAGS
nettle_LIBS=$pkg_cv_nettle_LIBS
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
LIBS="${LIBS} $nettle_LIBS"
CPPFLAGS="${CPPFLAGS} $nettle_CFLAGS"
fi
fi
if test "${ac_cv_header_nettle_sha_h+set}" = set; then
{ echo "$as_me:$LINENO: checking for nettle/sha.h" >&5
echo $ECHO_N "checking for nettle/sha.h... $ECHO_C" >&6; }
if test "${ac_cv_header_nettle_sha_h+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
fi
{ echo "$as_me:$LINENO: result: $ac_cv_header_nettle_sha_h" >&5
echo "${ECHO_T}$ac_cv_header_nettle_sha_h" >&6; }
else
# Is the header compilable?
{ echo "$as_me:$LINENO: checking nettle/sha.h usability" >&5
echo $ECHO_N "checking nettle/sha.h usability... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
$ac_includes_default
#include <nettle/sha.h>
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_header_compiler=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_compiler=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
echo "${ECHO_T}$ac_header_compiler" >&6; }
# Is the header present?
{ echo "$as_me:$LINENO: checking nettle/sha.h presence" >&5
echo $ECHO_N "checking nettle/sha.h presence... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <nettle/sha.h>
_ACEOF
if { (ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } >/dev/null && {
test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
test ! -s conftest.err
}; then
ac_header_preproc=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_preproc=no
fi
rm -f conftest.err conftest.$ac_ext
{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
echo "${ECHO_T}$ac_header_preproc" >&6; }
# So? What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
yes:no: )
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: accepted by the compiler, rejected by the preprocessor!" >&5
echo "$as_me: WARNING: nettle/sha.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: proceeding with the compiler's result" >&5
echo "$as_me: WARNING: nettle/sha.h: proceeding with the compiler's result" >&2;}
ac_header_preproc=yes
;;
no:yes:* )
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: present but cannot be compiled" >&5
echo "$as_me: WARNING: nettle/sha.h: present but cannot be compiled" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: check for missing prerequisite headers?" >&5
echo "$as_me: WARNING: nettle/sha.h: check for missing prerequisite headers?" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: see the Autoconf documentation" >&5
echo "$as_me: WARNING: nettle/sha.h: see the Autoconf documentation" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: section \"Present But Cannot Be Compiled\"" >&5
echo "$as_me: WARNING: nettle/sha.h: section \"Present But Cannot Be Compiled\"" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: proceeding with the preprocessor's result" >&5
echo "$as_me: WARNING: nettle/sha.h: proceeding with the preprocessor's result" >&2;}
{ echo "$as_me:$LINENO: WARNING: nettle/sha.h: in the future, the compiler will take precedence" >&5
echo "$as_me: WARNING: nettle/sha.h: in the future, the compiler will take precedence" >&2;}
( cat <<\_ASBOX
## ------------------------------------------- ##
## Report this to hugbug@users.sourceforge.net ##
## ------------------------------------------- ##
_ASBOX
) | sed "s/^/$as_me: WARNING: /" >&2
;;
esac
{ echo "$as_me:$LINENO: checking for nettle/sha.h" >&5
echo $ECHO_N "checking for nettle/sha.h... $ECHO_C" >&6; }
if test "${ac_cv_header_nettle_sha_h+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_cv_header_nettle_sha_h=$ac_header_preproc
fi
{ echo "$as_me:$LINENO: result: $ac_cv_header_nettle_sha_h" >&5
echo "${ECHO_T}$ac_cv_header_nettle_sha_h" >&6; }
fi
if test $ac_cv_header_nettle_sha_h = yes; then
FOUND=yes
else
FOUND=no
fi
if test "$FOUND" = "no"; then
{ { echo "$as_me:$LINENO: error: Couldn't find Nettle headers (sha.h)" >&5
echo "$as_me: error: Couldn't find Nettle headers (sha.h)" >&2;}
{ (exit 1); exit 1; }; }
fi
{ echo "$as_me:$LINENO: checking for library containing nettle_pbkdf2_hmac_sha256" >&5
echo $ECHO_N "checking for library containing nettle_pbkdf2_hmac_sha256... $ECHO_C" >&6; }
if test "${ac_cv_search_nettle_pbkdf2_hmac_sha256+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_func_search_save_LIBS=$LIBS
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char nettle_pbkdf2_hmac_sha256 ();
int
main ()
{
return nettle_pbkdf2_hmac_sha256 ();
;
return 0;
}
_ACEOF
for ac_lib in '' nettle; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_search_nettle_pbkdf2_hmac_sha256=$ac_res
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext
if test "${ac_cv_search_nettle_pbkdf2_hmac_sha256+set}" = set; then
break
fi
done
if test "${ac_cv_search_nettle_pbkdf2_hmac_sha256+set}" = set; then
:
else
ac_cv_search_nettle_pbkdf2_hmac_sha256=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_nettle_pbkdf2_hmac_sha256" >&5
echo "${ECHO_T}$ac_cv_search_nettle_pbkdf2_hmac_sha256" >&6; }
ac_res=$ac_cv_search_nettle_pbkdf2_hmac_sha256
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
FOUND=yes
else
FOUND=no
fi
if test "$FOUND" = "no"; then
{ { echo "$as_me:$LINENO: error: Couldn't find Nettle library, required when using GnuTLS" >&5
echo "$as_me: error: Couldn't find Nettle library, required when using GnuTLS" >&2;}
{ (exit 1); exit 1; }; }
fi
if test "$FOUND" = "yes"; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_NETTLE 1
_ACEOF
fi
@@ -11825,7 +12210,7 @@ exec 6>&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 17.1, which was
This file was extended by nzbget $as_me 18.1, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -11878,7 +12263,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 17.1
nzbget config.status 18.1
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -12203,6 +12588,8 @@ openssl_CFLAGS!$openssl_CFLAGS$ac_delim
openssl_LIBS!$openssl_LIBS$ac_delim
gnutls_CFLAGS!$gnutls_CFLAGS$ac_delim
gnutls_LIBS!$gnutls_LIBS$ac_delim
nettle_CFLAGS!$nettle_CFLAGS$ac_delim
nettle_LIBS!$nettle_LIBS$ac_delim
zlib_CFLAGS!$zlib_CFLAGS$ac_delim
zlib_LIBS!$zlib_LIBS$ac_delim
WITH_TESTS_TRUE!$WITH_TESTS_TRUE$ac_delim
@@ -12211,7 +12598,7 @@ LIBOBJS!$LIBOBJS$ac_delim
LTLIBOBJS!$LTLIBOBJS$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 15; then
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 17; then
break
elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5

View File

@@ -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-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
@@ -21,7 +21,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 17.1, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 18.1, hugbug@users.sourceforge.net)
AC_CONFIG_AUX_DIR(posix)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign])
@@ -385,8 +385,8 @@ if test "$USETLS" = "yes"; then
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([CRYPTO_set_locking_callback], [crypto],
AC_SEARCH_LIBS([SSL_library_init], [ssl],
AC_SEARCH_LIBS([ASN1_OBJECT_free], [crypto],
AC_SEARCH_LIBS([SSL_CTX_new], [ssl],
FOUND=yes,
FOUND=no),
FOUND=no)
@@ -395,7 +395,7 @@ if test "$USETLS" = "yes"; then
fi
if test "$FOUND" = "yes"; then
TLSLIB="OpenSSL"
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support.])
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support and decryption.])
fi
fi
fi
@@ -457,6 +457,39 @@ if test "$USETLS" = "yes"; then
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
fi
fi
if test "$TLSLIB" = "GnuTLS"; then
AC_ARG_WITH(libnettle_includes,
[AS_HELP_STRING([--with-libnettle-includes=DIR], [Nettle include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libnettle_libraries,
[AS_HELP_STRING([--with-libnettle-libraries=DIR], [Nettle library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES([nettle], [nettle],
[LIBS="${LIBS} $nettle_LIBS"]
[CPPFLAGS="${CPPFLAGS} $nettle_CFLAGS"])
fi
AC_CHECK_HEADER(nettle/sha.h,
FOUND=yes,
FOUND=no)
if test "$FOUND" = "no"; then
AC_MSG_ERROR([Couldn't find Nettle headers (sha.h)])
fi
AC_SEARCH_LIBS([nettle_pbkdf2_hmac_sha256], [nettle],
FOUND=yes,
FOUND=no)
if test "$FOUND" = "no"; then
AC_MSG_ERROR([Couldn't find Nettle library, required when using GnuTLS])
fi
if test "$FOUND" = "yes"; then
AC_DEFINE([HAVE_NETTLE],1,[Define to 1 to use Nettle library for decryption.])
fi
fi
fi
if test "$TLSLIB" = ""; then

View File

@@ -89,6 +89,12 @@ static struct gcry_thread_cbs gcry_threads_Mutex =
#ifdef HAVE_OPENSSL
#ifndef CRYPTO_set_locking_callback
#define NEED_CRYPTO_LOCKING
#endif
#ifdef NEED_CRYPTO_LOCKING
/**
* Mutexes for OpenSSL
*/
@@ -108,17 +114,6 @@ static void openssl_locking(int mode, int n, const char* file, int line)
}
}
/*
static uint32 openssl_thread_id(void)
{
#ifdef WIN32
return (uint32)GetCurrentThreadId();
#else
return (uint32)pthread_self();
#endif
}
*/
static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line)
{
return (CRYPTO_dynlock_value*)new Mutex();
@@ -143,6 +138,7 @@ static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const
}
}
#endif /* NEED_CRYPTO_LOCKING */
#endif /* HAVE_OPENSSL */
@@ -172,20 +168,22 @@ void TlsSocket::Init()
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
#ifdef NEED_CRYPTO_LOCKING
for (int i = 0, num = CRYPTO_num_locks(); i < num; i++)
{
g_OpenSSLMutexes.emplace_back(std::make_unique<Mutex>());
}
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
CRYPTO_set_locking_callback(openssl_locking);
//CRYPTO_set_id_callback(openssl_thread_id);
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock);
#endif /* NEED_CRYPTO_LOCKING */
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
#endif /* HAVE_OPENSSL */
@@ -287,7 +285,8 @@ bool TlsSocket::Start()
m_initialized = true;
const char* priority = !m_cipher.Empty() ? m_cipher.Str() : "NORMAL";
const char* priority = !m_cipher.Empty() ? m_cipher.Str() :
(m_certFile && m_keyFile ? "NORMAL:!VERS-SSL3.0" : "NORMAL");
m_retCode = gnutls_priority_set_direct((gnutls_session_t)m_session, priority, nullptr);
if (m_retCode != 0)
@@ -354,6 +353,12 @@ bool TlsSocket::Start()
Close();
return false;
}
if (!SSL_CTX_set_options((SSL_CTX*)m_context, SSL_OP_NO_SSLv3))
{
ReportError("Could not select minimum protocol version for TLS");
Close();
return false;
}
}
m_session = SSL_new((SSL_CTX*)m_context);

View File

@@ -54,7 +54,7 @@ void WebDownloader::Run()
SetStatus(adRunning);
int remainedDownloadRetries = g_Options->GetRetries() > 0 ? g_Options->GetRetries() : 1;
int remainedDownloadRetries = g_Options->GetUrlRetries() > 0 ? g_Options->GetUrlRetries() : 1;
int remainedConnectRetries = remainedDownloadRetries > 10 ? remainedDownloadRetries : 10;
if (!m_retry)
{
@@ -74,9 +74,9 @@ void WebDownloader::Run()
((Status == adConnectError) && (remainedConnectRetries > 1)))
&& !IsStopped() && !(!m_force && g_Options->GetPauseDownload()))
{
detail("Waiting %i sec to retry", g_Options->GetRetryInterval());
detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
int msec = 0;
while (!IsStopped() && (msec < g_Options->GetRetryInterval() * 1000) &&
while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
!(!m_force && g_Options->GetPauseDownload()))
{
usleep(100 * 1000);

View File

@@ -62,7 +62,7 @@ void FeedScriptController::ExecuteScript(ScriptConfig::Script* script)
if (exitCode != FEED_SUCCESS)
{
infoName[0] = 'F'; // uppercase
PrintMessage(Message::mkError, "%s failed", GetInfoName());
PrintMessage(Message::mkError, "%s failed", *infoName);
m_success = false;
}

View File

@@ -100,7 +100,7 @@ void PostScriptController::ExecuteScript(ScriptConfig::Script* script)
infoName[0] = 'P'; // uppercase
SetLogPrefix(nullptr);
ScriptStatus::EStatus status = AnalyseExitCode(exitCode);
ScriptStatus::EStatus status = AnalyseExitCode(exitCode, infoName);
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
@@ -176,7 +176,7 @@ void PostScriptController::PrepareParams(const char* scriptName)
PrepareEnvScript(m_postInfo->GetNzbInfo()->GetParameters(), scriptName);
}
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode)
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode, const char* upInfoName)
{
// The ScriptStatus is accumulated for all scripts:
// If any script has failed the status is "failure", etc.
@@ -184,28 +184,28 @@ ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode)
switch (exitCode)
{
case POSTPROCESS_SUCCESS:
PrintMessage(Message::mkInfo, "%s successful", GetInfoName());
PrintMessage(Message::mkInfo, "%s successful", upInfoName);
return ScriptStatus::srSuccess;
case POSTPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", GetInfoName());
PrintMessage(Message::mkError, "%s failed", upInfoName);
return ScriptStatus::srFailure;
case POSTPROCESS_NONE:
PrintMessage(Message::mkInfo, "%s skipped", GetInfoName());
PrintMessage(Message::mkInfo, "%s skipped", upInfoName);
return ScriptStatus::srNone;
#ifndef DISABLE_PARCHECK
case POSTPROCESS_PARCHECK:
if (m_postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped)
{
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", GetInfoName());
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", upInfoName);
return ScriptStatus::srFailure;
}
else
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", GetInfoName());
PrintMessage(Message::mkInfo, "%s requested par-check/repair", upInfoName);
m_postInfo->SetRequestParCheck(true);
m_postInfo->SetForceRepair(true);
return ScriptStatus::srSuccess;
@@ -214,7 +214,7 @@ ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode)
#endif
default:
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", GetInfoName());
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", upInfoName);
return ScriptStatus::srFailure;
}
}

View File

@@ -41,7 +41,7 @@ private:
ScriptConfig::Script* m_script;
void PrepareParams(const char* scriptName);
ScriptStatus::EStatus AnalyseExitCode(int exitCode);
ScriptStatus::EStatus AnalyseExitCode(int exitCode, const char* upInfoName);
};
#endif

View File

@@ -112,7 +112,7 @@ void QueueScriptController::Run()
{
nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
nzbInfo->SetDeleteStatus(NzbInfo::dsBad);
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, 0, nullptr);
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, nullptr);
}
}
@@ -336,21 +336,7 @@ bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo*
return false;
}
// check queue-scripts
const char* queueScript = g_Options->GetQueueScript();
if (!Util::EmptyStr(queueScript))
{
Tokenizer tok(queueScript, ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
}
// check post-processing-scripts assigned for that nzb
// check extension scripts assigned for that nzb
for (NzbParameter& parameter : nzbInfo->GetParameters())
{
const char* varname = parameter.GetName();
@@ -368,17 +354,17 @@ bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo*
}
}
// for URL-events the post-processing scripts are not assigned yet;
// instead we take the default post-processing scripts for the category (or global)
// for URL-events the extension scripts are not assigned yet;
// instead we take the default extension scripts for the category (or global)
if (event == qeUrlCompleted)
{
const char* postScript = g_Options->GetPostScript();
const char* postScript = g_Options->GetExtensions();
if (!Util::EmptyStr(nzbInfo->GetCategory()))
{
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
if (categoryObj && !Util::EmptyStr(categoryObj->GetPostScript()))
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
{
postScript = categoryObj->GetPostScript();
postScript = categoryObj->GetExtensions();
}
}

View File

@@ -25,6 +25,22 @@
#include "Log.h"
#include "FileSystem.h"
class ScanScriptCheck : public NzbScriptController
{
protected:
virtual void ExecuteScript(ScriptConfig::Script* script) { has |= script->GetScanScript(); }
bool has = false;
friend class ScanScriptController;
};
bool ScanScriptController::HasScripts()
{
ScanScriptCheck check;
check.ExecuteScriptList(g_Options->GetExtensions());
return check.has;
}
void ScanScriptController::ExecuteScripts(const char* nzbFilename,
const char* url, const char* directory, CString* nzbName, CString* category,
int* priority, NzbParameterList* parameters, bool* addTop, bool* addPaused,
@@ -45,7 +61,18 @@ void ScanScriptController::ExecuteScripts(const char* nzbFilename,
scriptController.m_dupeMode = dupeMode;
scriptController.m_prefixLen = 0;
scriptController.ExecuteScriptList(g_Options->GetScanScript());
const char* extensions = g_Options->GetExtensions();
if (!Util::EmptyStr(*category))
{
Options::Category* categoryObj = g_Options->FindCategory(*category, false);
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
{
extensions = categoryObj->GetExtensions();
}
}
scriptController.ExecuteScriptList(extensions);
}
void ScanScriptController::ExecuteScript(ScriptConfig::Script* script)

View File

@@ -30,6 +30,7 @@ public:
const char* directory, CString* nzbName, CString* category, int* priority,
NzbParameterList* parameters, bool* addTop, bool* addPaused,
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode);
static bool HasScripts();
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);

View File

@@ -69,11 +69,17 @@ void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* script)
return;
}
PrintMessage(Message::mkInfo, "Executing scheduler-script %s for Task%i", script->GetName(), m_taskId);
BString<1024> taskName(" for Task%i", m_taskId);
if (m_taskId == 0)
{
taskName = "";
}
PrintMessage(Message::mkInfo, "Executing scheduler-script %s%s", script->GetName(), *taskName);
SetArgs({script->GetLocation()});
BString<1024> infoName("scheduler-script %s for Task%i", script->GetName(), m_taskId);
BString<1024> infoName("scheduler-script %s%s", script->GetName(), *taskName);
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());

View File

@@ -33,25 +33,14 @@ static const char* SCHEDULER_SCRIPT_SIGNATURE = "SCHEDULER";
static const char* FEED_SCRIPT_SIGNATURE = "FEED";
static const char* END_SCRIPT_SIGNATURE = " SCRIPT";
static const char* QUEUE_EVENTS_SIGNATURE = "### QUEUE EVENTS:";
ScriptConfig::Script::Script(const char* name, const char* location)
{
m_name = name;
m_location = location;
m_displayName = name;
m_postScript = false;
m_scanScript = false;
m_queueScript = false;
m_schedulerScript = false;
m_feedScript = false;
}
static const char* TASK_TIME_SIGNATURE = "### TASK TIME:";
static const char* DEFINITION_SIGNATURE = "###";
void ScriptConfig::InitOptions()
{
InitScripts();
InitConfigTemplates();
CreateTasks();
}
bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
@@ -81,7 +70,7 @@ bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
CString optname;
CString optvalue;
if (g_Options->SplitOptionString(buf, optname, optvalue))
if (Options::SplitOptionString(buf, optname, optvalue))
{
optEntries->emplace_back(optname, optvalue);
}
@@ -89,6 +78,8 @@ bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
infile.Close();
Options::ConvertOldOptions(optEntries);
return true;
}
@@ -180,7 +171,7 @@ bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
LoadScripts(&scriptList);
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
for (Script& script : scriptList)
{
@@ -194,6 +185,7 @@ bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
StringBuilder templ;
char buf[1024];
bool inConfig = false;
bool inHeader = false;
while (infile.ReadLine(buf, sizeof(buf) - 1))
{
@@ -210,12 +202,13 @@ bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
break;
}
inConfig = true;
inHeader = true;
continue;
}
bool skip = !strncmp(buf, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen);
inHeader &= !strncmp(buf, DEFINITION_SIGNATURE, definitionSignatureLen);
if (inConfig && !skip)
if (inConfig && !inHeader)
{
templ.Append(buf);
}
@@ -287,11 +280,6 @@ void ScriptConfig::LoadScripts(Scripts* scripts)
void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir)
{
CharBuffer buffer(1024*10 + 1);
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
DirBrowser dir(directory);
while (const char* filename = dir.Next())
{
@@ -307,54 +295,10 @@ void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool i
continue;
}
// check if the file contains pp-script-signature
DiskFile infile;
if (infile.Open(fullFilename, DiskFile::omRead))
Script script(scriptName, fullFilename);
if (LoadScriptFile(&script))
{
// read first 10KB of the file and look for signature
int readBytes = (int)infile.Read(buffer, buffer.Size() - 1);
infile.Close();
buffer[readBytes] = '\0';
// split buffer into lines
Tokenizer tok(buffer, "\n\r", true);
while (char* line = tok.Next())
{
if (!strncmp(line, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) &&
strstr(line, END_SCRIPT_SIGNATURE))
{
bool postScript = strstr(line, POST_SCRIPT_SIGNATURE);
bool scanScript = strstr(line, SCAN_SCRIPT_SIGNATURE);
bool queueScript = strstr(line, QUEUE_SCRIPT_SIGNATURE);
bool schedulerScript = strstr(line, SCHEDULER_SCRIPT_SIGNATURE);
bool feedScript = strstr(line, FEED_SCRIPT_SIGNATURE);
if (postScript || scanScript || queueScript || schedulerScript || feedScript)
{
char* queueEvents = nullptr;
if (queueScript)
{
while (char* line = tok.Next())
{
if (!strncmp(line, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen))
{
queueEvents = line + queueEventsSignatureLen;
break;
}
}
}
scripts->emplace_back(scriptName, fullFilename);
Script& script = scripts->back();
script.SetPostScript(postScript);
script.SetScanScript(scanScript);
script.SetQueueScript(queueScript);
script.SetSchedulerScript(schedulerScript);
script.SetFeedScript(feedScript);
script.SetQueueEvents(queueEvents);
break;
}
}
}
scripts->push_back(std::move(script));
}
}
else if (!isSubDir)
@@ -365,6 +309,109 @@ void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool i
}
}
bool ScriptConfig::LoadScriptFile(Script* script)
{
DiskFile infile;
if (!infile.Open(script->GetLocation(), DiskFile::omRead))
{
return false;
}
CharBuffer buffer(1024 * 10 + 1);
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
const int taskTimeSignatureLen = strlen(TASK_TIME_SIGNATURE);
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
// check if the file contains pp-script-signature
// read first 10KB of the file and look for signature
int readBytes = (int)infile.Read(buffer, buffer.Size() - 1);
infile.Close();
buffer[readBytes] = '\0';
bool postScript = false;
bool scanScript = false;
bool queueScript = false;
bool schedulerScript = false;
bool feedScript = false;
char* queueEvents = nullptr;
char* taskTime = nullptr;
bool inConfig = false;
bool afterConfig = false;
// Declarations "QUEUE EVENT:" and "TASK TIME:" can be placed:
// - in script definition body (between opening and closing script signatures);
// - immediately before script definition (before opening script signature);
// - immediately after script definition (after closing script signature).
// The last two pissibilities are provided to increase compatibility of scripts with older
// nzbget versions which do not expect the extra declarations in the script defintion body.
Tokenizer tok(buffer, "\n\r", true);
while (char* line = tok.Next())
{
if (!strncmp(line, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen))
{
queueEvents = line + queueEventsSignatureLen;
}
else if (!strncmp(line, TASK_TIME_SIGNATURE, taskTimeSignatureLen))
{
taskTime = line + taskTimeSignatureLen;
}
bool header = !strncmp(line, DEFINITION_SIGNATURE, definitionSignatureLen);
if (!header && !inConfig)
{
queueEvents = nullptr;
taskTime = nullptr;
}
if (!header && afterConfig)
{
break;
}
if (!strncmp(line, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) && strstr(line, END_SCRIPT_SIGNATURE))
{
if (!inConfig)
{
inConfig = true;
postScript = strstr(line, POST_SCRIPT_SIGNATURE);
scanScript = strstr(line, SCAN_SCRIPT_SIGNATURE);
queueScript = strstr(line, QUEUE_SCRIPT_SIGNATURE);
schedulerScript = strstr(line, SCHEDULER_SCRIPT_SIGNATURE);
feedScript = strstr(line, FEED_SCRIPT_SIGNATURE);
}
else
{
afterConfig = true;
}
}
}
if (!(postScript || scanScript || queueScript || schedulerScript || feedScript))
{
return false;
}
// trim decorations
char* p;
while (queueEvents && *queueEvents && *(p = queueEvents + strlen(queueEvents) - 1) == '#') *p = '\0';
if (queueEvents) queueEvents = Util::Trim(queueEvents);
while (taskTime && *taskTime && *(p = taskTime + strlen(taskTime) - 1) == '#') *p = '\0';
if (taskTime) taskTime = Util::Trim(taskTime);
script->SetPostScript(postScript);
script->SetScanScript(scanScript);
script->SetQueueScript(queueScript);
script->SetSchedulerScript(schedulerScript);
script->SetFeedScript(feedScript);
script->SetQueueEvents(queueEvents);
script->SetTaskTime(taskTime);
return true;
}
BString<1024> ScriptConfig::BuildScriptName(const char* directory, const char* filename, bool isSubDir)
{
@@ -431,3 +478,23 @@ void ScriptConfig::BuildScriptDisplayNames(Scripts* scripts)
script.SetDisplayName(displayName);
}
}
void ScriptConfig::CreateTasks()
{
for (Script& script : m_scripts)
{
if (script.GetSchedulerScript() && !Util::EmptyStr(script.GetTaskTime()))
{
Tokenizer tok(g_Options->GetExtensions(), ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
g_Options->CreateSchedulerTask(0, script.GetTaskTime(),
nullptr, Options::scScript, script.GetName());
break;
}
}
}
}
}

View File

@@ -31,7 +31,8 @@ public:
class Script
{
public:
Script(const char* name, const char* location);
Script(const char* name, const char* location) :
m_name(name), m_location(location), m_displayName(name) {};
Script(Script&&) = default;
const char* GetName() { return m_name; }
const char* GetLocation() { return m_location; }
@@ -49,17 +50,20 @@ public:
void SetFeedScript(bool feedScript) { m_feedScript = feedScript; }
void SetQueueEvents(const char* queueEvents) { m_queueEvents = queueEvents; }
const char* GetQueueEvents() { return m_queueEvents; }
void SetTaskTime(const char* taskTime) { m_taskTime = taskTime; }
const char* GetTaskTime() { return m_taskTime; }
private:
CString m_name;
CString m_location;
CString m_displayName;
bool m_postScript;
bool m_scanScript;
bool m_queueScript;
bool m_schedulerScript;
bool m_feedScript;
bool m_postScript = false;
bool m_scanScript = false;
bool m_queueScript = false;
bool m_schedulerScript = false;
bool m_feedScript = false;
CString m_queueEvents;
CString m_taskTime;
};
typedef std::list<Script> Scripts;
@@ -75,8 +79,6 @@ public:
private:
Script m_script;
CString m_template;
friend class Options;
};
typedef std::deque<ConfigTemplate> ConfigTemplates;
@@ -94,9 +96,11 @@ private:
void InitScripts();
void InitConfigTemplates();
void CreateTasks();
void LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir);
void BuildScriptDisplayNames(Scripts* scripts);
void LoadScripts(Scripts* scripts);
bool LoadScriptFile(Script* script);
BString<1024>BuildScriptName(const char* directory, const char* filename, bool isSubDir);
bool ScriptExists(Scripts* scripts, const char* scriptName);
};

View File

@@ -239,14 +239,7 @@ void FeedCoordinator::StartFeedDownload(FeedInfo* feedInfo, bool force)
feedDownloader->Attach(this);
feedDownloader->SetFeedInfo(feedInfo);
feedDownloader->SetUrl(feedInfo->GetUrl());
if (strlen(feedInfo->GetName()) > 0)
{
feedDownloader->SetInfoName(feedInfo->GetName());
}
else
{
feedDownloader->SetInfoName(NzbInfo::MakeNiceUrlName(feedInfo->GetUrl(), ""));
}
feedDownloader->SetInfoName(feedInfo->GetName());
feedDownloader->SetForce(force || g_Options->GetUrlForce());
BString<1024> outFilename;
@@ -304,7 +297,7 @@ void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
{
bool scriptSuccess = true;
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(feedInfo->GetFeedScript()) ? feedInfo->GetFeedScript(): g_Options->GetFeedScript(),
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
feedInfo->GetOutputFilename(), feedInfo->GetId(), &scriptSuccess);
if (!scriptSuccess)
{
@@ -312,15 +305,13 @@ void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
return;
}
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename());
bool parsed = feedFile->Parse();
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo);
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
{
Guard guard(m_downloadsMutex);
if (parsed)
if (feedFile)
{
std::unique_ptr<FeedItemList> feedItems = feedFile->DetachFeedItems();
addedNzbs = ProcessFeed(feedInfo, feedItems.get());
@@ -469,7 +460,7 @@ std::shared_ptr<FeedItemList> FeedCoordinator::ViewFeed(int id)
return PreviewFeed(feedInfo->GetId(), feedInfo->GetName(), feedInfo->GetUrl(), feedInfo->GetFilter(),
feedInfo->GetBacklog(), feedInfo->GetPauseNzb(), feedInfo->GetCategory(),
feedInfo->GetPriority(), feedInfo->GetInterval(), feedInfo->GetFeedScript(), 0, nullptr);
feedInfo->GetPriority(), feedInfo->GetInterval(), feedInfo->GetExtensions(), 0, nullptr);
}
std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
@@ -535,14 +526,11 @@ std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
}
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(feedInfo->GetFeedScript()) ? feedInfo->GetFeedScript(): g_Options->GetFeedScript(),
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
feedInfo->GetOutputFilename(), feedInfo->GetId(), nullptr);
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename());
bool parsed = feedFile->Parse();
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
if (!parsed)
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo.get());
if (!feedFile)
{
return nullptr;
}
@@ -588,6 +576,21 @@ void FeedCoordinator::FetchFeed(int id)
}
}
std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
{
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename(), feedInfo->GetName());
if (feedFile->Parse())
{
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
}
else
{
error("Feed file %s kept for troubleshooting (will be deleted on next successful feed fetch)", feedInfo->GetOutputFilename());
feedFile.reset();
}
return std::move(feedFile);
}
void FeedCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
{
debug("Notification from URL-Coordinator received");

View File

@@ -26,6 +26,7 @@
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "FeedFile.h"
#include "FeedInfo.h"
#include "Observer.h"
#include "Util.h"
@@ -119,6 +120,7 @@ private:
void CleanupHistory();
void CleanupCache();
void CheckSaveFeeds();
std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
};
extern FeedCoordinator* g_FeedCoordinator;

View File

@@ -25,8 +25,8 @@
#include "Options.h"
#include "Util.h"
FeedFile::FeedFile(const char* fileName) :
m_fileName(fileName)
FeedFile::FeedFile(const char* fileName, const char* infoName) :
m_fileName(fileName), m_infoName(infoName)
{
debug("Creating FeedFile");
@@ -113,7 +113,7 @@ bool FeedFile::Parse()
{
_bstr_t r(doc->GetparseError()->reason);
const char* errMsg = r;
error("Error parsing rss feed: %s", errMsg);
error("Error parsing rss feed %s: %s", *m_infoName, errMsg);
return false;
}
@@ -248,7 +248,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
//<newznab:attr name="size" value="5423523453534" />
if (feedItemInfo.GetSize() == 0)
{
tag = node->selectSingleNode("newznab:attr[@name='size']");
tag = node->selectSingleNode("newznab:attr[@name='size'] | nZEDb:attr[@name='size']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -262,7 +262,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
}
//<newznab:attr name="imdb" value="1588173"/>
tag = node->selectSingleNode("newznab:attr[@name='imdb']");
tag = node->selectSingleNode("newznab:attr[@name='imdb'] | nZEDb:attr[@name='imdb']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -275,7 +275,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
}
//<newznab:attr name="rageid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='rageid']");
tag = node->selectSingleNode("newznab:attr[@name='rageid'] | nZEDb:attr[@name='rageid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -288,7 +288,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
}
//<newznab:attr name="tdvdbid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='tvdbid']");
tag = node->selectSingleNode("newznab:attr[@name='tvdbid'] | nZEDb:attr[@name='tvdbid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -301,7 +301,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
}
//<newznab:attr name="tvmazeid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='tvmazeid']");
tag = node->selectSingleNode("newznab:attr[@name='tvmazeid'] | nZEDb:attr[@name='tvmazeid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -315,7 +315,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
tag = node->selectSingleNode("newznab:attr[@name='episode']");
tag = node->selectSingleNode("newznab:attr[@name='episode'] | nZEDb:attr[@name='episode']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -328,7 +328,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
tag = node->selectSingleNode("newznab:attr[@name='season']");
tag = node->selectSingleNode("newznab:attr[@name='season'] | nZEDb:attr[@name='season']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
@@ -339,7 +339,7 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
}
}
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr");
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr | nZEDb:attr");
for (int i = 0; i < itemList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
@@ -373,7 +373,7 @@ bool FeedFile::Parse()
if (ret != 0)
{
error("Failed to parse rss feed");
error("Failed to parse rss feed %s", *m_infoName);
return false;
}
@@ -407,7 +407,8 @@ void FeedFile::Parse_StartElement(const char *name, const char **atts)
}
}
}
else if (m_feedItemInfo && !strcmp("newznab:attr", name) &&
else if (m_feedItemInfo &&
(!strcmp("newznab:attr", name) || !strcmp("nZEDb:attr", name)) &&
atts[0] && atts[1] && atts[2] && atts[3] &&
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
{
@@ -592,6 +593,6 @@ void FeedFile::SAX_error(FeedFile* file, const char *msg, ...)
// remove trailing CRLF
for (char* pend = errMsg + strlen(errMsg) - 1; pend >= errMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing rss feed: %s", errMsg);
error("Error parsing rss feed %s: %s", *file->m_infoName, errMsg);
}
#endif

View File

@@ -27,7 +27,7 @@
class FeedFile
{
public:
FeedFile(const char* fileName);
FeedFile(const char* fileName, const char* infoName);
bool Parse();
std::unique_ptr<FeedItemList> DetachFeedItems() { return std::move(m_feedItems); }
@@ -36,6 +36,7 @@ public:
private:
std::unique_ptr<FeedItemList> m_feedItems;
CString m_fileName;
CString m_infoName;
void ParseSubject(FeedItemInfo& feedItemInfo);
#ifdef WIN32

View File

@@ -23,16 +23,20 @@
#include "Util.h"
FeedInfo::FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
const char* filter, bool pauseNzb, const char* category, int priority, const char* feedScript) :
const char* filter, bool pauseNzb, const char* category, int priority, const char* extensions) :
m_backlog(backlog), m_interval(interval), m_pauseNzb(pauseNzb), m_priority(priority)
{
m_id = id;
m_name = name ? name : "";
if (m_name.Length() == 0)
{
m_name.Format("Feed%i", m_id);
}
m_url = url ? url : "";
m_filter = filter ? filter : "";
m_filterHash = Util::HashBJ96(m_filter, strlen(m_filter), 0);
m_category = category ? category : "";
m_feedScript = feedScript ? feedScript : "";
m_extensions = extensions ? extensions : "";
}

View File

@@ -38,7 +38,7 @@ public:
FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
const char* filter, bool pauseNzb, const char* category, int priority,
const char* feedScript);
const char* extensions);
int GetId() { return m_id; }
const char* GetName() { return m_name; }
const char* GetUrl() { return m_url; }
@@ -48,7 +48,7 @@ public:
bool GetPauseNzb() { return m_pauseNzb; }
const char* GetCategory() { return m_category; }
int GetPriority() { return m_priority; }
const char* GetFeedScript() { return m_feedScript; }
const char* GetExtensions() { return m_extensions; }
time_t GetLastUpdate() { return m_lastUpdate; }
void SetLastUpdate(time_t lastUpdate) { m_lastUpdate = lastUpdate; }
bool GetPreview() { return m_preview; }
@@ -73,7 +73,7 @@ private:
uint32 m_filterHash;
bool m_pauseNzb;
CString m_category;
CString m_feedScript;
CString m_extensions;
int m_priority;
time_t m_lastUpdate = 0;
bool m_preview = false;

View File

@@ -133,7 +133,7 @@ bool Frontend::ServerEditQueue(DownloadQueue::EEditAction action, int offset, in
}
else
{
return DownloadQueue::Guard()->EditEntry(id, action, offset, nullptr);
return DownloadQueue::Guard()->EditEntry(id, action, CString::FormatStr("%i", offset));
}
}

View File

@@ -72,8 +72,10 @@ 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_RETRIES = "Retries";
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
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";
@@ -87,13 +89,12 @@ static const char* OPTION_PARCHECK = "ParCheck";
static const char* OPTION_PARREPAIR = "ParRepair";
static const char* OPTION_PARSCAN = "ParScan";
static const char* OPTION_PARQUICK = "ParQuick";
static const char* OPTION_POSTSTRATEGY = "PostStrategy";
static const char* OPTION_PARRENAME = "ParRename";
static const char* OPTION_PARBUFFER = "ParBuffer";
static const char* OPTION_PARTHREADS = "ParThreads";
static const char* OPTION_RARRENAME = "RarRename";
static const char* OPTION_HEALTHCHECK = "HealthCheck";
static const char* OPTION_SCANSCRIPT = "ScanScript";
static const char* OPTION_QUEUESCRIPT = "QueueScript";
static const char* OPTION_FEEDSCRIPT = "FeedScript";
static const char* OPTION_UMASK = "UMask";
static const char* OPTION_UPDATEINTERVAL = "UpdateInterval";
static const char* OPTION_CURSESNZBNAME = "CursesNzbName";
@@ -119,9 +120,10 @@ static const char* OPTION_SEVENZIPCMD = "SevenZipCmd";
static const char* OPTION_UNPACKPASSFILE = "UnpackPassFile";
static const char* OPTION_UNPACKPAUSEQUEUE = "UnpackPauseQueue";
static const char* OPTION_SCRIPTORDER = "ScriptOrder";
static const char* OPTION_POSTSCRIPT = "PostScript";
static const char* OPTION_EXTENSIONS = "Extensions";
static const char* OPTION_EXTCLEANUPDISK = "ExtCleanupDisk";
static const char* OPTION_PARIGNOREEXT = "ParIgnoreExt";
static const char* OPTION_UNPACKIGNOREEXT = "UnpackIgnoreExt";
static const char* OPTION_FEEDHISTORY = "FeedHistory";
static const char* OPTION_URLFORCE = "UrlForce";
static const char* OPTION_TIMECORRECTION = "TimeCorrection";
@@ -155,6 +157,9 @@ static const char* OPTION_RESETLOG = "ResetLog";
static const char* OPTION_PARCLEANUPQUEUE = "ParCleanupQueue";
static const char* OPTION_DELETECLEANUPDISK = "DeleteCleanupDisk";
static const char* OPTION_HISTORYCLEANUPDISK = "HistoryCleanupDisk";
static const char* OPTION_SCANSCRIPT = "ScanScript";
static const char* OPTION_QUEUESCRIPT = "QueueScript";
static const char* OPTION_FEEDSCRIPT = "FeedScript";
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 };
@@ -325,6 +330,7 @@ void Options::Init(const char* exeName, const char* configFilename, bool noConfi
return;
}
ConvertOldOptions(&m_optEntries);
InitOptions();
CheckOptions();
@@ -429,8 +435,10 @@ void Options::InitDefaults()
SetOption(OPTION_BROKENLOG, "yes");
SetOption(OPTION_NZBLOG, "yes");
SetOption(OPTION_DECODE, "yes");
SetOption(OPTION_RETRIES, "3");
SetOption(OPTION_RETRYINTERVAL, "10");
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");
@@ -444,15 +452,14 @@ void Options::InitDefaults()
SetOption(OPTION_PARREPAIR, "yes");
SetOption(OPTION_PARSCAN, "extended");
SetOption(OPTION_PARQUICK, "yes");
SetOption(OPTION_POSTSTRATEGY, "sequential");
SetOption(OPTION_PARRENAME, "yes");
SetOption(OPTION_PARBUFFER, "16");
SetOption(OPTION_PARTHREADS, "1");
SetOption(OPTION_RARRENAME, "yes");
SetOption(OPTION_HEALTHCHECK, "none");
SetOption(OPTION_SCRIPTORDER, "");
SetOption(OPTION_POSTSCRIPT, "");
SetOption(OPTION_SCANSCRIPT, "");
SetOption(OPTION_QUEUESCRIPT, "");
SetOption(OPTION_FEEDSCRIPT, "");
SetOption(OPTION_EXTENSIONS, "");
SetOption(OPTION_DAEMONUSERNAME, "root");
SetOption(OPTION_UMASK, "1000");
SetOption(OPTION_UPDATEINTERVAL, "200");
@@ -485,6 +492,7 @@ void Options::InitDefaults()
SetOption(OPTION_UNPACKPAUSEQUEUE, "no");
SetOption(OPTION_EXTCLEANUPDISK, "");
SetOption(OPTION_PARIGNOREEXT, "");
SetOption(OPTION_UNPACKIGNOREEXT, "");
SetOption(OPTION_FEEDHISTORY, "7");
SetOption(OPTION_URLFORCE, "yes");
SetOption(OPTION_TIMECORRECTION, "0");
@@ -641,10 +649,7 @@ void Options::InitOptions()
m_configTemplate = GetOption(OPTION_CONFIGTEMPLATE);
m_scriptOrder = GetOption(OPTION_SCRIPTORDER);
m_postScript = GetOption(OPTION_POSTSCRIPT);
m_scanScript = GetOption(OPTION_SCANSCRIPT);
m_queueScript = GetOption(OPTION_QUEUESCRIPT);
m_feedScript = GetOption(OPTION_FEEDSCRIPT);
m_extensions = GetOption(OPTION_EXTENSIONS);
m_controlIp = GetOption(OPTION_CONTROLIP);
m_controlUsername = GetOption(OPTION_CONTROLUSERNAME);
m_controlPassword = GetOption(OPTION_CONTROLPASSWORD);
@@ -663,14 +668,17 @@ void Options::InitOptions()
m_unpackPassFile = GetOption(OPTION_UNPACKPASSFILE);
m_extCleanupDisk = GetOption(OPTION_EXTCLEANUPDISK);
m_parIgnoreExt = GetOption(OPTION_PARIGNOREEXT);
m_unpackIgnoreExt = GetOption(OPTION_UNPACKIGNOREEXT);
m_shellOverride = GetOption(OPTION_SHELLOVERRIDE);
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_retries = ParseIntValue(OPTION_RETRIES, 10);
m_retryInterval = ParseIntValue(OPTION_RETRYINTERVAL, 10);
m_articleRetries = ParseIntValue(OPTION_ARTICLERETRIES, 10);
m_articleInterval = ParseIntValue(OPTION_ARTICLEINTERVAL, 10);
m_urlRetries = ParseIntValue(OPTION_URLRETRIES, 10);
m_urlInterval = ParseIntValue(OPTION_URLINTERVAL, 10);
m_controlPort = ParseIntValue(OPTION_CONTROLPORT, 10);
m_securePort = ParseIntValue(OPTION_SECUREPORT, 10);
m_urlConnections = ParseIntValue(OPTION_URLCONNECTIONS, 10);
@@ -710,6 +718,7 @@ void Options::InitOptions()
m_parRepair = (bool)ParseEnumValue(OPTION_PARREPAIR, BoolCount, BoolNames, BoolValues);
m_parQuick = (bool)ParseEnumValue(OPTION_PARQUICK, BoolCount, BoolNames, BoolValues);
m_parRename = (bool)ParseEnumValue(OPTION_PARRENAME, BoolCount, BoolNames, BoolValues);
m_rarRename = (bool)ParseEnumValue(OPTION_RARRENAME, 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);
@@ -743,6 +752,11 @@ void Options::InitOptions()
const int ParScanCount = 4;
m_parScan = (EParScan)ParseEnumValue(OPTION_PARSCAN, ParScanCount, ParScanNames, ParScanValues);
const char* PostStrategyNames[] = { "sequential", "balanced", "aggressive", "rocket" };
const int PostStrategyValues[] = { ppSequential, ppBalanced, ppAggressive, ppRocket };
const int PostStrategyCount = 4;
m_postStrategy = (EPostStrategy)ParseEnumValue(OPTION_POSTSTRATEGY, PostStrategyCount, PostStrategyNames, PostStrategyValues);
const char* HealthCheckNames[] = { "pause", "delete", "park", "none" };
const int HealthCheckValues[] = { hcPause, hcDelete, hcPark, hcNone };
const int HealthCheckCount = 4;
@@ -1018,10 +1032,10 @@ void Options::InitCategories()
unpack = (bool)ParseEnumValue(BString<100>("Category%i.Unpack", n), BoolCount, BoolNames, BoolValues);
}
const char* npostscript = GetOption(BString<100>("Category%i.PostScript", n));
const char* nextensions = GetOption(BString<100>("Category%i.Extensions", n));
const char* naliases = GetOption(BString<100>("Category%i.Aliases", n));
bool definition = nname || ndestdir || nunpack || npostscript || naliases;
bool definition = nname || ndestdir || nunpack || nextensions || naliases;
bool completed = nname && strlen(nname) > 0;
if (!definition)
@@ -1037,7 +1051,7 @@ void Options::InitCategories()
CheckDir(destDir, BString<100>("Category%i.DestDir", n), m_destDir, false, false);
}
m_categories.emplace_back(nname, destDir, unpack, npostscript);
m_categories.emplace_back(nname, destDir, unpack, nextensions);
Category& category = m_categories.back();
// split Aliases into tokens and create items for each token
@@ -1068,7 +1082,7 @@ void Options::InitFeeds()
const char* nurl = GetOption(BString<100>("Feed%i.URL", n));
const char* nfilter = GetOption(BString<100>("Feed%i.Filter", n));
const char* ncategory = GetOption(BString<100>("Feed%i.Category", n));
const char* nfeedscript = GetOption(BString<100>("Feed%i.FeedScript", n));
const char* nextensions = GetOption(BString<100>("Feed%i.Extensions", n));
const char* nbacklog = GetOption(BString<100>("Feed%i.Backlog", n));
bool backlog = true;
@@ -1088,7 +1102,7 @@ void Options::InitFeeds()
const char* npriority = GetOption(BString<100>("Feed%i.Priority", n));
bool definition = nname || nurl || nfilter || ncategory || nbacklog || npausenzb ||
ninterval || npriority || nfeedscript;
ninterval || npriority || nextensions;
bool completed = nurl;
if (!definition)
@@ -1101,7 +1115,7 @@ void Options::InitFeeds()
if (m_extender)
{
m_extender->AddFeed(n, nname, nurl, ninterval ? atoi(ninterval) : 0, nfilter,
backlog, pauseNzb, ncategory, npriority ? atoi(npriority) : 0, nfeedscript);
backlog, pauseNzb, ncategory, npriority ? atoi(npriority) : 0, nextensions);
}
}
else
@@ -1170,13 +1184,6 @@ void Options::InitScheduler()
continue;
}
int weekDaysVal = 0;
if (weekDays && !ParseWeekDays(weekDays, &weekDaysVal))
{
ConfigError("Invalid value for option \"Task%i.WeekDays\": \"%s\"", n, weekDays);
continue;
}
if (taskCommand == scDownloadRate)
{
if (param)
@@ -1207,36 +1214,60 @@ void Options::InitScheduler()
continue;
}
int hours, minutes;
Tokenizer tok(time, ";,");
while (const char* oneTime = tok.Next())
{
if (!ParseTime(oneTime, &hours, &minutes))
{
ConfigError("Invalid value for option \"Task%i.Time\": \"%s\"", n, oneTime);
break;
}
CreateSchedulerTask(n, time, weekDays, taskCommand, param);
}
}
if (m_extender)
void Options::CreateSchedulerTask(int id, const char* time, const char* weekDays,
ESchedulerCommand command, const char* param)
{
if (!id)
{
m_configLine = 0;
}
int weekDaysVal = 0;
if (weekDays && !ParseWeekDays(weekDays, &weekDaysVal))
{
ConfigError("Invalid value for option \"Task%i.WeekDays\": \"%s\"", id, weekDays);
return;
}
int hours, minutes;
Tokenizer tok(time, ";,");
while (const char* oneTime = tok.Next())
{
if (!ParseTime(oneTime, &hours, &minutes))
{
ConfigError("Invalid value for option \"Task%i.Time\": \"%s\"", id, oneTime);
return;
}
if (m_extender)
{
if (hours == -2)
{
if (hours == -1)
for (int everyHour = 0; everyHour < 24; everyHour++)
{
for (int everyHour = 0; everyHour < 24; everyHour++)
{
m_extender->AddTask(n, everyHour, minutes, weekDaysVal, taskCommand, param);
}
}
else
{
m_extender->AddTask(n, hours, minutes, weekDaysVal, taskCommand, param);
m_extender->AddTask(id, everyHour, minutes, weekDaysVal, command, param);
}
}
else
{
m_extender->AddTask(id, hours, minutes, weekDaysVal, command, param);
}
}
}
}
bool Options::ParseTime(const char* time, int* hours, int* minutes)
{
if (!strcmp(time, "*"))
{
*hours = -1;
return true;
}
int colons = 0;
const char* p = time;
while (*p)
@@ -1265,7 +1296,7 @@ bool Options::ParseTime(const char* time, int* hours, int* minutes)
if (time[0] == '*')
{
*hours = -1;
*hours = -2;
}
else
{
@@ -1486,7 +1517,7 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
{
char* p = (char*)optname + 8;
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".name") || !strcasecmp(p, ".destdir") || !strcasecmp(p, ".postscript") ||
if (p && (!strcasecmp(p, ".name") || !strcasecmp(p, ".destdir") || !strcasecmp(p, ".extensions") ||
!strcasecmp(p, ".unpack") || !strcasecmp(p, ".aliases")))
{
return true;
@@ -1499,7 +1530,7 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".name") || !strcasecmp(p, ".url") || !strcasecmp(p, ".interval") ||
!strcasecmp(p, ".filter") || !strcasecmp(p, ".backlog") || !strcasecmp(p, ".pausenzb") ||
!strcasecmp(p, ".category") || !strcasecmp(p, ".priority") || !strcasecmp(p, ".feedscript")))
!strcasecmp(p, ".category") || !strcasecmp(p, ".priority") || !strcasecmp(p, ".extensions")))
{
return true;
}
@@ -1532,21 +1563,27 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
ConfigWarn("Option \"%s\" is obsolete, ignored", optname);
return true;
}
if (!strcasecmp(optname, OPTION_POSTPROCESS) ||
!strcasecmp(optname, OPTION_NZBPROCESS) ||
!strcasecmp(optname, OPTION_NZBADDEDPROCESS))
{
if (optvalue && strlen(optvalue) > 0)
{
ConfigError("Option \"%s\" is obsolete, ignored, use \"%s\" and \"%s\" instead", optname, OPTION_SCRIPTDIR,
!strcasecmp(optname, OPTION_POSTPROCESS) ? OPTION_POSTSCRIPT :
!strcasecmp(optname, OPTION_NZBPROCESS) ? OPTION_SCANSCRIPT :
!strcasecmp(optname, OPTION_NZBADDEDPROCESS) ? OPTION_QUEUESCRIPT :
"ERROR");
ConfigError("Option \"%s\" is obsolete, ignored, use \"%s\" and \"%s\" instead",
optname, OPTION_SCRIPTDIR, OPTION_EXTENSIONS);
}
return true;
}
if (!strcasecmp(optname, OPTION_SCANSCRIPT) ||
!strcasecmp(optname, OPTION_QUEUESCRIPT) ||
!strcasecmp(optname, OPTION_FEEDSCRIPT))
{
// will be automatically converted into "Extensions"
return true;
}
if (!strcasecmp(optname, OPTION_CREATELOG) || !strcasecmp(optname, OPTION_RESETLOG))
{
ConfigWarn("Option \"%s\" is obsolete, ignored, use \"%s\" instead", optname, OPTION_WRITELOG);
@@ -1600,16 +1637,22 @@ void Options::ConvertOldOption(CString& option, CString& value)
value = "extended";
}
if (!strcasecmp(option, "DefScript"))
if (!strcasecmp(option, "DefScript") || !strcasecmp(option, "PostScript"))
{
option = "PostScript";
option = "Extensions";
}
int nameLen = strlen(option);
if (!strncasecmp(option, "Category", 8) && nameLen > 10 &&
!strcasecmp(option + nameLen - 10, ".DefScript"))
if (!strncasecmp(option, "Category", 8) &&
((nameLen > 10 && !strcasecmp(option + nameLen - 10, ".DefScript")) ||
(nameLen > 11 && !strcasecmp(option + nameLen - 11, ".PostScript"))))
{
option.Replace(".DefScript", ".PostScript");
option.Replace(".DefScript", ".Extensions");
option.Replace(".PostScript", ".Extensions");
}
if (!strncasecmp(option, "Feed", 4) && nameLen > 11 && !strcasecmp(option + nameLen - 11, ".FeedScript"))
{
option.Replace(".FeedScript", ".Extensions");
}
if (!strcasecmp(option, "WriteBufferSize"))
@@ -1625,6 +1668,16 @@ void Options::ConvertOldOption(CString& option, CString& value)
option = "ArticleTimeout";
}
if (!strcasecmp(option, "Retries"))
{
option = "ArticleRetries";
}
if (!strcasecmp(option, "RetryInterval"))
{
option = "ArticleInterval";
}
if (!strcasecmp(option, "CreateBrokenLog"))
{
option = "BrokenLog";
@@ -1696,7 +1749,7 @@ void Options::CheckOptions()
if (sizeof(void*) == 4 && m_parBuffer + m_articleCache > 1900)
{
ConfigError("Options \"ArticleCache\" and \"ParBuffer\" in total cannot use more than 1900MB of memory in 32-Bit mode. Changed to 1500 and 400");
m_articleCache = 1900;
m_articleCache = 1500;
m_parBuffer = 400;
}
@@ -1705,3 +1758,77 @@ void Options::CheckOptions()
ConfigError("Invalid value for option \"UnpackPassFile\": %s. File not found", *m_unpackPassFile);
}
}
void Options::ConvertOldOptions(OptEntries* optEntries)
{
MergeOldScriptOption(optEntries, OPTION_SCANSCRIPT, true);
MergeOldScriptOption(optEntries, OPTION_QUEUESCRIPT, true);
MergeOldScriptOption(optEntries, OPTION_FEEDSCRIPT, false);
}
void Options::MergeOldScriptOption(OptEntries* optEntries, const char* optname, bool mergeCategories)
{
OptEntry* optEntry = optEntries->FindOption(optname);
if (!optEntry || Util::EmptyStr(optEntry->GetValue()))
{
return;
}
OptEntry* extensionsOpt = optEntries->FindOption(OPTION_EXTENSIONS);
if (!extensionsOpt)
{
optEntries->emplace_back(OPTION_EXTENSIONS, "");
extensionsOpt = optEntries->FindOption(OPTION_EXTENSIONS);
}
const char* scriptList = optEntry->GetValue();
Tokenizer tok(scriptList, ",;");
while (const char* scriptName = tok.Next())
{
// merge into global "Extensions"
if (!HasScript(extensionsOpt->m_value, scriptName))
{
if (!extensionsOpt->m_value.Empty())
{
extensionsOpt->m_value.Append(",");
}
extensionsOpt->m_value.Append(scriptName);
}
// merge into categories' "Extensions" (if not empty)
if (mergeCategories)
{
for (OptEntry& opt : optEntries)
{
const char* optname = opt.GetName();
if (!strncasecmp(optname, "category", 8))
{
char* p = (char*)optname + 8;
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".extensions")))
{
if (!opt.m_value.Empty() && !HasScript(opt.m_value, scriptName))
{
opt.m_value.Append(",");
opt.m_value.Append(scriptName);
}
}
}
}
}
}
}
bool Options::HasScript(const char* scriptList, const char* scriptName)
{
Tokenizer tok(scriptList, ",;");
while (const char* scriptName2 = tok.Next())
{
if (!strcasecmp(scriptName2, scriptName))
{
return true;
}
}
return false;
};

View File

@@ -85,6 +85,13 @@ public:
scDeactivateServer,
scFetchFeed
};
enum EPostStrategy
{
ppSequential,
ppBalanced,
ppAggressive,
ppRocket
};
class OptEntry
{
@@ -126,19 +133,19 @@ public:
class Category
{
public:
Category(const char* name, const char* destDir, bool unpack, const char* postScript) :
m_name(name), m_destDir(destDir), m_unpack(unpack), m_postScript(postScript) {}
Category(const char* name, const char* destDir, bool unpack, const char* extensions) :
m_name(name), m_destDir(destDir), m_unpack(unpack), m_extensions(extensions) {}
const char* GetName() { return m_name; }
const char* GetDestDir() { return m_destDir; }
bool GetUnpack() { return m_unpack; }
const char* GetPostScript() { return m_postScript; }
const char* GetExtensions() { return m_extensions; }
NameList* GetAliases() { return &m_aliases; }
private:
CString m_name;
CString m_destDir;
bool m_unpack;
CString m_postScript;
CString m_extensions;
NameList m_aliases;
};
@@ -159,7 +166,7 @@ public:
int level, int group, bool optional) = 0;
virtual void AddFeed(int id, const char* name, const char* url, int interval,
const char* filter, bool backlog, bool pauseNzb, const char* category,
int priority, const char* feedScript) {}
int priority, const char* extensions) {}
virtual void AddTask(int id, int hours, int minutes, int weekDaysBits, ESchedulerCommand command,
const char* param) {}
virtual void SetupFirstStart() {}
@@ -170,9 +177,12 @@ public:
Options(CmdOptList* commandLineOptions, Extender* extender);
~Options();
bool SplitOptionString(const char* option, CString& optName, CString& optValue);
static bool SplitOptionString(const char* option, CString& optName, CString& optValue);
static void ConvertOldOptions(OptEntries* optEntries);
bool GetFatalError() { return m_fatalError; }
GuardedOptEntries GuardOptEntries() { return GuardedOptEntries(&m_optEntries, &m_optEntriesMutex); }
void CreateSchedulerTask(int id, const char* time, const char* weekDays,
ESchedulerCommand command, const char* param);
// Options
const char* GetConfigFilename() { return m_configFilename; }
@@ -200,8 +210,10 @@ public:
bool GetDecode() { return m_decode; };
bool GetAppendCategoryDir() { return m_appendCategoryDir; }
bool GetContinuePartial() { return m_continuePartial; }
int GetRetries() { return m_retries; }
int GetRetryInterval() { return m_retryInterval; }
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; }
@@ -231,15 +243,14 @@ public:
bool GetParRepair() { return m_parRepair; }
EParScan GetParScan() { return m_parScan; }
bool GetParQuick() { return m_parQuick; }
EPostStrategy GetPostStrategy() { return m_postStrategy; }
bool GetParRename() { return m_parRename; }
int GetParBuffer() { return m_parBuffer; }
int GetParThreads() { return m_parThreads; }
bool GetRarRename() { return m_rarRename; }
EHealthCheck GetHealthCheck() { return m_healthCheck; }
const char* GetScriptOrder() { return m_scriptOrder; }
const char* GetPostScript() { return m_postScript; }
const char* GetScanScript() { return m_scanScript; }
const char* GetQueueScript() { return m_queueScript; }
const char* GetFeedScript() { return m_feedScript; }
const char* GetExtensions() { return m_extensions; }
int GetUMask() { return m_umask; }
int GetUpdateInterval() {return m_updateInterval; }
bool GetCursesNzbName() { return m_cursesNzbName; }
@@ -267,6 +278,7 @@ public:
bool GetUnpackPauseQueue() { return m_unpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_extCleanupDisk; }
const char* GetParIgnoreExt() { return m_parIgnoreExt; }
const char* GetUnpackIgnoreExt() { return m_unpackIgnoreExt; }
int GetFeedHistory() { return m_feedHistory; }
bool GetUrlForce() { return m_urlForce; }
int GetTimeCorrection() { return m_timeCorrection; }
@@ -343,8 +355,10 @@ private:
int m_terminateTimeout = 0;
bool m_appendCategoryDir = false;
bool m_continuePartial = false;
int m_retries = 0;
int m_retryInterval = 0;
int m_articleRetries = 0;
int m_articleInterval = 0;
int m_urlRetries = 0;
int m_urlInterval = 0;
bool m_saveQueue = false;
bool m_flushQueue = false;
bool m_dupeCheck = false;
@@ -374,15 +388,14 @@ private:
bool m_parRepair = false;
EParScan m_parScan = psLimited;
bool m_parQuick = true;
EPostStrategy m_postStrategy = ppSequential;
bool m_parRename = false;
int m_parBuffer = 0;
int m_parThreads = 0;
bool m_rarRename = false;
EHealthCheck m_healthCheck = hcNone;
CString m_postScript;
CString m_extensions;
CString m_scriptOrder;
CString m_scanScript;
CString m_queueScript;
CString m_feedScript;
int m_umask = 0;
int m_updateInterval = 0;
bool m_cursesNzbName = false;
@@ -410,6 +423,7 @@ private:
bool m_unpackPauseQueue;
CString m_extCleanupDisk;
CString m_parIgnoreExt;
CString m_unpackIgnoreExt;
int m_feedHistory = 0;
bool m_urlForce = false;
int m_timeCorrection = 0;
@@ -461,7 +475,9 @@ private:
void ConfigError(const char* msg, ...);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *optionName);
void ConvertOldOption(CString& option, CString& value);
static void ConvertOldOption(CString& option, CString& value);
static void MergeOldScriptOption(OptEntries* optEntries, const char* optname, bool mergeCategories);
static bool HasScript(const char* scriptList, const char* scriptName);
};
extern Options* g_Options;

View File

@@ -133,9 +133,8 @@ void Scheduler::CheckTasks()
}
bool weekDayOK = task->m_weekDaysBits == 0 || (task->m_weekDaysBits & (1 << (weekDay - 1)));
bool doTask = weekDayOK && localLastCheck < appoint && appoint <= localCurrent;
//debug("TEMP: 1) m_tLastCheck=%i, tLocalCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tLocalCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
bool doTask = (task->m_hours >= 0 && weekDayOK && localLastCheck < appoint && appoint <= localCurrent) ||
(task->m_hours == Task::STARTUP_TASK && task->m_lastExecuted == 0);
if (doTask)
{
@@ -162,6 +161,8 @@ void Scheduler::ExecuteTask(Task* task)
"Pause Scan", "Unpause Scan", "Enable Server", "Disable Server", "Fetch Feed" };
debug("Executing scheduled command: %s", commandName[task->m_command]);
bool executeProcess = m_executeProcess || task->m_hours == Task::STARTUP_TASK;
switch (task->m_command)
{
case scDownloadRate:
@@ -190,9 +191,9 @@ void Scheduler::ExecuteTask(Task* task)
m_pauseScanChanged = true;
break;
case scScript:
case scExtensions:
case scProcess:
if (m_executeProcess)
if (executeProcess)
{
SchedulerScriptController::StartScript(task->m_param, task->m_command == scProcess, task->m_id);
}
@@ -204,7 +205,7 @@ void Scheduler::ExecuteTask(Task* task)
break;
case scFetchFeed:
if (m_executeProcess)
if (executeProcess)
{
FetchFeed(task->m_param);
break;

View File

@@ -35,7 +35,7 @@ public:
scPausePostProcess,
scUnpausePostProcess,
scDownloadRate,
scScript,
scExtensions,
scProcess,
scPauseScan,
scUnpauseScan,
@@ -52,7 +52,7 @@ public:
m_id(id), m_hours(hours), m_minutes(minutes),
m_weekDaysBits(weekDaysBits), m_command(command), m_param(param) {}
friend class Scheduler;
static const int STARTUP_TASK = -1;
private:
int m_id;
int m_hours;

View File

@@ -60,6 +60,9 @@
#ifdef ENABLE_TESTS
#include "TestMain.h"
#endif
#ifndef DISABLE_NSERV
#include "NServMain.h"
#endif
// Prototypes
void RunMain();
@@ -127,6 +130,16 @@ int main(int argc, char *argv[], char *argp[])
TestCleanup();
#endif
if (argc > 1 && (!strcmp(argv[1], "--nserv")))
{
#ifndef DISABLE_NSERV
return NServMain(argc, argv);
#else
printf("ERROR: Could not start NServ, the program was compiled without NServ\n");
return 1;
#endif
}
#ifdef WIN32
InstallUninstallServiceCheck(argc, argv);
#endif
@@ -138,6 +151,7 @@ int main(int argc, char *argv[], char *argp[])
{
if (!strcmp(argv[i], "-D"))
{
AllocConsole(); // needed for sending CTRL+BREAK signal to child processes
StartService(RunMain);
return 0;
}
@@ -394,7 +408,7 @@ void NZBGet::BootConfig()
}
m_serverPool->SetTimeout(m_options->GetArticleTimeout());
m_serverPool->SetRetryInterval(m_options->GetRetryInterval());
m_serverPool->SetRetryInterval(m_options->GetArticleInterval());
m_scriptConfig->InitOptions();
}
@@ -865,16 +879,12 @@ void NZBGet::Daemonize()
// obtain a new process group
setsid();
// close all descriptors
for (int i = getdtablesize(); i >= 0; --i)
{
close(i);
}
// handle standart I/O
int d = open("/dev/null", O_RDWR);
dup(d);
dup(d);
dup2(d, 0);
dup2(d, 1);
dup2(d, 2);
close(d);
// set up lock-file
int lfp = -1;

View File

@@ -196,6 +196,7 @@ using namespace MSXML;
#include <iostream>
#include <fstream>
#include <memory>
#include <functional>
#ifdef HAVE_LIBGNUTLS
#ifdef WIN32
@@ -210,6 +211,9 @@ typedef int pid_t;
#ifdef NEED_GCRYPT_LOCKING
#include <gcrypt.h>
#endif /* NEED_GCRYPT_LOCKING */
#include <nettle/sha.h>
#include <nettle/pbkdf2.h>
#include <nettle/aes.h>
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
@@ -318,12 +322,16 @@ typedef int pid_t;
#ifdef HAVE_STDINT_H
typedef uint8_t uint8;
typedef int16_t int16;
typedef uint16_t uint16;
typedef uint32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
#else
typedef unsigned char uint8;
typedef signed short int16;
typedef unsigned short uint16;
typedef signed int int32;
typedef unsigned int uint32;
typedef signed long long int64;

View File

@@ -63,8 +63,8 @@ void ArticleDownloader::SetInfoName(const char* infoName)
- if download fails with error "Not-Found" (article or group not found) or with CRC error,
add the server to failed server list;
- if download fails with general failure error (article incomplete, other unknown error
codes), try the same server again as many times as defined by option <Retries>; if all attempts
fail, add the server to failed server list;
codes), try the same server again as many times as defined by option <ArticleRetries>;
if all attempts fail, add the server to failed server list;
- if all servers from current level were tried, increase level;
- if all servers from all levels were tried, break the loop with failure status.
<end-loop>
@@ -80,7 +80,7 @@ void ArticleDownloader::Run()
m_articleWriter.Prepare();
EStatus status = adFailed;
int retries = g_Options->GetRetries() > 0 ? g_Options->GetRetries() : 1;
int retries = g_Options->GetArticleRetries() > 0 ? g_Options->GetArticleRetries() : 1;
int remainedRetries = retries;
ServerPool::RawServerList failedServers;
failedServers.reserve(g_ServerPool->GetServers()->size());

View File

@@ -0,0 +1,146 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "NServFrontend.h"
#include "Util.h"
NServFrontend::NServFrontend()
{
#ifdef WIN32
m_console = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
}
void NServFrontend::Run()
{
while (!IsStopped())
{
Update();
usleep(100 * 1000);
}
// Printing the last messages
Update();
}
void NServFrontend::Update()
{
BeforePrint();
{
GuardedMessageList messages = g_Log->GuardMessages();
if (!messages->empty())
{
Message& firstMessage = messages->front();
int start = m_neededLogFirstId - firstMessage.GetId() + 1;
if (start < 0)
{
PrintSkip();
start = 0;
}
for (uint32 i = (uint32)start; i < messages->size(); i++)
{
PrintMessage(messages->at(i));
m_neededLogFirstId = messages->at(i).GetId();
}
}
}
fflush(stdout);
}
void NServFrontend::BeforePrint()
{
if (m_needGoBack)
{
// go back one line
#ifdef WIN32
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
GetConsoleScreenBufferInfo(m_console, &BufInfo);
BufInfo.dwCursorPosition.Y--;
SetConsoleCursorPosition(m_console, BufInfo.dwCursorPosition);
#else
printf("\r\033[1A");
#endif
m_needGoBack = false;
}
}
void NServFrontend::PrintMessage(Message& message)
{
#ifdef WIN32
switch (message.GetKind())
{
case Message::mkDebug:
SetConsoleTextAttribute(m_console, 8);
printf("[DEBUG] ");
break;
case Message::mkError:
SetConsoleTextAttribute(m_console, 4);
printf("[ERROR] ");
break;
case Message::mkWarning:
SetConsoleTextAttribute(m_console, 5);
printf("[WARNING]");
break;
case Message::mkInfo:
SetConsoleTextAttribute(m_console, 2);
printf("[INFO] ");
break;
case Message::mkDetail:
SetConsoleTextAttribute(m_console, 2);
printf("[DETAIL]");
break;
}
SetConsoleTextAttribute(m_console, 7);
CString msg = message.GetText();
CharToOem(msg, msg);
printf(" %s\n", *msg);
#else
const char* msg = message.GetText();
switch (message.GetKind())
{
case Message::mkDebug:
printf("[DEBUG] %s\033[K\n", msg);
break;
case Message::mkError:
printf("\033[31m[ERROR]\033[39m %s\033[K\n", msg);
break;
case Message::mkWarning:
printf("\033[35m[WARNING]\033[39m %s\033[K\n", msg);
break;
case Message::mkInfo:
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
break;
case Message::mkDetail:
printf("\033[32m[DETAIL]\033[39m %s\033[K\n", msg);
break;
}
#endif
}
void NServFrontend::PrintSkip()
{
#ifdef WIN32
printf(".....\n");
#else
printf(".....\033[K\n");
#endif
}

View File

@@ -0,0 +1,48 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 NSERVFRONTEND_H
#define NSERVFRONTEND_H
#include "Thread.h"
#include "Log.h"
class NServFrontend : public Thread
{
public:
NServFrontend();
private:
uint32 m_neededLogEntries = 0;
uint32 m_neededLogFirstId = 0;
bool m_needGoBack = false;
#ifdef WIN32
HANDLE m_console;
#endif
void Run();
void Update();
void BeforePrint();
void PrintMessage(Message& message);
void PrintSkip();
};
#endif

246
daemon/nserv/NServMain.cpp Normal file
View File

@@ -0,0 +1,246 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "Thread.h"
#include "Connection.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
#include "NServFrontend.h"
#include "NntpServer.h"
#include "NzbGenerator.h"
#include "Options.h"
struct NServOpts
{
CString dataDir;
CString cacheDir;
CString bindAddress;
int firstPort;
int instances;
CString logFile;
CString secureCert;
CString secureKey;
BString<1024> logOpt;
bool generateNzb;
int segmentSize;
bool quit;
NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts);
};
void NServPrintUsage(const char* com);
int NServMain(int argc, char* argv[])
{
Log log;
info("NServ %s (Test NNTP server)", Util::VersionRevision());
Options::CmdOptList cmdOpts;
NServOpts opts(argc, argv, cmdOpts);
if (opts.dataDir.Empty())
{
NServPrintUsage(argv[0]);
return 1;
}
if (!FileSystem::DirectoryExists(opts.dataDir))
{
// dataDir does not exist. Let's find out a bit more, and report:
if (FileSystem::FileExists(opts.dataDir))
{
error("Specified data-dir %s is not a directory, but a file", *opts.dataDir );
} else {
error("Specified data-dir %s does not exist", *opts.dataDir );
}
}
Options options(&cmdOpts, nullptr);
log.InitOptions();
Thread::Init();
Connection::Init();
#ifndef DISABLE_TLS
TlsSocket::Init();
#endif
NServFrontend frontend;
frontend.Start();
if (opts.generateNzb)
{
NzbGenerator gen(opts.dataDir, opts.segmentSize);
gen.Execute();
if (opts.quit)
{
return 0;
}
}
CString errmsg;
if (opts.cacheDir && !FileSystem::ForceDirectories(opts.cacheDir, errmsg))
{
error("Could not create directory %s: %s", *opts.cacheDir, *errmsg);
}
std::vector<std::unique_ptr<NntpServer>> instances;
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));
instances.back()->Start();
}
info("Press Ctrl+C to quit");
while (getchar()) usleep(1000*200);
for (std::unique_ptr<NntpServer>& serv: instances)
{
serv->Stop();
}
frontend.Stop();
bool hasRunning = false;
do
{
hasRunning = frontend.IsRunning();
for (std::unique_ptr<NntpServer>& serv : instances)
{
hasRunning |= serv->IsRunning();
}
usleep(50 * 1000);
} while (hasRunning);
return 0;
}
void NServPrintUsage(const char* com)
{
printf("Usage:\n"
" %s --nserv -d <data-dir> [optional switches] \n"
" -d <data-dir> - directory whose files will be served\n"
" Optional switches:\n"
" -c <cache-dir> - directory to store encoded articles\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"
" -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));
}
NServOpts::NServOpts(int argc, char* argv[], Options::CmdOptList& cmdOpts)
{
instances = 1;
bindAddress = "0.0.0.0";
firstPort = 6791;
generateNzb = false;
segmentSize = 500000;
quit = false;
int verbosity = 2;
char short_options[] = "b:c:d:l:p:i:s:v:z:q";
optind = 2;
while (true)
{
int c = getopt(argc, argv, short_options);
if (c == -1) break;
switch (c)
{
case 'd':
dataDir = optind > argc ? nullptr : argv[optind - 1];
break;
case 'c':
cacheDir = optind > argc ? nullptr : argv[optind - 1];
break;
case 'l':
logFile = optind > argc ? nullptr : argv[optind - 1];
break;
case 'b':
bindAddress= optind > argc ? "0.0.0.0" : argv[optind - 1];
break;
case 'p':
firstPort = atoi(optind > argc ? "6791" : argv[optind - 1]);
break;
case 's':
secureCert = optind > argc ? nullptr : argv[optind - 1];
optind++;
secureKey = optind > argc ? nullptr : argv[optind - 1];
break;
case 'i':
instances = atoi(optind > argc ? "1" : argv[optind - 1]);
break;
case 'v':
verbosity = atoi(optind > argc ? "1" : argv[optind - 1]);
break;
case 'z':
generateNzb = true;
segmentSize = atoi(optind > argc ? "500000" : argv[optind - 1]);
break;
case 'q':
quit = true;
break;
}
}
if (logFile.Empty())
{
cmdOpts.push_back("WriteLog=none");
}
else
{
cmdOpts.push_back("WriteLog=append");
logOpt.Format("LogFile=%s", *logFile);
cmdOpts.push_back(logOpt);
}
if (verbosity < 1)
{
cmdOpts.push_back("InfoTarget=none");
cmdOpts.push_back("WarningTarget=none");
cmdOpts.push_back("ErrorTarget=none");
}
if (verbosity < 2)
{
cmdOpts.push_back("DetailTarget=none");
}
if (verbosity > 2)
{
cmdOpts.push_back("DebugTarget=both");
}
}

26
daemon/nserv/NServMain.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 NSERVMAIN_H
#define NSERVMAIN_H
int NServMain(int argc, char * argv[]);
#endif

321
daemon/nserv/NntpServer.cpp Normal file
View File

@@ -0,0 +1,321 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "NntpServer.h"
#include "Log.h"
#include "Util.h"
#include "YEncoder.h"
class NntpProcessor : public Thread
{
public:
NntpProcessor(int id, int serverId, const char* dataDir, const char* cacheDir,
const char* secureCert, const char* secureKey) :
m_id(id), m_serverId(serverId), m_dataDir(dataDir), m_cacheDir(cacheDir),
m_secureCert(secureCert), m_secureKey(secureKey) {}
~NntpProcessor() { m_connection->Disconnect(); }
virtual void Run();
void SetConnection(std::unique_ptr<Connection>&& connection) { m_connection = std::move(connection); }
private:
int m_id;
int m_serverId;
std::unique_ptr<Connection> m_connection;
const char* m_dataDir;
const char* m_cacheDir;
const char* m_secureCert;
const char* m_secureKey;
const char* m_messageid;
CString m_filename;
int m_part;
int64 m_offset;
int m_size;
bool m_sendHeaders;
void ServArticle();
void SendSegment();
bool ServerInList(const char* servList);
};
void NntpServer::Run()
{
debug("Entering NntpServer-loop");
info("Listening on port %i", m_port);
int num = 1;
while (!IsStopped())
{
bool bind = true;
if (!m_connection)
{
m_connection = std::make_unique<Connection>(m_host, m_port, m_secureCert);
m_connection->SetTimeout(10);
m_connection->SetSuppressErrors(false);
bind = m_connection->Bind();
}
// Accept connections and store the new Connection
std::unique_ptr<Connection> acceptedConnection;
if (bind)
{
acceptedConnection = m_connection->Accept();
}
if (!bind || !acceptedConnection)
{
// Server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
{
break;
}
m_connection.reset();
usleep(500 * 1000);
continue;
}
NntpProcessor* commandThread = new NntpProcessor(num++, m_id,
m_dataDir, m_cacheDir, m_secureCert, m_secureKey);
commandThread->SetAutoDestroy(true);
commandThread->SetConnection(std::move(acceptedConnection));
commandThread->Start();
}
if (m_connection)
{
m_connection->Disconnect();
}
debug("Exiting NntpServer-loop");
}
void NntpServer::Stop()
{
Thread::Stop();
if (m_connection)
{
m_connection->SetSuppressErrors(true);
m_connection->Cancel();
#ifdef WIN32
m_connection->Disconnect();
#endif
}
}
void NntpProcessor::Run()
{
m_connection->SetSuppressErrors(false);
#ifndef DISABLE_TLS
if (m_secureCert && !m_connection->StartTls(false, m_secureCert, m_secureKey))
{
error("Could not establish secure connection to nntp-client: Start TLS failed");
return;
}
#endif
m_connection->WriteLine("200 Welcome (NServ)\r\n");
CharBuffer buf(1024);
int bytesRead = 0;
while (CString line = m_connection->ReadLine(buf, 1024, &bytesRead))
{
line.TrimRight();
detail("[%i] Received: %s", m_id, *line);
if (!strncasecmp(line, "ARTICLE ", 8))
{
m_messageid = line + 8;
m_sendHeaders = true;
ServArticle();
}
else if (!strncasecmp(line, "BODY ", 5))
{
m_messageid = line + 5;
m_sendHeaders = false;
ServArticle();
}
else if (!strncasecmp(line, "GROUP ", 6))
{
m_connection->WriteLine(CString::FormatStr("211 0 0 0 %s\r\n", line + 7));
}
else if (!strncasecmp(line, "AUTHINFO ", 9))
{
m_connection->WriteLine("281 Authentication accepted\r\n");
}
else if (!strcasecmp(line, "QUIT"))
{
detail("[%i] Closing connection", m_id);
m_connection->WriteLine("205 Connection closing\r\n");
break;
}
else
{
warn("[%i] Unknown command: %s", m_id, *line);
m_connection->WriteLine("500 Unknown command\r\n");
}
}
m_connection->SetGracefull(true);
m_connection->Disconnect();
}
/*
Message-id format:
<file-path-relative-to-dataDir?xxx=yyy:zzz!1,2,3>
where:
xxx - part number (integer)
xxx - offset from which to read the files (integer)
yyy - size of file block to return (integer)
1,2,3 - list of server ids, which have the article (optional),
if the list is given and current server is not in the list
the "article not found"-error is returned.
Examples:
<parchecker/testfile.dat?1=0:50000> - return first 50000 bytes starting from beginning
<parchecker/testfile.dat?2=50000:50000> - return 50000 bytes starting from offset 50000
<parchecker/testfile.dat?2=50000:50000!2> - article is missing on server 1
*/
void NntpProcessor::ServArticle()
{
detail("[%i] Serving: %s", m_id, m_messageid);
bool ok = false;
const char* from = strchr(m_messageid, '?');
const char* off = strchr(m_messageid, '=');
const char* to = strchr(m_messageid, ':');
const char* end = strchr(m_messageid, '>');
const char* serv = strchr(m_messageid, '!');
if (from && off && to && end)
{
m_filename.Set(m_messageid + 1, from - m_messageid - 1);
m_part = atoi(from + 1);
m_offset = atoll(off + 1);
m_size = atoi(to + 1);
ok = !serv || ServerInList(serv + 1);
if (ok)
{
SendSegment();
return;
}
if (!ok)
{
m_connection->WriteLine("430 No Such Article Found\r\n");
}
}
else
{
m_connection->WriteLine("430 No Such Article Found (invalid message id format)\r\n");
}
}
bool NntpProcessor::ServerInList(const char* servList)
{
Tokenizer tok(servList, ",");
while (const char* servid = tok.Next())
{
if (atoi(servid) == m_serverId)
{
return true;
}
}
return false;
}
void NntpProcessor::SendSegment()
{
detail("[%i] Sending segment %s (%i=%lli:%i)", m_id, *m_filename, m_part, (long long)m_offset, m_size);
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> cacheFullFilename("%s/%s", *cacheFileDir, *cacheFileName);
DiskFile cacheFile;
bool readCache = m_cacheDir && cacheFile.Open(cacheFullFilename, DiskFile::omRead);
bool writeCache = m_cacheDir && !readCache;
CString errmsg;
if (writeCache && !FileSystem::ForceDirectories(cacheFileDir, errmsg))
{
error("Could not create directory %s: %s", *cacheFileDir, *errmsg);
}
if (writeCache && !cacheFile.Open(cacheFullFilename, DiskFile::omWrite))
{
error("Could not create file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage());
}
if (!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)
{
if (writeCache)
{
cacheFile.Write(buf, size);
}
con->Send(buf, size);
});
if (!readCache && !encoder.OpenFile(errmsg))
{
m_connection->WriteLine(CString::FormatStr("403 %s\r\n", *errmsg));
return;
}
m_connection->WriteLine(CString::FormatStr("%i, 0 %s\r\n", m_sendHeaders ? 222 : 220, m_messageid));
if (m_sendHeaders)
{
m_connection->WriteLine(CString::FormatStr("Message-ID: %s\r\n", m_messageid));
m_connection->WriteLine(CString::FormatStr("Subject: \"%s\"\r\n", FileSystem::BaseFileName(m_filename)));
m_connection->WriteLine("\r\n");
}
if (readCache)
{
cacheFile.Seek(0, DiskFile::soEnd);
int size = (int)cacheFile.Position();
CharBuffer buf(size);
cacheFile.Seek(0);
if (cacheFile.Read((char*)buf, size) != size)
{
error("Could not read file %s: %s", *cacheFullFilename, *FileSystem::GetLastErrorMessage());
}
m_connection->Send(buf, size);
}
else
{
encoder.WriteSegment();
}
m_connection->WriteLine(".\r\n");
}

48
daemon/nserv/NntpServer.h Normal file
View File

@@ -0,0 +1,48 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 NNTPSERVER_H
#define NNTPSERVER_H
#include "Thread.h"
#include "Connection.h"
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) :
m_id(id), m_host(host), m_port(port), m_secureCert(secureCert),
m_secureKey(secureKey), m_dataDir(dataDir), m_cacheDir(cacheDir) {}
virtual void Run();
virtual void Stop();
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;
};
#endif

View File

@@ -0,0 +1,131 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "NzbGenerator.h"
#include "Util.h"
#include "FileSystem.h"
#include "Log.h"
void NzbGenerator::Execute()
{
info("Generating nzbs for %s", *m_dataDir);
DirBrowser dir(m_dataDir);
while (const char* filename = dir.Next())
{
BString<1024> fullFilename("%s%c%s", *m_dataDir, PATH_SEPARATOR, filename);
int len = strlen(filename);
if (len > 4 && !strcasecmp(filename + len - 4, ".nzb"))
{
// skip nzb-files
continue;
}
GenerateNzb(fullFilename);
}
info("Nzb generation finished");
}
void NzbGenerator::GenerateNzb(const char* path)
{
BString<1024> nzbFilename("%s%c%s.nzb", *m_dataDir, PATH_SEPARATOR, FileSystem::BaseFileName(path));
if (FileSystem::FileExists(nzbFilename))
{
return;
}
info("Generating nzb for %s", FileSystem::BaseFileName(path));
DiskFile outfile;
if (!outfile.Open(nzbFilename, DiskFile::omWrite))
{
error("Could not create file %s", *nzbFilename);
return;
}
outfile.Print("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
outfile.Print("<!DOCTYPE nzb PUBLIC \"-//newzBin//DTD NZB 1.0//EN\" \"http://www.newzbin.com/DTD/nzb/nzb-1.0.dtd\">\n");
outfile.Print("<nzb xmlns=\"http://www.newzbin.com/DTD/2003/nzb\">\n");
bool isDir = FileSystem::DirectoryExists(path);
if (isDir)
{
AppendDir(outfile, path);
}
else
{
AppendFile(outfile, path, nullptr);
}
outfile.Print("</nzb>\n");
outfile.Close();
}
void NzbGenerator::AppendDir(DiskFile& outfile, const char* path)
{
DirBrowser dir(path);
while (const char* filename = dir.Next())
{
BString<1024> fullFilename("%s%c%s", path, PATH_SEPARATOR, filename);
bool isDir = FileSystem::DirectoryExists(fullFilename);
if (!isDir)
{
AppendFile(outfile, fullFilename, FileSystem::BaseFileName(path));
}
}
}
void NzbGenerator::AppendFile(DiskFile& outfile, const char* filename, const char* relativePath)
{
detail("Processing %s", FileSystem::BaseFileName(filename));
int64 fileSize = FileSystem::FileSize(filename);
time_t timestamp = Util::CurrentTime();
int segmentCount = (int)((fileSize + m_segmentSize - 1) / m_segmentSize);
outfile.Print("<file poster=\"nserv\" date=\"%i\" subject=\"&quot;%s&quot; yEnc (1/%i)\">\n",
(int)timestamp, FileSystem::BaseFileName(filename), segmentCount);
outfile.Print("<groups>\n");
outfile.Print("<group>alt.binaries.test</group>\n");
outfile.Print("</groups>\n");
outfile.Print("<segments>\n");
int64 segOffset = 0;
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,
relativePath ? relativePath : "",
relativePath ? "/" : "",
FileSystem::BaseFileName(filename), segno, (long long)segOffset, (int)segSize);
segOffset += segSize;
}
outfile.Print("</segments>\n");
outfile.Print("</file>\n");
}

View File

@@ -0,0 +1,43 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 NZBGENERATOR_H
#define NZBGENERATOR_H
#include "NString.h"
#include "FileSystem.h"
class NzbGenerator
{
public:
NzbGenerator(const char* dataDir, int segmentSize) :
m_dataDir(dataDir), m_segmentSize(segmentSize) {};
void Execute();
private:
CString m_dataDir;
int m_segmentSize;
void GenerateNzb(const char* path);
void AppendFile(DiskFile& outfile, const char* filename, const char* relativePath);
void AppendDir(DiskFile& outfile, const char* path);
};
#endif

131
daemon/nserv/YEncoder.cpp Normal file
View File

@@ -0,0 +1,131 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "YEncoder.h"
#include "Util.h"
#include "FileSystem.h"
#include "Log.h"
bool YEncoder::OpenFile(CString& errmsg)
{
if (m_size < 0)
{
errmsg = "Invalid segment size";
return false;
}
if (!m_diskfile.Open(m_filename, DiskFile::omRead) || !m_diskfile.Seek(0, DiskFile::soEnd))
{
errmsg = "File not found";
return false;
}
m_fileSize = m_diskfile.Position();
if (m_size == 0)
{
m_size = (int)(m_fileSize - m_offset + 1);
}
if (m_fileSize < m_offset + m_size)
{
errmsg = "Invalid segment size";
return false;
}
if (!m_diskfile.Seek(m_offset))
{
errmsg = "Invalid segment offset";
return false;
}
return true;
}
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)));
uint32 crc = 0xFFFFFFFF;
CharBuffer inbuf(std::min(m_size, 16 * 1024 * 1024));
int lnsz = 0;
char* out = (char*)outbuf + outbuf.Length();
while (m_diskfile.Position() < m_offset + m_size)
{
int64 needBytes = std::min((int64)inbuf.Size(), m_offset + m_size - m_diskfile.Position());
int64 readBytes = m_diskfile.Read(inbuf, needBytes);
bool lastblock = m_diskfile.Position() == m_offset + m_size;
if (readBytes == 0)
{
return; // error;
}
crc = Util::Crc32m(crc, (uchar*)(const char*)inbuf, (int)readBytes);
char* in = inbuf;
while (readBytes > 0)
{
char ch = *in++;
readBytes--;
ch = (char)(((uchar)(ch) + 42) % 256);
if (ch == '\0' || ch == '\n' || ch == '\r' || ch == '=' || ch == ' ' || ch == '\t')
{
*out++ = '=';
lnsz++;
ch = (char)(((uchar)ch + 64) % 256);
}
if (ch == '.' && lnsz == 0)
{
*out++ = '.';
lnsz++;
}
*out++ = ch;
lnsz++;
if (lnsz >= 128 || (readBytes == 0 && lastblock))
{
*out++ = '\r';
*out++ = '\n';
lnsz += 2;
outbuf.SetLength(outbuf.Length() + lnsz);
if (outbuf.Length() > outbuf.Capacity() - 200)
{
m_writeFunc(outbuf, outbuf.Length());
outbuf.SetLength(0);
out = (char*)outbuf;
}
lnsz = 0;
}
}
}
crc ^= 0xFFFFFFFF;
m_diskfile.Close();
outbuf.Append(CString::FormatStr("=yend size=%i part=0 pcrc32=%08x\r\n", m_size, (unsigned int)crc));
m_writeFunc(outbuf, outbuf.Length());
}

47
daemon/nserv/YEncoder.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 YENCODER_H
#define YENCODER_H
#include "NString.h"
#include "FileSystem.h"
class YEncoder
{
public:
typedef std::function<void(const char* buf, int size)> WriteFunc;
YEncoder(const char* filename, int part, int64 offset, int size, WriteFunc writeFunc) :
m_filename(filename), m_part(part), m_offset(offset), m_size(size), m_writeFunc(writeFunc) {};
bool OpenFile(CString& errmsg);
void WriteSegment();
private:
DiskFile m_diskfile;
CString m_filename;
int m_part;
int64 m_offset;
int m_size;
int64 m_fileSize;
WriteFunc m_writeFunc;
};
#endif

View File

@@ -76,7 +76,6 @@ void MoveController::Run()
m_postInfo->GetNzbInfo()->SetMoveStatus(NzbInfo::msFailure);
}
m_postInfo->SetStage(PostInfo::ptQueued);
m_postInfo->SetWorking(false);
}
@@ -182,7 +181,6 @@ void CleanupController::Run()
m_postInfo->GetNzbInfo()->SetCleanupStatus(NzbInfo::csFailure);
}
m_postInfo->SetStage(PostInfo::ptQueued);
m_postInfo->SetWorking(false);
}

View File

@@ -55,7 +55,9 @@ class RepairThread;
class Repairer : public Par2::Par2Repairer, public ParChecker::AbstractRepairer
{
public:
Repairer(ParChecker* owner) { m_owner = owner; }
Repairer(ParChecker* owner):
Par2::Par2Repairer(owner->m_parCout, owner->m_parCerr),
m_owner(owner), commandLine(owner->m_parCout, owner->m_parCerr) {}
Par2::Result PreProcess(const char *parFilename);
Par2::Result Process(bool dorepair);
virtual Repairer* GetRepairer() { return this; }
@@ -364,13 +366,6 @@ int ParChecker::StreamBuf::overflow(int ch)
}
ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
Cleanup();
}
void ParChecker::Cleanup()
{
m_repairer.reset();
@@ -381,14 +376,11 @@ void ParChecker::Cleanup()
m_errMsg = nullptr;
}
void ParChecker::Run()
void ParChecker::Execute()
{
Par2::cout.rdbuf(&m_parOutStream);
Par2::cerr.rdbuf(&m_parErrStream);
m_status = RunParCheckAll();
if (m_status == psRepairNotNeeded && m_parQuick && m_forceRepair && !m_cancelled)
if (m_status == psRepairNotNeeded && m_parQuick && m_forceRepair && !IsStopped())
{
PrintMessage(Message::mkInfo, "Performing full par-check for %s", *m_nzbName);
m_parQuick = false;
@@ -396,9 +388,6 @@ void ParChecker::Run()
}
Completed();
Par2::cout.rdbuf(&Par2::nullStreamBuf);
Par2::cerr.rdbuf(&Par2::nullStreamBuf);
}
ParChecker::EStatus ParChecker::RunParCheckAll()
@@ -411,19 +400,18 @@ ParChecker::EStatus ParChecker::RunParCheckAll()
}
EStatus allStatus = psRepairNotNeeded;
m_cancelled = false;
m_parFull = true;
for (CString& parFilename : fileList)
{
debug("Found par: %s", *parFilename);
if (!IsStopped() && !m_cancelled)
if (!IsStopped())
{
BString<1024> fullParFilename( "%s%c%s", *m_destDir, (int)PATH_SEPARATOR, *parFilename);
int baseLen = 0;
ParParser::ParseParFilename(parFilename, &baseLen, nullptr);
ParParser::ParseParFilename(parFilename, true, &baseLen, nullptr);
BString<1024> infoName;
infoName.Set(parFilename, baseLen);
@@ -571,7 +559,7 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* parFilename)
}
}
if (m_cancelled)
if (IsStopped())
{
if (m_stage >= ptRepairing)
{
@@ -688,7 +676,7 @@ bool ParChecker::LoadMainParBak()
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool queuedParFilesChanged = false;
while (!queuedParFilesChanged && !IsStopped() && !m_cancelled)
while (!queuedParFilesChanged && !IsStopped())
{
{
Guard guard(m_queuedParFilesMutex);
@@ -754,7 +742,7 @@ int ParChecker::ProcessMorePars()
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool queuedParFilesChanged = false;
while (!queuedParFilesChanged && !IsStopped() && !m_cancelled)
while (!queuedParFilesChanged && !IsStopped())
{
{
Guard guard(m_queuedParFilesMutex);
@@ -765,7 +753,7 @@ int ParChecker::ProcessMorePars()
}
}
if (IsStopped() || m_cancelled)
if (IsStopped())
{
break;
}
@@ -973,7 +961,7 @@ bool ParChecker::AddExtraFiles(bool onlyMissing, bool externalDir, const char* d
// adding files one by one until all missing files are found
while (!IsStopped() && !m_cancelled && extrafiles.size() > 0)
while (!IsStopped() && extrafiles.size() > 0)
{
std::list<Par2::CommandLine::ExtraFile> extrafiles1;
extrafiles1.splice(extrafiles1.end(), extrafiles, extrafiles.begin());
@@ -1199,7 +1187,6 @@ void ParChecker::CheckEmptyFiles()
void ParChecker::Cancel()
{
GetRepairer()->cancelled = true;
m_cancelled = true;
QueueChanged();
}
@@ -1214,7 +1201,7 @@ void ParChecker::WriteBrokenLog(EStatus status)
{
if (status == psFailed)
{
if (m_cancelled)
if (IsStopped())
{
file.Print("Repair cancelled for %s\n", *m_infoName);
}

View File

@@ -25,13 +25,12 @@
#include "NString.h"
#include "Container.h"
#include "Thread.h"
#include "FileSystem.h"
#include "Log.h"
class Repairer;
class ParChecker : public Thread
class ParChecker
{
public:
enum EStatus
@@ -57,8 +56,7 @@ public:
virtual Repairer* GetRepairer() = 0;
};
virtual ~ParChecker();
virtual void Run();
void Execute();
void SetDestDir(const char* destDir) { m_destDir = destDir; }
const char* GetParFilename() { return m_parFilename; }
const char* GetInfoName() { return m_infoName; }
@@ -74,7 +72,6 @@ public:
void AddParFile(const char* parFilename);
void QueueChanged();
void Cancel();
bool GetCancelled() { return m_cancelled; }
protected:
class Segment
@@ -129,6 +126,7 @@ protected:
*/
virtual bool RequestMorePars(int blockNeeded, int* blockFound) = 0;
virtual void UpdateProgress() {}
virtual bool IsStopped() { return false; };
virtual void Completed() {}
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3) {}
virtual void RegisterParredFile(const char* filename) {}
@@ -186,6 +184,8 @@ private:
DupeSourceList m_dupeSources;
StreamBuf m_parOutStream{this, Message::mkDetail};
StreamBuf m_parErrStream{this, Message::mkError};
std::ostream m_parCout{&m_parOutStream};
std::ostream m_parCerr{&m_parErrStream};
// "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.

View File

@@ -33,7 +33,7 @@ bool ParParser::FindMainPars(const char* path, ParFileList* fileList)
while (const char* filename = dir.Next())
{
int baseLen = 0;
if (ParseParFilename(filename, &baseLen, nullptr))
if (ParseParFilename(filename, true, &baseLen, nullptr))
{
if (!fileList)
{
@@ -62,13 +62,13 @@ bool ParParser::FindMainPars(const char* path, ParFileList* fileList)
bool ParParser::SameParCollection(const char* filename1, const char* filename2)
{
int baseLen1 = 0, baseLen2 = 0;
return ParseParFilename(filename1, &baseLen1, nullptr) &&
ParseParFilename(filename2, &baseLen2, nullptr) &&
return ParseParFilename(filename1, false, &baseLen1, nullptr) &&
ParseParFilename(filename2, false, &baseLen2, nullptr) &&
baseLen1 == baseLen2 &&
!strncasecmp(filename1, filename2, baseLen1);
}
bool ParParser::ParseParFilename(const char* parFilename, int* baseNameLen, int* blocks)
bool ParParser::ParseParFilename(const char* parFilename, bool confirmedFilename, int* baseNameLen, int* blocks)
{
BString<1024> filename = parFilename;
for (char* p = filename; *p; p++) *p = tolower(*p); // convert string to lowercase
@@ -79,10 +79,13 @@ bool ParParser::ParseParFilename(const char* parFilename, int* baseNameLen, int*
return false;
}
// find last occurence of ".par2" and trim filename after it
char* end = filename;
while (char* p = strstr(end, ".par2")) end = p + 5;
*end = '\0';
if (!confirmedFilename)
{
// find last occurence of ".par2" and trim filename after it
char* end = filename;
while (char* p = strstr(end, ".par2")) end = p + 5;
*end = '\0';
}
len = strlen(filename);
if (len < 6)

View File

@@ -30,7 +30,7 @@ public:
typedef std::vector<CString> ParFileList;
static bool FindMainPars(const char* path, ParFileList* fileList);
static bool ParseParFilename(const char* parFilename, int* baseNameLen, int* blocks);
static bool ParseParFilename(const char* parFilename, bool confirmedFilename, int* baseNameLen, int* blocks);
static bool SameParCollection(const char* filename1, const char* filename2);
};

View File

@@ -36,31 +36,17 @@
class ParRenamerRepairer : public Par2::Par2Repairer
{
public:
ParRenamerRepairer() : Par2::Par2Repairer(m_nout, m_nout) {};
friend class ParRenamer;
private:
class NullStreamBuf : public std::streambuf {};
NullStreamBuf m_nullbuf;
std::ostream m_nout{&m_nullbuf};
};
void ParRenamer::Cleanup()
void ParRenamer::Execute()
{
m_dirList.clear();
m_fileHashList.clear();
}
void ParRenamer::Cancel()
{
m_cancelled = true;
}
void ParRenamer::Run()
{
Cleanup();
m_cancelled = false;
m_fileCount = 0;
m_curFile = 0;
m_renamedCount = 0;
m_hasMissedFiles = false;
m_status = psFailed;
m_progressLabel.Format("Checking renamed files for %s", *m_infoName);
m_stageProgress = 0;
UpdateProgress();
@@ -71,14 +57,17 @@ void ParRenamer::Run()
{
debug("Checking %s", *destDir);
m_fileHashList.clear();
LoadParFiles(destDir);
m_parInfoList.clear();
m_badParList.clear();
m_loadedParList.clear();
if (m_fileHashList.empty())
CheckFiles(destDir, true);
RenameParFiles(destDir);
LoadMainParFiles(destDir);
if (m_hasDamagedParFiles)
{
int savedCurFile = m_curFile;
CheckFiles(destDir, true);
m_curFile = savedCurFile; // restore progress indicator
LoadParFiles(destDir);
LoadExtraParFiles(destDir);
}
CheckFiles(destDir, false);
@@ -87,24 +76,12 @@ void ParRenamer::Run()
{
CheckMissing();
}
}
if (m_cancelled)
{
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", *m_infoName);
if (m_renamedCount > 0 && !m_badParList.empty())
{
RenameBadParFiles();
}
}
else if (m_renamedCount > 0)
{
PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", m_renamedCount, *m_infoName);
m_status = psSuccess;
}
else
{
PrintMessage(Message::mkInfo, "No renamed files found for %s", *m_infoName);
}
Cleanup();
Completed();
}
void ParRenamer::BuildDirList(const char* destDir)
@@ -115,7 +92,7 @@ void ParRenamer::BuildDirList(const char* destDir)
while (const char* filename = dirBrowser.Next())
{
if (!m_cancelled)
if (!IsStopped())
{
BString<1024> fullFilename("%s%c%s", destDir, PATH_SEPARATOR, filename);
if (FileSystem::DirectoryExists(fullFilename))
@@ -130,7 +107,7 @@ void ParRenamer::BuildDirList(const char* destDir)
}
}
void ParRenamer::LoadParFiles(const char* destDir)
void ParRenamer::LoadMainParFiles(const char* destDir)
{
ParParser::ParFileList parFileList;
ParParser::FindMainPars(destDir, &parFileList);
@@ -142,19 +119,52 @@ void ParRenamer::LoadParFiles(const char* destDir)
}
}
void ParRenamer::LoadExtraParFiles(const char* destDir)
{
DirBrowser dir(destDir);
while (const char* filename = dir.Next())
{
BString<1024> fullParFilename("%s%c%s", destDir, PATH_SEPARATOR, filename);
if (ParParser::ParseParFilename(fullParFilename, true, nullptr, nullptr))
{
bool knownBadParFile = std::find_if(m_badParList.begin(), m_badParList.end(),
[&fullParFilename](CString& filename)
{
return !strcmp(filename, fullParFilename);
}) != m_badParList.end();
bool loadedParFile = std::find_if(m_loadedParList.begin(), m_loadedParList.end(),
[&fullParFilename](CString& filename)
{
return !strcmp(filename, fullParFilename);
}) != m_loadedParList.end();
if (!knownBadParFile && !loadedParFile)
{
LoadParFile(fullParFilename);
}
}
}
}
void ParRenamer::LoadParFile(const char* parFilename)
{
ParRenamerRepairer repairer;
if (!repairer.LoadPacketsFromFile(parFilename))
if (!repairer.LoadPacketsFromFile(parFilename) || FileSystem::FileSize(parFilename) == 0)
{
PrintMessage(Message::mkWarning, "Could not load par2-file %s", parFilename);
m_hasDamagedParFiles = true;
m_badParList.emplace_back(parFilename);
return;
}
m_loadedParList.emplace_back(parFilename);
PrintMessage(Message::mkInfo, "Loaded par2-file %s for par-rename", FileSystem::BaseFileName(parFilename));
for (std::pair<const Par2::MD5Hash, Par2::Par2RepairerSourceFile*>& entry : repairer.sourcefilemap)
{
if (m_cancelled)
if (IsStopped())
{
break;
}
@@ -162,32 +172,46 @@ void ParRenamer::LoadParFile(const char* parFilename)
Par2::Par2RepairerSourceFile* sourceFile = entry.second;
if (!sourceFile || !sourceFile->GetDescriptionPacket())
{
PrintMessage(Message::mkWarning, "Damaged par2-file detected: %s", parFilename);
PrintMessage(Message::mkWarning, "Damaged par2-file detected: %s", FileSystem::BaseFileName(parFilename));
m_badParList.emplace_back(parFilename);
m_hasDamagedParFiles = true;
continue;
}
std::string filename = Par2::DiskFile::TranslateFilename(sourceFile->GetDescriptionPacket()->FileName());
m_fileHashList.emplace_back(filename.c_str(), sourceFile->GetDescriptionPacket()->Hash16k().print().c_str());
RegisterParredFile(filename.c_str());
std::string hash = sourceFile->GetDescriptionPacket()->Hash16k().print();
bool exists = std::find_if(m_fileHashList.begin(), m_fileHashList.end(),
[&hash](FileHash& fileHash)
{
return !strcmp(fileHash.GetHash(), hash.c_str());
})
!= m_fileHashList.end();
if (!exists)
{
m_fileHashList.emplace_back(filename.c_str(), hash.c_str());
RegisterParredFile(filename.c_str());
}
}
}
void ParRenamer::CheckFiles(const char* destDir, bool renamePars)
void ParRenamer::CheckFiles(const char* destDir, bool checkPars)
{
DirBrowser dir(destDir);
while (const char* filename = dir.Next())
{
if (!m_cancelled)
if (!IsStopped())
{
BString<1024> fullFilename("%s%c%s", destDir, PATH_SEPARATOR, filename);
if (!FileSystem::DirectoryExists(fullFilename))
{
m_progressLabel.Format("Checking file %s", filename);
m_stageProgress = m_fileCount > 0 ? m_curFile * 1000 / m_fileCount : 1000;
m_stageProgress = m_fileCount > 0 ? m_curFile * 1000 / m_fileCount / 2 : 1000;
UpdateProgress();
m_curFile++;
if (renamePars)
if (checkPars)
{
CheckParFile(destDir, fullFilename);
}
@@ -289,22 +313,10 @@ void ParRenamer::CheckRegularFile(const char* destDir, const char* filename)
}
}
/*
* For files not having par2-extensions: checks if the file is a par2-file and renames
* it according to its set-id.
*/
void ParRenamer::CheckParFile(const char* destDir, const char* filename)
{
debug("Checking par2-header for %s", filename);
const char* basename = FileSystem::BaseFileName(filename);
const char* extension = strrchr(basename, '.');
if (extension && !strcasecmp(extension, ".par2"))
{
// do not process files already having par2-extension
return;
}
DiskFile file;
if (!file.Open(filename, DiskFile::omRead))
{
@@ -337,13 +349,70 @@ void ParRenamer::CheckParFile(const char* destDir, const char* filename)
BString<100> setId = header.setid.print().c_str();
for (char* p = setId; *p; p++) *p = tolower(*p); // convert string to lowercase
debug("Renaming: %s; setid: %s", FileSystem::BaseFileName(filename), *setId);
debug("Storing: %s; setid: %s", FileSystem::BaseFileName(filename), *setId);
m_parInfoList.emplace_back(filename, setId);
}
void ParRenamer::RenameParFiles(const char* destDir)
{
if (NeedRenameParFiles())
{
for (ParInfo& parInfo : m_parInfoList)
{
RenameParFile(destDir, parInfo.GetFilename(), parInfo.GetSetId());
}
}
}
bool ParRenamer::NeedRenameParFiles()
{
for (ParInfoList::iterator it1 = m_parInfoList.begin(); it1 != m_parInfoList.end(); it1++)
{
ParInfo& parInfo1 = *it1;
const char* baseName1 = FileSystem::BaseFileName(parInfo1.GetFilename());
const char* extension = strrchr(baseName1, '.');
if (!extension || strcasecmp(extension, ".par2"))
{
// file doesn't have "par2" extension
return true;
}
int baseLen1;
ParParser::ParseParFilename(baseName1, true, &baseLen1, nullptr);
for (ParInfoList::iterator it2 = it1 + 1; it2 != m_parInfoList.end(); it2++)
{
ParInfo& parInfo2 = *it2;
if (!strcmp(parInfo1.GetSetId(), parInfo2.GetSetId()))
{
const char* baseName2 = FileSystem::BaseFileName(parInfo2.GetFilename());
int baseLen2;
ParParser::ParseParFilename(baseName2, true, &baseLen2, nullptr);
if (baseLen1 != baseLen2 || strncasecmp(baseName1, baseName2, baseLen1))
{
// same setid but different base file names
return true;
}
}
}
}
return false;
}
void ParRenamer::RenameParFile(const char* destDir, const char* filename, const char* setId)
{
debug("Renaming: %s; setid: %s", FileSystem::BaseFileName(filename), setId);
BString<1024> destFileName;
int num = 1;
while (num == 1 || FileSystem::FileExists(destFileName))
{
destFileName.Format("%s%c%s.vol%03i+01.PAR2", destDir, PATH_SEPARATOR, *setId, num);
destFileName.Format("%s%c%s.vol%03i+01.PAR2", destDir, PATH_SEPARATOR, setId, num);
num++;
}
@@ -366,4 +435,13 @@ void ParRenamer::RenameFile(const char* srcFilename, const char* destFileName)
RegisterRenamedFile(FileSystem::BaseFileName(srcFilename), FileSystem::BaseFileName(destFileName));
}
void ParRenamer::RenameBadParFiles()
{
for (CString& parFilename : m_badParList)
{
BString<1024> destFileName("%s.bad", *parFilename);
RenameFile(parFilename, destFileName);
}
}
#endif

View File

@@ -24,32 +24,23 @@
#ifndef DISABLE_PARCHECK
#include "NString.h"
#include "Thread.h"
#include "Log.h"
class ParRenamer : public Thread
class ParRenamer
{
public:
enum EStatus
{
psFailed,
psSuccess
};
virtual void Run();
void Execute();
void SetDestDir(const char* destDir) { m_destDir = destDir; }
const char* GetInfoName() { return m_infoName; }
void SetInfoName(const char* infoName) { m_infoName = infoName; }
void SetStatus(EStatus status);
EStatus GetStatus() { return m_status; }
void Cancel();
bool GetCancelled() { return m_cancelled; }
int GetRenamedCount() { return m_renamedCount; }
bool HasMissedFiles() { return m_hasMissedFiles; }
bool HasDamagedParFiles() { return m_hasDamagedParFiles; }
void SetDetectMissing(bool detectMissing) { m_detectMissing = detectMissing; }
protected:
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual bool IsStopped() { return false; };
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3) {}
virtual void RegisterParredFile(const char* filename) {}
virtual void RegisterRenamedFile(const char* oldFilename, const char* newFileName) {}
@@ -72,34 +63,52 @@ private:
bool m_fileExists = false;
};
class ParInfo
{
public:
ParInfo(const char* filename, const char* setId) :
m_filename(filename), m_setId(setId) {}
const char* GetFilename() { return m_filename; }
const char* GetSetId() { return m_setId; }
private:
CString m_filename;
CString m_setId;
};
typedef std::deque<FileHash> FileHashList;
typedef std::deque<CString> DirList;
typedef std::deque<ParInfo> ParInfoList;
typedef std::deque<CString> NameList;
CString m_infoName;
CString m_destDir;
EStatus m_status;
CString m_progressLabel;
int m_stageProgress;
bool m_cancelled;
DirList m_dirList;
int m_stageProgress = 0;
NameList m_dirList;
FileHashList m_fileHashList;
int m_fileCount;
int m_curFile;
int m_renamedCount;
bool m_hasMissedFiles;
ParInfoList m_parInfoList;
NameList m_badParList;
NameList m_loadedParList;
int m_fileCount = 0;
int m_curFile = 0;
int m_renamedCount = 0;
bool m_hasMissedFiles = false;
bool m_detectMissing = false;
bool m_hasDamagedParFiles = false;
void BuildDirList(const char* destDir);
void CheckDir(const char* destDir);
void LoadParFiles(const char* destDir);
void LoadMainParFiles(const char* destDir);
void LoadExtraParFiles(const char* destDir);
void LoadParFile(const char* parFilename);
void CheckFiles(const char* destDir, bool renamePars);
void CheckFiles(const char* destDir, bool checkPars);
void CheckRegularFile(const char* destDir, const char* filename);
void CheckParFile(const char* destDir, const char* filename);
bool IsSplittedFragment(const char* filename, const char* correctName);
void CheckMissing();
void RenameParFiles(const char* destDir);
void RenameParFile(const char* destDir, const char* filename, const char* setId);
bool NeedRenameParFiles();
void RenameFile(const char* srcFilename, const char* destFileName);
void Cleanup();
void RenameBadParFiles();
};
#endif

View File

@@ -29,6 +29,8 @@
#include "FileSystem.h"
#include "Unpack.h"
#include "Cleanup.h"
#include "Rename.h"
#include "Repair.h"
#include "NzbFile.h"
#include "QueueScript.h"
#include "ParParser.h"
@@ -37,8 +39,7 @@ PrePostProcessor::PrePostProcessor()
{
debug("Creating PrePostProcessor");
m_downloadQueueObserver.m_owner = this;
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
DownloadQueue::Guard()->Attach(this);
}
void PrePostProcessor::Run()
@@ -57,49 +58,108 @@ void PrePostProcessor::Run()
while (!IsStopped())
{
if (!g_Options->GetTempPausePostprocess())
if (!g_Options->GetTempPausePostprocess() && m_queuedJobs)
{
// check post-queue every 200 msec
CheckPostQueue();
}
Util::SetStandByMode(!m_curJob);
usleep(200 * 1000);
}
WaitJobs();
debug("Exiting PrePostProcessor-loop");
}
void PrePostProcessor::WaitJobs()
{
debug("PrePostProcessor: waiting for jobs to complete");
// wait 5 seconds until all jobs gracefully finish
time_t waitStart = Util::CurrentTime();
while (Util::CurrentTime() < waitStart + 5)
{
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (m_activeJobs.empty())
{
break;
}
}
CheckPostQueue();
usleep(200 * 1000);
}
// kill remaining jobs; not safe but we can't wait any longer
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* postJob : m_activeJobs)
{
if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
{
Thread* thread = postJob->GetPostInfo()->GetPostThread();
postJob->GetPostInfo()->SetPostThread(nullptr);
warn("Terminating active post-process job for %s", postJob->GetName());
thread->Kill();
delete thread;
}
}
}
debug("PrePostProcessor: Jobs are completed");
}
void PrePostProcessor::Stop()
{
Thread::Stop();
GuardedDownloadQueue guard = DownloadQueue::Guard();
#ifndef DISABLE_PARCHECK
m_parCoordinator.Stop();
#endif
if (m_curJob && m_curJob->GetPostInfo() &&
(m_curJob->GetPostInfo()->GetStage() == PostInfo::ptUnpacking ||
m_curJob->GetPostInfo()->GetStage() == PostInfo::ptExecutingScript) &&
m_curJob->GetPostInfo()->GetPostThread())
for (NzbInfo* postJob : m_activeJobs)
{
Thread* postThread = m_curJob->GetPostInfo()->GetPostThread();
m_curJob->GetPostInfo()->SetPostThread(nullptr);
postThread->SetAutoDestroy(true);
postThread->Stop();
if (postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread())
{
postJob->GetPostInfo()->GetPostThread()->Stop();
}
}
}
void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
/**
* Reset the state of items after reloading from disk and
* delete items which could not be resumed.
* Also count the number of post-jobs.
*/
void PrePostProcessor::SanitisePostQueue()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
{
PostInfo* postInfo = nzbInfo->GetPostInfo();
if (postInfo)
{
m_queuedJobs++;
if (postInfo->GetStage() == PostInfo::ptExecutingScript ||
!FileSystem::DirectoryExists(nzbInfo->GetDestDir()))
{
postInfo->SetStage(PostInfo::ptFinished);
}
else
{
postInfo->SetStage(PostInfo::ptQueued);
}
postInfo->SetWorking(false);
}
}
}
void PrePostProcessor::DownloadQueueUpdate(void* aspect)
{
if (IsStopped())
{
return;
}
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)Aspect;
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
if (queueAspect->action == DownloadQueue::eaNzbFound)
{
NzbFound(queueAspect->downloadQueue, queueAspect->nzbInfo);
@@ -128,9 +188,17 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
}
#ifndef DISABLE_PARCHECK
if (m_parCoordinator.AddPar(queueAspect->fileInfo, queueAspect->action == DownloadQueue::eaFileDeleted))
for (NzbInfo* postJob : m_activeJobs)
{
return;
if (postJob && queueAspect->fileInfo->GetNzbInfo() == postJob &&
postJob->GetPostInfo() && postJob->GetPostInfo()->GetPostThread() &&
postJob->GetPostInfo()->GetStage() >= PostInfo::ptLoadingPars &&
postJob->GetPostInfo()->GetStage() <= PostInfo::ptVerifyingRepaired &&
((RepairController*)postJob->GetPostInfo()->GetPostThread())->AddPar(
queueAspect->fileInfo, queueAspect->action == DownloadQueue::eaFileDeleted))
{
return;
}
}
#endif
@@ -170,7 +238,8 @@ void PrePostProcessor::NzbAdded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
if (g_Options->GetParCheck() != Options::pcForce)
{
m_parCoordinator.PausePars(downloadQueue, nzbInfo);
downloadQueue->EditEntry(nzbInfo->GetId(),
DownloadQueue::eaGroupPauseExtraPars, nullptr);
}
if (nzbInfo->GetDeleteStatus() == NzbInfo::dsDupe ||
@@ -199,7 +268,7 @@ void PrePostProcessor::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbI
nzbInfo->PrintMessage(Message::mkInfo, "Queueing %s for post-processing", nzbInfo->GetName());
nzbInfo->EnterPostProcess();
m_jobCount++;
m_queuedJobs++;
if (nzbInfo->GetParStatus() == NzbInfo::psNone &&
g_Options->GetParCheck() != Options::pcAlways &&
@@ -208,11 +277,6 @@ void PrePostProcessor::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbI
nzbInfo->SetParStatus(NzbInfo::psSkipped);
}
if (nzbInfo->GetRenameStatus() == NzbInfo::rsNone && !g_Options->GetParRename())
{
nzbInfo->SetRenameStatus(NzbInfo::rsSkipped);
}
downloadQueue->Save();
}
else
@@ -320,90 +384,138 @@ void PrePostProcessor::DeleteCleanup(NzbInfo* nzbInfo)
}
}
void PrePostProcessor::CheckPostQueue()
void PrePostProcessor::CheckRequestPar(DownloadQueue* downloadQueue)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (!m_curJob && m_jobCount > 0)
{
m_curJob = GetNextJob(downloadQueue);
}
if (m_curJob)
{
PostInfo* postInfo = m_curJob->GetPostInfo();
if (!postInfo->GetWorking() && !IsNzbFileDownloading(m_curJob))
{
#ifndef DISABLE_PARCHECK
if (postInfo->GetRequestParCheck() &&
(postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped ||
(postInfo->GetForceRepair() && !postInfo->GetNzbInfo()->GetParFull())) &&
g_Options->GetParCheck() != Options::pcManual)
{
postInfo->SetForceParFull(postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped);
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psNone);
postInfo->SetRequestParCheck(false);
postInfo->SetStage(PostInfo::ptQueued);
postInfo->GetNzbInfo()->GetScriptStatuses()->clear();
DeletePostThread(postInfo);
}
else if (postInfo->GetRequestParCheck() && postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped &&
g_Options->GetParCheck() == Options::pcManual)
{
postInfo->SetRequestParCheck(false);
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psManual);
DeletePostThread(postInfo);
for (NzbInfo* postJob : m_activeJobs)
{
PostInfo* postInfo = postJob->GetPostInfo();
if (!postInfo->GetNzbInfo()->GetFileList()->empty())
if (postInfo->GetRequestParCheck() &&
(postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped ||
(postInfo->GetForceRepair() && !postInfo->GetNzbInfo()->GetParFull())) &&
g_Options->GetParCheck() != Options::pcManual)
{
postInfo->SetForceParFull(postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped);
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psNone);
postInfo->SetRequestParCheck(false);
postInfo->GetNzbInfo()->GetScriptStatuses()->clear();
postInfo->SetWorking(false);
}
else if (postInfo->GetRequestParCheck() &&
postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped &&
g_Options->GetParCheck() == Options::pcManual)
{
postInfo->SetRequestParCheck(false);
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psManual);
if (!postInfo->GetNzbInfo()->GetFileList()->empty())
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Downloading all remaining files for manual par-check for %s", postInfo->GetNzbInfo()->GetName());
downloadQueue->EditEntry(postInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupResume, nullptr);
postInfo->SetStage(PostInfo::ptFinished);
}
else
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"There are no par-files remain for download for %s", postInfo->GetNzbInfo()->GetName());
}
postInfo->SetWorking(false);
}
}
#endif
}
void PrePostProcessor::CleanupJobs(DownloadQueue* downloadQueue)
{
m_activeJobs.erase(std::remove_if(m_activeJobs.begin(), m_activeJobs.end(),
[processor = this, downloadQueue](NzbInfo* postJob)
{
PostInfo* postInfo = postJob->GetPostInfo();
if (!postInfo->GetWorking())
{
delete postInfo->GetPostThread();
postInfo->SetPostThread(nullptr);
postInfo->SetStageTime(0);
postInfo->SetStageProgress(0);
postInfo->SetFileProgress(0);
postInfo->SetProgressLabel("");
if (postInfo->GetStartTime() > 0)
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Downloading all remaining files for manual par-check for %s", postInfo->GetNzbInfo()->GetName());
downloadQueue->EditEntry(postInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupResume, 0, nullptr);
postInfo->SetStage(PostInfo::ptFinished);
postJob->SetPostTotalSec(postJob->GetPostTotalSec() +
(int)(Util::CurrentTime() - postInfo->GetStartTime()));
postInfo->SetStartTime(0);
}
if (postInfo->GetStage() == PostInfo::ptFinished || postInfo->GetDeleted())
{
processor->JobCompleted(downloadQueue, postInfo);
}
else
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"There are no par-files remain for download for %s", postInfo->GetNzbInfo()->GetName());
postInfo->SetStage(PostInfo::ptQueued);
}
return true;
}
#endif
if (postInfo->GetDeleted())
{
postInfo->SetStage(PostInfo::ptFinished);
}
if (postInfo->GetStage() == PostInfo::ptQueued &&
(!g_Options->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
{
DeletePostThread(postInfo);
StartJob(downloadQueue, postInfo);
}
else if (postInfo->GetStage() == PostInfo::ptFinished)
{
UpdatePauseState(false, nullptr);
JobCompleted(downloadQueue, postInfo);
}
else if (!g_Options->GetPausePostProcess())
{
error("Internal error: invalid state in post-processor");
// TODO: cancel (delete) current job
}
}
}
return false;
}),
m_activeJobs.end());
}
NzbInfo* PrePostProcessor::GetNextJob(DownloadQueue* downloadQueue)
bool PrePostProcessor::CanRunMoreJobs(bool* allowPar)
{
int totalJobs = (int)m_activeJobs.size();
int parJobs = 0;
int otherJobs = 0;
bool repairJobs = false;
for (NzbInfo* postJob : m_activeJobs)
{
bool parJob = postJob->GetPostInfo()->GetStage() >= PostInfo::ptLoadingPars &&
postJob->GetPostInfo()->GetStage() <= PostInfo::ptVerifyingRepaired;
repairJobs |= postJob->GetPostInfo()->GetStage() == PostInfo::ptRepairing;
parJobs += parJob ? 1 : 0;
otherJobs += parJob ? 0 : 1;
}
switch (g_Options->GetPostStrategy())
{
case Options::ppSequential:
*allowPar = true;
return totalJobs == 0;
case Options::ppBalanced:
*allowPar = parJobs == 0;
return otherJobs == 0 && (parJobs == 0 || repairJobs);
case Options::ppAggressive:
*allowPar = parJobs < 1;
return totalJobs < 3;
case Options::ppRocket:
*allowPar = parJobs < 2;
return totalJobs < 6;
}
return false;
}
NzbInfo* PrePostProcessor::PickNextJob(DownloadQueue* downloadQueue, bool allowPar)
{
NzbInfo* nzbInfo = nullptr;
for (NzbInfo* nzbInfo1: downloadQueue->GetQueue())
{
if (nzbInfo1->GetPostInfo() && !g_QueueScriptCoordinator->HasJob(nzbInfo1->GetId(), nullptr) &&
if (nzbInfo1->GetPostInfo() && !nzbInfo1->GetPostInfo()->GetWorking() &&
!g_QueueScriptCoordinator->HasJob(nzbInfo1->GetId(), nullptr) &&
(!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()) &&
(!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()))
(!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
(allowPar || !nzbInfo1->GetPostInfo()->GetNeedParCheck()) &&
(std::find(m_activeJobs.begin(), m_activeJobs.end(), nzbInfo1) == m_activeJobs.end()) &&
IsNzbFileCompleted(nzbInfo1, true))
{
nzbInfo = nzbInfo1;
}
@@ -412,73 +524,92 @@ NzbInfo* PrePostProcessor::GetNextJob(DownloadQueue* downloadQueue)
return nzbInfo;
}
/**
* Reset the state of items after reloading from disk and
* delete items which could not be resumed.
* Also count the number of post-jobs.
*/
void PrePostProcessor::SanitisePostQueue()
void PrePostProcessor::CheckPostQueue()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
size_t countBefore = m_activeJobs.size();
CheckRequestPar(downloadQueue);
CleanupJobs(downloadQueue);
bool changed = m_activeJobs.size() != countBefore;
bool allowPar;
while (CanRunMoreJobs(&allowPar) && !IsStopped())
{
PostInfo* postInfo = nzbInfo->GetPostInfo();
if (postInfo)
NzbInfo* postJob = PickNextJob(downloadQueue, allowPar);
if (!postJob)
{
m_jobCount++;
if (postInfo->GetStage() == PostInfo::ptExecutingScript ||
!FileSystem::DirectoryExists(nzbInfo->GetDestDir()))
{
postInfo->SetStage(PostInfo::ptFinished);
}
else
{
postInfo->SetStage(PostInfo::ptQueued);
}
break;
}
m_activeJobs.push_back(postJob);
PostInfo* postInfo = postJob->GetPostInfo();
if (postInfo->GetStage() == PostInfo::ptQueued &&
(!g_Options->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
{
StartJob(downloadQueue, postInfo, allowPar);
CheckRequestPar(downloadQueue);
CleanupJobs(downloadQueue);
changed = true;
}
}
if (changed)
{
downloadQueue->Save();
UpdatePauseState();
}
Util::SetStandByMode(m_activeJobs.empty());
}
void PrePostProcessor::DeletePostThread(PostInfo* postInfo)
{
delete postInfo->GetPostThread();
postInfo->SetPostThread(nullptr);
}
void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo)
void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo, bool allowPar)
{
if (!postInfo->GetStartTime())
{
postInfo->SetStartTime(Util::CurrentTime());
}
postInfo->SetStageTime(Util::CurrentTime());
postInfo->SetStageProgress(0);
postInfo->SetFileProgress(0);
postInfo->SetProgressLabel("");
#ifndef DISABLE_PARCHECK
if (postInfo->GetNzbInfo()->GetRenameStatus() == NzbInfo::rsNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone)
if (postInfo->GetNzbInfo()->GetParRenameStatus() == NzbInfo::rsNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone &&
g_Options->GetParRename())
{
UpdatePauseState(g_Options->GetParPauseQueue(), "par-rename");
m_parCoordinator.StartParRenameJob(postInfo);
EnterStage(downloadQueue, postInfo, PostInfo::ptParRenaming);
RenameController::StartJob(postInfo, RenameController::jkPar);
return;
}
else if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone &&
#ifndef DISABLE_PARCHECK
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone)
{
if (ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
{
UpdatePauseState(g_Options->GetParPauseQueue(), "par-check");
m_parCoordinator.StartParCheckJob(postInfo);
if (!allowPar)
{
postInfo->SetNeedParCheck(true);
return;
}
EnterStage(downloadQueue, postInfo, PostInfo::ptLoadingPars);
postInfo->SetNeedParCheck(false);
RepairController::StartJob(postInfo);
}
else
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Nothing to par-check for %s", postInfo->GetNzbInfo()->GetName());
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psSkipped);
postInfo->SetWorking(false);
postInfo->SetStage(PostInfo::ptQueued);
}
return;
}
else if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
((g_Options->GetParScan() != Options::psDupe &&
postInfo->GetNzbInfo()->CalcHealth() < postInfo->GetNzbInfo()->CalcCriticalHealth(false) &&
postInfo->GetNzbInfo()->CalcCriticalHealth(false) < 1000) ||
@@ -500,7 +631,8 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psFailure);
return;
}
else if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
postInfo->GetNzbInfo()->GetFailedSize() - postInfo->GetNzbInfo()->GetParFailedSize() > 0 &&
ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
{
@@ -513,15 +645,23 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
#endif
NzbParameter* unpackParameter = postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:", false);
bool unpackParam = !(unpackParameter && !strcasecmp(unpackParameter->GetValue(), "no"));
bool unpack = unpackParam && postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone &&
bool wantUnpack = !(unpackParameter && !strcasecmp(unpackParameter->GetValue(), "no"));
bool unpack = wantUnpack && postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone;
if (postInfo->GetNzbInfo()->GetRarRenameStatus() == NzbInfo::rsNone &&
unpack && g_Options->GetRarRename())
{
EnterStage(downloadQueue, postInfo, PostInfo::ptRarRenaming);
RenameController::StartJob(postInfo, RenameController::jkRar);
return;
}
bool parFailed = postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psFailure ||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psRepairPossible ||
postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psManual;
bool cleanup = !unpack &&
bool cleanup = !unpack && wantUnpack &&
postInfo->GetNzbInfo()->GetCleanupStatus() == NzbInfo::csNone &&
!Util::EmptyStr(g_Options->GetExtCleanupDisk()) &&
((postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSuccess &&
@@ -548,8 +688,6 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
!strncmp(postInfo->GetNzbInfo()->GetDestDir(), g_Options->GetInterDir(), strlen(g_Options->GetInterDir())) &&
postInfo->GetNzbInfo()->GetDestDir()[strlen(g_Options->GetInterDir())] == PATH_SEPARATOR;
bool postScript = true;
if (unpack && parFailed)
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkWarning,
@@ -559,55 +697,38 @@ void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo
unpack = false;
}
if (!unpack && !moveInter && !postScript)
{
postInfo->SetStage(PostInfo::ptFinished);
return;
}
postInfo->SetProgressLabel(unpack ? "Unpacking" : moveInter ? "Moving" : "Executing post-process-script");
postInfo->SetWorking(true);
postInfo->SetStage(unpack ? PostInfo::ptUnpacking : moveInter ? PostInfo::ptMoving : PostInfo::ptExecutingScript);
postInfo->SetFileProgress(0);
postInfo->SetStageProgress(0);
downloadQueue->Save();
postInfo->SetStageTime(Util::CurrentTime());
if (unpack)
{
UpdatePauseState(g_Options->GetUnpackPauseQueue(), "unpack");
EnterStage(downloadQueue, postInfo, PostInfo::ptUnpacking);
UnpackController::StartJob(postInfo);
}
else if (cleanup)
{
UpdatePauseState(g_Options->GetUnpackPauseQueue() || g_Options->GetScriptPauseQueue(), "cleanup");
EnterStage(downloadQueue, postInfo, PostInfo::ptCleaningUp);
CleanupController::StartJob(postInfo);
}
else if (moveInter)
{
UpdatePauseState(g_Options->GetUnpackPauseQueue() || g_Options->GetScriptPauseQueue(), "move");
EnterStage(downloadQueue, postInfo, PostInfo::ptMoving);
MoveController::StartJob(postInfo);
}
else
{
UpdatePauseState(g_Options->GetScriptPauseQueue(), "post-process-script");
EnterStage(downloadQueue, postInfo, PostInfo::ptExecutingScript);
PostScriptController::StartJob(postInfo);
}
}
void PrePostProcessor::EnterStage(DownloadQueue* downloadQueue, PostInfo* postInfo, PostInfo::EStage stage)
{
postInfo->SetWorking(true);
postInfo->SetStage(stage);
}
void PrePostProcessor::JobCompleted(DownloadQueue* downloadQueue, PostInfo* postInfo)
{
NzbInfo* nzbInfo = postInfo->GetNzbInfo();
if (postInfo->GetStartTime() > 0)
{
nzbInfo->SetPostTotalSec((int)(Util::CurrentTime() - postInfo->GetStartTime()));
postInfo->SetStartTime(0);
}
DeletePostThread(postInfo);
nzbInfo->LeavePostProcess();
if (IsNzbFileCompleted(nzbInfo, true))
@@ -615,13 +736,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* downloadQueue, PostInfo* post
NzbCompleted(downloadQueue, nzbInfo, false);
}
if (nzbInfo == m_curJob)
{
m_curJob = nullptr;
}
m_jobCount--;
downloadQueue->Save();
m_queuedJobs--;
}
bool PrePostProcessor::IsNzbFileCompleted(NzbInfo* nzbInfo, bool ignorePausedPars)
@@ -643,39 +758,52 @@ bool PrePostProcessor::IsNzbFileCompleted(NzbInfo* nzbInfo, bool ignorePausedPar
return true;
}
bool PrePostProcessor::IsNzbFileDownloading(NzbInfo* nzbInfo)
void PrePostProcessor::UpdatePauseState()
{
if (nzbInfo->GetActiveDownloads())
bool needPause = false;
for (NzbInfo* postJob : m_activeJobs)
{
return true;
}
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (!fileInfo->GetPaused())
switch (postJob->GetPostInfo()->GetStage())
{
return true;
case PostInfo::ptLoadingPars:
case PostInfo::ptVerifyingSources:
case PostInfo::ptRepairing:
case PostInfo::ptVerifyingRepaired:
case PostInfo::ptParRenaming:
needPause |= g_Options->GetParPauseQueue();
break;
case PostInfo::ptRarRenaming:
case PostInfo::ptUnpacking:
case PostInfo::ptCleaningUp:
case PostInfo::ptMoving:
needPause |= g_Options->GetUnpackPauseQueue();
break;
case PostInfo::ptExecutingScript:
needPause |= g_Options->GetScriptPauseQueue();
break;
case PostInfo::ptQueued:
case PostInfo::ptFinished:
break;
}
}
return false;
}
void PrePostProcessor::UpdatePauseState(bool needPause, const char* reason)
{
if (needPause && !g_Options->GetTempPauseDownload())
{
info("Pausing download before %s", reason);
info("Pausing download before post-processing");
}
else if (!needPause && g_Options->GetTempPauseDownload())
{
info("Unpausing download after %s", m_pauseReason);
info("Unpausing download after post-processing");
}
g_Options->SetTempPauseDownload(needPause);
m_pauseReason = reason;
}
bool PrePostProcessor::EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text)
bool PrePostProcessor::EditList(DownloadQueue* downloadQueue, IdList* idList,
DownloadQueue::EEditAction action, const char* args)
{
debug("Edit-command for post-processor received");
switch (action)
@@ -704,19 +832,9 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* downloadQueue, IdList* idL
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Deleting active post-job %s", postInfo->GetNzbInfo()->GetName());
postInfo->SetDeleted(true);
#ifndef DISABLE_PARCHECK
if (PostInfo::ptLoadingPars <= postInfo->GetStage() && postInfo->GetStage() <= PostInfo::ptRenaming)
{
if (m_parCoordinator.Cancel())
{
ok = true;
}
}
else
#endif
if (postInfo->GetPostThread())
{
debug("Terminating %s for %s", (postInfo->GetStage() == PostInfo::ptUnpacking ? "unpack" : "post-process-script"), postInfo->GetNzbInfo()->GetName());
debug("Terminating post-process thread for %s", postInfo->GetNzbInfo()->GetName());
postInfo->GetPostThread()->Stop();
ok = true;
}
@@ -730,6 +848,14 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* downloadQueue, IdList* idL
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Deleting queued post-job %s", postInfo->GetNzbInfo()->GetName());
JobCompleted(downloadQueue, postInfo);
m_activeJobs.erase(std::remove_if(m_activeJobs.begin(), m_activeJobs.end(),
[postInfo](NzbInfo* postJob)
{
return postInfo == postJob->GetPostInfo();
}),
m_activeJobs.end());
ok = true;
}
break;
@@ -737,5 +863,10 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* downloadQueue, IdList* idL
}
}
if (ok)
{
downloadQueue->Save();
}
return ok;
}

View File

@@ -24,50 +24,45 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "ParCoordinator.h"
class PrePostProcessor : public Thread
class PrePostProcessor : public Thread, public Observer
{
public:
PrePostProcessor();
virtual void Run();
virtual void Stop();
bool HasMoreJobs() { return m_jobCount > 0; }
int GetJobCount() { return m_jobCount; }
bool HasMoreJobs() { return m_queuedJobs > 0; }
int GetJobCount() { return m_queuedJobs; }
bool EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action,
int offset, const char* text);
const char* args);
void NzbAdded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
protected:
virtual void Update(Subject* caller, void* aspect) { DownloadQueueUpdate(aspect); }
private:
class DownloadQueueObserver: public Observer
{
public:
PrePostProcessor* m_owner;
virtual void Update(Subject* Caller, void* Aspect) { m_owner->DownloadQueueUpdate(Caller, Aspect); }
};
int m_queuedJobs = 0;
RawNzbList m_activeJobs;
ParCoordinator m_parCoordinator;
DownloadQueueObserver m_downloadQueueObserver;
int m_jobCount = 0;
NzbInfo* m_curJob = nullptr;
const char* m_pauseReason = nullptr;
bool IsNzbFileCompleted(NzbInfo* nzbInfo, bool ignorePausedPars);
bool IsNzbFileDownloading(NzbInfo* nzbInfo);
void CheckPostQueue();
void JobCompleted(DownloadQueue* downloadQueue, PostInfo* postInfo);
void StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo);
void CheckRequestPar(DownloadQueue* downloadQueue);
void CleanupJobs(DownloadQueue* downloadQueue);
bool CanRunMoreJobs(bool* allowPar);
NzbInfo* PickNextJob(DownloadQueue* downloadQueue, bool allowPar);
void StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo, bool allowPar);
void EnterStage(DownloadQueue* downloadQueue, PostInfo* postInfo, PostInfo::EStage stage);
void SanitisePostQueue();
void UpdatePauseState(bool needPause, const char* reason);
void UpdatePauseState();
void NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void NzbDeleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void NzbCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, bool saveQueue);
void JobCompleted(DownloadQueue* downloadQueue, PostInfo* postInfo);
bool PostQueueDelete(DownloadQueue* downloadQueue, IdList* idList);
void DeletePostThread(PostInfo* postInfo);
NzbInfo* GetNextJob(DownloadQueue* downloadQueue);
void DownloadQueueUpdate(Subject* Caller, void* Aspect);
void DownloadQueueUpdate(void* aspect);
void DeleteCleanup(NzbInfo* nzbInfo);
bool IsNzbFileCompleted(NzbInfo* nzbInfo, bool ignorePausedPars);
void WaitJobs();
};
extern PrePostProcessor* g_PrePostProcessor;

View File

@@ -0,0 +1,730 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "RarReader.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
// RAR3 constants
static const uint16 RAR3_MAIN_VOLUME = 0x0001;
static const uint16 RAR3_MAIN_NEWNUMBERING = 0x0010;
static const uint16 RAR3_MAIN_PASSWORD = 0x0080;
static const uint8 RAR3_BLOCK_MAIN = 0x73; // s
static const uint8 RAR3_BLOCK_FILE = 0x74; // t
static const uint8 RAR3_BLOCK_ENDARC = 0x7b; // {
static const uint16 RAR3_BLOCK_ADDSIZE = 0x8000;
static const uint16 RAR3_FILE_ADDSIZE = 0x0100;
static const uint16 RAR3_FILE_SPLITBEFORE = 0x0001;
static const uint16 RAR3_FILE_SPLITAFTER = 0x0002;
static const uint16 RAR3_ENDARC_NEXTVOL = 0x0001;
static const uint16 RAR3_ENDARC_DATACRC = 0x0002;
static const uint16 RAR3_ENDARC_VOLNUMBER = 0x0008;
// RAR5 constants
static const uint8 RAR5_BLOCK_MAIN = 1;
static const uint8 RAR5_BLOCK_FILE = 2;
static const uint8 RAR5_BLOCK_ENCRYPTION = 4;
static const uint8 RAR5_BLOCK_ENDARC = 5;
static const uint8 RAR5_BLOCK_EXTRADATA = 0x01;
static const uint8 RAR5_BLOCK_DATAAREA = 0x02;
static const uint8 RAR5_BLOCK_SPLITBEFORE = 0x08;
static const uint8 RAR5_BLOCK_SPLITAFTER = 0x10;
static const uint8 RAR5_MAIN_ISVOL = 0x01;
static const uint8 RAR5_MAIN_VOLNR = 0x02;
static const uint8 RAR5_FILE_TIME = 0x02;
static const uint8 RAR5_FILE_CRC = 0x04;
static const uint8 RAR5_FILE_EXTRATIME = 0x03;
static const uint8 RAR5_FILE_EXTRATIMEUNIXFORMAT = 0x01;
static const uint8 RAR5_ENDARC_NEXTVOL = 0x01;
bool RarVolume::Read()
{
debug("Checking file %s", *m_filename);
DiskFile file;
if (!file.Open(m_filename, DiskFile::omRead))
{
return false;
}
m_version = DetectRarVersion(file);
file.Seek(0);
bool ok = false;
switch (m_version)
{
case 3:
ok = ReadRar3Volume(file);
break;
case 5:
ok = ReadRar5Volume(file);
break;
}
file.Close();
DecryptFree();
LogDebugInfo();
return ok;
}
int RarVolume::DetectRarVersion(DiskFile& file)
{
static char RAR3_SIGNATURE[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 };
static char RAR5_SIGNATURE[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00 };
char fileSignature[8];
int cnt = 0;
cnt = (int)file.Read(fileSignature, sizeof(fileSignature));
bool rar5 = cnt == sizeof(fileSignature) && !strcmp(RAR5_SIGNATURE, fileSignature);
bool rar3 = !rar5 && cnt == sizeof(fileSignature) && !strcmp(RAR3_SIGNATURE, fileSignature);
return rar3 ? 3 : rar5 ? 5 : 0;
}
bool RarVolume::Read(DiskFile& file, RarBlock* block, void* buffer, int64 size)
{
if (m_encrypted)
{
if (!DecryptRead(file, buffer, size)) return false;
}
else
{
if (file.Read(buffer, size) != size) return false;
}
if (block)
{
block->trailsize -= size;
}
return true;
}
bool RarVolume::Read16(DiskFile& file, RarBlock* block, uint16* result)
{
uint8 buf[2];
if (!Read(file, block, buf, sizeof(buf))) return false;
*result = ((uint16)buf[1] << 8) + buf[0];
return true;
}
bool RarVolume::Read32(DiskFile& file, RarBlock* block, uint32* result)
{
uint8 buf[4];
if (!Read(file, block, buf, sizeof(buf))) return false;
*result = ((uint32)buf[3] << 24) + ((uint32)buf[2] << 16) + ((uint32)buf[1] << 8) + buf[0];
return true;
}
bool RarVolume::ReadV(DiskFile& file, RarBlock* block, uint64* result)
{
*result = 0;
uint8 val;
uint8 bits = 0;
do
{
if (Read(file, block, &val, sizeof(val)) != sizeof(val)) return false;
*result += (uint64)(val & 0x7f) << bits;
bits += 7;
} while (val & 0x80);
return true;
}
bool RarVolume::Skip(DiskFile& file, RarBlock* block, int64 size)
{
uint8 buf[256];
while (size > 0)
{
int64 len = size <= sizeof(buf) ? size : sizeof(buf);
if (!Read(file, block, buf, len)) return false;
size -= len;
}
return true;
}
bool RarVolume::ReadRar3Volume(DiskFile& file)
{
debug("Reading rar3-file %s", *m_filename);
while (!file.Eof())
{
RarBlock block = ReadRar3Block(file);
if (!block.type)
{
return false;
}
if (block.type == RAR3_BLOCK_MAIN)
{
if (block.flags & RAR3_MAIN_PASSWORD)
{
m_encrypted = true;
if (m_password.Empty()) return false;
}
m_newNaming = block.flags & RAR3_MAIN_NEWNUMBERING;
m_multiVolume = block.flags & RAR3_MAIN_VOLUME;
}
else if (block.type == RAR3_BLOCK_FILE)
{
RarFile innerFile;
if (!ReadRar3File(file, block, innerFile)) return false;
m_files.push_back(std::move(innerFile));
}
else if (block.type == RAR3_BLOCK_ENDARC)
{
if (block.flags & RAR3_ENDARC_DATACRC)
{
if (!Skip(file, &block, 4)) return false;
}
if (block.flags & RAR3_ENDARC_VOLNUMBER)
{
if (!Read32(file, &block, &m_volumeNo)) return false;
m_hasNextVolume = (block.flags & RAR3_ENDARC_NEXTVOL) != 0;
}
break;
}
else if (block.type < 0x72 || block.type > 0x7b)
{
// inlvaid block type
return false;
}
uint64 skip = block.trailsize;
if (m_encrypted)
{
skip -= 16 - m_decryptPos;
m_decryptPos = 16;
DecryptFree();
}
if (!file.Seek(skip, DiskFile::soCur))
{
return false;
}
}
return true;
}
RarVolume::RarBlock RarVolume::ReadRar3Block(DiskFile& file)
{
RarBlock block {0};
uint8 salt[8];
if (m_encrypted &&
!(file.Read(salt, sizeof(salt)) == sizeof(salt) &&
DecryptRar3Prepare(salt) && DecryptInit(128)))
{
return {0};
}
uint8 buf[7];
if (!Read(file, nullptr, &buf, sizeof(buf))) return {0};
block.crc = ((uint16)buf[1] << 8) + buf[0];
block.type = buf[2];
block.flags = ((uint16)buf[4] << 8) + buf[3];
uint16 size = ((uint16)buf[6] << 8) + buf[5];
uint32 blocksize = size;
block.trailsize = blocksize - sizeof(buf);
uint8 addbuf[4];
if ((block.flags & RAR3_BLOCK_ADDSIZE) && !Read(file, nullptr, &addbuf, sizeof(addbuf)))
{
return {0};
}
block.addsize = ((uint32)addbuf[3] << 24) + ((uint32)addbuf[2] << 16) + ((uint32)addbuf[1] << 8) + addbuf[0];
if (block.flags & RAR3_BLOCK_ADDSIZE)
{
blocksize += (uint32)block.addsize;
block.trailsize = blocksize - sizeof(buf) - 4;
}
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);
return block;
}
bool RarVolume::ReadRar3File(DiskFile& file, RarBlock& block, RarFile& innerFile)
{
innerFile.m_splitBefore = block.flags & RAR3_FILE_SPLITBEFORE;
innerFile.m_splitAfter = block.flags & RAR3_FILE_SPLITAFTER;
uint16 namelen;
uint32 size;
if (!Read32(file, &block, &size)) return false;
innerFile.m_size = size;
if (!Skip(file, &block, 1)) return false;
if (!Skip(file, &block, 4)) return false;
if (!Read32(file, &block, &innerFile.m_time)) return false;
if (!Skip(file, &block, 2)) return false;
if (!Read16(file, &block, &namelen)) return false;
if (!Read32(file, &block, &innerFile.m_attr)) return false;
if (block.flags & RAR3_FILE_ADDSIZE)
{
uint32 highsize;
if (!Read32(file, &block, &highsize)) return false;
block.trailsize += (uint64)highsize << 32;
if (!Read32(file, &block, &highsize)) return false;
innerFile.m_size += (uint64)highsize << 32;
}
if (namelen > 8192) return false; // an error
CharBuffer name;
name.Reserve(namelen + 1);
if (!Read(file, &block, (char*)name, namelen)) return false;
name[namelen] = '\0';
innerFile.m_filename = name;
debug("%i, %i, %s", (int)block.trailsize, (int)namelen, (const char*)name);
return true;
}
bool RarVolume::ReadRar5Volume(DiskFile& file)
{
debug("Reading rar5-file %s", *m_filename);
file.Seek(8);
while (!file.Eof())
{
RarBlock block = ReadRar5Block(file);
if (!block.type)
{
return false;
}
if (block.type == RAR5_BLOCK_MAIN)
{
uint64 arcflags;
if (!ReadV(file, &block, &arcflags)) return false;
if (arcflags & RAR5_MAIN_VOLNR)
{
uint64 volnr;
if (!ReadV(file, &block, &volnr)) return false;
m_volumeNo = (uint32)volnr;
}
m_newNaming = true;
m_multiVolume = (arcflags & RAR5_MAIN_ISVOL) != 0;
}
else if (block.type == RAR5_BLOCK_ENCRYPTION)
{
uint64 val;
if (!ReadV(file, &block, &val)) return false;
if (val != 0) return false; // supporting only AES
if (!ReadV(file, &block, &val)) return false;
uint8 kdfCount;
uint8 salt[16];
if (!Read(file, &block, &kdfCount, sizeof(kdfCount))) return false;
if (!Read(file, &block, &salt, sizeof(salt))) return false;
m_encrypted = true;
if (m_password.Empty()) return false;
if (!DecryptRar5Prepare(kdfCount, salt)) return false;
}
else if (block.type == RAR5_BLOCK_FILE)
{
RarFile innerFile;
if (!ReadRar5File(file, block, innerFile)) return false;
m_files.push_back(std::move(innerFile));
}
else if (block.type == RAR5_BLOCK_ENDARC)
{
uint64 endflags;
if (!ReadV(file, &block, &endflags)) return false;
m_hasNextVolume = (endflags & RAR5_ENDARC_NEXTVOL) != 0;
break;
}
else if (block.type < 1 || block.type > 5)
{
// inlvaid block type
return false;
}
uint64 skip = block.trailsize;
if (m_encrypted)
{
skip -= 16 - m_decryptPos;
if (m_decryptPos < 16)
{
skip += skip % 16 > 0 ? 16 - skip % 16 : 0;
m_decryptPos = 16;
}
DecryptFree();
}
if (!file.Seek(skip, DiskFile::soCur))
{
return false;
}
}
return true;
}
RarVolume::RarBlock RarVolume::ReadRar5Block(DiskFile& file)
{
RarBlock block {0};
uint64 buf = 0;
if (m_encrypted &&
!(file.Read(m_decryptIV, sizeof(m_decryptIV)) == sizeof(m_decryptIV) &&
DecryptInit(256)))
{
return {0};
}
if (!Read32(file, nullptr, &block.crc)) return {0};
if (!ReadV(file, nullptr, &buf)) return {0};
uint32 size = (uint32)buf;
block.trailsize = size;
if (!ReadV(file, &block, &buf)) return {0};
block.type = (uint8)buf;
if (!ReadV(file, &block, &buf)) return {0};
block.flags = (uint16)buf;
block.addsize = 0;
if ((block.flags & RAR5_BLOCK_EXTRADATA) && !ReadV(file, &block, &block.addsize)) return {0};
uint64 datasize = 0;
if ((block.flags & RAR5_BLOCK_DATAAREA) && !ReadV(file, &block, &datasize)) return {0};
block.trailsize += datasize;
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);
return block;
}
bool RarVolume::ReadRar5File(DiskFile& file, RarBlock& block, RarFile& innerFile)
{
innerFile.m_splitBefore = block.flags & RAR5_BLOCK_SPLITBEFORE;
innerFile.m_splitAfter = block.flags & RAR5_BLOCK_SPLITAFTER;
uint64 val;
uint64 fileflags;
if (!ReadV(file, &block, &fileflags)) return false;
if (!ReadV(file, &block, &val)) return false; // skip
innerFile.m_size = (int64)val;
if (!ReadV(file, &block, &val)) return false;
innerFile.m_attr = (uint32)val;
if (fileflags & RAR5_FILE_TIME && !Read32(file, &block, &innerFile.m_time)) return false;
if (fileflags & RAR5_FILE_CRC && !Skip(file, &block, 4)) return false;
if (!ReadV(file, &block, &val)) return false; // skip
if (!ReadV(file, &block, &val)) return false; // skip
uint64 namelen;
if (!ReadV(file, &block, &namelen)) return false;
if (namelen > 8192) return false; // an error
CharBuffer name;
name.Reserve((uint32)namelen + 1);
if (!Read(file, &block, (char*)name, namelen)) return false;
name[namelen] = '\0';
innerFile.m_filename = name;
// reading extra headers to find file time
if (block.flags & RAR5_BLOCK_EXTRADATA)
{
uint64 remsize = block.addsize;
while (remsize > 0)
{
uint64 trailsize = block.trailsize;
uint64 len;
if (!ReadV(file, &block, &len)) return false;
remsize -= trailsize - block.trailsize + len;
trailsize = block.trailsize;
uint64 type;
if (!ReadV(file, &block, &type)) return false;
if (type == RAR5_FILE_EXTRATIME)
{
uint64 flags;
if (!ReadV(file, &block, &flags)) return false;
if (flags & RAR5_FILE_EXTRATIMEUNIXFORMAT)
{
if (!Read32(file, &block, &innerFile.m_time)) return false;
}
else
{
uint32 timelow, timehigh;
if (!Read32(file, &block, &timelow)) return false;
if (!Read32(file, &block, &timehigh)) return false;
uint64 wintime = ((uint64)timehigh << 32) + timelow;
innerFile.m_time = (uint32)(wintime / 10000000 - 11644473600LL);
}
}
len -= trailsize - block.trailsize;
if (!Skip(file, &block, len)) return false;
}
}
debug("%llu, %i, %s", (long long)block.trailsize, (int)namelen, (const char*)name);
return true;
}
void RarVolume::LogDebugInfo()
{
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);
}
}
bool RarVolume::DecryptRar3Prepare(const uint8 salt[8])
{
WString wstr(*m_password);
int len = wstr.Length();
if (len == 0) return false;
CharBuffer seed(len * 2 + 8);
for (int i = 0; i < len; i++)
{
wchar_t ch = wstr[i];
seed[i * 2] = ch & 0xFF;
seed[i * 2 + 1] = (ch & 0xFF00) >> 8;
}
memcpy(seed + len * 2, salt, 8);
debug("seed: %s", *Util::FormatBuffer((const char*)seed, seed.Size()));
#ifdef HAVE_OPENSSL
EVP_MD_CTX* context = EVP_MD_CTX_create();
if (!EVP_DigestInit(context, EVP_sha1()))
{
EVP_MD_CTX_destroy(context);
return false;
}
#elif defined(HAVE_NETTLE)
sha1_ctx context;
sha1_init(&context);
#else
return false;
#endif
uint8 digest[20];
const int rounds = 0x40000;
for (int i = 0; i < rounds; i++)
{
#ifdef HAVE_OPENSSL
EVP_DigestUpdate(context, *seed, seed.Size());
#elif defined(HAVE_NETTLE)
sha1_update(&context, seed.Size(), (const uint8_t*)*seed);
#endif
uint8 buf[3];
buf[0] = (uint8)i;
buf[1] = (uint8)(i >> 8);
buf[2] = (uint8)(i >> 16);
#ifdef HAVE_OPENSSL
EVP_DigestUpdate(context, buf, sizeof(buf));
#elif defined(HAVE_NETTLE)
sha1_update(&context, sizeof(buf), buf);
#endif
if (i % (rounds / 16) == 0)
{
#ifdef HAVE_OPENSSL
EVP_MD_CTX* ivContext = EVP_MD_CTX_create();
EVP_MD_CTX_copy(ivContext, context);
EVP_DigestFinal(ivContext, digest, nullptr);
EVP_MD_CTX_destroy(ivContext);
#elif defined(HAVE_NETTLE)
sha1_ctx ivContext = context;
sha1_digest(&ivContext, sizeof(digest), digest);
#endif
m_decryptIV[i / (rounds / 16)] = digest[sizeof(digest) - 1];
}
}
#ifdef HAVE_OPENSSL
EVP_DigestFinal(context, digest, nullptr);
EVP_MD_CTX_destroy(context);
#elif defined(HAVE_NETTLE)
sha1_digest(&context, sizeof(digest), digest);
#endif
debug("digest: %s", *Util::FormatBuffer((const char*)digest, sizeof(digest)));
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
m_decryptKey[i * 4 + j] = digest[i * 4 + 3 - j];
}
}
debug("key: %s", *Util::FormatBuffer((const char*)m_decryptKey, sizeof(m_decryptKey)));
debug("iv: %s", *Util::FormatBuffer((const char*)m_decryptIV, sizeof(m_decryptIV)));
return true;
}
bool RarVolume::DecryptRar5Prepare(uint8 kdfCount, const uint8 salt[16])
{
if (kdfCount > 24) return false;
int iterations = 1 << kdfCount;
#ifdef HAVE_OPENSSL
if (!PKCS5_PBKDF2_HMAC(m_password, m_password.Length(), salt, 16,
iterations, EVP_sha256(), sizeof(m_decryptKey), m_decryptKey)) return false;
return true;
#elif defined(HAVE_NETTLE)
pbkdf2_hmac_sha256(m_password.Length(), (const uint8_t*)*m_password,
iterations, 16, salt, sizeof(m_decryptKey), m_decryptKey);
return true;
#else
return false;
#endif
}
bool RarVolume::DecryptInit(int keyLength)
{
#ifdef HAVE_OPENSSL
if (!(m_context = EVP_CIPHER_CTX_new())) return false;
if (!EVP_DecryptInit((EVP_CIPHER_CTX*)m_context,
keyLength == 128 ? EVP_aes_128_cbc() : EVP_aes_256_cbc(),
m_decryptKey, m_decryptIV))
return false;
return true;
#elif defined(HAVE_NETTLE)
m_context = new aes_ctx;
aes_set_decrypt_key((aes_ctx*)m_context, keyLength == 128 ? 16 : 32, m_decryptKey);
return true;
#else
return false;
#endif
}
bool RarVolume::DecryptBuf(const uint8 in[16], uint8 out[16])
{
#ifdef HAVE_OPENSSL
uint8 outbuf[32];
int outlen = 0;
if (!EVP_DecryptUpdate((EVP_CIPHER_CTX*)m_context, outbuf, &outlen, in, 16)) return false;
memcpy(out, outbuf + outlen, 16);
debug("decrypted: %s", *Util::FormatBuffer((const char*)out, 16));
return true;
#elif defined(HAVE_NETTLE)
aes_decrypt((aes_ctx*)m_context, 16, out, in);
for (int i = 0; i < 16; i++)
{
out[i] ^= m_decryptIV[i];
}
memcpy(m_decryptIV, in, 16);
debug("decrypted: %s", *Util::FormatBuffer((const char*)out, 16));
return true;
#else
return false;
#endif
}
void RarVolume::DecryptFree()
{
if (m_context)
{
#ifdef HAVE_OPENSSL
EVP_CIPHER_CTX_free((EVP_CIPHER_CTX*)m_context);
#elif defined(HAVE_NETTLE)
delete (aes_ctx*)m_context;
#endif
m_context = nullptr;
}
}
bool RarVolume::DecryptRead(DiskFile& file, void* buffer, int64 size)
{
while (size > 0)
{
if (m_decryptPos >= 16)
{
uint8 buf[16];
if (file.Read(&buf, sizeof(buf)) != sizeof(buf)) return false;
m_decryptPos = 0;
if (!DecryptBuf(buf, m_decryptBuf)) return false;
}
uint8 remainingBuf = 16 - m_decryptPos;
uint8 len = size <= remainingBuf ? (uint8)size : remainingBuf;
memcpy(buffer, m_decryptBuf + m_decryptPos, len);
m_decryptPos += len;
size -= len;
buffer = (char*)buffer + len;
}
return true;
}

View File

@@ -0,0 +1,114 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 RARREADER_H
#define RARREADER_H
#include "NString.h"
#include "Log.h"
#include "FileSystem.h"
class RarFile
{
public:
const char* GetFilename() { return m_filename; }
uint32 GetTime() { return m_time; }
uint32 GetAttr() { return m_attr; }
int64 GetSize() { return m_size; }
bool GetSplitBefore() { return m_splitBefore; }
bool GetSplitAfter() { return m_splitAfter; }
private:
CString m_filename;
uint32 m_time = 0;
uint32 m_attr = 0;
int64 m_size = 0;
bool m_splitBefore = false;
bool m_splitAfter = false;
friend class RarVolume;
};
class RarVolume
{
public:
typedef std::deque<RarFile> FileList;
RarVolume(const char* filename) : m_filename(filename) {}
bool Read();
const char* GetFilename() { return m_filename; }
int GetVersion() { return m_version; }
uint32 GetVolumeNo() { return m_volumeNo; }
bool GetNewNaming() { return m_newNaming; }
bool GetHasNextVolume() { return m_hasNextVolume; }
bool GetMultiVolume() { return m_multiVolume; }
bool GetEncrypted() { return m_encrypted; }
void SetPassword(const char* password) { m_password = password; }
FileList* GetFiles() { return &m_files; }
private:
struct RarBlock
{
uint32 crc;
uint8 type;
uint16 flags;
uint64 addsize;
uint64 trailsize;
};
CString m_filename;
int m_version = 0;
uint32 m_volumeNo = 0;
bool m_newNaming = false;
bool m_hasNextVolume = false;
bool m_multiVolume = false;
FileList m_files;
bool m_encrypted = false;
CString m_password;
uint8 m_decryptKey[32];
uint8 m_decryptIV[16];
uint8 m_decryptBuf[16];
uint8 m_decryptPos = 16;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TlsSocket.h
void* m_context = nullptr;
void* m_session = nullptr;
int DetectRarVersion(DiskFile& file);
void LogDebugInfo();
bool Skip(DiskFile& file, RarBlock* block, int64 size);
bool Read(DiskFile& file, RarBlock* block, void* buffer, int64 size);
bool Read16(DiskFile& file, RarBlock* block, uint16* result);
bool Read32(DiskFile& file, RarBlock* block, uint32* result);
bool ReadV(DiskFile& file, RarBlock* block, uint64* result);
bool ReadRar3Volume(DiskFile& file);
bool ReadRar5Volume(DiskFile& file);
RarBlock ReadRar3Block(DiskFile& file);
RarBlock ReadRar5Block(DiskFile& file);
bool ReadRar3File(DiskFile& file, RarBlock& block, RarFile& innerFile);
bool ReadRar5File(DiskFile& file, RarBlock& block, RarFile& innerFile);
bool DecryptRar3Prepare(const uint8 salt[8]);
bool DecryptRar5Prepare(uint8 kdfCount, const uint8 salt[16]);
bool DecryptInit(int keyLength);
bool DecryptBuf(const uint8 in[16], uint8 out[16]);
void DecryptFree();
bool DecryptRead(DiskFile& file, void* buffer, int64 size);
};
#endif

View File

@@ -0,0 +1,325 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "RarRenamer.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
void RarRenamer::Execute()
{
m_progressLabel.Format("Checking renamed rar-files for %s", *m_infoName);
m_stageProgress = 0;
UpdateProgress();
BuildDirList(m_destDir);
for (CString& destDir : m_dirList)
{
debug("Checking %s", *destDir);
CheckFiles(destDir);
}
}
void RarRenamer::BuildDirList(const char* destDir)
{
m_dirList.push_back(destDir);
DirBrowser dirBrowser(destDir);
while (const char* filename = dirBrowser.Next())
{
if (!IsStopped())
{
BString<1024> fullFilename("%s%c%s", destDir, PATH_SEPARATOR, filename);
if (FileSystem::DirectoryExists(fullFilename))
{
BuildDirList(fullFilename);
}
else
{
m_fileCount++;
}
}
}
}
void RarRenamer::CheckFiles(const char* destDir)
{
DirBrowser dir(destDir);
while (const char* filename = dir.Next())
{
if (!IsStopped())
{
BString<1024> fullFilename("%s%c%s", destDir, PATH_SEPARATOR, filename);
if (!FileSystem::DirectoryExists(fullFilename))
{
m_progressLabel.Format("Checking file %s", filename);
m_stageProgress = m_fileCount > 0 ? m_curFile * 1000 / m_fileCount : 1000;
UpdateProgress();
m_curFile++;
CheckOneFile(fullFilename);
}
}
}
if (!m_volumes.empty())
{
RenameFiles(destDir);
}
}
void RarRenamer::CheckOneFile(const char* filename)
{
if (m_ignoreExt && Util::MatchFileExt(FileSystem::BaseFileName(filename), m_ignoreExt, ",;"))
{
return;
}
RarVolume volume(filename);
volume.SetPassword(m_password);
if (volume.Read())
{
m_volumes.push_back(std::move(volume));
}
}
void RarRenamer::RenameFile(const char* srcFilename, const char* destFileName)
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", FileSystem::BaseFileName(srcFilename), FileSystem::BaseFileName(destFileName));
if (!FileSystem::MoveFile(srcFilename, destFileName))
{
PrintMessage(Message::mkError, "Could not rename %s to %s: %s", srcFilename, destFileName,
*FileSystem::GetLastErrorMessage());
return;
}
m_renamedCount++;
// notify about new file name
RegisterRenamedFile(FileSystem::BaseFileName(srcFilename), FileSystem::BaseFileName(destFileName));
}
void RarRenamer::RenameFiles(const char* destDir)
{
MakeSets();
for (RarVolumeSet& set : m_sets)
{
if (!IsSetProperlyNamed(set))
{
RarFile* mainFile = FindMainFile(set);
BString<1024> mainBasename = FileSystem::BaseFileName(mainFile->GetFilename());
char* ext = strrchr(mainBasename, '.');
// strip extension if its length is 3 chars
if (ext && strlen(ext) == 4)
{
*ext = '\0';
}
BString<1024> newBasename = *mainBasename;
int num = 0;
bool willOverwrite = true;
while (willOverwrite)
{
if (num++)
{
newBasename.Format("%s-%i", *mainBasename, num);
}
for (RarVolume* volume : set)
{
CString destfilename = GenNewVolumeFilename(destDir, newBasename, volume);
willOverwrite = strcmp(volume->GetFilename(), destfilename) && FileSystem::FileExists(destfilename);
if (willOverwrite)
{
break;
}
}
}
for (RarVolume* volume : set)
{
CString destfilename = GenNewVolumeFilename(destDir, newBasename, volume);
if (strcmp(volume->GetFilename(), destfilename))
{
RenameFile(volume->GetFilename(), destfilename);
}
}
}
}
}
CString RarRenamer::GenNewVolumeFilename(const char* destDir, const char* newBasename, RarVolume* volume)
{
CString extension = volume->GetNewNaming() ? GenNewExtension(volume->GetVolumeNo()) : GenOldExtension(volume->GetVolumeNo());
return CString::FormatStr("%s%c%s.%s", destDir, PATH_SEPARATOR, newBasename, *extension);
}
CString RarRenamer::GenNewExtension(int volumeNo)
{
return CString::FormatStr("part%04i.rar", volumeNo + 1);
}
CString RarRenamer::GenOldExtension(int volumeNo)
{
if (volumeNo == 0)
{
return "rar";
}
else
{
unsigned char ch = 'r' + (volumeNo - 1) / 100;
return CString::FormatStr("%c%02d", ch, (volumeNo - 1) % 100);
}
}
void RarRenamer::MakeSets()
{
m_sets.clear();
// find first volumes and create initial incomplete sets
for (RarVolume& volume : m_volumes)
{
if (!volume.GetFiles()->empty() && volume.GetVolumeNo() == 0)
{
m_sets.push_back({&volume});
}
}
// complete sets, discard sets which cannot be completed
m_sets.erase(std::remove_if(m_sets.begin(), m_sets.end(),
[volumes = &m_volumes](RarVolumeSet& set)
{
debug("*** Building set %s", FileSystem::BaseFileName(set[0]->GetFilename()));
bool found = true;
while (found)
{
found = false;
RarVolume* lastVolume = set.back();
for (RarVolume& volume : *volumes)
{
if (!volume.GetFiles()->empty() && volume.GetMultiVolume() &&
volume.GetVolumeNo() == lastVolume->GetVolumeNo() + 1 &&
volume.GetVersion() == lastVolume->GetVersion() &&
lastVolume->GetHasNextVolume() &&
((volume.GetFiles()->at(0).GetSplitBefore() &&
lastVolume->GetFiles()->at(0).GetSplitAfter() &&
!strcmp(volume.GetFiles()->at(0).GetFilename(), lastVolume->GetFiles()->at(0).GetFilename())) ||
(!volume.GetFiles()->at(0).GetSplitBefore() && !lastVolume->GetFiles()->at(0).GetSplitAfter())))
{
debug(" adding %s", FileSystem::BaseFileName(volume.GetFilename()));
set.push_back(&volume);
found = true;
break;
}
}
}
bool completed = !set.back()->GetHasNextVolume();
return !completed;
}),
m_sets.end());
// debug log
for (RarVolumeSet& set : m_sets)
{
debug("*** Set ***");
for (RarVolume* volume : set)
{
debug(" %s", FileSystem::BaseFileName(volume->GetFilename()));
}
}
}
bool RarRenamer::IsSetProperlyNamed(RarVolumeSet& set)
{
RegEx regExPart(".*.part([0-9]+)\\.rar$");
const char* setBasename = FileSystem::BaseFileName(set[0]->GetFilename());
int setPartLen = 0;
for (RarVolume* volume : set)
{
const char* filename = FileSystem::BaseFileName(volume->GetFilename());
if (strlen(setBasename) != strlen(filename))
{
return false;
}
if (volume->GetNewNaming())
{
if (!regExPart.Match(filename))
{
return false;
}
BString<1024> partNo(filename + regExPart.GetMatchStart(1), regExPart.GetMatchLen(1));
if (setPartLen == 0)
{
setPartLen = partNo.Length();
}
bool ok = atoi(partNo) == volume->GetVolumeNo() + 1 &&
partNo.Length() == setPartLen &&
!strncmp(setBasename, filename, regExPart.GetMatchStart(1));
if (!ok)
{
return false;
}
}
else
{
const char* ext = strrchr(filename, '.');
if (!ext || strcmp(ext + 1, GenOldExtension(volume->GetVolumeNo())) ||
strncmp(setBasename, filename, ext - filename))
{
return false;
}
}
}
return true;
}
RarFile* RarRenamer::FindMainFile(RarVolumeSet& set)
{
std::deque<RarFile*> allFiles;
for (RarVolume* volume : set)
{
for (RarFile& file : *volume->GetFiles())
{
allFiles.push_back(&file);
}
}
std::deque<RarFile*>::iterator it = std::max_element(allFiles.begin(), allFiles.end(),
[](RarFile* file1, RarFile* file2)
{
return file1->GetSize() < file2->GetSize();
});
return *it;
}

View File

@@ -0,0 +1,81 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 RARRENAMER_H
#define RARRENAMER_H
#include "NString.h"
#include "Log.h"
#include "FileSystem.h"
#include "RarReader.h"
class RarRenamer
{
public:
void Execute();
void SetDestDir(const char* destDir) { m_destDir = destDir; }
const char* GetInfoName() { return m_infoName; }
void SetInfoName(const char* infoName) { m_infoName = infoName; }
void SetPassword(const char* password) { m_password = password; }
void SetIgnoreExt(const char* ignoreExt) { m_ignoreExt = ignoreExt; }
int GetRenamedCount() { return m_renamedCount; }
protected:
virtual void UpdateProgress() {}
virtual bool IsStopped() { return false; };
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3) {}
virtual void RegisterRenamedFile(const char* oldFilename, const char* newFileName) {}
const char* GetProgressLabel() { return m_progressLabel; }
int GetStageProgress() { return m_stageProgress; }
private:
typedef std::deque<CString> DirList;
typedef std::deque<RarVolume> RarVolumeList;
typedef std::deque<RarVolume*> RarVolumeSet;
typedef std::deque<RarVolumeSet> RarSets;
CString m_infoName;
CString m_destDir;
CString m_progressLabel;
int m_stageProgress = 0;
bool m_cancelled = false;
DirList m_dirList;
int m_fileCount = 0;
int m_curFile = 0;
int m_renamedCount = 0;
RarVolumeList m_volumes;
RarSets m_sets;
CString m_password;
CString m_ignoreExt;
void BuildDirList(const char* destDir);
void CheckFiles(const char* destDir);
void CheckOneFile(const char* filename);
void RenameFile(const char* srcFilename, const char* destFileName);
void RenameFiles(const char* destDir);
CString GenNewVolumeFilename(const char* destDir, const char* newBasename, RarVolume* volume);
CString GenNewExtension(int volumeNo);
CString GenOldExtension(int volumeNo);
void MakeSets();
bool IsSetProperlyNamed(RarVolumeSet& set);
RarFile* FindMainFile(RarVolumeSet& set);
};
#endif

View File

@@ -0,0 +1,221 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 "Options.h"
#include "DiskState.h"
#include "Log.h"
#include "FileSystem.h"
#include "Rename.h"
#ifndef DISABLE_PARCHECK
void RenameController::PostParRenamer::PrintMessage(Message::EKind kind, const char* format, ...)
{
char text[1024];
va_list args;
va_start(args, format);
vsnprintf(text, 1024, format, args);
va_end(args);
text[1024-1] = '\0';
m_owner->m_postInfo->GetNzbInfo()->AddMessage(kind, text);
}
#endif
void RenameController::PostRarRenamer::PrintMessage(Message::EKind kind, const char* format, ...)
{
char text[1024];
va_list args;
va_start(args, format);
vsnprintf(text, 1024, format, args);
va_end(args);
text[1024 - 1] = '\0';
m_owner->m_postInfo->GetNzbInfo()->AddMessage(kind, text);
}
RenameController::RenameController()
{
debug("Creating RenameController");
#ifndef DISABLE_PARCHECK
m_parRenamer.m_owner = this;
#endif
m_rarRenamer.m_owner = this;
}
void RenameController::StartJob(PostInfo* postInfo, EJobKind kind)
{
RenameController* renameController = new RenameController();
renameController->m_postInfo = postInfo;
renameController->m_kind = kind;
renameController->SetAutoDestroy(false);
postInfo->SetPostThread(renameController);
renameController->Start();
}
void RenameController::Run()
{
BString<1024> nzbName;
CString destDir;
CString finalDir;
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
nzbName = m_postInfo->GetNzbInfo()->GetName();
destDir = m_postInfo->GetNzbInfo()->GetDestDir();
finalDir = m_postInfo->GetNzbInfo()->GetFinalDir();
}
BString<1024> infoName("rename for %s", *nzbName);
SetInfoName(infoName);
PrintMessage(Message::mkInfo, "Checking renamed %sfiles for %s",
m_kind == jkRar ? "archive " : "", *nzbName);
ExecRename(destDir, finalDir, nzbName);
if (IsStopped())
{
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", *nzbName);
}
else if (m_renamedCount > 0)
{
PrintMessage(Message::mkInfo, "Successfully renamed %i %sfile(s) for %s",
m_renamedCount, m_kind == jkRar ? "archive " : "", *nzbName);
}
else
{
PrintMessage(Message::mkInfo, "No renamed %sfiles found for %s",
m_kind == jkRar ? "archive " : "", *nzbName);
}
RenameCompleted();
}
void RenameController::AddMessage(Message::EKind kind, const char* text)
{
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
}
void RenameController::ExecRename(const char* destDir, const char* finalDir, const char* nzbName)
{
if (m_kind == jkPar)
{
#ifndef DISABLE_PARCHECK
m_parRenamer.SetDestDir(m_postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usSuccess &&
!Util::EmptyStr(finalDir) ? finalDir : destDir);
m_parRenamer.SetInfoName(nzbName);
m_parRenamer.SetDetectMissing(m_postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone);
m_parRenamer.Execute();
#endif
}
else if (m_kind == jkRar)
{
m_rarRenamer.SetDestDir(destDir);
m_rarRenamer.SetInfoName(nzbName);
m_rarRenamer.SetIgnoreExt(g_Options->GetUnpackIgnoreExt());
NzbParameter* parameter = m_postInfo->GetNzbInfo()->GetParameters()->Find("*Unpack:Password", false);
if (parameter)
{
m_rarRenamer.SetPassword(parameter->GetValue());
}
m_rarRenamer.Execute();
}
}
void RenameController::RenameCompleted()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (m_kind == jkPar)
{
m_postInfo->GetNzbInfo()->SetParRenameStatus(m_renamedCount > 0 ? NzbInfo::rsSuccess : NzbInfo::rsNothing);
#ifndef DISABLE_PARCHECK
// request another par2-file if the renaming has failed due to damaged par2-files
if (m_renamedCount == 0 && m_parRenamer.HasDamagedParFiles() &&
m_postInfo->GetNzbInfo()->GetRemainingParCount() > 0)
{
m_parRenamer.PrintMessage(Message::mkInfo, "Requesting extra par2-files for %s to perform par-rename", m_parRenamer.GetInfoName());
downloadQueue->EditEntry(m_postInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupResume, nullptr);
downloadQueue->EditEntry(m_postInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupPauseExtraPars, nullptr);
if (m_postInfo->GetNzbInfo()->GetRemainingSize() > 0)
{
// reset rename status to execute renamer again, after the new par2-file is downloaded
m_postInfo->GetNzbInfo()->SetParRenameStatus(NzbInfo::rsNone);
}
}
#endif
}
else if (m_kind == jkRar)
{
m_postInfo->GetNzbInfo()->SetRarRenameStatus(m_renamedCount > 0 ? NzbInfo::rsSuccess : NzbInfo::rsNothing);
}
#ifndef DISABLE_PARCHECK
if (m_parRenamer.HasMissedFiles() && m_postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped)
{
m_parRenamer.PrintMessage(Message::mkInfo, "Requesting par-check/repair for %s to restore missing files ", m_parRenamer.GetInfoName());
m_postInfo->SetRequestParCheck(true);
}
#endif
m_postInfo->SetWorking(false);
}
#ifndef DISABLE_PARCHECK
void RenameController::UpdateParRenameProgress()
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->SetProgressLabel(m_parRenamer.GetProgressLabel());
m_postInfo->SetStageProgress(m_parRenamer.GetStageProgress());
}
#endif
void RenameController::UpdateRarRenameProgress()
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->SetProgressLabel(m_rarRenamer.GetProgressLabel());
m_postInfo->SetStageProgress(m_rarRenamer.GetStageProgress());
}
/**
* Update file name in the CompletedFiles-list of NZBInfo
*/
void RenameController::RegisterRenamedFile(const char* oldFilename, const char* newFileName)
{
for (CompletedFile& completedFile : m_postInfo->GetNzbInfo()->GetCompletedFiles())
{
if (!strcasecmp(completedFile.GetFileName(), oldFilename))
{
completedFile.SetFileName(newFileName);
break;
}
}
m_renamedCount++;
}

View File

@@ -0,0 +1,98 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2016 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 RENAME_H
#define RENAME_H
#include "Thread.h"
#include "DownloadInfo.h"
#include "Script.h"
#include "RarRenamer.h"
#ifndef DISABLE_PARCHECK
#include "ParRenamer.h"
#endif
class RenameController : public Thread, public ScriptController
{
public:
enum EJobKind
{
jkPar,
jkRar
};
RenameController();
virtual void Run();
static void StartJob(PostInfo* postInfo, EJobKind kind);
protected:
virtual void AddMessage(Message::EKind kind, const char* text);
private:
PostInfo* m_postInfo;
CString m_destDir;
int m_renamedCount = 0;
EJobKind m_kind;
#ifndef DISABLE_PARCHECK
class PostParRenamer : public ParRenamer
{
protected:
virtual void UpdateProgress() { m_owner->UpdateParRenameProgress(); }
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3);
virtual void RegisterParredFile(const char* filename)
{ m_owner->m_postInfo->GetParredFiles()->push_back(filename); }
virtual void RegisterRenamedFile(const char* oldFilename, const char* newFileName)
{ m_owner->RegisterRenamedFile(oldFilename, newFileName); }
virtual bool IsStopped() { return m_owner->IsStopped(); };
private:
RenameController* m_owner;
friend class RenameController;
};
PostParRenamer m_parRenamer;
void UpdateParRenameProgress();
#endif
class PostRarRenamer : public RarRenamer
{
protected:
virtual void UpdateProgress() { m_owner->UpdateRarRenameProgress(); }
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3);
virtual void RegisterRenamedFile(const char* oldFilename, const char* newFileName)
{ m_owner->RegisterRenamedFile(oldFilename, newFileName); }
virtual bool IsStopped() { return m_owner->IsStopped(); };
private:
RenameController* m_owner;
friend class RenameController;
};
PostRarRenamer m_rarRenamer;
void UpdateRarRenameProgress();
void ExecRename(const char* destDir, const char* finalDir, const char* nzbName);
void RenameCompleted();
void RegisterRenamedFile(const char* oldFilename, const char* newFileName);
};
#endif

View File

@@ -19,7 +19,7 @@
#include "nzbget.h"
#include "ParCoordinator.h"
#include "Repair.h"
#include "DupeCoordinator.h"
#include "ParParser.h"
#include "Options.h"
@@ -28,17 +28,17 @@
#include "FileSystem.h"
#ifndef DISABLE_PARCHECK
bool ParCoordinator::PostParChecker::RequestMorePars(int blockNeeded, int* blockFound)
bool RepairController::PostParChecker::RequestMorePars(int blockNeeded, int* blockFound)
{
return m_owner->RequestMorePars(m_postInfo->GetNzbInfo(), GetParFilename(), blockNeeded, blockFound);
}
void ParCoordinator::PostParChecker::UpdateProgress()
void RepairController::PostParChecker::UpdateProgress()
{
m_owner->UpdateParCheckProgress();
}
void ParCoordinator::PostParChecker::PrintMessage(Message::EKind kind, const char* format, ...)
void RepairController::PostParChecker::PrintMessage(Message::EKind kind, const char* format, ...)
{
char text[1024];
va_list args;
@@ -50,12 +50,12 @@ void ParCoordinator::PostParChecker::PrintMessage(Message::EKind kind, const cha
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
}
void ParCoordinator::PostParChecker::RegisterParredFile(const char* filename)
void RepairController::PostParChecker::RegisterParredFile(const char* filename)
{
m_postInfo->GetParredFiles()->push_back(filename);
}
bool ParCoordinator::PostParChecker::IsParredFile(const char* filename)
bool RepairController::PostParChecker::IsParredFile(const char* filename)
{
for (CString& parredFile : m_postInfo->GetParredFiles())
{
@@ -67,7 +67,7 @@ bool ParCoordinator::PostParChecker::IsParredFile(const char* filename)
return false;
}
ParChecker::EFileStatus ParCoordinator::PostParChecker::FindFileCrc(const char* filename,
ParChecker::EFileStatus RepairController::PostParChecker::FindFileCrc(const char* filename,
uint32* crc, SegmentList* segments)
{
CompletedFile* completedFile = nullptr;
@@ -114,7 +114,7 @@ ParChecker::EFileStatus ParCoordinator::PostParChecker::FindFileCrc(const char*
ParChecker::fsUnknown;
}
void ParCoordinator::PostParChecker::RequestDupeSources(DupeSourceList* dupeSourceList)
void RepairController::PostParChecker::RequestDupeSources(DupeSourceList* dupeSourceList)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
@@ -145,7 +145,7 @@ void ParCoordinator::PostParChecker::RequestDupeSources(DupeSourceList* dupeSour
}
}
void ParCoordinator::PostParChecker::StatDupeSources(DupeSourceList* dupeSourceList)
void RepairController::PostParChecker::StatDupeSources(DupeSourceList* dupeSourceList)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
@@ -169,44 +169,8 @@ void ParCoordinator::PostParChecker::StatDupeSources(DupeSourceList* dupeSourceL
m_postInfo->GetNzbInfo()->SetExtraParBlocks(m_postInfo->GetNzbInfo()->GetExtraParBlocks() + totalExtraParBlocks);
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_owner->UpdateParRenameProgress();
}
void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind kind, const char* format, ...)
{
char text[1024];
va_list args;
va_start(args, format);
vsnprintf(text, 1024, format, args);
va_end(args);
text[1024-1] = '\0';
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
}
void ParCoordinator::PostParRenamer::RegisterParredFile(const char* filename)
{
m_postInfo->GetParredFiles()->push_back(filename);
}
/**
* Update file name in the CompletedFiles-list of NZBInfo
*/
void ParCoordinator::PostParRenamer::RegisterRenamedFile(const char* oldFilename, const char* newFileName)
{
for (CompletedFile& completedFile : m_postInfo->GetNzbInfo()->GetCompletedFiles())
{
if (!strcasecmp(completedFile.GetFileName(), oldFilename))
{
completedFile.SetFileName(newFileName);
break;
}
}
}
void ParCoordinator::PostDupeMatcher::PrintMessage(Message::EKind kind, const char* format, ...)
void RepairController::PostDupeMatcher::PrintMessage(Message::EKind kind, const char* format, ...)
{
char text[1024];
va_list args;
@@ -220,127 +184,66 @@ void ParCoordinator::PostDupeMatcher::PrintMessage(Message::EKind kind, const ch
#endif
ParCoordinator::ParCoordinator()
RepairController::RepairController()
{
debug("Creating ParCoordinator");
debug("Creating RepairController");
#ifndef DISABLE_PARCHECK
m_parChecker.m_owner = this;
m_parRenamer.m_owner = this;
#endif
}
ParCoordinator::~ParCoordinator()
void RepairController::Stop()
{
debug("Destroying ParCoordinator");
debug("Stopping RepairController");
Thread::Stop();
#ifndef DISABLE_PARCHECK
m_parChecker.Cancel();
#endif
}
#ifndef DISABLE_PARCHECK
void ParCoordinator::Stop()
void RepairController::StartJob(PostInfo* postInfo)
{
debug("Stopping ParCoordinator");
RepairController* repairController = new RepairController();
repairController->m_postInfo = postInfo;
repairController->SetAutoDestroy(false);
m_stopped = true;
postInfo->SetPostThread(repairController);
if (m_parChecker.IsRunning())
repairController->Start();
}
void RepairController::Run()
{
BString<1024> nzbName;
CString destDir;
{
m_parChecker.Stop();
int mSecWait = 5000;
while (m_parChecker.IsRunning() && mSecWait > 0)
{
usleep(50 * 1000);
mSecWait -= 50;
}
if (m_parChecker.IsRunning())
{
warn("Terminating par-check for %s", m_parChecker.GetInfoName());
m_parChecker.Kill();
}
GuardedDownloadQueue guard = DownloadQueue::Guard();
nzbName = m_postInfo->GetNzbInfo()->GetName();
destDir = m_postInfo->GetNzbInfo()->GetDestDir();
}
}
#endif
void ParCoordinator::PausePars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
debug("ParCoordinator: Pausing pars");
downloadQueue->EditEntry(nzbInfo->GetId(),
DownloadQueue::eaGroupPauseExtraPars, 0, nullptr);
}
#ifndef DISABLE_PARCHECK
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParCheckJob(PostInfo* postInfo)
{
m_currentJob = jkParCheck;
m_parChecker.SetPostInfo(postInfo);
m_parChecker.SetDestDir(postInfo->GetNzbInfo()->GetDestDir());
m_parChecker.SetNzbName(postInfo->GetNzbInfo()->GetName());
m_parChecker.SetPostInfo(m_postInfo);
m_parChecker.SetDestDir(destDir);
m_parChecker.SetNzbName(nzbName);
m_parChecker.SetParTime(Util::CurrentTime());
m_parChecker.SetDownloadSec(postInfo->GetNzbInfo()->GetDownloadSec());
m_parChecker.SetParQuick(g_Options->GetParQuick() && !postInfo->GetForceParFull());
m_parChecker.SetForceRepair(postInfo->GetForceRepair());
m_parChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", postInfo->GetNzbInfo()->GetName());
postInfo->SetWorking(true);
m_parChecker.Start();
m_parChecker.SetDownloadSec(m_postInfo->GetNzbInfo()->GetDownloadSec());
m_parChecker.SetParQuick(g_Options->GetParQuick() && !m_postInfo->GetForceParFull());
m_parChecker.SetForceRepair(m_postInfo->GetForceRepair());
m_parChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", *nzbName);
m_parChecker.Execute();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParRenameJob(PostInfo* postInfo)
bool RepairController::AddPar(FileInfo* fileInfo, bool deleted)
{
const char* destDir = postInfo->GetNzbInfo()->GetDestDir();
if (postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usSuccess &&
!Util::EmptyStr(postInfo->GetNzbInfo()->GetFinalDir()))
{
destDir = postInfo->GetNzbInfo()->GetFinalDir();
}
m_currentJob = jkParRename;
m_parRenamer.SetPostInfo(postInfo);
m_parRenamer.SetDestDir(destDir);
m_parRenamer.SetInfoName(postInfo->GetNzbInfo()->GetName());
m_parRenamer.SetDetectMissing(postInfo->GetNzbInfo()->GetUnpackStatus() == NzbInfo::usNone);
m_parRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", postInfo->GetNzbInfo()->GetName());
postInfo->SetWorking(true);
m_parRenamer.Start();
}
bool ParCoordinator::Cancel()
{
if (m_currentJob == jkParCheck)
{
if (!m_parChecker.GetCancelled())
{
debug("Cancelling par-repair for %s", m_parChecker.GetInfoName());
m_parChecker.Cancel();
return true;
}
}
else if (m_currentJob == jkParRename)
{
if (!m_parRenamer.GetCancelled())
{
debug("Cancelling par-rename for %s", m_parRenamer.GetInfoName());
m_parRenamer.Cancel();
return true;
}
}
return false;
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
bool ParCoordinator::AddPar(FileInfo* fileInfo, bool deleted)
{
bool sameCollection = m_parChecker.IsRunning() &&
fileInfo->GetNzbInfo() == m_parChecker.GetPostInfo()->GetNzbInfo();
bool sameCollection = fileInfo->GetNzbInfo() == m_parChecker.GetPostInfo()->GetNzbInfo();
if (sameCollection && !deleted)
{
BString<1024> fullFilename("%s%c%s", fileInfo->GetNzbInfo()->GetDestDir(), (int)PATH_SEPARATOR, fileInfo->GetFilename());
@@ -353,7 +256,7 @@ bool ParCoordinator::AddPar(FileInfo* fileInfo, bool deleted)
return sameCollection;
}
void ParCoordinator::ParCheckCompleted()
void RepairController::ParCheckCompleted()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
@@ -385,9 +288,6 @@ void ParCoordinator::ParCheckCompleted()
postInfo->GetNzbInfo()->SetParFull(m_parChecker.GetParFull());
postInfo->SetWorking(false);
postInfo->SetStage(PostInfo::ptQueued);
downloadQueue->Save();
}
/**
@@ -397,80 +297,68 @@ void ParCoordinator::ParCheckCompleted()
* special case: returns true if there are any unpaused par2-files in the queue regardless
* of the amount of blocks; this is to keep par-checker wait for download completion.
*/
bool ParCoordinator::RequestMorePars(NzbInfo* nzbInfo, const char* parFilename, int blockNeeded, int* blockFoundOut)
bool RepairController::RequestMorePars(NzbInfo* nzbInfo, const char* parFilename, int blockNeeded, int* blockFoundOut)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
Blocks blocks;
blocks.clear();
Blocks availableBlocks;
Blocks selectedBlocks;
int blockFound = 0;
int curBlockFound = 0;
FindPars(downloadQueue, nzbInfo, parFilename, blocks, true, true, &curBlockFound);
FindPars(downloadQueue, nzbInfo, parFilename, availableBlocks, true, true, &curBlockFound);
blockFound += curBlockFound;
if (blockFound < blockNeeded)
{
FindPars(downloadQueue, nzbInfo, parFilename, blocks, true, false, &curBlockFound);
FindPars(downloadQueue, nzbInfo, parFilename, availableBlocks, true, false, &curBlockFound);
blockFound += curBlockFound;
}
if (blockFound < blockNeeded)
{
FindPars(downloadQueue, nzbInfo, parFilename, blocks, false, false, &curBlockFound);
FindPars(downloadQueue, nzbInfo, parFilename, availableBlocks, false, false, &curBlockFound);
blockFound += curBlockFound;
}
std::sort(availableBlocks.begin(), availableBlocks.end(),
[](BlockInfo& block1, BlockInfo& block2)
{
return block1.m_blockCount < block2.m_blockCount;
});
if (blockFound >= blockNeeded)
{
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
// starting from the file with max block count.
// if par-collection was built exponentially and all par-files present,
// this step selects par-files with exact number of blocks we need.
while (blockNeeded > 0)
// collect as much blocks as needed
for (Blocks::iterator it = availableBlocks.begin(); blockNeeded > 0 && it != availableBlocks.end(); it++)
{
BlockInfo* bestBlockInfo = nullptr;
Blocks::iterator bestBlockIter;
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
BlockInfo& blockInfo = *it;
selectedBlocks.push_front(blockInfo);
blockNeeded -= blockInfo.m_blockCount;
}
// discarding superfluous blocks
for (Blocks::iterator it = selectedBlocks.begin(); it != selectedBlocks.end(); )
{
BlockInfo& blockInfo = *it;
if (blockNeeded + blockInfo.m_blockCount <= 0)
{
BlockInfo& blockInfo = *it;
if (blockInfo.m_blockCount <= blockNeeded &&
(!bestBlockInfo || bestBlockInfo->m_blockCount < blockInfo.m_blockCount))
{
bestBlockInfo = &blockInfo;
bestBlockIter = it;
}
}
if (bestBlockInfo)
{
if (bestBlockInfo->m_fileInfo->GetPaused())
{
m_parChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", nzbInfo->GetName(), (int)PATH_SEPARATOR, bestBlockInfo->m_fileInfo->GetFilename());
bestBlockInfo->m_fileInfo->SetPaused(false);
bestBlockInfo->m_fileInfo->SetExtraPriority(true);
}
blockNeeded -= bestBlockInfo->m_blockCount;
blocks.erase(bestBlockIter);
blockNeeded += blockInfo.m_blockCount;
it = selectedBlocks.erase(it);
}
else
{
break;
it++;
}
}
// 2. then unpause other files
// this step only needed if the par-collection was built not exponentially
// or not all par-files present (or some of them were corrupted)
// this step is not optimal, but we hope, that the first step will work good
// in most cases and we will not need the second step often
while (blockNeeded > 0)
// unpause files with blocks
for (BlockInfo& blockInfo : selectedBlocks)
{
BlockInfo& blockInfo = blocks.front();
if (blockInfo.m_fileInfo->GetPaused())
{
m_parChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", nzbInfo->GetName(), (int)PATH_SEPARATOR, blockInfo.m_fileInfo->GetFilename());
blockInfo.m_fileInfo->SetPaused(false);
blockInfo.m_fileInfo->SetExtraPriority(true);
}
blockNeeded -= blockInfo.m_blockCount;
}
}
@@ -494,7 +382,7 @@ bool ParCoordinator::RequestMorePars(NzbInfo* nzbInfo, const char* parFilename,
return ok;
}
void ParCoordinator::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, const char* parFilename,
void RepairController::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, const char* parFilename,
Blocks& blocks, bool strictParName, bool exactParName, int* blockFound)
{
*blockFound = 0;
@@ -502,7 +390,7 @@ void ParCoordinator::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, co
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* baseParFilename = FileSystem::BaseFileName(parFilename);
int mainBaseLen = 0;
if (!ParParser::ParseParFilename(baseParFilename, &mainBaseLen, nullptr))
if (!ParParser::ParseParFilename(baseParFilename, true, &mainBaseLen, nullptr))
{
// should not happen
nzbInfo->PrintMessage(Message::mkError, "Internal error: could not parse filename %s", baseParFilename);
@@ -515,7 +403,7 @@ void ParCoordinator::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, co
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
int blockCount = 0;
if (ParParser::ParseParFilename(fileInfo->GetFilename(), nullptr, &blockCount) &&
if (ParParser::ParseParFilename(fileInfo->GetFilename(), fileInfo->GetFilenameConfirmed(), nullptr, &blockCount) &&
blockCount > 0)
{
bool useFile = true;
@@ -566,7 +454,7 @@ void ParCoordinator::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, co
}
}
void ParCoordinator::UpdateParCheckProgress()
void RepairController::UpdateParCheckProgress()
{
PostInfo* postInfo;
@@ -600,7 +488,7 @@ void ParCoordinator::UpdateParCheckProgress()
}
bool parCancel = false;
if (!m_parChecker.GetCancelled())
if (!IsStopped())
{
if ((g_Options->GetParTimeLimit() > 0) &&
m_parChecker.GetStage() == PostParChecker::ptRepairing &&
@@ -621,14 +509,14 @@ void ParCoordinator::UpdateParCheckProgress()
if (parCancel)
{
m_parChecker.Cancel();
Stop();
}
}
CheckPauseState(postInfo);
}
void ParCoordinator::CheckPauseState(PostInfo* postInfo)
void RepairController::CheckPauseState(PostInfo* postInfo)
{
if (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority())
{
@@ -639,7 +527,7 @@ void ParCoordinator::CheckPauseState(PostInfo* postInfo)
time_t waitTime = Util::CurrentTime();
// wait until Post-processor is unpaused
while (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !m_stopped)
while (g_Options->GetPausePostProcess() && !postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
{
usleep(50 * 1000);
@@ -667,44 +555,4 @@ void ParCoordinator::CheckPauseState(PostInfo* postInfo)
}
}
void ParCoordinator::ParRenameCompleted()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
PostInfo* postInfo = m_parRenamer.GetPostInfo();
postInfo->GetNzbInfo()->SetRenameStatus(m_parRenamer.GetStatus() == ParRenamer::psSuccess ? NzbInfo::rsSuccess : NzbInfo::rsFailure);
if (m_parRenamer.HasMissedFiles() && postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped)
{
m_parRenamer.PrintMessage(Message::mkInfo, "Requesting par-check/repair for %s to restore missing files ", m_parRenamer.GetInfoName());
postInfo->SetRequestParCheck(true);
}
postInfo->SetWorking(false);
postInfo->SetStage(PostInfo::ptQueued);
downloadQueue->Save();
}
void ParCoordinator::UpdateParRenameProgress()
{
PostInfo* postInfo;
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
postInfo = m_parRenamer.GetPostInfo();
postInfo->SetProgressLabel(m_parRenamer.GetProgressLabel());
postInfo->SetStageProgress(m_parRenamer.GetStageProgress());
time_t current = Util::CurrentTime();
if (postInfo->GetStage() != PostInfo::ptRenaming)
{
postInfo->SetStage(PostInfo::ptRenaming);
postInfo->SetStageTime(current);
}
}
CheckPauseState(postInfo);
}
#endif

View File

@@ -18,36 +18,32 @@
*/
#ifndef PARCOORDINATOR_H
#define PARCOORDINATOR_H
#ifndef REPAIR_H
#define REPAIR_H
#include "DownloadInfo.h"
#include "Thread.h"
#include "Script.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#include "ParRenamer.h"
#include "DupeMatcher.h"
#endif
class ParCoordinator
class RepairController : public Thread, public ScriptController
{
public:
ParCoordinator();
virtual ~ParCoordinator();
void PausePars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
RepairController();
virtual void Stop();
#ifndef DISABLE_PARCHECK
virtual void Run();
static void StartJob(PostInfo* postInfo);
bool AddPar(FileInfo* fileInfo, bool deleted);
void StartParCheckJob(PostInfo* postInfo);
void StartParRenameJob(PostInfo* postInfo);
void Stop();
bool Cancel();
protected:
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void ParCheckCompleted();
void ParRenameCompleted();
void CheckPauseState(PostInfo* postInfo);
bool RequestMorePars(NzbInfo* nzbInfo, const char* parFilename, int blockNeeded, int* blockFound);
@@ -66,6 +62,7 @@ private:
protected:
virtual bool RequestMorePars(int blockNeeded, int* blockFound);
virtual void UpdateProgress();
virtual bool IsStopped() { return m_owner->IsStopped(); };
virtual void Completed() { m_owner->ParCheckCompleted(); }
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3);
virtual void RegisterParredFile(const char* filename);
@@ -74,31 +71,13 @@ private:
virtual void RequestDupeSources(DupeSourceList* dupeSourceList);
virtual void StatDupeSources(DupeSourceList* dupeSourceList);
private:
ParCoordinator* m_owner;
RepairController* m_owner;
PostInfo* m_postInfo;
time_t m_parTime;
time_t m_repairTime;
int m_downloadSec;
friend class ParCoordinator;
};
class PostParRenamer: public ParRenamer
{
public:
PostInfo* GetPostInfo() { return m_postInfo; }
void SetPostInfo(PostInfo* postInfo) { m_postInfo = postInfo; }
protected:
virtual void UpdateProgress();
virtual void Completed() { m_owner->ParRenameCompleted(); }
virtual void PrintMessage(Message::EKind kind, const char* format, ...) PRINTF_SYNTAX(3);
virtual void RegisterParredFile(const char* filename);
virtual void RegisterRenamedFile(const char* oldFilename, const char* newFileName);
private:
ParCoordinator* m_owner;
PostInfo* m_postInfo;
friend class ParCoordinator;
friend class RepairController;
};
class PostDupeMatcher: public DupeMatcher
@@ -124,16 +103,8 @@ private:
typedef std::deque<BlockInfo> Blocks;
enum EJobKind
{
jkParCheck,
jkParRename
};
PostInfo* m_postInfo;
PostParChecker m_parChecker;
bool m_stopped = false;
PostParRenamer m_parRenamer;
EJobKind m_currentJob;
void FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, const char* parFilename,
Blocks& blocks, bool strictParName, bool exactParName, int* blockFound);

View File

@@ -87,16 +87,13 @@ void UnpackController::Run()
if (unpack)
{
bool scanNonStdFiles = m_postInfo->GetNzbInfo()->GetRenameStatus() > NzbInfo::rsSkipped ||
m_postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSuccess ||
!m_hasParFiles;
CheckArchiveFiles(scanNonStdFiles);
CheckArchiveFiles();
}
SetInfoName(m_infoName);
SetWorkingDir(m_destDir);
bool hasFiles = m_hasRarFiles || m_hasNonStdRarFiles || m_hasSevenZipFiles || m_hasSevenZipMultiFiles || m_hasSplittedFiles;
bool hasFiles = m_hasRarFiles || m_hasSevenZipFiles || m_hasSevenZipMultiFiles || m_hasSplittedFiles;
if (m_postInfo->GetUnpackTried() && !m_postInfo->GetParRepaired() &&
(!m_password.Empty() || Util::EmptyStr(g_Options->GetUnpackPassFile()) || m_postInfo->GetPassListTried()))
@@ -107,7 +104,6 @@ void UnpackController::Run()
"%s failed: checksum error in the encrypted file. Corrupt file or wrong password." : "%s failed.",
*m_infoNameUp);
m_postInfo->GetNzbInfo()->SetUnpackStatus((NzbInfo::EUnpackStatus)m_postInfo->GetLastUnpackStatus());
m_postInfo->SetStage(PostInfo::ptQueued);
}
else if (unpack && hasFiles)
{
@@ -115,7 +111,7 @@ void UnpackController::Run()
CreateUnpackDir();
if (m_hasRarFiles || m_hasNonStdRarFiles)
if (m_hasRarFiles)
{
UnpackArchives(upUnrar, false);
}
@@ -139,23 +135,35 @@ void UnpackController::Run()
m_joinedFiles.clear();
}
else
else if (unpack)
{
PrintMessage(Message::mkInfo, (unpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), *m_name);
#ifndef DISABLE_PARCHECK
if (unpack && m_postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped &&
m_postInfo->GetNzbInfo()->GetRenameStatus() <= NzbInfo::rsSkipped && m_hasParFiles)
if (m_postInfo->GetNzbInfo()->GetParStatus() <= NzbInfo::psSkipped &&
m_postInfo->GetNzbInfo()->GetParRenameStatus() <= NzbInfo::rsSkipped &&
m_hasParFiles)
{
PrintMessage(Message::mkInfo, "Nothing to unpack for %s", *m_name);
RequestParCheck(false);
}
else
#endif
if (m_hasRenamedArchiveFiles)
{
PrintMessage(Message::mkError, "Could not unpack %s due to renamed archive files", *m_name);
m_postInfo->GetNzbInfo()->SetUnpackStatus(NzbInfo::usFailure);
}
else
{
PrintMessage(Message::mkInfo, "Nothing to unpack for %s", *m_name);
m_postInfo->GetNzbInfo()->SetUnpackStatus(NzbInfo::usSkipped);
m_postInfo->SetStage(PostInfo::ptQueued);
}
}
else
{
PrintMessage(Message::mkInfo, "Unpack for %s skipped", *m_name);
m_postInfo->GetNzbInfo()->SetUnpackStatus(NzbInfo::usSkipped);
}
int unpackSec = (int)(Util::CurrentTime() - start);
m_postInfo->GetNzbInfo()->SetUnpackSec(m_postInfo->GetNzbInfo()->GetUnpackSec() + unpackSec);
@@ -268,7 +276,7 @@ void UnpackController::ExecuteUnrar(const char* password)
params.emplace_back("-o+");
}
params.emplace_back(m_hasNonStdRarFiles ? "*.*" : "*.rar");
params.emplace_back("*.rar");
params.push_back(FileSystem::MakeExtendedPath(BString<1024>("%s%c", *m_unpackDir, PATH_SEPARATOR), true));
SetArgs(std::move(params));
SetLogPrefix("Unrar");
@@ -532,9 +540,8 @@ void UnpackController::Completed()
if (g_Options->GetParRename())
{
//request par-rename check for extracted files
m_postInfo->GetNzbInfo()->SetRenameStatus(NzbInfo::rsNone);
m_postInfo->GetNzbInfo()->SetParRenameStatus(NzbInfo::rsNone);
}
m_postInfo->SetStage(PostInfo::ptQueued);
}
else
{
@@ -558,7 +565,6 @@ void UnpackController::Completed()
m_unpackSpaceError ? NzbInfo::usSpace :
m_unpackPasswordError || m_unpackDecryptError ? NzbInfo::usPassword :
NzbInfo::usFailure);
m_postInfo->SetStage(PostInfo::ptQueued);
}
}
}
@@ -569,7 +575,6 @@ void UnpackController::RequestParCheck(bool forceRepair)
PrintMessage(Message::mkInfo, "%s requested %s", *m_infoNameUp, forceRepair ? "par-check with forced repair" : "par-check/repair");
m_postInfo->SetRequestParCheck(true);
m_postInfo->SetForceRepair(forceRepair);
m_postInfo->SetStage(PostInfo::ptFinished);
m_postInfo->SetUnpackTried(true);
m_postInfo->SetPassListTried(m_passListTried);
m_postInfo->SetLastUnpackStatus((int)(m_unpackSpaceError ? NzbInfo::usSpace :
@@ -609,19 +614,18 @@ void UnpackController::CreateUnpackDir()
}
}
void UnpackController::CheckArchiveFiles(bool scanNonStdFiles)
void UnpackController::CheckArchiveFiles()
{
m_hasRarFiles = false;
m_hasNonStdRarFiles = false;
m_hasRenamedArchiveFiles = false;
m_hasSevenZipFiles = false;
m_hasSevenZipMultiFiles = false;
m_hasSplittedFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.[0-9]{3}$");
DirBrowser dir(m_destDir);
@@ -646,16 +650,16 @@ void UnpackController::CheckArchiveFiles(bool scanNonStdFiles)
{
m_hasSevenZipMultiFiles = true;
}
else if (scanNonStdFiles && !m_hasNonStdRarFiles && extNum > 1 &&
!regExRarMultiSeq.Match(filename) && regExNumExt.Match(filename) &&
FileHasRarSignature(fullFilename))
{
m_hasNonStdRarFiles = true;
}
else if (regExSplitExt.Match(filename) && (extNum == 0 || extNum == 1))
{
m_hasSplittedFiles = true;
}
else if (!m_hasRenamedArchiveFiles && !regExRarMultiSeq.Match(filename) &&
!Util::MatchFileExt(filename, g_Options->GetUnpackIgnoreExt(), ",;") &&
FileHasRarSignature(fullFilename))
{
m_hasRenamedArchiveFiles = true;
}
}
}
}
@@ -737,7 +741,6 @@ bool UnpackController::Cleanup()
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.[0-9]{3}$");
DirBrowser dir(m_destDir);
@@ -749,7 +752,6 @@ bool UnpackController::Cleanup()
(m_interDir || !extractedFiles.Exists(filename)) &&
(regExRar.Match(filename) || regExSevenZip.Match(filename) ||
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(fullFilename)) ||
(m_hasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(fullFilename)) ||
(m_hasSplittedFiles && regExSplitExt.Match(filename) && m_joinedFiles.Exists(filename))))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
@@ -871,7 +873,7 @@ 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))
{
m_unpackPasswordError = true;
}

View File

@@ -71,7 +71,7 @@ private:
bool m_noFilesMessageReceived;
bool m_hasParFiles;
bool m_hasRarFiles;
bool m_hasNonStdRarFiles;
bool m_hasRenamedArchiveFiles;
bool m_hasSevenZipFiles;
bool m_hasSevenZipMultiFiles;
bool m_hasSplittedFiles;
@@ -96,7 +96,7 @@ private:
void Completed();
void CreateUnpackDir();
bool Cleanup();
void CheckArchiveFiles(bool scanNonStdFiles);
void CheckArchiveFiles();
void SetProgressLabel(const char* progressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck(bool forceRepair);

View File

@@ -265,7 +265,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory
bool ok = true;
{
StateFile stateFile("queue", 57, true);
StateFile stateFile("queue", 59, true);
if (!downloadQueue->GetQueue()->empty())
{
StateDiskFile* outfile = stateFile.BeginWrite();
@@ -288,7 +288,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory
if (saveHistory)
{
StateFile stateFile("history", 57, true);
StateFile stateFile("history", 59, true);
if (!downloadQueue->GetHistory()->empty())
{
StateDiskFile* outfile = stateFile.BeginWrite();
@@ -320,7 +320,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers
int formatVersion = 0;
{
StateFile stateFile("queue", 57, true);
StateFile stateFile("queue", 59, true);
if (stateFile.FileExists())
{
StateDiskFile* infile = stateFile.BeginRead();
@@ -349,7 +349,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* downloadQueue, Servers* servers
if (formatVersion == 0 || formatVersion >= 57)
{
StateFile stateFile("history", 57, true);
StateFile stateFile("history", 59, true);
if (stateFile.FileExists())
{
StateDiskFile* infile = stateFile.BeginRead();
@@ -428,9 +428,9 @@ void DiskState::SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile)
outfile.PrintLine("%i,%i,%i,%i,%i", (int)nzbInfo->GetPriority(),
nzbInfo->GetPostInfo() ? (int)nzbInfo->GetPostInfo()->GetStage() + 1 : 0,
(int)nzbInfo->GetDeletePaused(), (int)nzbInfo->GetManyDupeFiles(), nzbInfo->GetFeedId());
outfile.PrintLine("%i,%i,%i,%i,%i,%i,%i", (int)nzbInfo->GetParStatus(), (int)nzbInfo->GetUnpackStatus(),
(int)nzbInfo->GetMoveStatus(), (int)nzbInfo->GetRenameStatus(), (int)nzbInfo->GetDeleteStatus(),
(int)nzbInfo->GetMarkStatus(), (int)nzbInfo->GetUrlStatus());
outfile.PrintLine("%i,%i,%i,%i,%i,%i,%i,%i", (int)nzbInfo->GetParStatus(), (int)nzbInfo->GetUnpackStatus(),
(int)nzbInfo->GetMoveStatus(), (int)nzbInfo->GetParRenameStatus(), (int)nzbInfo->GetRarRenameStatus(),
(int)nzbInfo->GetDeleteStatus(), (int)nzbInfo->GetMarkStatus(), (int)nzbInfo->GetUrlStatus());
outfile.PrintLine("%i,%i,%i", (int)nzbInfo->GetUnpackCleanedUpDisk(), (int)nzbInfo->GetHealthPaused(),
(int)nzbInfo->GetAddUrlPaused());
outfile.PrintLine("%i,%i,%i", nzbInfo->GetFileCount(), nzbInfo->GetParkedFileCount(),
@@ -556,17 +556,29 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
if (postStage > 0)
{
nzbInfo->EnterPostProcess();
if (formatVersion < 59 && postStage == 6)
{
postStage++;
}
else if (formatVersion < 59 && postStage > 6)
{
postStage += 2;
}
nzbInfo->GetPostInfo()->SetStage((PostInfo::EStage)postStage);
}
nzbInfo->SetFeedId(feedId);
int parStatus, unpackStatus, moveStatus, renameStatus, deleteStatus, markStatus, urlStatus;
if (infile.ScanLine("%i,%i,%i,%i,%i,%i,%i", &parStatus, &unpackStatus, &moveStatus,
&renameStatus, &deleteStatus, &markStatus, &urlStatus) != 7) goto error;
int parStatus, unpackStatus, moveStatus, parRenameStatus, rarRenameStatus, deleteStatus, markStatus, urlStatus;
if (formatVersion < 58 && infile.ScanLine("%i,%i,%i,%i,%i,%i,%i", &parStatus, &unpackStatus, &moveStatus,
&parRenameStatus, &deleteStatus, &markStatus, &urlStatus) != 7) goto error;
rarRenameStatus = 0;
if (formatVersion >= 58 && infile.ScanLine("%i,%i,%i,%i,%i,%i,%i,%i", &parStatus, &unpackStatus, &moveStatus,
&parRenameStatus, &rarRenameStatus, &deleteStatus, &markStatus, &urlStatus) != 8) goto error;
nzbInfo->SetParStatus((NzbInfo::EParStatus)parStatus);
nzbInfo->SetUnpackStatus((NzbInfo::EUnpackStatus)unpackStatus);
nzbInfo->SetMoveStatus((NzbInfo::EMoveStatus)moveStatus);
nzbInfo->SetRenameStatus((NzbInfo::ERenameStatus)renameStatus);
nzbInfo->SetParRenameStatus((NzbInfo::ERenameStatus)parRenameStatus);
nzbInfo->SetRarRenameStatus((NzbInfo::ERenameStatus)rarRenameStatus);
nzbInfo->SetDeleteStatus((NzbInfo::EDeleteStatus)deleteStatus);
nzbInfo->SetMarkStatus((NzbInfo::EMarkStatus)markStatus);
if (nzbInfo->GetKind() == NzbInfo::nkNzb ||

View File

@@ -336,7 +336,7 @@ public:
{
rsNone,
rsSkipped,
rsFailure,
rsNothing,
rsSuccess
};
@@ -485,8 +485,10 @@ public:
void BuildDestDirName();
CString BuildFinalDirName();
CompletedFileList* GetCompletedFiles() { return &m_completedFiles; }
ERenameStatus GetRenameStatus() { return m_renameStatus; }
void SetRenameStatus(ERenameStatus renameStatus) { m_renameStatus = renameStatus; }
ERenameStatus GetParRenameStatus() { return m_parRenameStatus; }
void SetParRenameStatus(ERenameStatus renameStatus) { m_parRenameStatus = renameStatus; }
ERenameStatus GetRarRenameStatus() { return m_rarRenameStatus; }
void SetRarRenameStatus(ERenameStatus renameStatus) { m_rarRenameStatus = renameStatus; }
EParStatus GetParStatus() { return m_parStatus; }
void SetParStatus(EParStatus parStatus) { m_parStatus = parStatus; }
EUnpackStatus GetUnpackStatus() { return m_unpackStatus; }
@@ -616,7 +618,8 @@ private:
time_t m_maxTime = 0;
int m_priority = 0;
CompletedFileList m_completedFiles;
ERenameStatus m_renameStatus = rsNone;
ERenameStatus m_parRenameStatus = rsNone;
ERenameStatus m_rarRenameStatus = rsNone;
EParStatus m_parStatus = psNone;
EUnpackStatus m_unpackStatus = usNone;
ECleanupStatus m_cleanupStatus = csNone;
@@ -685,8 +688,10 @@ public:
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptRenaming,
ptParRenaming,
ptRarRenaming,
ptUnpacking,
ptCleaningUp,
ptMoving,
ptExecutingScript,
ptFinished
@@ -726,6 +731,8 @@ public:
void SetPassListTried(bool passListTried) { m_passListTried = passListTried; }
int GetLastUnpackStatus() { return m_lastUnpackStatus; }
void SetLastUnpackStatus(int unpackStatus) { m_lastUnpackStatus = unpackStatus; }
bool GetNeedParCheck() { return m_needParCheck; }
void SetNeedParCheck(bool needParCheck) { m_needParCheck = needParCheck; }
Thread* GetPostThread() { return m_postThread; }
void SetPostThread(Thread* postThread) { m_postThread = postThread; }
ParredFiles* GetParredFiles() { return &m_parredFiles; }
@@ -741,6 +748,7 @@ private:
bool m_unpackTried = false;
bool m_passListTried = false;
int m_lastUnpackStatus = 0;
bool m_needParCheck = false;
EStage m_stage = ptQueued;
CString m_progressLabel = "";
int m_fileProgress = 0;
@@ -867,9 +875,11 @@ public:
eaFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eaFileReorder, // set file order
eaFileSplit, // split - create new group from selected files
eaGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
eaGroupMoveOffset, // move group to offset relative to the current position in download-queue
eaGroupMoveTop, // move group to the top of download-queue
eaGroupMoveBottom, // move group to the bottom of download-queue
eaGroupMoveBefore, // move group to a certain position
eaGroupMoveAfter, // move group to a certain position
eaGroupPause, // pause group
eaGroupResume, // resume (unpause) group
eaGroupDelete, // delete group and put to history, delete already downloaded files
@@ -918,8 +928,8 @@ public:
static GuardedDownloadQueue Guard() { return GuardedDownloadQueue(g_DownloadQueue, &g_DownloadQueue->m_lockMutex); }
NzbList* GetQueue() { return &m_queue; }
HistoryList* GetHistory() { return &m_history; }
virtual bool EditEntry(int ID, EEditAction action, int offset, const char* text) = 0;
virtual bool EditList(IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, int offset, const char* text) = 0;
virtual bool EditEntry(int ID, EEditAction action, const char* args) = 0;
virtual bool EditList(IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, const char* args) = 0;
virtual void HistoryChanged() = 0;
virtual void Save() = 0;
void CalcRemainingSize(int64* remaining, int64* remainingForced);

View File

@@ -287,7 +287,7 @@ void DupeCoordinator::NzbFound(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
info("Moving collection %s with lower duplicate score to history", queuedNzbInfo->GetName());
queuedNzbInfo->SetDeleteStatus(NzbInfo::dsDupe);
downloadQueue->EditEntry(queuedNzbInfo->GetId(),
DownloadQueue::eaGroupDelete, 0, nullptr);
DownloadQueue::eaGroupDelete, nullptr);
it = downloadQueue->GetQueue()->begin() + index;
}
}

View File

@@ -232,7 +232,8 @@ void HistoryCoordinator::PrepareEdit(DownloadQueue* downloadQueue, IdList* idLis
}
}
bool HistoryCoordinator::EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text)
bool HistoryCoordinator::EditList(DownloadQueue* downloadQueue, IdList* idList,
DownloadQueue::EEditAction action, const char* args)
{
bool ok = false;
PrepareEdit(downloadQueue, idList, action);
@@ -270,22 +271,22 @@ bool HistoryCoordinator::EditList(DownloadQueue* downloadQueue, IdList* idList,
break;
case DownloadQueue::eaHistorySetParameter:
ok = HistorySetParameter(historyInfo, text);
ok = HistorySetParameter(historyInfo, args);
break;
case DownloadQueue::eaHistorySetCategory:
ok = HistorySetCategory(historyInfo, text);
ok = HistorySetCategory(historyInfo, args);
break;
case DownloadQueue::eaHistorySetName:
ok = HistorySetName(historyInfo, text);
ok = HistorySetName(historyInfo, args);
break;
case DownloadQueue::eaHistorySetDupeKey:
case DownloadQueue::eaHistorySetDupeScore:
case DownloadQueue::eaHistorySetDupeMode:
case DownloadQueue::eaHistorySetDupeBackup:
HistorySetDupeParam(historyInfo, action, text);
HistorySetDupeParam(historyInfo, action, args);
break;
case DownloadQueue::eaHistoryMarkBad:
@@ -379,7 +380,8 @@ void HistoryCoordinator::MoveToQueue(DownloadQueue* downloadQueue, HistoryList::
{
nzbInfo->SetUnpackStatus(NzbInfo::usNone);
nzbInfo->SetCleanupStatus(NzbInfo::csNone);
nzbInfo->SetRenameStatus(NzbInfo::rsNone);
nzbInfo->SetParRenameStatus(NzbInfo::rsNone);
nzbInfo->SetRarRenameStatus(NzbInfo::rsNone);
nzbInfo->SetPostTotalSec(nzbInfo->GetPostTotalSec() - nzbInfo->GetUnpackSec());
nzbInfo->SetUnpackSec(0);
@@ -489,7 +491,8 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* downloadQueue, History
nzbInfo->SetMoveStatus(NzbInfo::msNone);
nzbInfo->SetUnpackCleanedUpDisk(false);
nzbInfo->SetParStatus(NzbInfo::psNone);
nzbInfo->SetRenameStatus(NzbInfo::rsNone);
nzbInfo->SetParRenameStatus(NzbInfo::rsNone);
nzbInfo->SetRarRenameStatus(NzbInfo::rsNone);
nzbInfo->SetDownloadedSize(0);
nzbInfo->SetDownloadSec(0);
nzbInfo->SetPostTotalSec(0);
@@ -631,7 +634,7 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
if (g_Options->GetParCheck() != Options::pcForce)
{
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, 0, nullptr);
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, nullptr);
}
}

View File

@@ -28,7 +28,7 @@ class HistoryCoordinator : public Service
{
public:
void AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
bool EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text);
bool EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action, const char* args);
void DeleteDiskFiles(NzbInfo* nzbInfo);
void HistoryHide(DownloadQueue* downloadQueue, HistoryInfo* historyInfo, int rindex);
void Redownload(DownloadQueue* downloadQueue, HistoryInfo* historyInfo);

View File

@@ -32,16 +32,16 @@
#include "StatMeter.h"
bool QueueCoordinator::CoordinatorDownloadQueue::EditEntry(
int ID, EEditAction action, int offset, const char* text)
int ID, EEditAction action, const char* args)
{
return m_owner->m_queueEditor.EditEntry(&m_owner->m_downloadQueue, ID, action, offset, text);
return m_owner->m_queueEditor.EditEntry(&m_owner->m_downloadQueue, ID, action, args);
}
bool QueueCoordinator::CoordinatorDownloadQueue::EditList(
IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, int offset, const char* text)
IdList* idList, NameList* nameList, EMatchMode matchMode, EEditAction action, const char* args)
{
m_massEdit = true;
bool ret = m_owner->m_queueEditor.EditList(&m_owner->m_downloadQueue, idList, nameList, matchMode, action, offset, text);
bool ret = m_owner->m_queueEditor.EditList(&m_owner->m_downloadQueue, idList, nameList, matchMode, action, args);
m_massEdit = false;
if (m_wantSave)
{
@@ -256,23 +256,31 @@ void QueueCoordinator::Run()
}
}
WaitJobs();
SavePartialState();
debug("Exiting QueueCoordinator-loop");
}
void QueueCoordinator::WaitJobs()
{
// waiting for downloads
debug("QueueCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
while (true)
{
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
completed = m_activeDownloads.size() == 0;
if (m_activeDownloads.empty())
{
break;
}
}
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("QueueCoordinator: Downloads are completed");
SavePartialState();
debug("Exiting QueueCoordinator-loop");
}
/*
@@ -371,7 +379,10 @@ NzbInfo* QueueCoordinator::AddNzbFileToQueue(std::unique_ptr<NzbInfo> nzbInfo, N
{
// in a case if none of listeners did already delete the temporary object - we do it ourselves
downloadQueue->GetQueue()->Remove(addedNzb);
addedNzb = nullptr;
if (!downloadQueue->GetHistory()->Find(addedNzb->GetId()))
{
addedNzb = nullptr;
}
}
downloadQueue->Save();
@@ -557,12 +568,14 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
debug("Article downloaded");
FileInfo* fileInfo = articleDownloader->GetFileInfo();
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
ArticleInfo* articleInfo = articleDownloader->GetArticleInfo();
bool retry = false;
bool fileCompleted = false;
bool completeFileParts = false;
{
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
ArticleInfo* articleInfo = articleDownloader->GetArticleInfo();
bool retry = false;
bool fileCompleted = false;
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (articleDownloader->GetStatus() == ArticleDownloader::adFinished)
@@ -631,35 +644,44 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
{
fileCompleted = true;
}
completeFileParts = fileCompleted && (!fileInfo->GetDeleted() || nzbInfo->GetParking());
if (!completeFileParts)
{
DeleteDownloader(downloadQueue, articleDownloader, false);
}
}
bool deleteFileObj = false;
if (fileCompleted && (!fileInfo->GetDeleted() || nzbInfo->GetParking()))
if (completeFileParts)
{
// all jobs done
articleDownloader->CompleteFileParts();
fileInfo->SetPartialChanged(false);
deleteFileObj = true;
}
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
DeleteDownloader(downloadQueue, articleDownloader, true);
}
}
bool hasOtherDownloaders = fileInfo->GetActiveDownloads() > 1;
deleteFileObj |= fileInfo->GetDeleted() && !hasOtherDownloaders;
void QueueCoordinator::DeleteDownloader(DownloadQueue* downloadQueue,
ArticleDownloader* articleDownloader, bool fileCompleted)
{
FileInfo* fileInfo = articleDownloader->GetFileInfo();
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
bool hasOtherDownloaders = fileInfo->GetActiveDownloads() > 1;
bool deleteFileObj = fileCompleted || (fileInfo->GetDeleted() && !hasOtherDownloaders);
// remove downloader from downloader list
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), articleDownloader));
// remove downloader from downloader list
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), articleDownloader));
fileInfo->SetActiveDownloads(fileInfo->GetActiveDownloads() - 1);
nzbInfo->SetActiveDownloads(nzbInfo->GetActiveDownloads() - 1);
fileInfo->SetActiveDownloads(fileInfo->GetActiveDownloads() - 1);
nzbInfo->SetActiveDownloads(nzbInfo->GetActiveDownloads() - 1);
if (deleteFileObj)
{
DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
downloadQueue->Save();
}
if (deleteFileObj)
{
DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
downloadQueue->Save();
}
}
@@ -808,7 +830,7 @@ void QueueCoordinator::CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileI
warn("Pausing %s due to health %.1f%% below critical %.1f%%", fileInfo->GetNzbInfo()->GetName(),
fileInfo->GetNzbInfo()->CalcHealth() / 10.0, fileInfo->GetNzbInfo()->CalcCriticalHealth(true) / 10.0);
fileInfo->GetNzbInfo()->SetHealthPaused(true);
downloadQueue->EditEntry(fileInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupPause, 0, nullptr);
downloadQueue->EditEntry(fileInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupPause, nullptr);
}
else if (g_Options->GetHealthCheck() == Options::hcDelete ||
g_Options->GetHealthCheck() == Options::hcPark)
@@ -820,7 +842,7 @@ void QueueCoordinator::CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileI
fileInfo->GetNzbInfo()->SetDeleteStatus(NzbInfo::dsHealth);
downloadQueue->EditEntry(fileInfo->GetNzbInfo()->GetId(),
g_Options->GetHealthCheck() == Options::hcPark ? DownloadQueue::eaGroupParkDelete : DownloadQueue::eaGroupDelete,
0, nullptr);
nullptr);
}
}

View File

@@ -60,9 +60,9 @@ private:
class CoordinatorDownloadQueue : public DownloadQueue
{
public:
virtual bool EditEntry(int ID, EEditAction action, int offset, const char* text);
virtual bool EditEntry(int ID, EEditAction action, const char* args);
virtual bool EditList(IdList* idList, NameList* nameList, EMatchMode matchMode,
EEditAction action, int offset, const char* text);
EEditAction action, const char* args);
virtual void HistoryChanged() { m_historyChanged = true; }
virtual void Save();
private:
@@ -83,6 +83,7 @@ private:
bool GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
void StartArticleDownload(FileInfo* fileInfo, ArticleInfo* articleInfo, NntpConnection* connection);
void ArticleCompleted(ArticleDownloader* articleDownloader);
void DeleteDownloader(DownloadQueue* downloadQueue, ArticleDownloader* articleDownloader, bool fileCompleted);
void DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fileInfo, bool completed);
void CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileInfo);
void ResetHangingDownloads();
@@ -90,6 +91,7 @@ private:
void Load();
void SavePartialState();
void LoadPartialState(FileInfo* fileInfo);
void WaitJobs();
};
extern QueueCoordinator* g_QueueCoordinator;

View File

@@ -62,8 +62,6 @@ private:
QueueEditor::ItemList* m_sortItemList;
ESortCriteria m_sortCriteria;
ESortOrder m_sortOrder;
void AlignSelectedGroups();
};
bool GroupSorter::Execute(const char* sort)
@@ -112,8 +110,6 @@ bool GroupSorter::Execute(const char* sort)
m_sortOrder = soAuto;
}
AlignSelectedGroups();
RawNzbList tempList;
for (NzbInfo* nzbInfo : m_nzbList)
{
@@ -126,7 +122,7 @@ bool GroupSorter::Execute(const char* sort)
m_sortOrder = soDescending;
}
std::sort(m_nzbList->begin(), m_nzbList->end(), *this);
std::stable_sort(m_nzbList->begin(), m_nzbList->end(), *this);
if (origSortOrder == soAuto &&
std::equal(tempList.begin(), tempList.end(), m_nzbList->begin(),
@@ -136,7 +132,7 @@ bool GroupSorter::Execute(const char* sort)
}))
{
m_sortOrder = m_sortOrder == soDescending ? soAscending : soDescending;
std::sort(m_nzbList->begin(), m_nzbList->end(), *this);
std::stable_sort(m_nzbList->begin(), m_nzbList->end(), *this);
}
return true;
@@ -200,44 +196,6 @@ bool GroupSorter::operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const
return ret;
}
void GroupSorter::AlignSelectedGroups()
{
NzbInfo* lastNzbInfo = nullptr;
uint32 lastNum = 0;
uint32 num = 0;
while (num < m_nzbList->size())
{
std::unique_ptr<NzbInfo>& nzbInfo = m_nzbList->at(num);
bool selected = false;
for (QueueEditor::EditItem& item : m_sortItemList)
{
if (item.m_nzbInfo == nzbInfo.get())
{
selected = true;
break;
}
}
if (selected)
{
if (lastNzbInfo && num - lastNum > 1)
{
std::unique_ptr<NzbInfo> movedNzbInfo = std::move(*(m_nzbList->begin() + num));
m_nzbList->erase(m_nzbList->begin() + num);
m_nzbList->insert(m_nzbList->begin() + lastNum + 1, std::move(movedNzbInfo));
lastNum++;
}
else
{
lastNum = num;
}
lastNzbInfo = nzbInfo.get();
}
num++;
}
}
FileInfo* QueueEditor::FindFileInfo(int id)
{
@@ -252,17 +210,11 @@ FileInfo* QueueEditor::FindFileInfo(int id)
return nullptr;
}
/*
* Set the pause flag of the specific entry in the queue
*/
void QueueEditor::PauseUnpauseEntry(FileInfo* fileInfo, bool pause)
{
fileInfo->SetPaused(pause);
}
/*
* Removes entry
*/
void QueueEditor::DeleteEntry(FileInfo* fileInfo)
{
if (!fileInfo->GetDeleted())
@@ -274,9 +226,6 @@ void QueueEditor::DeleteEntry(FileInfo* fileInfo)
}
}
/*
* Moves entry in the queue
*/
void QueueEditor::MoveEntry(FileInfo* fileInfo, int offset)
{
int entry = 0;
@@ -309,9 +258,6 @@ void QueueEditor::MoveEntry(FileInfo* fileInfo, int offset)
}
}
/*
* Moves group in the queue
*/
void QueueEditor::MoveGroup(NzbInfo* nzbInfo, int offset)
{
int entry = 0;
@@ -344,24 +290,24 @@ void QueueEditor::MoveGroup(NzbInfo* nzbInfo, int offset)
}
}
bool QueueEditor::EditEntry(DownloadQueue* downloadQueue, int ID, DownloadQueue::EEditAction action, int offset, const char* text)
bool QueueEditor::EditEntry(DownloadQueue* downloadQueue, int ID, DownloadQueue::EEditAction action, const char* args)
{
m_downloadQueue = downloadQueue;
IdList cIdList;
cIdList.push_back(ID);
return InternEditList(nullptr, &cIdList, action, offset, text);
return InternEditList(nullptr, &cIdList, action, args);
}
bool QueueEditor::EditList(DownloadQueue* downloadQueue, IdList* idList, NameList* nameList, DownloadQueue::EMatchMode matchMode,
DownloadQueue::EEditAction action, int offset, const char* text)
DownloadQueue::EEditAction action, const char* args)
{
if (action == DownloadQueue::eaPostDelete)
{
return g_PrePostProcessor->EditList(downloadQueue, idList, action, offset, text);
return g_PrePostProcessor->EditList(downloadQueue, idList, action, args);
}
else if (DownloadQueue::eaHistoryDelete <= action && action <= DownloadQueue::eaHistorySetName)
{
return g_HistoryCoordinator->EditList(downloadQueue, idList, action, offset, text);
return g_HistoryCoordinator->EditList(downloadQueue, idList, action, args);
}
m_downloadQueue = downloadQueue;
@@ -376,7 +322,7 @@ bool QueueEditor::EditList(DownloadQueue* downloadQueue, IdList* idList, NameLis
ok = BuildIdListFromNameList(idList, nameList, matchMode, action);
}
ok = ok && (InternEditList(nullptr, idList, action, offset, text) || matchMode == DownloadQueue::mmRegEx);
ok = ok && (InternEditList(nullptr, idList, action, args) || matchMode == DownloadQueue::mmRegEx);
m_downloadQueue->Save();
@@ -384,12 +330,14 @@ bool QueueEditor::EditList(DownloadQueue* downloadQueue, IdList* idList, NameLis
}
bool QueueEditor::InternEditList(ItemList* itemList,
IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text)
IdList* idList, DownloadQueue::EEditAction action, const char* args)
{
ItemList workItems;
if (!itemList)
{
itemList = &workItems;
int offset = args && (action == DownloadQueue::eaFileMoveOffset ||
action == DownloadQueue::eaGroupMoveOffset) ? atoi(args) : 0;
PrepareList(itemList, idList, action, offset);
}
@@ -404,10 +352,14 @@ bool QueueEditor::InternEditList(ItemList* itemList,
return MergeGroups(itemList);
case DownloadQueue::eaGroupSort:
return SortGroups(itemList, text);
return SortGroups(itemList, args);
case DownloadQueue::eaGroupMoveAfter:
case DownloadQueue::eaGroupMoveBefore:
return MoveGroupsTo(itemList, idList, action == DownloadQueue::eaGroupMoveBefore, args);
case DownloadQueue::eaFileSplit:
return SplitGroup(itemList, text);
return SplitGroup(itemList, args);
case DownloadQueue::eaFileReorder:
ReorderFiles(itemList);
@@ -437,26 +389,26 @@ bool QueueEditor::InternEditList(ItemList* itemList,
break;
case DownloadQueue::eaGroupSetPriority:
SetNzbPriority(item.m_nzbInfo, text);
SetNzbPriority(item.m_nzbInfo, args);
break;
case DownloadQueue::eaGroupSetCategory:
case DownloadQueue::eaGroupApplyCategory:
SetNzbCategory(item.m_nzbInfo, text, action == DownloadQueue::eaGroupApplyCategory);
SetNzbCategory(item.m_nzbInfo, args, action == DownloadQueue::eaGroupApplyCategory);
break;
case DownloadQueue::eaGroupSetName:
SetNzbName(item.m_nzbInfo, text);
SetNzbName(item.m_nzbInfo, args);
break;
case DownloadQueue::eaGroupSetDupeKey:
case DownloadQueue::eaGroupSetDupeScore:
case DownloadQueue::eaGroupSetDupeMode:
SetNzbDupeParam(item.m_nzbInfo, action, text);
SetNzbDupeParam(item.m_nzbInfo, action, args);
break;
case DownloadQueue::eaGroupSetParameter:
SetNzbParameter(item.m_nzbInfo, text);
SetNzbParameter(item.m_nzbInfo, args);
break;
case DownloadQueue::eaGroupMoveTop:
@@ -469,7 +421,7 @@ bool QueueEditor::InternEditList(ItemList* itemList,
case DownloadQueue::eaGroupResume:
case DownloadQueue::eaGroupPauseAllPars:
case DownloadQueue::eaGroupPauseExtraPars:
EditGroup(item.m_nzbInfo, action, offset, text);
EditGroup(item.m_nzbInfo, action, args);
break;
case DownloadQueue::eaGroupDelete:
@@ -482,7 +434,7 @@ bool QueueEditor::InternEditList(ItemList* itemList,
}
else
{
EditGroup(item.m_nzbInfo, action, offset, text);
EditGroup(item.m_nzbInfo, action, args);
}
@@ -566,15 +518,15 @@ void QueueEditor::PrepareList(ItemList* itemList, IdList* idList,
}
}
}
else if ((offset != 0) &&
(action == DownloadQueue::eaGroupMoveOffset || action == DownloadQueue::eaGroupMoveTop || action == DownloadQueue::eaGroupMoveBottom))
else if (((offset != 0) &&
(action == DownloadQueue::eaGroupMoveOffset || action == DownloadQueue::eaGroupMoveTop || action == DownloadQueue::eaGroupMoveBottom)) ||
action == DownloadQueue::eaGroupMoveBefore || action == DownloadQueue::eaGroupMoveAfter)
{
// add IDs to list in order they currently have in download queue
// per group only one FileInfo is added to the list
int nrEntries = (int)m_downloadQueue->GetQueue()->size();
int lastDestPos = -1;
int start, end, step;
if (offset < 0)
if (offset <= 0)
{
start = 0;
end = nrEntries;
@@ -756,7 +708,7 @@ bool QueueEditor::BuildIdListFromNameList(IdList* idList, NameList* nameList, Do
return true;
}
bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, int offset, const char* text)
bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, const char* args)
{
ItemList itemList;
bool allPaused = true;
@@ -803,6 +755,8 @@ bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action,
DownloadQueue::eaFileMoveOffset,
DownloadQueue::eaFileMoveTop,
DownloadQueue::eaFileMoveBottom,
(DownloadQueue::EEditAction)0,
(DownloadQueue::EEditAction)0,
DownloadQueue::eaFilePause,
DownloadQueue::eaFileResume,
DownloadQueue::eaFileDelete,
@@ -816,7 +770,7 @@ bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action,
(DownloadQueue::EEditAction)0,
(DownloadQueue::EEditAction)0 };
bool ok = InternEditList(&itemList, nullptr, GroupToFileMap[action], offset, text);
bool ok = InternEditList(&itemList, nullptr, GroupToFileMap[action], args);
if ((action == DownloadQueue::eaGroupDelete || action == DownloadQueue::eaGroupDupeDelete || action == DownloadQueue::eaGroupFinalDelete) &&
// NZBInfo could have been destroyed already
@@ -950,16 +904,16 @@ void QueueEditor::SetNzbCategory(NzbInfo* nzbInfo, const char* category, bool ap
debug("QueueEditor: setting category '%s' for '%s'", category, nzbInfo->GetName());
bool oldUnpack = g_Options->GetUnpack();
const char* oldPostScript = g_Options->GetPostScript();
const char* oldExtensions = g_Options->GetExtensions();
if (applyParams && !Util::EmptyStr(nzbInfo->GetCategory()))
{
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
if (categoryObj)
{
oldUnpack = categoryObj->GetUnpack();
if (!Util::EmptyStr(categoryObj->GetPostScript()))
if (!Util::EmptyStr(categoryObj->GetExtensions()))
{
oldPostScript = categoryObj->GetPostScript();
oldExtensions = categoryObj->GetExtensions();
}
}
}
@@ -972,16 +926,16 @@ void QueueEditor::SetNzbCategory(NzbInfo* nzbInfo, const char* category, bool ap
}
bool newUnpack = g_Options->GetUnpack();
const char* newPostScript = g_Options->GetPostScript();
const char* newExtensions = g_Options->GetExtensions();
if (!Util::EmptyStr(nzbInfo->GetCategory()))
{
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
if (categoryObj)
{
newUnpack = categoryObj->GetUnpack();
if (!Util::EmptyStr(categoryObj->GetPostScript()))
if (!Util::EmptyStr(categoryObj->GetExtensions()))
{
newPostScript = categoryObj->GetPostScript();
newExtensions = categoryObj->GetExtensions();
}
}
}
@@ -991,15 +945,15 @@ void QueueEditor::SetNzbCategory(NzbInfo* nzbInfo, const char* category, bool ap
nzbInfo->GetParameters()->SetParameter("*Unpack:", newUnpack ? "yes" : "no");
}
if (strcasecmp(oldPostScript, newPostScript))
if (strcasecmp(oldExtensions, newExtensions))
{
// add new params not existed in old category
Tokenizer tokNew(newPostScript, ",;");
Tokenizer tokNew(newExtensions, ",;");
while (const char* newScriptName = tokNew.Next())
{
bool found = false;
const char* oldScriptName;
Tokenizer tokOld(oldPostScript, ",;");
Tokenizer tokOld(oldExtensions, ",;");
while ((oldScriptName = tokOld.Next()) && !found)
{
found = !strcasecmp(newScriptName, oldScriptName);
@@ -1011,12 +965,12 @@ void QueueEditor::SetNzbCategory(NzbInfo* nzbInfo, const char* category, bool ap
}
// remove old params not existed in new category
Tokenizer tokOld(oldPostScript, ",;");
Tokenizer tokOld(oldExtensions, ",;");
while (const char* oldScriptName = tokOld.Next())
{
bool found = false;
const char* newScriptName;
Tokenizer tokNew(newPostScript, ",;");
Tokenizer tokNew(newExtensions, ",;");
while ((newScriptName = tokNew.Next()) && !found)
{
found = !strcasecmp(newScriptName, oldScriptName);
@@ -1084,10 +1038,137 @@ bool QueueEditor::SplitGroup(ItemList* itemList, const char* name)
bool QueueEditor::SortGroups(ItemList* itemList, const char* sort)
{
AlignGroups(itemList);
GroupSorter sorter(m_downloadQueue->GetQueue(), itemList);
return sorter.Execute(sort);
}
void QueueEditor::AlignGroups(ItemList* itemList)
{
NzbList* nzbList = m_downloadQueue->GetQueue();
NzbInfo* lastNzbInfo = nullptr;
uint32 lastNum = 0;
uint32 num = 0;
while (num < nzbList->size())
{
std::unique_ptr<NzbInfo>& nzbInfo = nzbList->at(num);
bool selected = false;
for (QueueEditor::EditItem& item : itemList)
{
if (item.m_nzbInfo == nzbInfo.get())
{
selected = true;
break;
}
}
if (selected)
{
if (lastNzbInfo && num - lastNum > 1)
{
std::unique_ptr<NzbInfo> movedNzbInfo = std::move(*(nzbList->begin() + num));
nzbList->erase(nzbList->begin() + num);
nzbList->insert(nzbList->begin() + lastNum + 1, std::move(movedNzbInfo));
lastNum++;
}
else
{
lastNum = num;
}
lastNzbInfo = nzbInfo.get();
}
num++;
}
}
bool QueueEditor::ItemListContainsItem(ItemList* itemList, int id)
{
return std::find_if(itemList->begin(), itemList->end(),
[id](const EditItem& item)
{
return item.m_nzbInfo->GetId() == id;
}) != itemList->end();
};
bool QueueEditor::MoveGroupsTo(ItemList* itemList, IdList* idList, bool before, const char* args)
{
if (itemList->size() == 0 || Util::EmptyStr(args))
{
return false;
}
int targetId = atoi(args);
int offset = 0;
// check if target is in list of moved items
if (ItemListContainsItem(itemList, targetId))
{
// find the next item to use as target-before
bool found = false;
bool targetSet = false;
for (NzbInfo* nzbInfo : m_downloadQueue->GetQueue())
{
if (found)
{
if (!ItemListContainsItem(itemList, nzbInfo->GetId()))
{
targetId = nzbInfo->GetId();
before = true;
targetSet = true;
break;
}
}
else if (targetId == nzbInfo->GetId())
{
found = true;
}
}
if (!targetSet)
{
// there are no next item; move to the bottom then
offset = MAX_ID;
}
}
AlignGroups(itemList);
if (offset == 0)
{
// calculate offset between first moving item and target
int moveId = itemList->at(0).m_nzbInfo->GetId();
bool progress = false;
int step = 0;
for (NzbInfo* nzbInfo : m_downloadQueue->GetQueue())
{
int id = nzbInfo->GetId();
if (id == targetId || id == moveId)
{
if (!progress)
{
step = id == targetId ? -1 : 1;
offset = (before ? 0 : 1) - (step > 0 ? itemList->size() : 0);
progress = true;
}
else
{
break;
}
}
if (progress)
{
offset += step;
}
}
}
return InternEditList(nullptr, idList, DownloadQueue::eaGroupMoveOffset,
CString::FormatStr("%i", offset));
}
void QueueEditor::ReorderFiles(ItemList* itemList)
{
if (itemList->size() == 0)

View File

@@ -26,8 +26,8 @@
class QueueEditor
{
public:
bool EditEntry(DownloadQueue* downloadQueue, int ID, DownloadQueue::EEditAction action, int offset, const char* text);
bool EditList(DownloadQueue* downloadQueue, IdList* idList, NameList* nameList, DownloadQueue::EMatchMode matchMode, DownloadQueue::EEditAction action, int offset, const char* text);
bool EditEntry(DownloadQueue* downloadQueue, int ID, DownloadQueue::EEditAction action, const char* args);
bool EditList(DownloadQueue* downloadQueue, IdList* idList, NameList* nameList, DownloadQueue::EMatchMode matchMode, DownloadQueue::EEditAction action, const char* args);
private:
class EditItem
@@ -46,10 +46,10 @@ private:
DownloadQueue* m_downloadQueue;
FileInfo* FindFileInfo(int id);
bool InternEditList(ItemList* itemList, IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text);
bool InternEditList(ItemList* itemList, IdList* idList, DownloadQueue::EEditAction action, const char* args);
void PrepareList(ItemList* itemList, IdList* idList, DownloadQueue::EEditAction action, int offset);
bool BuildIdListFromNameList(IdList* idList, NameList* nameList, DownloadQueue::EMatchMode matchMode, DownloadQueue::EEditAction action);
bool EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, int offset, const char* text);
bool EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, const char* args);
void PauseParsInGroups(ItemList* itemList, bool extraParsOnly);
void PausePars(RawFileList* fileList, bool extraParsOnly);
void SetNzbPriority(NzbInfo* nzbInfo, const char* priority);
@@ -57,15 +57,18 @@ private:
void SetNzbName(NzbInfo* nzbInfo, const char* name);
bool MergeGroups(ItemList* itemList);
bool SortGroups(ItemList* itemList, const char* sort);
void AlignGroups(ItemList* itemList);
bool MoveGroupsTo(ItemList* itemList, IdList* idList, bool before, const char* args);
bool SplitGroup(ItemList* itemList, const char* name);
bool DeleteUrl(NzbInfo* nzbInfo, DownloadQueue::EEditAction action);
void ReorderFiles(ItemList* itemList);
void SetNzbParameter(NzbInfo* nzbInfo, const char* paramString);
void SetNzbDupeParam(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, const char* text);
void SetNzbDupeParam(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, const char* args);
void PauseUnpauseEntry(FileInfo* fileInfo, bool pause);
void DeleteEntry(FileInfo* fileInfo);
void MoveEntry(FileInfo* fileInfo, int offset);
void MoveGroup(NzbInfo* nzbInfo, int offset);
bool ItemListContainsItem(ItemList* itemList, int id);
friend class GroupSorter;
};

View File

@@ -72,8 +72,7 @@ void Scanner::QueueData::SetNzbId(int nzbId)
void Scanner::InitOptions()
{
m_nzbDirInterval = g_Options->GetNzbDirInterval() * 1000;
const char* scanScript = g_Options->GetScanScript();
m_scanScript = scanScript && strlen(scanScript) > 0;
m_scanScript = ScanScriptController::HasScripts();
}
void Scanner::ServiceWork()
@@ -96,7 +95,7 @@ void Scanner::ServiceWork()
CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
if (!checkStat && m_scanScript)
{
// if immediate scan requested, we need second scan to process files extracted by NzbProcess-script
// if immediate scan requested, we need second scan to process files extracted by scan-scripts
CheckIncomingNzbs(g_Options->GetNzbDir(), "", checkStat);
}
m_scanning = false;
@@ -105,7 +104,7 @@ void Scanner::ServiceWork()
// 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 NzbProcess-script;
// - 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())
{
@@ -347,7 +346,7 @@ void Scanner::ProcessIncomingFile(const char* directory, const char* baseFilenam
void Scanner::InitPPParameters(const char* category, NzbParameterList* parameters, bool reset)
{
bool unpack = g_Options->GetUnpack();
const char* postScript = g_Options->GetPostScript();
const char* extensions = g_Options->GetExtensions();
if (!Util::EmptyStr(category))
{
@@ -355,9 +354,9 @@ void Scanner::InitPPParameters(const char* category, NzbParameterList* parameter
if (categoryObj)
{
unpack = categoryObj->GetUnpack();
if (!Util::EmptyStr(categoryObj->GetPostScript()))
if (!Util::EmptyStr(categoryObj->GetExtensions()))
{
postScript = categoryObj->GetPostScript();
extensions = categoryObj->GetExtensions();
}
}
}
@@ -372,13 +371,20 @@ void Scanner::InitPPParameters(const char* category, NzbParameterList* parameter
parameters->SetParameter("*Unpack:", unpack ? "yes" : "no");
if (!Util::EmptyStr(postScript))
if (!Util::EmptyStr(extensions))
{
// split szPostScript into tokens and create pp-parameter for each token
Tokenizer tok(postScript, ",;");
// create pp-parameter for each post-processing or queue- script
Tokenizer tok(extensions, ",;");
while (const char* scriptName = tok.Next())
{
parameters->SetParameter(BString<1024>("%s:", scriptName), "yes");
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if ((script.GetPostScript() || script.GetQueueScript()) &&
FileSystem::SameFilename(scriptName, script.GetName()))
{
parameters->SetParameter(BString<1024>("%s:", scriptName), "yes");
}
}
}
}
}

View File

@@ -117,21 +117,30 @@ void UrlCoordinator::Run()
}
}
WaitJobs();
debug("Exiting UrlCoordinator-loop");
}
void UrlCoordinator::WaitJobs()
{
// waiting for downloads
debug("UrlCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
while (true)
{
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
completed = m_activeDownloads.size() == 0;
if (m_activeDownloads.empty())
{
break;
}
}
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("UrlCoordinator: Downloads are completed");
debug("Exiting UrlCoordinator-loop");
debug("UrlCoordinator: Downloads are completed");
}
void UrlCoordinator::Stop()

View File

@@ -56,6 +56,7 @@ private:
void StartUrlDownload(NzbInfo* nzbInfo);
void UrlCompleted(UrlDownloader* urlDownloader);
void ResetHangingDownloads();
void WaitJobs();
};
extern UrlCoordinator* g_UrlCoordinator;

View File

@@ -860,7 +860,9 @@ void EditQueueBinCommand::Execute()
bool ok = DownloadQueue::Guard()->EditList(
nrIdEntries > 0 ? &cIdList : nullptr,
nrNameEntries > 0 ? &cNameList : nullptr,
(DownloadQueue::EMatchMode)matchMode, (DownloadQueue::EEditAction)action, offset, text);
(DownloadQueue::EMatchMode)matchMode, (DownloadQueue::EEditAction)action,
action == DownloadQueue::eaFileMoveOffset || action == DownloadQueue::eaGroupMoveOffset ?
*CString::FormatStr("%i", offset) : text);
if (ok)
{

View File

@@ -22,7 +22,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32 NZBMESSAGE_SIGNATURE = 0x6E7A6228; // = "nzb-XX" (protocol version)
static const int32 NZBMESSAGE_SIGNATURE = 0x6E7A6230; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;

View File

@@ -957,7 +957,9 @@ bool RemoteClient::RequestPostQueue()
completed.Format(", %i%s", (int)(stageProgress / 10), "%");
}
const char* postStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Unpacking", ", Executing postprocess-script", "" };
const char* postStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing",
", Verifying repaired files", ", Par-Renaming", ", Rar-Renaming", ", Unpacking", ", Cleaning up",
", Moving", ", Executing postprocess-script", "" };
char* infoName = bufPtr + sizeof(SNzbPostQueueResponseEntry) + ntohl(postQueueAnswer->m_nzbFilenameLen);
printf("[%i] %s%s%s\n", ntohl(postQueueAnswer->m_id), infoName, postStageName[ntohl(postQueueAnswer->m_stage)], *completed);

View File

@@ -1746,8 +1746,8 @@ void NzbInfoXmlCommand::AppendNzbInfoFields(NzbInfo* nzbInfo)
*EncodeStr(nzbInfo->GetDupeKey()), nzbInfo->GetDupeScore(), dupeModeName[nzbInfo->GetDupeMode()],
BoolToStr(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone),
downloadedSizeLo, downloadedSizeHi, downloadedSizeMB, nzbInfo->GetDownloadSec(),
nzbInfo->GetPostInfo() && nzbInfo->GetPostInfo()->GetStartTime() ?
Util::CurrentTime() - nzbInfo->GetPostInfo()->GetStartTime() : nzbInfo->GetPostTotalSec(),
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
@@ -1966,7 +1966,7 @@ void ListGroupsXmlCommand::Execute()
const char* ListGroupsXmlCommand::DetectStatus(NzbInfo* nzbInfo)
{
const char* postStageName[] = { "PP_QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING",
"VERIFYING_REPAIRED", "RENAMING", "UNPACKING", "MOVING", "EXECUTING_SCRIPT", "PP_FINISHED" };
"VERIFYING_REPAIRED", "RENAMING", "RENAMING", "UNPACKING", "MOVING", "MOVING", "EXECUTING_SCRIPT", "PP_FINISHED" };
const char* status = nullptr;
@@ -2019,6 +2019,8 @@ EditCommandEntry EditCommandNameMap[] = {
{ DownloadQueue::eaGroupMoveOffset, "GroupMoveOffset" },
{ DownloadQueue::eaGroupMoveTop, "GroupMoveTop" },
{ DownloadQueue::eaGroupMoveBottom, "GroupMoveBottom" },
{ DownloadQueue::eaGroupMoveBefore, "GroupMoveBefore" },
{ DownloadQueue::eaGroupMoveAfter, "GroupMoveAfter" },
{ DownloadQueue::eaGroupPause, "GroupPause" },
{ DownloadQueue::eaGroupResume, "GroupResume" },
{ DownloadQueue::eaGroupDelete, "GroupDelete" },
@@ -2057,6 +2059,10 @@ EditCommandEntry EditCommandNameMap[] = {
{ 0, nullptr }
};
// v18:
// bool editqueue(string Command, string Args, int[] IDs)
// v17:
// bool editqueue(string Command, int Offset, string Args, int[] IDs)
void EditQueueXmlCommand::Execute()
{
if (!CheckSafeMethod())
@@ -2089,30 +2095,34 @@ void EditQueueXmlCommand::Execute()
}
int offset = 0;
if (!NextParamAsInt(&offset))
bool hasOffset = NextParamAsInt(&offset);
char* args;
if (!NextParamAsStr(&args))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
debug("Args=%s", args);
char* editText;
if (!NextParamAsStr(&editText))
DecodeStr(args);
BString<100> offsetStr("%i", offset);
if (hasOffset && (action == DownloadQueue::eaFileMoveOffset ||
action == DownloadQueue::eaGroupMoveOffset))
{
BuildErrorResponse(2, "Invalid parameter");
return;
args = *offsetStr;
}
debug("EditText=%s", editText);
DecodeStr(editText);
IdList cIdList;
IdList idList;
int id = 0;
while (NextParamAsInt(&id))
{
cIdList.push_back(id);
idList.push_back(id);
}
bool ok = DownloadQueue::Guard()->EditList(&cIdList, nullptr, DownloadQueue::mmId, (DownloadQueue::EEditAction)action, offset, editText);
bool ok = DownloadQueue::Guard()->EditList(&idList, nullptr, DownloadQueue::mmId,
(DownloadQueue::EEditAction)action, args);
BuildBoolResponse(ok);
}
@@ -2312,7 +2322,7 @@ void PostQueueXmlCommand::Execute()
"}";
const char* postStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING",
"VERIFYING_REPAIRED", "RENAMING", "UNPACKING", "MOVING", "EXECUTING_SCRIPT", "FINISHED" };
"VERIFYING_REPAIRED", "RENAMING", "RENAMING", "UNPACKING", "MOVING", "MOVING", "EXECUTING_SCRIPT", "FINISHED" };
int index = 0;
@@ -2706,6 +2716,8 @@ void ConfigTemplatesXmlCommand::Execute()
"<member><name>QueueScript</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>SchedulerScript</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>FeedScript</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>QueueEvents</name><value><string>%s</string></value></member>\n"
"<member><name>TaskTime</name><value><string>%s</string></value></member>\n"
"<member><name>Template</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
@@ -2718,6 +2730,8 @@ void ConfigTemplatesXmlCommand::Execute()
"\"QueueScript\" : %s,\n"
"\"SchedulerScript\" : %s,\n"
"\"FeedScript\" : %s,\n"
"\"QueueEvents\" : \"%s\",\n"
"\"TaskTime\" : \"%s\",\n"
"\"Template\" : \"%s\"\n"
"}";
@@ -2752,6 +2766,8 @@ void ConfigTemplatesXmlCommand::Execute()
BoolToStr(configTemplate.GetScript()->GetQueueScript()),
BoolToStr(configTemplate.GetScript()->GetSchedulerScript()),
BoolToStr(configTemplate.GetScript()->GetFeedScript()),
*EncodeStr(configTemplate.GetScript()->GetQueueEvents()),
*EncodeStr(configTemplate.GetScript()->GetTaskTime()),
*EncodeStr(configTemplate.GetTemplate()));
}

View File

@@ -1168,7 +1168,7 @@ int64 DiskFile::Position()
return ftell(m_file);
}
int64 DiskFile::Seek(int64 position, ESeekOrigin origin)
bool DiskFile::Seek(int64 position, ESeekOrigin origin)
{
return fseek(m_file, position,
origin == soCur ? SEEK_CUR :

View File

@@ -23,10 +23,6 @@
#include "NString.h"
#ifdef WIN32
class WString;
#endif
class FileSystem
{
public:
@@ -139,7 +135,7 @@ public:
int64 Read(void* buffer, int64 size);
int64 Write(const void* buffer, int64 size);
int64 Position();
int64 Seek(int64 position, ESeekOrigin origin = soSet);
bool Seek(int64 position, ESeekOrigin origin = soSet);
bool Eof();
bool Error();
int64 Print(const char* format, ...) PRINTF_SYNTAX(2);

View File

@@ -244,6 +244,7 @@ void CString::Reserve(int capacity)
if (capacity > curLen || curLen == 0)
{
m_data = (char*)realloc(m_data, capacity + 1);
m_data[curLen] = '\0';
}
}
@@ -275,14 +276,39 @@ void CString::TrimRight()
}
#ifdef WIN32
WString::WString(const char* utfstr)
{
int len = MultiByteToWideChar(CP_UTF8, 0, utfstr, -1, nullptr, 0);
m_data = (wchar_t*)malloc((len + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, utfstr, -1, m_data, len);
m_data = (wchar_t*)malloc((strlen(utfstr) * 2 + 1) * sizeof(wchar_t));
wchar_t* out = m_data;
unsigned int codepoint;
while (*utfstr != 0)
{
unsigned char ch = (unsigned char)*utfstr;
if (ch <= 0x7f)
codepoint = ch;
else if (ch <= 0xbf)
codepoint = (codepoint << 6) | (ch & 0x3f);
else if (ch <= 0xdf)
codepoint = ch & 0x1f;
else if (ch <= 0xef)
codepoint = ch & 0x0f;
else
codepoint = ch & 0x07;
++utfstr;
if (((*utfstr & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
{
if (codepoint > 0xffff)
{
*out++ = (wchar_t)(0xd800 + (codepoint >> 10));
*out++ = (wchar_t)(0xdc00 + (codepoint & 0x03ff));
}
else if (codepoint < 0xd800 || codepoint >= 0xe000)
*out++ = (wchar_t)(codepoint);
}
}
*out = '\0';
}
#endif
void StringBuilder::Clear()

View File

@@ -94,14 +94,13 @@ protected:
char* m_data = nullptr;
};
#ifdef WIN32
/*
Wide-character string, Windows specific.
Wide-character string.
*/
class WString
{
public:
WString(wchar_t* wstr) : m_data(_wcsdup(wstr)) {}
WString(wchar_t* wstr) : m_data(wcsdup(wstr)) {}
WString(const char* utfstr);
~WString() { free(m_data); }
WString(WString&& other) noexcept { m_data = other.m_data; other.m_data = nullptr; }
@@ -113,7 +112,6 @@ public:
protected:
wchar_t* m_data = nullptr;
};
#endif
/*
StringBuilder preallocates storage space and is best suitable for often "Append"s.
@@ -126,6 +124,7 @@ public:
explicit operator char*() { return m_data; }
const char* operator*() const { return m_data; }
int Length() const { return m_length; }
void SetLength(int length) { m_length = length; }
int Capacity() const { return m_capacity; }
void Reserve(int capacity, bool exact = false);
bool Empty() const { return m_length == 0; }

View File

@@ -304,6 +304,7 @@ int ScriptController::Execute()
PrepareEnvOptions(nullptr);
PrepareArgs();
m_completed = false;
int exitCode = 0;
#ifdef CHILD_WATCHDOG
@@ -315,6 +316,7 @@ int ScriptController::Execute()
int pipein = StartProcess();
if (pipein == -1)
{
m_completed = true;
return -1;
}
@@ -322,8 +324,9 @@ int ScriptController::Execute()
m_readpipe = fdopen(pipein, "r");
if (!m_readpipe)
{
PrintMessage(Message::mkError, "Could not open pipe to %s", m_infoName);
PrintMessage(Message::mkError, "Could not open pipe to %s", *m_infoName);
close(pipein);
m_completed = true;
return -1;
}
@@ -383,7 +386,7 @@ int ScriptController::Execute()
if (m_terminated && m_infoName)
{
warn("Interrupted %s", m_infoName);
warn("Interrupted %s", *m_infoName);
}
exitCode = 0;
@@ -404,7 +407,7 @@ int ScriptController::Execute()
#endif
debug("Exit code %i", exitCode);
m_completed = true;
return exitCode;
}
@@ -474,7 +477,7 @@ int ScriptController::StartProcess()
std::unique_ptr<wchar_t[]> environmentStrings = m_environmentStrings.GetStrings();
BOOL ok = CreateProcessW(nullptr, WString(cmdLine), nullptr, nullptr, TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
NORMAL_PRIORITY_CLASS | CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT,
environmentStrings.get(), wideWorkingDir, &startupInfo, &processInfo);
if (!ok)
{
@@ -483,11 +486,11 @@ int ScriptController::StartProcess()
errMsg[255 - 1] = '\0';
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, errCode, 0, errMsg, 255, nullptr))
{
PrintMessage(Message::mkError, "Could not start %s: %s", m_infoName, errMsg);
PrintMessage(Message::mkError, "Could not start %s: %s", *m_infoName, errMsg);
}
else
{
PrintMessage(Message::mkError, "Could not start %s: error %i", m_infoName, errCode);
PrintMessage(Message::mkError, "Could not start %s: error %i", *m_infoName, errCode);
}
if (!FileSystem::FileExists(script))
{
@@ -505,6 +508,7 @@ int ScriptController::StartProcess()
debug("Child Process-ID: %i", (int)processInfo.dwProcessId);
m_processId = processInfo.hProcess;
m_dwProcessId = processInfo.dwProcessId;
// close unused "write" end
CloseHandle(writePipe);
@@ -541,7 +545,7 @@ int ScriptController::StartProcess()
if (pid == -1)
{
PrintMessage(Message::mkError, "Could not start %s: errno %i", m_infoName, errno);
PrintMessage(Message::mkError, "Could not start %s: errno %i", *m_infoName, errno);
close(pipein);
close(pipeout);
return -1;
@@ -632,11 +636,11 @@ int ScriptController::WaitProcess()
void ScriptController::Terminate()
{
debug("Stopping %s", m_infoName);
debug("Stopping %s", *m_infoName);
m_terminated = true;
#ifdef WIN32
BOOL ok = TerminateProcess(m_processId, -1);
BOOL ok = TerminateProcess(m_processId, -1) || m_completed;
if (ok)
{
// wait 60 seconds for process to terminate
@@ -655,19 +659,19 @@ void ScriptController::Terminate()
// if the child process has its own group (setsid() was successful), kill the whole group
killId = -killId;
}
bool ok = killId && kill(killId, SIGKILL) == 0;
bool ok = (killId && kill(killId, SIGKILL) == 0) || m_completed;
#endif
if (ok)
{
debug("Terminated %s", m_infoName);
debug("Terminated %s", *m_infoName);
}
else
{
error("Could not terminate %s", m_infoName);
error("Could not terminate %s", *m_infoName);
}
debug("Stopped %s", m_infoName);
debug("Stopped %s", *m_infoName);
}
void ScriptController::TerminateAll()
@@ -677,14 +681,45 @@ void ScriptController::TerminateAll()
{
if (script->m_processId && !script->m_detached)
{
// send break signal and wait up to 5 seconds for graceful termination
if (script->Break())
{
time_t curtime = Util::CurrentTime();
while (!script->m_completed && std::abs(curtime - Util::CurrentTime()) <= 10)
{
usleep(100 * 1000);
}
}
script->Terminate();
}
}
}
bool ScriptController::Break()
{
debug("Sending break signal to %s", *m_infoName);
#ifdef WIN32
BOOL ok = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, m_dwProcessId);
#else
bool ok = kill(m_processId, SIGINT) == 0;
#endif
if (ok)
{
debug("Sent break signal to %s", *m_infoName);
}
else
{
warn("Could not send break signal to %s", *m_infoName);
}
return ok;
}
void ScriptController::Detach()
{
debug("Detaching %s", m_infoName);
debug("Detaching %s", *m_infoName);
m_detached = true;
FILE* readpipe = m_readpipe;
m_readpipe = nullptr;

View File

@@ -54,6 +54,7 @@ public:
virtual ~ScriptController();
int Execute();
void Terminate();
bool Break();
void Resume();
void Detach();
static void TerminateAll();
@@ -87,14 +88,16 @@ protected:
private:
ArgList m_args;
const char* m_workingDir = nullptr;
const char* m_infoName = nullptr;
CString m_infoName;
const char* m_logPrefix = nullptr;
EnvironmentStrings m_environmentStrings;
bool m_terminated = false;
bool m_completed = false;
bool m_detached = false;
FILE* m_readpipe;
#ifdef WIN32
HANDLE m_processId = 0;
DWORD m_dwProcessId = 0;
char m_cmdLine[2048];
#else
pid_t m_processId = 0;

View File

@@ -276,6 +276,17 @@ CString Util::FormatTime(time_t timeSec)
return result;
}
CString Util::FormatBuffer(const char* buf, int len)
{
CString result;
result.Reserve(len * 3 + 1);
while (len--)
{
result.AppendFmt("%02x ", (int)(uchar)*buf++);
}
return result;
}
bool Util::MatchFileExt(const char* filename, const char* extensionList, const char* listSeparator)
{
int filenameLen = strlen(filename);

View File

@@ -72,6 +72,7 @@ public:
static CString FormatSpeed(int bytesPerSecond);
static CString FormatSize(int64 fileSize);
static CString FormatBuffer(const char* buf, int len);
/*
* Returns program version and revision number as string formatted like "0.7.0-r295".

View File

@@ -31,10 +31,6 @@ static char THIS_FILE[]=__FILE__;
namespace Par2
{
NullStreamBuf nullStreamBuf;
std::ostream cout(&nullStreamBuf);
std::ostream cerr(&nullStreamBuf);
CommandLine::ExtraFile::ExtraFile(void)
: filename()
, filesize(0)
@@ -62,7 +58,7 @@ CommandLine::ExtraFile::ExtraFile(const string &name, u64 size)
}
CommandLine::CommandLine(void)
CommandLine::CommandLine(std::ostream& cout, std::ostream& cerr)
: operation(opNone)
, version(verUnknown)
, noiselevel(nlUnknown)
@@ -80,12 +76,14 @@ CommandLine::CommandLine(void)
, totalsourcesize(0)
, largestsourcesize(0)
, memorylimit(0)
, cout(cout)
, cerr(cerr)
{
}
void CommandLine::usage(void)
{
cout <<
std::cout <<
"\n"
"Usage:\n"
"\n"

View File

@@ -30,7 +30,7 @@ namespace Par2
class CommandLine
{
public:
CommandLine(void);
CommandLine(std::ostream& cout, std::ostream& cerr);
// Parse the supplied command line arguments.
bool Parse(int argc, char *argv[]);
@@ -153,6 +153,9 @@ protected:
size_t memorylimit; // How much memory is permitted to be used
// for the output buffer when creating
// or repairing.
std::ostream& cout;
std::ostream& cerr;
};
typedef list<CommandLine::ExtraFile>::const_iterator ExtraFileIterator;

View File

@@ -40,7 +40,8 @@ namespace Par2
#define LengthType unsigned int
#define MaxLength 0xffffffffUL
DiskFile::DiskFile(void)
DiskFile::DiskFile(std::ostream& cerr) :
cerr(cerr)
{
filename;
filesize = 0;
@@ -365,7 +366,8 @@ list<string>* DiskFile::FindFiles(string path, string wildcard)
#define LengthType unsigned int
#define MaxLength 0xffffffffUL
DiskFile::DiskFile(void)
DiskFile::DiskFile(std::ostream& cerr) :
cerr(cerr)
{
//filename;
filesize = 0;

View File

@@ -29,7 +29,7 @@ namespace Par2
class DiskFile
{
public:
DiskFile(void);
DiskFile(std::ostream& cerr);
~DiskFile(void);
// Create a file and set its length
@@ -106,6 +106,8 @@ protected:
#ifdef WIN32
static string ErrorMessage(DWORD error);
#endif
std::ostream& cerr;
};
// This class keeps track of which DiskFile objects exist

View File

@@ -32,7 +32,7 @@ namespace Par2
{
// Construct the main packet from the source files and the block size
/*
bool MainPacket::Create(vector<Par2CreatorSourceFile*> &sourcefiles, u64 _blocksize)
{
recoverablefilecount = totalfilecount =(u32)sourcefiles.size();
@@ -80,6 +80,7 @@ bool MainPacket::Create(vector<Par2CreatorSourceFile*> &sourcefiles, u64 _blocks
return true;
}
*/
// Load a main packet from a specified file

View File

@@ -39,8 +39,8 @@ public:
public:
// Construct the main packet from the source file list and block size.
// "sourcefiles" will be sorted base on their FileId value.
bool Create(vector<Par2CreatorSourceFile*> &sourcefiles,
u64 _blocksize);
/*bool Create(vector<Par2CreatorSourceFile*> &sourcefiles,
u64 _blocksize);*/
// Load a main packet from a specified file
bool Load(DiskFile *diskfile, u64 offset, PACKET_HEADER &header);

View File

@@ -141,11 +141,6 @@ typedef enum Result
} Result;
class NullStreamBuf : public std::streambuf {};
extern NullStreamBuf nullStreamBuf;
extern std::ostream cout;
extern std::ostream cerr;
} // end namespace Par2
#define LONGMULTIPLY
@@ -171,7 +166,7 @@ using namespace std;
#include "datablock.h"
#include "criticalpacket.h"
#include "par2creatorsourcefile.h"
//#include "par2creatorsourcefile.h"
#include "mainpacket.h"
#include "creatorpacket.h"

View File

@@ -1,348 +0,0 @@
// This file is part of par2cmdline (a PAR 2.0 compatible file verification and
// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
//
// Copyright (c) 2003 Peter Brian Clements
//
// par2cmdline 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.
//
// par2cmdline is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#include "nzbget.h"
#include "par2cmdline.h"
#ifdef _MSC_VER
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif
namespace Par2
{
Par2CreatorSourceFile::Par2CreatorSourceFile(void)
{
descriptionpacket = 0;
verificationpacket = 0;
diskfile = 0;
blockcount = 0;
//diskfilename;
//parfilename;
contextfull = 0;
}
Par2CreatorSourceFile::~Par2CreatorSourceFile(void)
{
delete descriptionpacket;
delete verificationpacket;
delete diskfile;
delete contextfull;
}
// Open the source file, compute the MD5 Hash of the whole file and the first
// 16k of the file, and then compute the FileId and store the results
// in a file description packet and a file verification packet.
bool Par2CreatorSourceFile::Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation)
{
// Get the filename and filesize
diskfilename = extrafile.FileName();
filesize = extrafile.FileSize();
// Work out how many blocks the file will be sliced into
blockcount = (u32)((filesize + blocksize-1) / blocksize);
// Determine what filename to record in the PAR2 files
string::size_type where;
if (string::npos != (where = diskfilename.find_last_of('\\')) ||
string::npos != (where = diskfilename.find_last_of('/')))
{
parfilename = diskfilename.substr(where+1);
}
else
{
parfilename = diskfilename;
}
// Create the Description and Verification packets
descriptionpacket = new DescriptionPacket;
descriptionpacket->Create(parfilename, filesize);
verificationpacket = new VerificationPacket;
verificationpacket->Create(blockcount);
// Create the diskfile object
diskfile = new DiskFile;
// Open the source file
if (!diskfile->Open(diskfilename, filesize))
return false;
// Do we want to defer the computation of the full file hash, and
// the block crc and hashes. This is only permitted if there
// is sufficient memory available to create all recovery blocks
// in one pass of the source files (i.e. chunksize == blocksize)
if (deferhashcomputation)
{
// Initialise a buffer to read the first 16k of the source file
size_t buffersize = 16 * 1024;
if (buffersize > filesize)
buffersize = (size_t)filesize;
char *buffer = new char[buffersize];
// Read the data from the file
if (!diskfile->Read(0, buffer, buffersize))
{
diskfile->Close();
delete [] buffer;
return false;
}
// Compute the hash of the data read from the file
MD5Context context;
context.Update(buffer, buffersize);
delete [] buffer;
MD5Hash hash;
context.Final(hash);
// Store the hash in the descriptionpacket and compute the file id
descriptionpacket->Hash16k(hash);
// Compute the fileid and store it in the verification packet.
descriptionpacket->ComputeFileId();
verificationpacket->FileId(descriptionpacket->FileId());
// Allocate an MD5 context for computing the file hash
// during the recovery data generation phase
contextfull = new MD5Context;
}
else
{
// Initialise a buffer to read the source file
size_t buffersize = 1024*1024;
if (buffersize > min(blocksize,filesize))
buffersize = (size_t)min(blocksize,filesize);
char *buffer = new char[buffersize];
// Get ready to start reading source file to compute the hashes and crcs
u64 offset = 0;
u32 blocknumber = 0;
u64 need = blocksize;
MD5Context filecontext;
MD5Context blockcontext;
u32 blockcrc = 0;
// Whilst we have not reached the end of the file
while (offset < filesize)
{
// Work out how much we can read
size_t want = (size_t)min(filesize-offset, (u64)buffersize);
// Read some data from the file into the buffer
if (!diskfile->Read(offset, buffer, want))
{
diskfile->Close();
delete [] buffer;
return false;
}
// If the new data passes the 16k boundary, compute the 16k hash for the file
if (offset < 16384 && offset + want >= 16384)
{
filecontext.Update(buffer, (size_t)(16384-offset));
MD5Context temp = filecontext;
MD5Hash hash;
temp.Final(hash);
// Store the 16k hash in the file description packet
descriptionpacket->Hash16k(hash);
if (offset + want > 16384)
{
filecontext.Update(&buffer[16384-offset], (size_t)(offset+want)-16384);
}
}
else
{
filecontext.Update(buffer, want);
}
// Get ready to update block hashes and crcs
u32 used = 0;
// Whilst we have not used all of the data we just read
while (used < want)
{
// How much of it can we use for the current block
u32 use = (u32)min(need, (u64)(want-used));
blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, use, &buffer[used]);
blockcontext.Update(&buffer[used], use);
used += use;
need -= use;
// Have we finished the current block
if (need == 0)
{
MD5Hash blockhash;
blockcontext.Final(blockhash);
// Store the block hash and block crc in the file verification packet.
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
blocknumber++;
// More blocks
if (blocknumber < blockcount)
{
need = blocksize;
blockcontext.Reset();
blockcrc = 0;
}
}
}
if (noiselevel > CommandLine::nlQuiet)
{
// Display progress
u32 oldfraction = (u32)(1000 * offset / filesize);
offset += want;
u32 newfraction = (u32)(1000 * offset / filesize);
if (oldfraction != newfraction)
{
cout << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
}
}
}
// Did we finish the last block
if (need > 0)
{
blockcrc = ~0 ^ CRCUpdateBlock(~0 ^ blockcrc, (size_t)need);
blockcontext.Update((size_t)need);
MD5Hash blockhash;
blockcontext.Final(blockhash);
// Store the block hash and block crc in the file verification packet.
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
blocknumber++;
need = 0;
}
// Finish computing the file hash.
MD5Hash filehash;
filecontext.Final(filehash);
// Store the file hash in the file description packet.
descriptionpacket->HashFull(filehash);
// Did we compute the 16k hash.
if (offset < 16384)
{
// Store the 16k hash in the file description packet.
descriptionpacket->Hash16k(filehash);
}
delete [] buffer;
// Compute the fileid and store it in the verification packet.
descriptionpacket->ComputeFileId();
verificationpacket->FileId(descriptionpacket->FileId());
}
return true;
}
void Par2CreatorSourceFile::Close(void)
{
diskfile->Close();
}
void Par2CreatorSourceFile::RecordCriticalPackets(list<CriticalPacket*> &criticalpackets)
{
// Add the file description packet and file verification packet to
// the critical packet list.
criticalpackets.push_back(descriptionpacket);
criticalpackets.push_back(verificationpacket);
}
bool Par2CreatorSourceFile::CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right)
{
// Sort source files based on fileid
return left->descriptionpacket->FileId() < right->descriptionpacket->FileId();
}
const MD5Hash& Par2CreatorSourceFile::FileId(void) const
{
// Get the file id hash
return descriptionpacket->FileId();
}
void Par2CreatorSourceFile::InitialiseSourceBlocks(vector<DataBlock>::iterator &sourceblock, u64 blocksize)
{
for (u32 blocknum=0; blocknum<blockcount; blocknum++)
{
// Configure each source block to an appropriate offset and length within the source file.
sourceblock->SetLocation(diskfile, // file
blocknum * blocksize); // offset
sourceblock->SetLength(min(blocksize, filesize - (u64)blocknum * blocksize)); // length
sourceblock++;
}
}
void Par2CreatorSourceFile::UpdateHashes(u32 blocknumber, const void *buffer, size_t length)
{
// Compute the crc and hash of the data
u32 blockcrc = ~0 ^ CRCUpdateBlock(~0, length, buffer);
MD5Context blockcontext;
blockcontext.Update(buffer, length);
MD5Hash blockhash;
blockcontext.Final(blockhash);
// Store the results in the verification packet
verificationpacket->SetBlockHashAndCRC(blocknumber, blockhash, blockcrc);
// Update the full file hash, but don't go beyond the end of the file
if (length > filesize - blocknumber * length)
{
length = (size_t)(filesize - blocknumber * (u64)length);
}
assert(contextfull != 0);
contextfull->Update(buffer, length);
}
void Par2CreatorSourceFile::FinishHashes(void)
{
assert(contextfull != 0);
// Finish computation of the full file hash
MD5Hash hash;
contextfull->Final(hash);
// Store it in the description packet
descriptionpacket->HashFull(hash);
}
} // end namespace Par2

View File

@@ -1,86 +0,0 @@
// This file is part of par2cmdline (a PAR 2.0 compatible file verification and
// repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
//
// Copyright (c) 2003 Peter Brian Clements
//
// par2cmdline 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.
//
// par2cmdline is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
#ifndef __PAR2CREATORSOURCEFILE_H__
#define __PAR2CREATORSOURCEFILE_H__
namespace Par2
{
class DescriptionPacket;
class VerificationPacket;
class DiskFile;
// The Par2CreatorSourceFile contains the file verification and file description
// packet for one source file.
class Par2CreatorSourceFile
{
private:
// Don't permit copying or assignment
Par2CreatorSourceFile(const Par2CreatorSourceFile &other);
Par2CreatorSourceFile& operator=(const Par2CreatorSourceFile &other);
public:
Par2CreatorSourceFile(void);
~Par2CreatorSourceFile(void);
// Open the source file and compute the Hashes and CRCs.
bool Open(CommandLine::NoiseLevel noiselevel, const CommandLine::ExtraFile &extrafile, u64 blocksize, bool deferhashcomputation);
void Close(void);
// Recover the file description and file verification packets
// in the critical packet list.
void RecordCriticalPackets(list<CriticalPacket*> &criticalpackets);
// Get the file id
const MD5Hash& FileId(void) const;
// Sort source files based on the file id hash
static bool CompareLess(const Par2CreatorSourceFile* const &left, const Par2CreatorSourceFile* const &right);
// Allocate the appropriate number of source blocks to the source file
void InitialiseSourceBlocks(vector<DataBlock>::iterator &sourceblock, u64 blocksize);
// Update the file hash and the block crc and hashes
void UpdateHashes(u32 blocknumber, const void *buffer, size_t length);
// Finish computation of the file hash
void FinishHashes(void);
// How many blocks does this source file use
u32 BlockCount(void) const {return blockcount;}
protected:
DescriptionPacket *descriptionpacket; // The file description packet.
VerificationPacket *verificationpacket; // The file verification packet.
DiskFile *diskfile; // The source file
u64 filesize; // The size of the source file.
string diskfilename; // The filename of the source file on disk.
string parfilename; // The filename that will be recorded in the file description packet.
u32 blockcount; // How many blocks the file will be divided into.
MD5Context *contextfull; // MD5 context used to calculate the hash of the whole file
};
} // end namespace Par2
#endif // __PAR2CREATORSOURCEFILE_H__

View File

@@ -31,7 +31,8 @@ static char THIS_FILE[]=__FILE__;
namespace Par2
{
Par2Repairer::Par2Repairer(void)
Par2Repairer::Par2Repairer(std::ostream& cout, std::ostream& cerr):
cout(cout), cerr(cerr), rs(cout, cerr)
{
firstpacket = true;
mainpacket = 0;
@@ -360,7 +361,7 @@ bool Par2Repairer::LoadPacketsFromFile(string filename)
return true;
}
DiskFile *diskfile = new DiskFile;
DiskFile *diskfile = new DiskFile(cerr);
// Open the file
if (!diskfile->Open(filename))
@@ -1276,7 +1277,7 @@ bool Par2Repairer::VerifySourceFiles(void)
return false;
}
DiskFile *diskfile = new DiskFile;
DiskFile *diskfile = new DiskFile(cerr);
// Does the target file exist
if (diskfile->Open(filename))
@@ -1340,7 +1341,7 @@ bool Par2Repairer::VerifyExtraFiles(const list<CommandLine::ExtraFile> &extrafil
// Has this file already been dealt with
if (diskFileMap.Find(filename) == 0)
{
DiskFile *diskfile = new DiskFile;
DiskFile *diskfile = new DiskFile(cerr);
// Does the file exist
if (!diskfile->Open(filename))
@@ -2099,7 +2100,7 @@ bool Par2Repairer::CreateTargetFiles(void)
// If the file does not exist
if (!sourcefile->GetTargetExists())
{
DiskFile *targetfile = new DiskFile;
DiskFile *targetfile = new DiskFile(cerr);
string filename = sourcefile->TargetFileName();
u64 filesize = sourcefile->GetDescriptionPacket()->FileSize();

View File

@@ -27,7 +27,7 @@ namespace Par2 {
class Par2Repairer
{
public:
Par2Repairer(void);
Par2Repairer(std::ostream& cout, std::ostream& cerr);
~Par2Repairer(void);
Result PreProcess(const CommandLine &commandline);
@@ -196,6 +196,9 @@ protected:
u64 totalsize; // Total data size
bool cancelled; // repair cancelled
std::ostream& cout;
std::ostream& cerr;
};
} // end namespace Par2

View File

@@ -47,7 +47,7 @@ class ReedSolomon
public:
typedef g G;
ReedSolomon(void);
ReedSolomon(std::ostream& cout, std::ostream& cerr);
~ReedSolomon(void);
// Set which input blocks are present or missing
@@ -105,10 +105,14 @@ protected:
#ifdef LONGMULTIPLY
GaloisLongMultiplyTable<g> *glmt; // A multiplication table used by Process()
#endif
std::ostream& cout;
std::ostream& cerr;
};
template<class g>
inline ReedSolomon<g>::ReedSolomon(void)
inline ReedSolomon<g>::ReedSolomon(std::ostream& cout, std::ostream& cerr) :
cout(cout), cerr(cerr)
{
inputcount = 0;

View File

@@ -36,8 +36,8 @@ InterDir=${MainDir}/inter
# Directory for incoming nzb-files.
#
# If a new nzb-file is added to queue via web-interface or RPC-API, it
# is saved into this directory and then processed by preprocessing
# script (option <ScanScript>).
# is saved into this directory and then processed by extension
# scripts (option <Extensions>).
#
# This directory is also monitored for new nzb-files. If a new file
# is found it is added to download queue. The directory can have
@@ -344,7 +344,7 @@ AddPassword=
# Secure control of NZBGet server (yes, no).
#
# Activate the option if you want to access NZBGet built-in web-server
# Activate the option if you want to access NZBGet built-in web-server
# via HTTPS (web-interface and RPC). You should also provide certificate
# and key files, see option <SecureCert> and option <SecureKey>.
SecureControl=no
@@ -418,10 +418,10 @@ Category1.DestDir=
# For more information see global option <Unpack>.
Category1.Unpack=yes
# Default list of post-processing scripts.
# List of extension scripts for this category.
#
# For more information see global option <PostScript>.
Category1.PostScript=
# For more information see global option <Extensions>.
Category1.Extensions=
# List of aliases.
#
@@ -622,10 +622,14 @@ Category4.Name=Software
# post-processed even if the program is in paused state (force mode).
#Feed1.Priority=0
# List of rss feed scripts to execute before rss feed content is processed.
# List of rss feed extension scripts to execute for rss content.
#
# For more information see global option <FeedScript>.
#Feed1.FeedScript=
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
# paths relative to <ScriptDir> must be entered here.
#
# NOTE: For developer documentation visit http://nzbget.net/Extension_scripts.
#Feed1.Extensions=
##############################################################################
@@ -639,8 +643,7 @@ AppendCategoryDir=yes
#
# Value "0" disables the check.
#
# NOTE: nzb-files are processed by scan and queue scripts. See
# options <ScanScript> and <QueueScript>.
# NOTE: nzb-files are processed by extension scripts. See option <Extensions>.
NzbDirInterval=5
# How old nzb-file should at least be for it to be loaded to queue (seconds).
@@ -714,7 +717,7 @@ ContinuePartial=yes
# Propagation delay to your news servers (minutes).
#
# The option sets minimum post age for nzb-files. Very recent files
# are not downloaded to avoid download failures. The files remain
# are not downloaded to avoid download failures. The files remain
# on hold in the download queue until the propagation delay expires,
# after that they are downloaded.
PropagationDelay=0
@@ -768,7 +771,7 @@ ArticleCache=0
# all articles of the file are downloaded or when the cache becomes
# full to 90%.
#
# The direct write relies on the ability of file system to create
# The direct write relies on the ability of file system to create
# empty files without allocating the space on the drive (sparse files),
# which most modern file systems support including EXT3, EXT4
# and NTFS. The notable exception is HFS+ (default file system on OSX).
@@ -812,65 +815,25 @@ WriteBuffer=0
# slow CPU disabling of CRC-Check may improve performance.
CrcCheck=yes
# How many retries should be attempted if a download error occurs (0-99).
# Post-processing strategy (sequential, balanced, aggressive, rocket).
#
# If download fails because of incomplete or damaged article or due to
# CRC-error the program tries to re-download the article from the same
# news server as many times as defined in option <Retries>. If all
# attempts fail the program tries another news server.
# Sequential - downloaded items are post processed from a queue, one item at a
# time, to dedicate the most computer resources to each
# item. Therefore, a post process par repair will prevent another
# task from running even if the item does not require a par repair;
# Balanced - items that do not need par repair are post processed one at a
# time while par repair tasks may also run simultaneously one after
# another at the same time. This means that a post process par
# repair will not prevent another task from running, but at a cost
# of using more computer resource;
# Aggressive - will simultaneously post process up to three items including
# one par repair task;
# Rocket - will simultaneously post process up to six items including one
# or two par repair tasks.
#
# If download fails because of "article or group not found error" the
# program tries another news server without retrying on the failed server.
#
# If download fails because of interrupted connection the program
# tries another news server or the same server after the block interval
# expires.
Retries=3
# Wait interval between retries (seconds).
#
# If download of an article fails because of interrupted connection
# the server is temporary blocked until the retry interval expires.
RetryInterval=10
# Connection timeout for article downloading (seconds).
ArticleTimeout=60
# Connection timeout for URL fetching (seconds).
#
# This includes fetching of nzb-files via URLs and fetching of RSS feeds.
UrlTimeout=60
# Timeout until a download-thread should be killed (seconds).
#
# This can help on hanging downloads, but is dangerous.
# Do not use small values!
TerminateTimeout=600
# Set the maximum download rate on program start (kilobytes/sec).
#
# The download rate can be changed later via remote calls.
#
# Value "0" means no speed control.
DownloadRate=0
# Accurate speed rate calculation (yes, no).
#
# During downloading using several connections the download threads may
# interfere with each other when updating statistical data for speed
# meter. This may cause small errors in current download speed reported
# by the program. The speed meter recovers automatically from such errors
# after max. 30 seconds (time window used for speed calculation).
#
# Enable the option to use thread synchronisation mechanisms in order to
# provide absolutely accurate speed calculations.
#
# NOTE: Thread synchronisation increases CPU load and therefore can
# decrease download speed. Do not activate this option on computers with
# limited CPU power. Before activating the option it is recommended to
# run tests to determine how the option affects the CPU usage and the
# download speed on a particular system.
AccurateRate=no
# NOTE: Computer resources are in heavy demand when post-processing with
# simultaneous tasks - make sure the hardware is capable.
PostStrategy=balanced
# Pause if disk space gets below this value (megabytes).
#
@@ -902,13 +865,16 @@ NzbCleanupDisk=yes
#
# If option <DupeCheck> is NOT active the items are removed from history.
#
# When a failed item is removed from history or become hidden all downloaded
# files of that item are deleted from disk.
#
# Value "0" disables history. Duplicate check will not work.
KeepHistory=30
# Keep the history of outdated feed items (days).
#
# After fetching of an RSS feed the information about included items (nzb-files)
# is saved to disk. This allows to detect new items on next fetch. Feed
# is saved to disk. This allows to detect new items on next fetch. Feed
# providers update RSS feeds constantly. Since the feed length is limited
# (usually 100 items or less) the old items get pushed away by new
# ones. When an item is not present in the feed anymore it's not necessary
@@ -922,9 +888,80 @@ KeepHistory=30
# has technical issues and may response with empty feeds (or with missing
# items). When the technical issue is fixed the items may reappear in the
# feed causing the program to re-download items if they were not found in
# the feed history.
# the feed history.
FeedHistory=7
##############################################################################
### CONNECTION ###
# How many retries should be attempted if a download error occurs (0-99).
#
# If download fails because of incomplete or damaged article or due to
# CRC-error the program tries to re-download the article from the same
# news server as many times as defined in this option. If all attempts fail
# the program tries another news server.
#
# If download fails because of "article or group not found error" the
# program tries another news server without retrying on the failed server.
ArticleRetries=3
# Article retry interval (seconds).
#
# If download of article fails because of interrupted connection
# the server is temporary blocked until the retry interval expires.
ArticleInterval=10
# Connection timeout for article downloading (seconds).
ArticleTimeout=60
# Number of download attempts for URL fetching (0-99).
#
# If fetching of nzb-file via URL or fetching of RSS feed fails another
# attempt is made after the retry interval (option <UrlInterval>).
UrlRetries=3
# URL fetching retry interval (seconds).
#
# If fetching of nzb-file via URL or fetching of RSS feed fails another
# attempt is made after the retry interval.
UrlInterval=10
# Connection timeout for URL fetching (seconds).
#
# Connection timeout when fetching nzb-files via URLs and fetching RSS feeds.
UrlTimeout=60
# Timeout until a download-thread should be killed (seconds).
#
# This can help on hanging downloads, but is dangerous.
# Do not use small values!
TerminateTimeout=600
# Set the maximum download rate on program start (kilobytes/sec).
#
# The download rate can be changed later in web-interface or via remote calls.
#
# Value "0" means no speed control.
DownloadRate=0
# Accurate speed rate calculation (yes, no).
#
# During downloading using several connections the download threads may
# interfere with each other when updating statistical data for speed
# meter. This may cause small errors in current download speed reported
# by the program. The speed meter recovers automatically from such errors
# after max. 30 seconds (time window used for speed calculation).
#
# Enable the option to use thread synchronisation mechanisms in order to
# provide absolutely accurate speed calculations.
#
# NOTE: Thread synchronisation increases CPU load and therefore can
# decrease download speed. Do not activate this option on computers with
# limited CPU power. Before activating the option it is recommended to
# run tests to determine how the option affects the CPU usage and the
# download speed on a particular system.
AccurateRate=no
# Maximum number of simultaneous connections for nzb URL downloads (0-999).
#
# When NZB-files are added to queue via URL, the program downloads them
@@ -1083,9 +1120,10 @@ UpdateInterval=200
# Time to execute the command (HH:MM).
#
# Multiple comma-separated values are accepted.
# An asterisk placed in the hours location will run every hour.
# An asterisk placed in the hours location will run task every hour (e. g. "*:00").
# An asterisk without minutes will run task at program startup (e. g. "*").
#
# Examples: "08:00", "00:00,06:00,12:00,18:00", "*:00", "*:00,*:30".
# Examples: "08:00", "00:00,06:00,12:00,18:00", "*:00", "*,*:00,*:30".
#
# NOTE: Also see option <TimeCorrection>.
#Task1.Time=08:00
@@ -1131,14 +1169,15 @@ UpdateInterval=200
# list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option
# <ScriptDir> and paths relative to <ScriptDir> must be
# entered here. For more info see below;
# entered here. For developer documentation visit
# http://nzbget.net/Extension_scripts;
# Process - path to the program to execute and its parameters.
# Example: /home/user/fetch.sh.
# If filename or any parameter contains spaces it
# must be surrounded with single quotation
# marks. If filename/parameter contains single quotation marks,
# each of them must be replaced (escaped) with two single quotation
# marks and the resulting filename/parameter must be
# each of them must be replaced (escaped) with two single quotation
# marks and the resulting filename/parameter must be
# surrounded with single quotation marks.
# Example: '/home/user/download/my scripts/task process.sh' 'world''s fun'.
# In this example one parameter (world's fun) is passed
@@ -1153,27 +1192,6 @@ UpdateInterval=200
# Example: bookmarks feed, another feed.
# NOTE: feed names should not have commas.
# NOTE: use feed id "0" to fetch all feeds.
#
# INFO FOR DEVELOPERS:
# The rest of the description is for command "Script".
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#
# NZBGet passes following arguments to scheduler script as environment
# variables:
# NZBSP_TASKID - id number of scheduler Task.
#
# In addition to these arguments NZBGet passes all nzbget.conf-options
# as environment variables. These variables have prefix "NZBOP_" and
# are written in UPPER CASE. For Example option "ParRepair" is passed as
# environment variable "NZBOP_PARREPAIR". The dots in option names are
# replaced with underscores, for example "SERVER1_HOST". For options
# with predefined possible values (yes/no, etc.) the values are passed
# always in lower case.
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#Task1.Param=
#Task2.Time=20:00
@@ -1183,7 +1201,7 @@ UpdateInterval=200
##############################################################################
### PAR CHECK/REPAIR ###
### CHECK AND REPAIR ###
# Whether and how par-verification must be performed (auto, always, force, manual).
#
@@ -1193,7 +1211,7 @@ UpdateInterval=200
# is enabled;
# Always - check every download (even undamaged). One par2-file is
# always downloaded. Additional par2-files are downloaded
# if needed for repair. Repair is performed if the option
# if needed for repair. Repair is performed if the option
# <ParRepair> is enabled;
# Force - force par-check for every download (even undamaged). All
# par2-files are always downloaded. Repair is performed if
@@ -1205,17 +1223,6 @@ UpdateInterval=200
# eventually on another faster computer.
ParCheck=auto
# Check for renamed and missing files (yes, no).
#
# Par-rename restores original file names using information stored
# in par2-files. It also detects missing files (files listed in
# par2-files but not present on disk). When enabled the par-rename is
# performed as the first step of post-processing for every nzb-file.
#
# Par-rename is very fast and is highly recommended, especially if
# unpack is disabled.
ParRename=yes
# Automatic par-repair after par-verification (yes, no).
#
# If option <ParCheck> is set to "Auto" or "Force" this option defines
@@ -1240,9 +1247,9 @@ ParScan=extended
# Quick file verification during par-check (yes, no).
#
# If the option is active the files are quickly verified using
# checksums calculated during download; quick verification is very fast
# because it doesn't require the reading of files from disk, NZBGet
# knows checksums of downloaded files and quickly compares them with
# checksums calculated during download; quick verification is very fast
# because it doesn't require the reading of files from disk, NZBGet
# knows checksums of downloaded files and quickly compares them with
# checksums stored in the par-file.
#
# If the option is disabled the files are verified as usual. That's
@@ -1290,6 +1297,30 @@ ParThreads=0
# Example: .sfv, .nzb, .nfo
ParIgnoreExt=.sfv, .nzb, .nfo
# Check for renamed and missing files using par-files (yes, no).
#
# Par-rename restores original file names using information stored
# in par2-files. It also detects missing files (files listed in
# par2-files but not present on disk). When enabled the par-rename is
# performed as the first step of post-processing for every nzb-file.
#
# Par-rename is very fast and is highly recommended, especially if
# unpack is disabled.
ParRename=yes
# Check for renamed rar-files (yes, no).
#
# Rar-rename restores original file names using information stored
# in rar-files. When enabled the rar-rename is performed as one of the
# first steps of post-processing for every nzb-file.
#
# Rar-rename is useful for downloads not having par2-files or for
# downloads those files were renamed before creating par2-files. In
# both cases par-rename (option <ParRename>) can't rename files
# and the rar-rename makes it possible to unpack downloads which
# would fail otherwise.
RarRename=yes
# What to do if download health drops below critical health (delete, park,
# pause, none).
#
@@ -1377,7 +1408,7 @@ UnpackCleanupDisk=yes
#
# Example: /usr/bin/unrar.
#
# The option can also contain extra switches to pass to unrar. To the
# The option can also contain extra switches to pass to unrar. To the
# here defined command line NZBGet adds the following switches:
# x -y -p- -o+ *.rar ./_unpack/
#
@@ -1413,9 +1444,9 @@ SevenZipCmd=7z
#
# List of file extensions, file names or file masks to delete after
# successful download. If either unpack or par-check fail the cleanup is
# not performed. If neither unpack nor par-check were made (because they
# were disabled or the download doesn't contain archives and/or par-files
# the cleanup is performed if the health is 100%.
# not performed. If download doesn't contain archives nor par-files
# the cleanup is performed if the health is 100%. If parameter "unpack"
# is disabled for that nzb-file the cleanup isn't performed.
#
# The entries must be separated with commas. The entries can be file
# extensions, file names or file masks containing wildcard
@@ -1424,6 +1455,29 @@ SevenZipCmd=7z
# Example: .par2, .sfv
ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
# Files to ignore during unpack.
#
# List of file extensions to ignore when unpacking archives or renaming
# obfuscated archive files. The entries must be separated with commas.
#
# Archive files with non standard extensions belong to one of two categories: they
# are either obfuscated files or files with special purposes which should not be
# unpacked. List the files of second type here to avoid attempts to unpack them.
#
# This option has effect on two post-processing stages.
#
# First, during rar-rename (option <RarRename>) rar-files with non-standard
# extensions are renamed back to rar-extension, which is required for successful
# unpacking. Files with extensions listed here will not be renamed.
#
# Second, if during unpack no rar-files are found but instead rar-archives
# with non-rar extensions are found the unpack fails. For files listed here
# no unpack failure occurs and download is considered not having archive
# files and be successful.
#
# Example: .cbr
UnpackIgnoreExt=.cbr
# Path to file containing unpack passwords.
#
# If the option is set the program will try all passwords from the file
@@ -1434,15 +1488,31 @@ ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
# then the password-file is not used for that nzb-file.
#
# NOTE: Trying multiple passwords is a time consuming task. Whenever possible
# passwords should be set per nzb-file in their post-processing settings.
# passwords should be set per nzb-file in their post-processing settings.
UnpackPassFile=
##############################################################################
### EXTENSION SCRIPTS ###
# Default list of post-processing scripts to execute after the download
# of nzb-file is completed and possibly par-checked/repaired and unpacked.
# List of active extension scripts for new downloads.
#
# Extension scripts associated with nzb-files are executed before, during
# or after download as defined by script developer.
#
# Each download (nzb-file) has its own list of extension scripts; the list
# can be viewed and changed in web-interface in download details dialog or
# via API. Option <Extensions> sets defaults for new downloads; changes
# to option <Extensions> do not affect downloads which are already in queue.
#
# When nzb-file is added to queue it can have a category assigned to it. In this
# case option <CategoryX.Extensions> (if not empty) have precedence and
# defines the scripts for that nzb-file; consequently global option <Extensions>
# has no effect for that nzb-file.
#
# Certain extensions work globally for the whole program instead of
# per-nzb basis. Such extensions are activated once and cannot be overriden
# per category or per nzb.
#
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
@@ -1450,421 +1520,19 @@ UnpackPassFile=
#
# Example: Cleanup.sh, Move.sh, EMail.py.
#
# Each download (nzb-file) has its own list of post-processing scripts. The option
# <PostScript> is the default value assigned to download when it is added to
# queue. The list of post-processing scripts for a particular download can be
# changed in the edit dialog in web-interface or using remote command "--edit/-E".
#
# When nzb-file is added to queue it can have a category assigned to it. In this
# case the option <CategoryX.PostScript> (if not empty) overrides the
# global option <PostScript>.
#
# NOTE: The script execution order is controlled by option <ScriptOrder>, not
# by their order in option <PostScript>.
# by their order in option <Extensions>.
#
# NOTE: Changing options <PostScript> and <CategoryX.PostScript> doesn't affect
# already queued downloads.
#
# NOTE: For the list of interesting post-processing scripts see
# NOTE: For the list of interesting extension scripts see
# http://nzbget.net/Catalog_of_post-processing_scripts.
#
# INFO FOR DEVELOPERS:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#
# NZBGet passes following arguments to post-processing script as environment
# variables:
# NZBPP_DIRECTORY - path to destination directory for downloaded files;
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - original name of processed nzb-file. It includes file extension
# and may include full path;
# NZBPP_QUEUEDFILE - full filename of the queued (renamed) nzb-file;
# NZBPP_FINALDIR - final destination path if set by one of previous pp-scripts;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_DUPEKEY - duplicate key of nzb-file;
# NZBPP_DUPESCORE - duplicate score of nzb-file;
# NZBPP_DUPEMODE - duplicate mode of nzb-file: SCORE, ALL, FORCE;
# NZBPP_TOTALSTATUS - total status of nzb-file:
# SUCCESS - everything OK;
# WARNING - download is damaged but probably can
# be repaired; user intervention is
# required;
# FAILURE - download has failed or a serious error
# occurred during post-processing (unpack, par);
# DELETED - download was deleted; post-processing
# scripts are usually not called in this case;
# however it's possible to force calling
# scripts with command "post-process again";
# NZBPP_STATUS - complete status info for nzb-file: it consists
# of total status and status detail separated with
# slash, for example: "FAILURE/UNPACK"; for possible
# status details see documentation on web site;
# NZBPP_SCRIPTSTATUS - summary status of the scripts executed before the
# current one:
# NONE - no other scripts were executed yet or all
# of them have ended with exit code "NONE";
# SUCCESS - all other scripts have ended with exit
# code "SUCCESS" ;
# FAILURE - at least one of the script has failed;
# NZBPP_HEALTH - download health: an integer value in the range
# from 0 (all articles failed) to 1000 (all articles
# successfully downloaded);
# NZBPP_CRITICALHEALTH - critical health for this nzb-file: an integer
# value in the range 0-1000. The critical health
# is calculated based on number and size of
# par-files. If nzb-file doesn't have any par-files
# the critical health is 1000 (100.0%). If a half
# of nzb-file were par-files its critical health
# would be 0. If NZBPP_HEALTH goes down below
# NZBPP_CRITICALHEALTH the download becomes unrepairable;
# NZBPP_TOTALARTICLES - number of articles in nzb-file;
# NZBPP_SUCCESSARTICLES - number of successfully downloaded articles;
# NZBPP_FAILEDARTICLES - number of failed articles;
# NZBPP_SERVERX_SUCCESSARTICLES - number of successfully downloaded
# articles from ServerX (X is replaced with server
# number, for example NZBPP_SERVER1_SUCCESSARTICLES);
# NZBPP_SERVERX_FAILEDARTICLES - number of failed articles from ServerX.
#
# If the script defines own options they are also passed as environment
# variables. These variables have prefix "NZBPO_" in their names. For
# example, option "myoption" will be passed as environment variable
# "NZBPO_myoption" and in addition in uppercase as "NZBPO_MYOPTION".
#
# If the script defines own post-processing parameters, they are also passed as
# environment variables. These variables have prefix "NZBPR_" in their
# names. For example, pp-parameter "myparam" will be passed as environment
# variable "NZBPR_myparam" and in addition in uppercase as "NZBPR_MYPARAM".
#
# In addition to arguments, pp-options and pp-parameters NZBGet passes all
# nzbget.conf-options to pp-script as environment variables. These
# variables have prefix "NZBOP_" and are written in UPPER CASE. For Example
# option "ParRepair" is passed as environment variable "NZBOP_PARREPAIR". The
# dots in option names are replaced with underscores, for example
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
# the values are passed always in lower case.
#
# If the script moves files it can inform the program about new location
# by printing special message into standard output (which is processed
# by NZBGet):
# echo "[NZB] DIRECTORY=/path/to/moved/files";
# or:
# echo "[NZB] FINALDIR=/path/to/moved/files";
#
# Command "DIRECTORY" changes the destination path of the download and
# affects the scripts executed after the current script as well as the
# program code itself, for example the command "Post-process again"
# will work on new location. Command "FINALDIR" just sets a separate
# property of the download and should be used when the files are moved
# into an existing directory containing other files to avoid the processing
# of those files by other scripts.
#
# To assign post-processing parameters:
# echo "[NZB] NZBPR_myvar=my value";
#
# The prefix "NZBPR_" will be removed. In this example a post-processing
# parameter with name "myvar" and value "my value" will be associated
# with nzb-file.
#
# To inform NZBGet about bad download:
# echo "[NZB] MARK=BAD";
#
# Return value: NZBGet processes the exit code returned by the script:
# 93 - post-process successful (status = SUCCESS);
# 94 - post-process failed (status = FAILURE);
# 95 - post-process skipped (status = NONE). Use this code when you script
# terminates immediately without doing any job and when this is not
# a failure termination;
# 92 - request NZBGet to do par-check/repair for current nzb-file.
#
# All other return codes are interpreted as failure (status = FAILURE).
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
PostScript=
# NOTE: For developer documentation visit http://nzbget.net/Extension_scripts.
Extensions=
# List of scan scripts to execute before a nzb-file is added to queue.
#
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
# paths relative to <ScriptDir> must be entered here.
#
# The scripts are executed each time a new file is found in incoming
# directory (option <NzbDir>) or a file is received via RPC (web-interface,
# command "nzbget --append", etc.).
#
# Example: UnzipNzb.sh, ScanNotify.py.
#
# The scripts can unpack archives which were put in incoming directory, make
# filename cleanup, change nzb-name, category, priority and post-processing
# parameters of the nzb-file or do other things.
#
# INFO FOR DEVELOPERS:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#
# NZBGet passes following arguments to the script as environment
# variables:
# NZBNP_DIRECTORY - path to directory, where file is located. It is a directory
# specified by the option <NzbDir> or a subdirectory;
# NZBNP_FILENAME - name of file to be processed;
# NZBNP_NZBNAME - nzb-name (without path but with extension);
# NZBNP_CATEGORY - category of nzb-file;
# NZBNP_PRIORITY - priority of nzb-file;
# NZBNP_TOP - flag indicating that the file will be added to the top
# of queue: 0 or 1;
# NZBNP_PAUSED - flag indicating that the file will be added as
# paused: 0 or 1;
# NZBNP_DUPEKEY - duplicate key of nzb-file;
# NZBNP_DUPESCORE - duplicate score of nzb-file;
# NZBNP_DUPEMODE - duplicate mode of nzb-file: SCORE, ALL, FORCE.
#
# In addition to these arguments NZBGet passes all nzbget.conf-options
# as environment variables. These variables have prefix "NZBOP_" and
# are written in UPPER CASE. For , the option "ParRepair" is passed as
# environment variable "NZBOP_PARREPAIR". The dots in option names are
# replaced with underscores, for example "SERVER1_HOST". For options
# with predefined possible values (yes/no, etc.) the values are passed
# always in lower case.
#
# The script can change nzb-name, category, priority,
# post-processing parameters and top-/paused-flags of the nzb-file
# by printing special messages into standard output (which is processed
# by NZBGet).
#
# To change nzb-name use following syntax:
# echo "[NZB] NZBNAME=my download";
#
# To change category:
# echo "[NZB] CATEGORY=my category";
#
# To change priority:
# echo "[NZB] PRIORITY=signed_integer_value";
#
# for example: to set priority higher than normal:
# echo "[NZB] PRIORITY=50";
#
# another example: use a negative value for "lower than normal" priority:
# echo "[NZB] PRIORITY=-100";
#
# Although priority can be any integer value, the web-interface operates
# with six predefined priorities:
# -100 - very low priority;
# -50 - low priority;
# 0 - normal priority (default);
# 50 - high priority;
# 100 - very high priority;
# 900 - force priority.
#
# Downloads with priorities equal to or greater than 900 are downloaded and
# post-processed even if the program is in paused state (force mode).
#
# To assign post-processing parameters:
# echo "[NZB] NZBPR_myvar=my value";
#
# The prefix "NZBPR_" will be removed. In this example a post-processing
# parameter with name "myvar" and value "my value" will be associated
# with nzb-file.
#
# To change top-flag (nzb-file will be added to the top of queue):
# echo "[NZB] TOP=1";
#
# To change paused-flag (nzb-file will be added in paused state):
# echo "[NZB] PAUSED=1";
#
# To change duplicate key:
# echo "[NZB] DUPEKEY=tv show s01e02";
#
# To change duplicate score:
# echo "[NZB] DUPESCORE=integer_value";
#
# To change duplicate mode:
# echo "[NZB] DUPEMODE=(SCORE|ALL|FORCE)";
#
# The script can delete processed file, rename it or move somewhere.
# After the calling of the script the file will be either added to queue
# (if it was an nzb-file) or renamed by adding the extension ".processed".
#
# NOTE: Files with extensions ".processed", ".queued" and ".error" are skipped
# during the directory scanning.
#
# NOTE: Files with extension ".nzb_processed" are not passed to
# scan-script before adding to queue. This feature allows scan-script
# to prevent the scanning of nzb-files extracted from archives, if
# they were already processed by the script.
#
# NOTE: Files added via RPC calls in particular from web-interface are
# saved into incoming nzb-directory and then processed by the script.
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
ScanScript=
# List of queue scripts to execute on queue events.
#
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
# paths relative to <ScriptDir> must be entered here.
#
# The scripts are executed on certain queue events such as adding
# a new nzb-file to queue, etc.
#
# Example: DeleteQueueSamples.sh, NzbAddedNotify.py.
#
# The script can modify the files in download queue (for example
# delete or pause all .nfo, .sfv, sample files) or do something else.
#
# INFO FOR DEVELOPERS:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#
# NZBGet passes following arguments to the queue script as environment
# variables:
# NZBNA_NZBNAME - name of nzb-group. This name can be used in calls
# to nzbget edit-command using the subswitch "-GN name";
# NZBNA_FILENAME - filename of the nzb-file. If the file was added
# from nzb-directory this is the full name with path.
# If the file was added via web-interface it contains
# only filename without path;
# NZBNA_EVENT - describes why the script was called:
# NZB_ADDED - after adding of nzb-file to queue;
# FILE_DOWNLOADED - after a file included in nzb is
# downloaded;
# NZB_DOWNLOADED - after all files in nzb are downloaded
# (before post-processing);
# NZB_DELETED - when nzb is deleted from queue (moved
# to history). See NZBNA_DELETESTATUS for details;
# NZB_MARKED - when a history item is marked as good, bad or
# success. See NZBNA_MARKSTATUS for details;
# URL_COMPLETED - after an URL download is completed
# and the downloaded file was not added to queue
# (not nzb-extension, download error, parse
# error). See NZBNA_URLSTATUS for details;
# In the future the list of supported events may be
# extended. To avoid conflicts with future NZBGet
# versions the script must exit if the parameter
# has a value unknown to the script;
# NZBNA_QUEUEDFILE - full filename of the queued (renamed) nzb-file;
# NZBNA_DELETESTATUS - delete status info, NZBNA_EVENT=NZB_DELETED:
# MANUAL - deleted by user or via API call;
# HEALTH - deleted by health check;
# DUPE - moved to history by duplicate check, can be
# reused later if necessary;
# GOOD - moved to history by duplicate check because
# there is already a duplicate marked as good;
# BAD - marked as bad by user or by queue-script;
# COPY - already in queue or in history;
# SCAN - malformed nzb-file, cannot be parsed;
# NZBNA_URLSTATUS - URL status info, when NZBNA_EVENT=URL_COMPLETED:
# FAILURE - fetch error (could not be downloaded);
# SCAN_SKIPPED - downloaded file doesn't have
# nzb-extension and was skipped;
# SCAN_FAILED - file format error;
# NZBNA_MARKSTATUS - mark status info, NZBNA_EVENT=NZB_MARKED:
# GOOD - marked as good by user or by a script;
# BAD - marked as bad;
# SUCCESS - marked as success;
# NZBNA_CATEGORY - category of nzb-file (if assigned);
# NZBNA_NZBID - id of the nzb-file. This ID can be used with
# calls to nzbget edit-command;
# NZBNA_PRIORITY - priority (default is 0);
# NZBNA_DUPEKEY - duplicate key of nzb-file;
# NZBNA_DUPESCORE - duplicate score of nzb-file;
# NZBNA_DUPEMODE - duplicate mode of nzb-file: SCORE, ALL, FORCE.
#
# In addition to these arguments NZBGet passes all nzbget.conf-options
# to the script as environment variables. These variables have prefix
# "NZBOP_" and are written in UPPER CASE. For Example, the option "ParRepair"
# is passed as environment variable "NZBOP_PARREPAIR". The dots in option
# names are replaced with underscores, for example "SERVER1_HOST". For
# options with predefined possible values (yes/no, etc.) the values are
# passed always in lower case.
#
# The script can printing special messages into standard output (which
# is processed by NZBGet).
#
# To assign post-processing parameters:
# echo "[NZB] NZBPR_myvar=my value";
#
# The prefix "NZBPR_" will be removed. In this example a post-processing
# parameter with name "myvar" and value "my value" will be associated
# with nzb-file.
#
# To inform NZBGet about bad download:
# echo "[NZB] MARK=BAD";
#
# To set destination directory (only from event "NZB_DOWNLOADED"):
# echo "[NZB] DIRECTORY=/destination/path/for/this/nzb";
#
# Examples of what the script can do:
# 1) pausing nzb-file using file-id:
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E G P $NZBNA_NZBID;
# 2) setting category using nzb-name:
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E GN K "my cat" "$NZBNA_NZBNAME";
# 3) pausing files with extension "nzb":
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E FR P "$NZBNA_NZBNAME/.*\.nzb";
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
QueueScript=
# List of rss feed scripts to execute before a rss feed content is processed.
#
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
# paths relative to <ScriptDir> must be entered here.
#
# If rss feed has option <FeedX.FeedScript> defined (if not empty)
# the scripts defined there override the global option <FeedScript>.
#
# The scripts are executed after rss feed is read from server and before it
# is processed by the feed parser. Once the feed is fetched it is saved
# to a temporary file and the feed scripts are executed. The scripts
# can modify the content of the temporary feed file. Then the file is
# read by the feed parser and processed.
#
# Example: Rss.sh, Filter.py.
#
# The feed content is usually filtered using option <FeedX.Filter>. If a
# required filtering cannot be achieved via built-in filter commands the
# more advanced processing of the feed can be made using feed scripts.
#
# INFO FOR DEVELOPERS:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
#
# NZBGet passes following arguments to the script as environment
# variables:
# NZBFP_FILENAME - name of feed file to be processed;
# NZBFP_FEEDID - ID of the feed.
#
# In addition to these arguments NZBGet passes all nzbget.conf-options
# as environment variables. These variables have prefix "NZBOP_" and
# are written in UPPER CASE. For Example option "ParRepair" is passed as
# environment variable "NZBOP_PARREPAIR". The dots in option names are
# replaced with underscores, for example "SERVER1_HOST". For options
# with predefined possible values (yes/no, etc.) the values are passed
# always in lower case.
#
# Return value: NZBGet processes the exit code returned by the script:
# 93 - script successful (status = SUCCESS).
# All other return codes are interpreted as failure (status = FAILURE).
#
# If the script doesn't end with SUCCESS-status the whole content of RSS
# feed is ignored. This is to prevent accidental enqueuing of many
# nzb-files if a feed script unexpectedly terminates before processing
# of the feed.
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.net/Extension_scripts.
FeedScript=
# Execution order for scripts.
# Execution order for extension scripts.
#
# If you assign multiple scripts to one nzb-file, they are executed in the
# order defined by this option. Scripts not listed here are executed at
# the end in their alphabetical order.
# order defined by this option.
#
# The scripts in the list must be separated with commas or semicolons. All
# scripts must be stored in directory set by option <ScriptDir> and
@@ -1894,25 +1562,18 @@ ScriptPauseQueue=no
# Example: .py=/usr/bin/python2;.py3=/usr/bin/python3;.sh=/usr/bin/bash.
ShellOverride=
# Minimum interval between calls of queue-scripts (seconds).
# Minimum interval between queue events (seconds).
#
# Queue-scripts are executed during download, after every file included in
# nzb-file is downloaded. If the files are small they may be downloaded
# very fast causing queue-scripts to be working all the time. Sometimes
# this may lead to a performance decrease on systems with slow CPUs.
# Extension scripts can opt-in for progress notifcations during
# download. For downloads containing many small files the events can
# be fired way too often increasing load on the system due to script
# execution.
#
# This option allows to reduce the number of calls of queue-scripts by
# skipping "file-downloaded"-events if the previous call of queue-scripts
# for the same download (nzb-file) were performed a short time ago
# (as defined by the option).
# This option allows to reduce the number of calls of scripts by
# skipping "file-downloaded"-events if the previous call for the same
# download (nzb-file) were performed a short time ago (as defined by
# the option).
#
# Value "-1" disables executing of queue-scripts on
# "file-downloaded"-events. Scripts are still executed on events
# "nzb-added" and "nzb-downloaded".
#
# NOTE: This options affects only queue-scripts and only
# "file-downloaded"-events. Queue-scripts can be activated using
# option <QueueScript> (for pure queue-scripts) or option <PostScript>
# (for dual-mode scripts which act as queue- and post-processing-scripts
# at the same time).
# Value "-1" disables "file-downloaded"-events. Scripts are still
# notified on other events (such as "nzb-added" or "nzb-downloaded").
EventInterval=0

View File

@@ -51,8 +51,8 @@
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.1";_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nserv;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="18.1";_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@@ -71,8 +71,8 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.1";NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nserv;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="18.1";NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>Use</PrecompiledHeader>
@@ -124,13 +124,21 @@
<ClCompile Include="daemon\nntp\NntpConnection.cpp" />
<ClCompile Include="daemon\nntp\ServerPool.cpp" />
<ClCompile Include="daemon\nntp\StatMeter.cpp" />
<ClCompile Include="daemon\nserv\NntpServer.cpp" />
<ClCompile Include="daemon\nserv\NServFrontend.cpp" />
<ClCompile Include="daemon\nserv\NServMain.cpp" />
<ClCompile Include="daemon\nserv\NzbGenerator.cpp" />
<ClCompile Include="daemon\nserv\YEncoder.cpp" />
<ClCompile Include="daemon\postprocess\Cleanup.cpp" />
<ClCompile Include="daemon\postprocess\DupeMatcher.cpp" />
<ClCompile Include="daemon\postprocess\ParChecker.cpp" />
<ClCompile Include="daemon\postprocess\ParCoordinator.cpp" />
<ClCompile Include="daemon\postprocess\Repair.cpp" />
<ClCompile Include="daemon\postprocess\ParParser.cpp" />
<ClCompile Include="daemon\postprocess\ParRenamer.cpp" />
<ClCompile Include="daemon\postprocess\PrePostProcessor.cpp" />
<ClCompile Include="daemon\postprocess\RarReader.cpp" />
<ClCompile Include="daemon\postprocess\RarRenamer.cpp" />
<ClCompile Include="daemon\postprocess\Rename.cpp" />
<ClCompile Include="daemon\postprocess\Unpack.cpp" />
<ClCompile Include="daemon\queue\DiskState.cpp" />
<ClCompile Include="daemon\queue\DownloadInfo.cpp" />
@@ -171,7 +179,6 @@
<ClCompile Include="lib\par2\galois.cpp" />
<ClCompile Include="lib\par2\mainpacket.cpp" />
<ClCompile Include="lib\par2\md5.cpp" />
<ClCompile Include="lib\par2\par2creatorsourcefile.cpp" />
<ClCompile Include="lib\par2\par2fileformat.cpp" />
<ClCompile Include="lib\par2\par2repairer.cpp" />
<ClCompile Include="lib\par2\par2repairersourcefile.cpp" />
@@ -214,13 +221,21 @@
<ClInclude Include="daemon\nntp\NntpConnection.h" />
<ClInclude Include="daemon\nntp\ServerPool.h" />
<ClInclude Include="daemon\nntp\StatMeter.h" />
<ClInclude Include="daemon\nserv\NntpServer.h" />
<ClInclude Include="daemon\nserv\NServFrontend.h" />
<ClInclude Include="daemon\nserv\NServMain.h" />
<ClInclude Include="daemon\nserv\NzbGenerator.h" />
<ClInclude Include="daemon\nserv\YEncoder.h" />
<ClInclude Include="daemon\postprocess\Cleanup.h" />
<ClInclude Include="daemon\postprocess\DupeMatcher.h" />
<ClInclude Include="daemon\postprocess\ParChecker.h" />
<ClInclude Include="daemon\postprocess\ParCoordinator.h" />
<ClInclude Include="daemon\postprocess\Repair.h" />
<ClInclude Include="daemon\postprocess\ParParser.h" />
<ClInclude Include="daemon\postprocess\ParRenamer.h" />
<ClInclude Include="daemon\postprocess\PrePostProcessor.h" />
<ClInclude Include="daemon\postprocess\RarReader.h" />
<ClInclude Include="daemon\postprocess\RarRenamer.h" />
<ClInclude Include="daemon\postprocess\Rename.h" />
<ClInclude Include="daemon\postprocess\Unpack.h" />
<ClInclude Include="daemon\queue\DiskState.h" />
<ClInclude Include="daemon\queue\DownloadInfo.h" />
@@ -261,7 +276,6 @@
<ClInclude Include="lib\par2\mainpacket.h" />
<ClInclude Include="lib\par2\md5.h" />
<ClInclude Include="lib\par2\par2cmdline.h" />
<ClInclude Include="lib\par2\par2creatorsourcefile.h" />
<ClInclude Include="lib\par2\par2fileformat.h" />
<ClInclude Include="lib\par2\par2repairer.h" />
<ClInclude Include="lib\par2\par2repairersourcefile.h" />
@@ -284,4 +298,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View File

@@ -25,9 +25,14 @@
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2007-2016 Andrey Prygunkov</string>
<string>Copyright © 2007-2017 Andrey Prygunkov</string>
<key>NSMainNibFile</key>
<string>MainApp</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>

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