Compare commits

...

135 Commits
v17.0 ... v18.0

Author SHA1 Message Date
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
Andrey Prygunkov
0c1fce27a2 update version string to "17.1" 2016-09-05 19:36:02 +02:00
Andrey Prygunkov
606cf56150 Merge branch 'develop' for v17.1 2016-09-05 19:19:34 +02:00
Andrey Prygunkov
af8451e16e updated ChangeLog for v17.1 2016-09-05 19:05:52 +02:00
Andrey Prygunkov
203a828bbd #224: corrected text in history delete confirmation dialog 2016-08-28 15:00:48 +02:00
Andrey Prygunkov
a990078884 fixed #265: compilation error if configured without TLS 2016-08-21 22:44:48 +02:00
Andrey Prygunkov
3e5bd203f3 fixed #86: javascript error on Chrome for Linux
Renamed “Notification” to “PopupNotification” to avoid conflict with
browsers built-in interface with the same name.
2016-08-21 22:34:42 +02:00
Andrey Prygunkov
042979d122 #245: removed debug logging 2016-08-16 20:43:13 +02:00
Andrey Prygunkov
11c464ed46 fixed #262: crash on malformed articles 2016-08-12 23:45:57 +02:00
Andrey Prygunkov
070054a814 fixed #261: hanging after marking as BAD from queue script 2016-08-12 22:28:37 +02:00
Andrey Prygunkov
5841cf5002 updated version string to 17.1-testing 2016-08-09 00:14:31 +02:00
Andrey Prygunkov
6dda360986 #253: reset stats if file on disk is missing on retry
Fixed: if by retrying download an included file is missing on disk it
was downloaded again but its stats (remaining size, etc.) were not
correctly reset.
2016-08-09 00:01:55 +02:00
Andrey Prygunkov
12ff14b397 #253: park active file too when deleting
If by deleting download only one file remains and it is partially
downloaded it should be counted as remaining (parked) file for command
“Download remaining files” be available (RemaingFileCount must be > 0).
2016-08-08 23:52:58 +02:00
Andrey Prygunkov
20aa83e0f7 #253: new field "RetryData" in RPC-method "history"
to more carefully indicate if “Retry failed articles” is possible.
2016-08-08 23:42:18 +02:00
Andrey Prygunkov
4c8c7ef46b #253: do not park when deleting without keeping files 2016-08-08 22:57:29 +02:00
Andrey Prygunkov
43a6717394 #253: pause-state when unparking par-files
if option ParCheck=Force then all par-files must be unpaused, otherwise
one par-file must be unpaused.
2016-08-06 22:09:08 +02:00
Andrey Prygunkov
b4fc57977b #253: do not park if all articles failed 2016-08-06 21:28:07 +02:00
Andrey Prygunkov
4f9dbdadc3 #253: fixed: not parking if all articles were successful 2016-08-06 21:16:13 +02:00
Chris
418acb052f #256: compatibility with gcc 4.8 2016-08-05 07:51:10 +02:00
Gokturk Yuksek
c741297d84 #255: do not compile par-tests if parcheck is disabled 2016-08-04 22:38:02 +02:00
Andrey Prygunkov
55fb4df411 fixed #254: installing multiple versions on Windows 2016-08-03 20:37:35 +02:00
Andrey Prygunkov
35643b0207 fixed #253: delete-with-parking sometimes didn't park 2016-08-03 19:55:07 +02:00
Andrey Prygunkov
45753410df fixed #251: performance issue on certain Windows systems 2016-08-02 20:03:41 +02:00
Andrey Prygunkov
b58de541b3 #162: corrected option description 2016-07-30 12:35:19 +02:00
Andrey Prygunkov
3d577777bb #136: improved error reporting on certain file operations 2016-07-29 16:45:15 +02:00
Andrey Prygunkov
a0e9c537a3 fixed #247: root drive paths on Windows
.. could not be used (for example “NzbDir=N:\”).
2016-07-29 16:22:11 +02:00
Andrey Prygunkov
a2d87de7b9 updated version string to 18.0-testing 2016-07-29 16:18:54 +02:00
167 changed files with 8731 additions and 2988 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

102
ChangeLog
View File

@@ -1,3 +1,105 @@
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;
- partial compatibility with gcc 4.8;
- removed unnecessary debug logging to javascript console;
- improved error reporting on certain file operations;
- corrected option description;
- corrected text in history delete confirmation dialog;
- fixed performance issue on certain Windows systems;
- fixed: root drive paths on Windows could not be used (for example
"NzbDir=N:\");
- fixed hanging after marking as BAD from queue script;
- fixed: old nzbget.exe was deleted even when installing into a new directory
(Windows only);
- fixed: compilation error if configured with unit tests but without par-module;
- fixed crash on malformed articles;
- fixed javascript error on Chrome for Linux;
- fixed compilation error if configured without TLS.
nzbget-17.0:
- reworked the full source code base to utilize modern C++ features:
- with the main motivation to make the code nicer and more fun to work

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
@@ -217,15 +232,21 @@ nzbget_SOURCES += \
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
if WITH_PAR2
nzbget_SOURCES += \
tests/postprocess/ParCheckerTest.cpp \
tests/postprocess/ParRenamerTest.cpp
endif
AM_CPPFLAGS += \
-I$(srcdir)/lib/catch \
-I$(srcdir)/tests/suite
@@ -362,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.0.
# Generated by GNU Autoconf 2.61 for nzbget 18.0.
#
# 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.0'
PACKAGE_STRING='nzbget 17.0'
PACKAGE_VERSION='18.0'
PACKAGE_STRING='nzbget 18.0'
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.0 to adapt to many kinds of systems.
\`configure' configures nzbget 18.0 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.0:";;
short | recursive ) echo "Configuration of nzbget 18.0:";;
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.0
nzbget configure 18.0
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.0, which was
It was created by nzbget $as_me 18.0, 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.0'
VERSION='18.0'
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.0, which was
This file was extended by nzbget $as_me 18.0, 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.0
nzbget config.status 18.0
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.0, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 18.0, 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

@@ -110,9 +110,9 @@ void QueueScriptController::Run()
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_id);
if (nzbInfo)
{
PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
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);
}
}
@@ -215,9 +215,7 @@ void QueueScriptController::AddMessage(Message::EKind kind, const char* text)
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
if (nzbInfo)
{
SetLogPrefix(nullptr);
PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName);
SetLogPrefix(m_script->GetDisplayName());
nzbInfo->PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName);
nzbInfo->SetMarkStatus(NzbInfo::ksBad);
}
}
@@ -338,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();
@@ -370,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;
@@ -957,7 +971,8 @@ void Options::InitServers()
#ifdef DISABLE_TLS
if (tls)
{
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", optname);
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support",
*BString<100>("Server%i.Encryption", n));
tls = false;
}
#endif
@@ -1017,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)
@@ -1036,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
@@ -1067,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;
@@ -1087,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)
@@ -1100,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
@@ -1169,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)
@@ -1206,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)
@@ -1264,7 +1296,7 @@ bool Options::ParseTime(const char* time, int* hours, int* minutes)
if (time[0] == '*')
{
*hours = -1;
*hours = -2;
}
else
{
@@ -1485,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;
@@ -1498,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;
}
@@ -1531,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);
@@ -1599,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"))
@@ -1624,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";
@@ -1695,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;
}
@@ -1704,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

@@ -40,7 +40,7 @@ void Scheduler::FirstCheck()
Guard guard(m_taskListMutex);
std::sort(m_taskList.begin(), m_taskList.end(),
[](std::unique_ptr<Task>& task1, std::unique_ptr<Task>& task2)
[](const std::unique_ptr<Task>& task1, const std::unique_ptr<Task>& task2)
{
return (task1->m_hours < task2->m_hours) ||
((task1->m_hours == task2->m_hours) && (task1->m_minutes < task2->m_minutes));
@@ -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;
@@ -340,4 +348,19 @@ typedef unsigned char uchar;
#define SCANF_SYNTAX(strindex)
#endif
// providing "std::make_unique" for GCC 4.8.x (only 4.8.x)
#if __GNUC__ && __cplusplus < 201402L && __cpp_generic_lambdas < 201304
namespace std {
template<class T> struct _Unique_if { typedef unique_ptr<T> _Single_object; };
template<class T> struct _Unique_if<T[]> { typedef unique_ptr<T[]> _Unknown_bound; };
template<class T, class... Args> typename _Unique_if<T>::_Single_object make_unique(Args&&... args) {
return unique_ptr<T>(new T(std::forward<Args>(args)...));
}
template<class T> typename _Unique_if<T>::_Unknown_bound make_unique(size_t n) {
typedef typename remove_extent<T>::type U;
return unique_ptr<T>(new U[n]());
}
}
#endif
#endif /* NZBGET_H */

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,137 @@ 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()) &&
IsNzbFileCompleted(nzbInfo1, true))
{
nzbInfo = nzbInfo1;
}
@@ -412,73 +523,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;
}
Util::SetStandByMode(false);
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();
}
}
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 +630,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 +644,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 +687,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 +696,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 +735,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 +757,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 +831,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;
}

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

@@ -129,13 +129,13 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
// park remaining files
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
fileInfo->GetNzbInfo()->UpdateCompletedStats(fileInfo);
fileInfo->GetNzbInfo()->GetCompletedFiles()->emplace_back(fileInfo->GetId(),
nzbInfo->UpdateCompletedStats(fileInfo);
nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(),
fileInfo->GetFilename(), CompletedFile::cfNone, 0);
}
// Cleaning up parked files if par-check was successful or unpack was successful or
// health is 100% (if unpack and par-check were not performed)
// health is 100% (if unpack and par-check were not performed) or if deleted
bool cleanupParkedFiles =
((nzbInfo->GetParStatus() == NzbInfo::psSuccess ||
nzbInfo->GetParStatus() == NzbInfo::psRepairPossible) &&
@@ -147,7 +147,13 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
(nzbInfo->GetUnpackStatus() <= NzbInfo::usSkipped &&
nzbInfo->GetParStatus() != NzbInfo::psFailure &&
nzbInfo->GetFailedSize() - nzbInfo->GetParFailedSize() == 0) ||
nzbInfo->GetUnpackCleanedUpDisk();
(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone);
// Do not cleanup when parking
cleanupParkedFiles &= !nzbInfo->GetParking();
// Parking not possible if files were already deleted
cleanupParkedFiles |= nzbInfo->GetUnpackCleanedUpDisk();
if (cleanupParkedFiles)
{
@@ -158,7 +164,10 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
nzbInfo->SetParkedFileCount(0);
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
if (completedFile.GetStatus() == CompletedFile::cfNone)
if (completedFile.GetStatus() == CompletedFile::cfNone ||
// consider last completed file with partial status not completely tried
(completedFile.GetStatus() == CompletedFile::cfPartial &&
&completedFile == &*nzbInfo->GetCompletedFiles()->rbegin()))
{
nzbInfo->PrintMessage(Message::mkDetail, "Parking file %s", completedFile.GetFileName());
nzbInfo->SetParkedFileCount(nzbInfo->GetParkedFileCount() + 1);
@@ -223,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);
@@ -261,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:
@@ -370,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);
@@ -480,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);
@@ -576,7 +588,6 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
(resetFailed || fileInfo->GetRemainingSize() > 0))))
{
fileInfo->SetFilename(completedFile.GetFileName());
fileInfo->SetPaused(fileInfo->GetParFile());
fileInfo->SetNzbInfo(nzbInfo);
BString<1024> outputFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, fileInfo->GetFilename());
@@ -595,6 +606,7 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
else if (!reprocess)
{
nzbInfo->PrintMessage(Message::mkWarning, "File %s could not be found on disk, downloading again", fileInfo->GetFilename());
fileInfo->SetPartialState(FileInfo::psNone);
}
}
@@ -619,6 +631,11 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
nzbInfo->UpdateCurrentStats();
MoveToQueue(downloadQueue, itHistory, historyInfo, reprocess);
if (g_Options->GetParCheck() != Options::pcForce)
{
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, nullptr);
}
}
void HistoryCoordinator::ResetArticles(FileInfo* fileInfo, bool allFailed, bool resetFailed)

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();
@@ -709,7 +720,8 @@ void QueueCoordinator::DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fi
{
fileInfo->GetNzbInfo()->GetCompletedFiles()->emplace_back(
fileInfo->GetId(),
completed ? FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(),
completed && fileInfo->GetOutputFilename() ?
FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(),
fileStatus,
fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0);
}
@@ -807,7 +819,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)
@@ -819,7 +831,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:
@@ -90,6 +90,7 @@ private:
void Load();
void SavePartialState();
void LoadPartialState(FileInfo* fileInfo);
void WaitJobs();
};
extern QueueCoordinator* g_QueueCoordinator;

View File

@@ -38,7 +38,7 @@ public:
GroupSorter(NzbList* nzbList, QueueEditor::ItemList* sortItemList) :
m_nzbList(nzbList), m_sortItemList(sortItemList) {}
bool Execute(const char* sort);
bool operator()(std::unique_ptr<NzbInfo>& refNzbInfo1, std::unique_ptr<NzbInfo>& refNzbInfo2) const;
bool operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const std::unique_ptr<NzbInfo>& refNzbInfo2) const;
private:
enum ESortCriteria
@@ -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,23 +122,23 @@ 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(), m_nzbList->end(),
std::equal(tempList.begin(), tempList.end(), m_nzbList->begin(),
[](NzbInfo* nzbInfo1, std::unique_ptr<NzbInfo>& nzbInfo2)
{
return nzbInfo1 == nzbInfo2.get();
}))
{
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;
}
bool GroupSorter::operator()(std::unique_ptr<NzbInfo>& refNzbInfo1, std::unique_ptr<NzbInfo>& refNzbInfo2) const
bool GroupSorter::operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const std::unique_ptr<NzbInfo>& refNzbInfo2) const
{
NzbInfo* nzbInfo1 = refNzbInfo1.get();
NzbInfo* nzbInfo2 = refNzbInfo2.get();
@@ -200,44 +196,6 @@ bool GroupSorter::operator()(std::unique_ptr<NzbInfo>& refNzbInfo1, std::unique_
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;
@@ -778,7 +730,7 @@ bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action,
nzbInfo->SetParking(action == DownloadQueue::eaGroupParkDelete &&
g_Options->GetKeepHistory() > 0 &&
!nzbInfo->GetUnpackCleanedUpDisk() &&
(nzbInfo->GetSuccessArticles() > 0 || nzbInfo->GetFailedArticles() > 0));
nzbInfo->GetCurrentSuccessArticles() > 0);
nzbInfo->SetAvoidHistory(action == DownloadQueue::eaGroupFinalDelete);
nzbInfo->SetDeletePaused(allPaused);
if (action == DownloadQueue::eaGroupDupeDelete)
@@ -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;
@@ -2426,6 +2436,7 @@ void HistoryXmlCommand::Execute()
"<member><name>ID</name><value><i4>%i</i4></value></member>\n" // Deprecated, use "NZBID" instead
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>RemainingFileCount</name><value><i4>%i</i4></value></member>\n"
"<member><name>RetryData</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>HistoryTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>Status</name><value><string>%s</string></value></member>\n"
"<member><name>Log</name><value><array><data></data></array></value></member>\n"; // Deprected, always empty
@@ -2435,6 +2446,7 @@ void HistoryXmlCommand::Execute()
"\"ID\" : %i,\n" // Deprecated, use "NZBID" instead
"\"Name\" : \"%s\",\n"
"\"RemainingFileCount\" : %i,\n"
"\"RetryData\" : %s,\n"
"\"HistoryTime\" : %i,\n"
"\"Status\" : \"%s\",\n"
"\"Log\" : [],\n"; // Deprected, always empty
@@ -2506,7 +2518,7 @@ void HistoryXmlCommand::Execute()
AppendFmtResponse(IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
historyInfo->GetId(), *EncodeStr(historyInfo->GetName()), nzbInfo->GetParkedFileCount(),
historyInfo->GetTime(), status);
BoolToStr(nzbInfo->GetCompletedFiles()->size()), historyInfo->GetTime(), status);
}
else if (historyInfo->GetKind() == HistoryInfo::hkDup)
{
@@ -2704,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";
@@ -2716,6 +2730,8 @@ void ConfigTemplatesXmlCommand::Execute()
"\"QueueScript\" : %s,\n"
"\"SchedulerScript\" : %s,\n"
"\"FeedScript\" : %s,\n"
"\"QueueEvents\" : \"%s\",\n"
"\"TaskTime\" : \"%s\",\n"
"\"Template\" : \"%s\"\n"
"}";
@@ -2750,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

@@ -30,6 +30,16 @@ CString FileSystem::GetLastErrorMessage()
{
BString<1024> msg;
strerror_r(errno, msg, msg.Capacity());
#ifdef WIN32
if (!errno)
{
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
msg, 1024, nullptr);
}
#endif
return *msg;
}
@@ -105,6 +115,7 @@ bool FileSystem::ForceDirectories(const char* path, CString& errmsg)
}
}
errmsg.Format("path %s does not exist and could not be created", *normPath);
return false;
}
#else
@@ -249,6 +260,7 @@ bool FileSystem::AllocateFile(const char* filename, int64 size, bool sparse, CSt
HANDLE hFile = CreateFileW(UtfPathToWidePath(filename), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, 0, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
{
errno = 0; // wanting error message from WinAPI instead of C-lib
errmsg = GetLastErrorMessage();
return false;
}
@@ -526,17 +538,21 @@ bool FileSystem::DirectoryExists(const char* dirFilename)
{
#ifdef WIN32
WIN32_FIND_DATAW findData;
// extra "\*" needed for network shares
HANDLE handle = FindFirstFileW(UtfPathToWidePath(
BString<1024>(dirFilename && dirFilename[strlen(dirFilename) - 1] == PATH_SEPARATOR ? "%s*" : "%s\\*", dirFilename)),
&findData);
if (handle != INVALID_HANDLE_VALUE)
{
bool exists = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) ||
(strlen(dirFilename) == 3 && dirFilename[1] == ':');
(dirFilename[0] != '\0' && dirFilename[1] == ':' && (dirFilename[2] == '\0' || dirFilename[3] == '\0'));
FindClose(handle);
return exists;
}
if (GetLastError() == ERROR_FILE_NOT_FOUND)
{
// path exists but doesn't have any file/directory entries - possible only for root paths (e. g. "C:\")
return true;
}
return false;
#else
struct stat buffer;
@@ -841,10 +857,8 @@ bool FileSystem::FlushFileBuffers(int fileDescriptor, CString& errmsg)
BOOL ok = ::FlushFileBuffers((HANDLE)_get_osfhandle(fileDescriptor));
if (!ok)
{
errmsg.Reserve(1024 - 1);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
errmsg, 1024, nullptr);
errno = 0; // wanting error message from WinAPI instead of C-lib
errmsg = GetLastErrorMessage();
}
return ok;
#else
@@ -1154,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);
@@ -506,7 +517,23 @@ bool Util::RegReadStr(HKEY keyRoot, const char* keyName, const char* valueName,
time_t Util::CurrentTime()
{
#ifdef WIN32
// C-library function "time()" works on Windows too but is very CPU intensive
// since it uses high performance timer which we don't need anyway.
// A combination of GetSystemTime() + Timegm() works much faster.
SYSTEMTIME systm;
GetSystemTime(&systm);
struct tm tm;
tm.tm_year = systm.wYear - 1900;
tm.tm_mon = systm.wMonth - 1;
tm.tm_mday = systm.wDay;
tm.tm_hour = systm.wHour;
tm.tm_min = systm.wMinute;
tm.tm_sec = systm.wSecond;
return Timegm(&tm);
#else
return ::time(nullptr);
#endif
}
/* From boost */

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

@@ -14,11 +14,6 @@ MainDir=~/downloads
#
# If you want to distinguish between partially downloaded files and
# completed downloads, use also option <InterDir>.
#
# It is allowed to enter multiple directories here by separating them with comma
# or semicolon. NZBGet checks how much free disk space is available in each
# directory (assuming all directories are located on different drives) and
# chooses the directory with the most free space.
DestDir=${MainDir}/dst
# Directory to store intermediate files.
@@ -41,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
@@ -349,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
@@ -423,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.
#
@@ -627,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=
##############################################################################
@@ -644,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).
@@ -719,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
@@ -773,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).
@@ -817,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).
#
@@ -907,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
@@ -927,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
@@ -1088,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
@@ -1136,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
@@ -1158,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
@@ -1188,7 +1201,7 @@ UpdateInterval=200
##############################################################################
### PAR CHECK/REPAIR ###
### CHECK AND REPAIR ###
# Whether and how par-verification must be performed (auto, always, force, manual).
#
@@ -1198,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
@@ -1210,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
@@ -1245,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
@@ -1295,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).
#
@@ -1382,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/
#
@@ -1418,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
@@ -1429,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
@@ -1439,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
@@ -1455,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
@@ -1899,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.0";_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.0";_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.0";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.0";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