Compare commits

..
265 Commits
Author SHA1 Message Date
Andrey Prygunkov a4cca673dc fixed #398: crash between post-processing steps 2017-06-16 17:51:28 +02:00
Andrey Prygunkov fb9b84a23b #371: wait for direct rename completion before direct unpack
Redo accidentally undone 5df06a2626.
2017-06-16 16:45:55 +02:00
Andrey Prygunkov bc2b9de6a9 #392: added link to wiki page 2017-06-10 18:07:31 +02:00
Simon Nicolussi 7793f64b77 #386: don't write beyond buffer when reading a signature 2017-06-10 18:04:30 +02:00
Andrey Prygunkov cad3116b5b #392: Windows crash dump support
Also renamed option "DumpCore" to "CrashDump"; new option "CrashTrace".
2017-06-10 13:13:16 +02:00
Andrey Prygunkov dd714355c4 nzbget/nzbget#388: updated wiki links to use new url format 2017-06-09 18:41:40 +02:00
Andrey Prygunkov 0a73a0c31f fixed #387: asterix passed as parameter to extension scripts
(Windows only)
2017-06-07 18:51:16 +02:00
Andrey Prygunkov 7f393d050c #362: removed unnecessary code 2017-06-07 17:25:24 +02:00
Andrey Prygunkov 5dcca7294f #371: respect PostStrategy when starting another direct unpack
to avoid running way too many direct unpack jobs at the same time.
2017-06-06 15:32:24 +02:00
Andrey Prygunkov 53da5725c2 #362, #382: articles yEncoded with less than 68 characters
are now correctly processed by direct rename.
2017-05-31 22:00:25 +02:00
Andrey Prygunkov 77cabd7bce #371: do not direct unpack if article decoding is disabled
via option Decode=no.
2017-05-30 21:19:38 +02:00
Andrey Prygunkov 2336d4bcfe #362: do not direct rename if article decoding is disabled
via option Decode=no.
2017-05-30 21:18:54 +02:00
Andrey Prygunkov 580e1974bc #362, #382: fixed crash during direct rename
which may happen if articles were yEncoded with less than 68 character
length.
2017-05-30 21:17:47 +02:00
Andrey Prygunkov 8790ee685f updated link to documentation 2017-05-26 00:36:51 +02:00
Andrey Prygunkov 32f0bbae58 #371: do not terminate unrar if it's not running
when cancelling direct unpack
2017-05-23 21:05:25 +02:00
Andrey Prygunkov 8ffd6c24fe #364: do not reorder files on nzb parse errors 2017-05-23 20:53:11 +02:00
Andrey Prygunkov 98cc4817fe #362, #382: fixed crash during direct rename
which may happen if download errors occurred.
2017-05-23 20:51:23 +02:00
Andrey Prygunkov 14b40d6712 #362: discard unneeded data after direct rename
Now also discarding data when download completes without direct rename
being able to process files (due to download errors).
2017-05-23 19:39:09 +02:00
Andrey Prygunkov 629640898d #362: prevent repeating of error messages
If a file got lost before cache flushing.
2017-05-23 19:21:07 +02:00
Andrey Prygunkov 5df06a2626 #371: wait for direct rename completion before direct unpack 2017-05-22 22:08:09 +02:00
Andrey Prygunkov 4ca95b2989 #371: reset direct unpack status on post-process again 2017-05-22 22:05:52 +02:00
Andrey Prygunkov b3cc316092 #252: new option to force news servers to ipv4 or ipv6 2017-05-22 22:03:30 +02:00
Andrey Prygunkov 298fde6cd4 #379: compile unrar for Linux with fallocate 2017-05-20 14:05:24 +02:00
Andrey Prygunkov 015b3be461 #371: better cleanup on cancelling unpack
Deleting destination if it contains only hidden files.
2017-05-18 22:00:45 +02:00
Andrey Prygunkov f6adbe848d #362: avoid unnecessary renaming of par-sets 2017-05-18 20:32:46 +02:00
Andrey Prygunkov 08de827d7b refactor: removed unneeded type casts 2017-05-18 18:43:08 +02:00
Andrey Prygunkov d1bda91954 refactor: removed one override for FileExists 2017-05-17 21:26:27 +02:00
Andrey Prygunkov e40e3178da #378: removed debug logging 2017-05-16 19:38:01 +02:00
Andrey Prygunkov cf3985f228 #378: save nzb download statistics on idle or reload 2017-05-16 19:19:54 +02:00
Andrey Prygunkov f2329dada4 #362: log statistics for direct rename 2017-05-16 18:57:32 +02:00
Andrey Prygunkov a73d1ba56e #31: backup icon in history in phone theme 2017-05-12 19:14:02 +02:00
Andrey Prygunkov 8d5ce3ddc3 fixed #376: startup scheduler tasks can be executed again 2017-05-12 19:08:31 +02:00
Andrey Prygunkov d3362f9280 #371: detect not unpacked archives
Detect not unpacked archives which may appear after rar-rename and
unpack them during post-processing.
2017-05-12 18:37:15 +02:00
Andrey Prygunkov 85dcde36c5 374: unrar tests for Travis CI 2017-05-09 21:46:15 +02:00
Andrey Prygunkov 716bb3130f #374: added gcc 4.8 to Travis CI test matrix 2017-05-09 21:45:32 +02:00
Andrey Prygunkov 725e2c7376 #371: fixed zombies after reload during unpack 2017-05-09 20:36:48 +02:00
Andrey Prygunkov 3a07dd378a #371: restart direct unpack after program reload
When program is reloaded during direct unpack the unpack restarts after
one (any) inner file is downloaded.
2017-05-08 23:27:49 +02:00
Andrey Prygunkov ff02b53ed0 #371: better handling of cancelling direct unpack 2017-05-06 23:45:31 +02:00
Andrey Prygunkov 39089b6f2f #371: disable direct unpack tests in Travis CI
because it misses unrar 5 required for the tests
2017-05-05 22:49:03 +02:00
Andrey Prygunkov 61af2b3446 #371: integrated direct unpack into pp-workflow 2017-05-05 21:57:52 +02:00
Andrey Prygunkov 20f4f3020b #371: 6b546394b2: corrected stdin redirection 2017-05-05 21:56:54 +02:00
Andrey Prygunkov df5a1fe959 #371: direct unpack volume handling
Archive parts are now unpacked as they arrive.
2017-05-03 21:42:59 +02:00
Andrey Prygunkov 6b546394b2 #371: added stdin-redirecting support 2017-05-03 21:39:57 +02:00
Andrey Prygunkov 160b274ce8 #371: new module "DirectUnpack"
without implementation and with a new failing unit test.
2017-05-03 21:30:35 +02:00
Andrey Prygunkov 4104a2357b #371: new option "DirectUnpack"
without implementation.
2017-05-03 21:08:21 +02:00
Andrey Prygunkov ba1e51a8d8 #362: new queue event after direct rename
Queue-event NZB_NAMED, sent after the inner files are renamed
2017-05-01 17:48:20 +02:00
Andrey Prygunkov 9f7d6655b2 #364: logging for file reordering 2017-05-01 17:31:14 +02:00
Andrey Prygunkov f6a9253a53 #364: fixed compiling error on Linux 2017-04-30 21:41:23 +02:00
Andrey Prygunkov 48020e8901 #361: fixed failing test 2017-04-30 21:36:35 +02:00
Andrey Prygunkov 5afa20d655 #364: better ordering for rar- and par-files 2017-04-30 21:27:51 +02:00
Andrey Prygunkov 197baf066a updated option descriptions in configuration file 2017-04-30 14:39:46 +02:00
Andrey Prygunkov c873647aae #361: new option "FileNaming"
replace pp-parameter “*naming”.
2017-04-30 14:21:49 +02:00
Andrey Prygunkov fc1847588d #362: save and restore disk state for direct rename 2017-04-30 00:10:17 +02:00
Andrey Prygunkov 62b3d47b43 #362: redesigned renaming
Spread RenameInfo into FileInfo and NzbInfo to make the state easier to
save on disk
2017-04-28 23:55:41 +02:00
Andrey Prygunkov ddb9333ca6 #362: discard partially downloaded par2-files
to solve issues with par-checker, par-renamer and avoid saving state
for these files.
2017-04-28 18:36:27 +02:00
Andrey Prygunkov 830e0e4858 #362: proper rename on completion
Fixed: if the file was renamed during finalizing stage (completion) the
file may not be properly renamed.
2017-04-27 17:31:39 +02:00
Andrey Prygunkov 085c612f97 #24: fixed errors when adding many files at once 2017-04-25 21:36:53 +02:00
Andrey Prygunkov dea9fb2090 #24: double click handler for windows 2017-04-25 19:33:31 +02:00
Andrey Prygunkov 5813c903eb #362: compatibility with gcc 4.8 2017-04-25 19:27:39 +02:00
Andrey Prygunkov 4f499e2c2e fixed #367: fatal-messages when compiling from sources 2017-04-25 19:26:53 +02:00
wtf911 3884328251 #365, #24: option for file association in windows installer
This will add a checkbox so after installation finishes there will be an option to associate .nzb files with NZBGet. It will also remove the association if NZBGet is uninstalled.
2017-04-25 00:40:19 +02:00
Andrey Prygunkov 7e9e2471ef #362: handling of download interruptions
if they happen during downloading of first articles
2017-04-24 23:10:14 +02:00
Andrey Prygunkov 2a433ee7fb #362: fixed inner sorting 2017-04-24 23:09:05 +02:00
Andrey Prygunkov 118f835385 #362: multiple par-sets and renaming par2-files 2017-04-23 20:20:59 +02:00
Andrey Prygunkov 9a6a42bd44 #362: unpause par2-file
as we need it to download first and ExtraPriority-flag doesn’t work on
paused files
2017-04-23 13:48:39 +02:00
Andrey Prygunkov 9434149842 #364: remote command for sorting of inner files
subcommand “SF” of remote command “-E“
2017-04-22 21:12:47 +02:00
Andrey Prygunkov 4107536c03 #364: implemented file reordering
- reordering inner files after adding nzb to queue;
- reordering inner files after adding direct renaming;
- new command “GroupSortFiles” in api-method “editqueue”.
2017-04-22 20:13:45 +02:00
Andrey Prygunkov 2aec782f58 #364: new option "ReorderFiles" 2017-04-22 20:10:00 +02:00
Andrey Prygunkov eaaa943af3 #362: save new filenames into disk state 2017-04-21 20:33:23 +02:00
Andrey Prygunkov 964e8311a9 #362: renaming files 2017-04-20 21:28:06 +02:00
Andrey Prygunkov 895dd12e4d #362: another functional test for direct renamer 2017-04-19 18:59:09 +02:00
Andrey Prygunkov d16036aa78 #362: loading hashes from par2-flle 2017-04-19 16:43:58 +02:00
Andrey Prygunkov 8b79a81eaf #362: functional tests for direct renamer
(new tests currently fail)
2017-04-18 19:58:12 +02:00
Andrey Prygunkov 231e94dd2e #362: detecting par2-files 2017-04-18 19:57:35 +02:00
Andrey Prygunkov f7be22893d #362: computing 16k-hashes for downloaded files 2017-04-17 15:32:32 +02:00
Andrey Prygunkov 3ac91a4bb6 #362: fixed restoring of partial state
when direct write was disabled
2017-04-16 15:01:34 +02:00
Andrey Prygunkov 43441a8d55 #362: downloading first article of each file
, then other articles, when option “DirectRename” is active.
2017-04-16 14:58:54 +02:00
Andrey Prygunkov b02059f196 #362: new option "DirectRename" 2017-04-16 14:57:10 +02:00
Andrey Prygunkov f107802f0e #361: pp-param "*naming"
to define naming scheme for downloaded files: “nzb” - use file names
from nzb, “article” - use file names from article metadata (default).
2017-04-15 01:39:40 +02:00
Andrey Prygunkov 7faf1fe64b #361: prefer file names from nzb
to names from article body; to better handle obfuscated posts.
2017-04-14 20:36:00 +02:00
Andrey Prygunkov bf2fea64e7 #360: removed unnecessary requests to news servers 2017-04-14 14:39:37 +02:00
Andrey Prygunkov de3eb3de9d #352: parameters for api-method "servervolumes"
as a performance optimization measure to reduce amount of transferred
data.
2017-04-12 19:47:58 +02:00
Andrey Prygunkov 55d080ac96 #50: clear script execution log
before executing script
2017-04-12 19:41:28 +02:00
Andrey Prygunkov 795bacb4fe merge from master v18.1 2017-04-09 13:38:01 +02:00
Andrey Prygunkov f7930b56a6 updated version string to "18.1" 2017-04-08 17:37:57 +02:00
Andrey Prygunkov b2bf488d59 fixed #338: "undefined" in reorder extension scripts
When editing option “ScriptOrder” in web-interface.
2017-04-08 17:28:41 +02:00
Andrey Prygunkov 8e4b75b21e fixed #347: possible crash at the end of post-processing 2017-04-08 17:27:51 +02:00
Andrey Prygunkov 5b17bebbd6 fixed #348: queue was not saved after deleting of queued post-jobs 2017-04-08 17:27:22 +02:00
Andrey Prygunkov bda1eaa192 fixed #350: sleep mode no longer works on Windows 2017-04-08 17:26:15 +02:00
Andrey Prygunkov 4bcdbbeb09 fixed #356: crash during download caused by a race condition 2017-04-08 17:25:29 +02:00
Andrey Prygunkov f0ee12f92e #352: option "ServerX.Notes"
for user comments on news servers.
2017-04-07 20:43:06 +02:00
Andrey Prygunkov 0b14a2f869 #352: showing volume stats from settings page
New button “Volume Statistics” in section “News Servers” of settings
page. Shows the same volume data as in global statistics dialog.
2017-04-07 20:40:46 +02:00
Andrey Prygunkov 2579ce65b9 fixed #356: crash during download caused by a race condition 2017-03-31 17:46:02 +02:00
Andrey Prygunkov 77f86988cb #353: support for ECC certificates in built-in web-server 2017-03-21 23:29:48 +01:00
Andrey Prygunkov b9b62dcd75 #353: better error reporting for TLS error in built-in web-server 2017-03-21 23:29:07 +01:00
Andrey Prygunkov bfee7c55cd #50: confirmation for commands marked as dangerous 2017-03-21 23:22:54 +01:00
Andrey Prygunkov 34efa87699 #50: "send test e-mail" command in EMail.py 2017-03-20 21:03:40 +01:00
Andrey Prygunkov e839db7107 #50: pass not yet saved options to script 2017-03-20 20:59:34 +01:00
Andrey Prygunkov ffb16aa7bb #50: progress dialog for script execution log
New API-method “logscript”.
2017-03-20 20:30:19 +01:00
Andrey Prygunkov a3b0b7675e #50: execute custom commands for scripts
New API-method “startscript”.
2017-03-17 20:24:14 +01:00
Andrey Prygunkov 602f62c17c #50: define custom buttons in extension scripts 2017-03-17 19:23:56 +01:00
Andrey Prygunkov ace7a1968d #349: added host name to all error messages 2017-03-15 20:36:45 +01:00
Andrey Prygunkov 6efefe3780 fixed #350: sleep mode no longer works on Windows 2017-03-15 20:13:55 +01:00
Andrey Prygunkov c02f708d74 #346: save changes before performing actions in history dialog 2017-03-13 19:53:54 +01:00
Andrey Prygunkov e7c47f523a fixed #348: queue was not saved after deleting of queued post-jobs 2017-03-13 18:04:44 +01:00
Andrey Prygunkov 5c8be152f4 fixed #347: possible crash at the end of post-processing 2017-03-13 17:58:15 +01:00
Andrey Prygunkov 4cdbfc84dd #346: changing category via drop-down changes pp-parameters
accordingly to the new category.
2017-03-12 20:09:50 +01:00
Andrey Prygunkov 75ec856af3 #346: improved hover effect
Increased clickable area and made the carets (triangles) clickable too.
2017-03-11 23:21:50 +01:00
Andrey Prygunkov 88b5e16597 #346: multi-edit via drop-downs
If multiple items are selected and drop-down is shown for one of them
the menus work on all selected items.
2017-03-09 20:55:04 +01:00
Andrey Prygunkov fff6fdd4eb #346: unified hover feedback for all drop-downs 2017-03-09 20:44:57 +01:00
Andrey Prygunkov c4ff544459 #346: drop-down for status in downloads and history 2017-03-08 20:16:17 +01:00
Andrey Prygunkov 9c87942b43 #346: drop-down for category in history 2017-03-08 20:12:38 +01:00
Andrey Prygunkov 0e4c9275b0 #346: drop-down for category in downloads 2017-03-08 20:10:23 +01:00
Andrey Prygunkov f11dce4269 #346: drop-down for priority in downloads 2017-03-08 20:07:25 +01:00
Andrey Prygunkov f1340eb542 #263: improvements in RSS feed view in phone mode 2017-03-04 18:17:14 +01:00
Andrey Prygunkov e89f5cbd8a #31: backup-badge for items in history
if backup news servers were used; that’s similar to backup-badge on
downloads tab.
2017-02-26 21:35:09 +01:00
Andrey Prygunkov 975016700e #23: proper exit code on client command success or failure 2017-02-26 20:40:55 +01:00
Andrey Prygunkov a9017be606 #123: set name, password and dupe info when adding via URL
by click on a button near URL field in web-interface
2017-02-26 19:23:33 +01:00
Andrey Prygunkov d81d6831dc #331: support for HTTP-header "X-Forwarded-For" in IP-logging 2017-02-26 12:40:37 +01:00
Andrey Prygunkov 1b94373c7e fixed #338: "undefined" in reorder extension scripts
When editing option “ScriptOrder” in web-interface.
2017-02-24 16:27:05 +01:00
Andrey Prygunkov d5e881ce1a #330: optimisations for Safari 2017-02-24 14:55:05 +01:00
Andrey Prygunkov a3f84aca0e #330: better session handling in form login 2017-02-23 20:49:50 +01:00
Andrey Prygunkov 0ab86b90f0 #330: authentication via form in web-interface
, new option “FormAuth”.
2017-02-22 17:41:25 +01:00
Andrey Prygunkov b6a606db35 #339: extended error messages with a link to wiki-page 2017-02-20 18:24:30 +01:00
Andrey Prygunkov 578731f239 #339: integrated root certificate store file into Linux installer 2017-02-19 18:14:04 +01:00
Andrey Prygunkov 71db4ffe9c #192: adjusted linux build script 2017-02-19 18:12:23 +01:00
Andrey Prygunkov 7421ec7b1a #339: added missing "define" for Windows build 2017-02-19 15:29:14 +01:00
Andrey Prygunkov 958712663e fixed compile error under gcc 4.8 2017-02-18 20:14:48 +01:00
Andrey Prygunkov d96fa66487 #339: 36ac548842: fixed compile error on gcc and clang 2017-02-18 20:12:53 +01:00
Andrey Prygunkov 36ac548842 #339: TLS certificate verification with GnuTLS 2017-02-18 19:47:20 +01:00
Andrey Prygunkov fc44ab6128 #339: simplified verification code 2017-02-17 23:44:15 +01:00
Andrey Prygunkov f0da3936e5 #339: prevent compilation failure on older OpenSSL versions 2017-02-17 13:22:07 +01:00
Andrey Prygunkov 04e694799d #339: TLS certificate verification with OpenSSL 2017-02-17 12:32:20 +01:00
Andrey Prygunkov 712cedb84f #339: new options "CertStore" and "CertCheck" 2017-02-17 11:27:49 +01:00
Andrey Prygunkov 4fb3ddcf84 updated version string to "19.0-testing" 2017-02-17 11:03:57 +01:00
Andrey Prygunkov 69d40c11fd Merge branch 'develop' for v18.0 2017-02-12 17:20:07 +01:00
Andrey Prygunkov 58893710d8 updated version string to "18.0" 2017-02-12 17:05:21 +01:00
Andrey Prygunkov 6c6f781510 updated ChangeLog for v18.0 2017-02-12 17:03:04 +01:00
Andrey Prygunkov 569ec22ee8 updated copyright string in Windows and OS X apps 2017-02-12 17:02:56 +01:00
Andrey Prygunkov b06d3eca86 #327: printing only base file name in warnings 2017-02-12 14:35:44 +01:00
Andrey Prygunkov dcf63b4db7 #277: allow control of what tab is shown when opening web-interface
Add “#downloads”, “#history”, “#messages” or “#settings” to the URL,
for example “http://localhost:6789/#history” or
“http://localhost:6789/index.html#history”
2017-01-27 20:10:57 +01:00
Andrey Prygunkov 06e7573572 #329: improved selecting of par2-file for repair 2017-01-19 21:48:35 +01:00
Andrey Prygunkov 639e0b6bfb #327: reverted non-strict par2-filename matching
To handle article subjects with non-parseable filenames. Also created a
functional test for this case.
2017-01-17 22:18:34 +01:00
Andrey Prygunkov f2073ff920 #327: better handing of damaged par2-files in par-renamer
If par-renamer can’t load par2-file another par2-file is downloaded and
par-renamer tries again.
2017-01-17 00:23:29 +01:00
Sander 4ba2e07d94 #328: NServ: option -b to specify on which address to listen 2017-01-08 19:37:43 +01:00
Sander 91515b20e5 #324, #325: Nserv: Check if specified data-dir is an existing directory 2017-01-06 21:11:42 +01:00
Andrey Prygunkov 7226e1c186 #319: fixed automatic changing of pp-scripts when changing category in web-interface 2016-12-30 22:29:14 +01:00
Andrey Prygunkov 98c2dd46b7 #291: fixed "PostTotalTimeSec" in API-method "listgroups" 2016-12-27 17:21:06 +01:00
Andrey Prygunkov f0b08dbd63 #315: reset feed search box 2016-12-22 20:24:00 +01:00
Sander 25ddfd5659 #320, #321: newlines added to 281 and 205 server answers 2016-12-21 19:46:55 +01:00
Andrey Prygunkov 7450b97871 #319: scheduler scripts in option "Extensions"
Scheduler scripts can now be selected in option “Extensions” if the
scripts provide default time definition.
2016-12-20 22:22:09 +01:00
Andrey Prygunkov 9f7e0ee972 #304: safer termination of scripts 2016-12-19 19:33:38 +01:00
Andrey Prygunkov ae7719e948 #319: auto migration of old script settings 2016-12-16 21:03:59 +01:00
Andrey Prygunkov 9a92896678 #319: unified extension scripts settings
- new option “Extension” as replacement for options “PostScript”,
“QueueScript”, “ScanScript”, “FeedScript”;
- renamed “CategoryX.PostScript” to “CategoryX.Extensions”;
- renamed “FeedX.PostScript” to “FeedX.Extensions”.
2016-12-14 20:34:14 +01:00
Andrey Prygunkov ab0723adda #288: rar-rename only if unpack is enabled 2016-12-14 18:50:14 +01:00
Andrey Prygunkov 13b98fca83 fixed incompatibility with macOS El Capitan and XCode 7/8 2016-12-13 21:52:32 +01:00
Andrey Prygunkov 5901998cb5 #242: fixed enter-key in feed filter dialog 2016-12-10 20:13:19 +01:00
Andrey Prygunkov 9ddd73368e #282: 0d67e322a3: fixed confirmation dialog 2016-12-07 21:02:55 +01:00
Andrey Prygunkov 12daa683e5 #313: 5f71c4a5a7: fixed test failure on Linux 2016-12-06 19:18:06 +01:00
Andrey Prygunkov 5f71c4a5a7 #313: better handling of renamed par-files 2016-12-06 18:49:12 +01:00
Andrey Prygunkov 13db817395 #313: better handling of obfuscated par-files in par-renamer
Avoid false detection of missing files.
2016-12-04 19:04:39 +01:00
Andrey Prygunkov 84f4cf2f33 #312: extended description of option "KeepHistory" 2016-12-03 12:54:49 +01:00
Andrey Prygunkov 0d67e322a3 #282: extra warning when deleting from history
An extra warning is shown when deleting 50 or more records from history
and there are selected records on other pages.
2016-11-29 18:56:00 +01:00
Andrey Prygunkov 374f706354 #282: visual indication of records selected on other pages 2016-11-22 21:36:20 +01:00
Andrey Prygunkov f7cefadf33 use default SDK when building mac app 2016-11-20 19:31:06 +01:00
Andrey Prygunkov c111a114b9 #242: keyboard support in web-interface 2016-11-20 14:24:27 +01:00
Andrey Prygunkov 2cd3f0fc68 #309: allow sending e-mails to multiple recipients
in the example pp-script “EMail.py”.
2016-11-20 13:12:50 +01:00
Andrey Prygunkov 92db59116e #301: showing drag grip
and extended the drag grip area to the full cell containing check box.
2016-11-18 19:59:33 +01:00
Andrey Prygunkov 760aed68fb #288: fixed incorrect renaming
of archives containing directory entries
2016-11-16 21:41:34 +01:00
Andrey Prygunkov e612257c28 #304: graceful termination of scheduler scripts
If a script is running when the program must shutdown, the script
receives signal SIGINT (CTRL+BREAK on Windows) and has 10 seconds to
gracefully terminate until it is killed.
2016-11-15 19:22:52 +01:00
Andrey Prygunkov 31208a5816 #304: scheduler tasks can be started at program launch
Using asterisk as TaskX.Time.
2016-11-15 19:00:49 +01:00
Andrey Prygunkov a03e7cd550 #295: disabled SSLv3 in built-in web-server 2016-11-15 18:47:30 +01:00
Andrey Prygunkov 02b33fb559 #306: splitted options "Retries" and "RetryInterval"
into four options: ArticleRetries, ArticleInterval, UrlRetries, and
UrlInterval.
2016-11-13 13:05:37 +01:00
Andrey Prygunkov a9ed53daa8 fixed #303: rar tests fail with TLS disabled 2016-11-12 18:37:48 +01:00
Andrey Prygunkov a3c460ed40 #301: showing row content while dragging
Implemented alternative UI for drag and drop.
2016-11-12 14:23:19 +01:00
Andrey Prygunkov df11d1acb4 #301: functional tests for api-method "editqueue"
And a small fix in edit-commands GroupMoveAfter and GroupMoveBefore.
2016-11-08 21:02:36 +01:00
Andrey Prygunkov e49d4c59af #301: auto scrolling during dragging 2016-11-06 21:16:19 +01:00
Andrey Prygunkov 5dc9c07a58 #301: moved drag-n-drop code into module fasttable.js
Ready for reuse on other places.
2016-11-04 22:02:26 +01:00
Andrey Prygunkov 3bb0751c86 #301: drag-n-drop for touch screens
; also no more dragging via priority-icon but only via check mark.
2016-11-03 19:20:03 +01:00
Andrey Prygunkov a3b3921bea #301: API extension for drag-n-drop support
New actions “GroupMoveBefore” and “GroupMoveAfter” in API-method
“editqueue”; parameter “args” contains id of the target item to move
before/after.
2016-11-01 12:27:42 +01:00
Andrey Prygunkov b19d26aee8 #301: drag and drop handling in web-interface 2016-11-01 12:23:33 +01:00
Andrey Prygunkov 7fae337360 fixed #300: sorting of selected items may give wrong results 2016-10-31 19:05:15 +01:00
Andrey Prygunkov 80debf521a #299: removed parameter "offset" from api-method "editqueue"
When needed the “offset” is now passed within parameter “Args” as
string.
2016-10-31 15:58:02 +01:00
Andrey Prygunkov 528133482e #298: added compatibility with openssl 1.1.0 2016-10-31 10:11:23 +01:00
Andrey Prygunkov a98bbd7d0d #279: cache support in built-in nntp server 2016-10-29 16:55:12 +02:00
Andrey Prygunkov 2681fe187b #288: sooner detection of damaged rar-files 2016-10-22 15:05:19 +02:00
Andrey Prygunkov e16cab67fb #288: ed4761db37: reverted std-input handling on Windows 2016-10-22 15:04:11 +02:00
Andrey Prygunkov 50e2e0cf37 #291: refactor: handling of shutdown/reload 2016-10-18 21:53:48 +02:00
Andrey Prygunkov d2d20f29c1 #291: better handling of shutdown/reload
for post-processing jobs
2016-10-18 21:52:30 +02:00
Andrey Prygunkov 53f4992eeb #279: corrected generation of test files 2016-10-18 21:51:22 +02:00
Andrey Prygunkov 3ae1be1ca4 #294: handling of incomplete obfuscated rar-archives
and special file extensions; new option “UnpackIgnoreExt” honored by
rar-renamer and unpacker.
2016-10-16 13:21:58 +02:00
Andrey Prygunkov ce1e1b61e7 #291: fixed status in api-method "listgroups" 2016-10-15 16:12:48 +02:00
Andrey Prygunkov cac25ed290 #291: distinguishing pp-stages par-renaming and rar-renaming
to better handling temporary pause.
2016-10-15 13:37:34 +02:00
Andrey Prygunkov 8ed0b70df5 #291: eacfacc5a2: fixed compilation error on Windows 2016-10-15 13:11:54 +02:00
Andrey Prygunkov d72071c8ed #291: temporary pause during post-processing
Options ParPauseQueue, UnpackPauseQueue, PostPauseQueue work properly.
2016-10-15 13:00:52 +02:00
Andrey Prygunkov ba05dfc202 #291: tweaking balanced pp-startegy
Other job can be run only during repair stage but not during
verification stage.
2016-10-14 11:20:57 +02:00
Andrey Prygunkov f3adab5690 #293: split section "Download Queue"
Many options moved into new section “Connection”.
2016-10-14 00:19:14 +02:00
Andrey Prygunkov b1b5405809 #291: new option "PostStrategy" to configure multi-post-processing
instead of option “ParExclusive”.
2016-10-14 00:07:13 +02:00
Andrey Prygunkov 99fcd164ac #291: smoother transition between pp-stages
without intermediate “pp-queued” state shown in web-interface
2016-10-13 00:07:44 +02:00
Andrey Prygunkov eacfacc5a2 #291: non-global cout/cerr in par2-module
Fixed global cout/cerr in par2-module to prevent crashes when executing
multiple par2-instances.
2016-10-12 16:00:08 +02:00
Andrey Prygunkov 3404b544de #291: removed not used unit of par2-module 2016-10-12 15:58:08 +02:00
Andrey Prygunkov 3604534850 #291: functional tests for multi-processing 2016-10-12 00:30:23 +02:00
Andrey Prygunkov 2e18021e08 #291: new option "ParExclusive"
to configure simultaneous post-processing of a second item if currently
performing par-check/repair for the first item in pp-queue
2016-10-12 00:22:34 +02:00
Andrey Prygunkov 2e19382ccc #291: multi post-processing
During par-check/repair another pp-item can be post-processed at the
same time, as long as it doesn’t require par-check/repair.
2016-10-12 00:13:50 +02:00
Andrey Prygunkov a6dc20dc8e #291: refactor: made par-checker non-global
and more similar to other post-processing stages.
2016-10-11 20:43:11 +02:00
Andrey Prygunkov a1bef9146e #286: corrected html in priority column 2016-10-08 15:33:04 +02:00
Andrey Prygunkov 61f18f81b7 #29, #278: small UI tweak 2016-10-08 15:32:26 +02:00
Andrey Prygunkov 129125faa1 #286: priority column and sorting
Also removed horizontal lines in tables, for better visuals.
2016-10-07 23:46:21 +02:00
Andrey Prygunkov b97c987f66 #274: fixed error on history page
Fixed javascript error on history page when showing hidden items.
2016-10-04 22:11:32 +02:00
Andrey Prygunkov 53e504d974 fixed failing functional tests 2016-10-03 23:39:34 +02:00
Andrey Prygunkov 5885258c35 #288: reading encrypted archives using nettle library
when compiled with GnuTLS instead of OpenSSL.
2016-10-03 22:38:48 +02:00
Andrey Prygunkov 2034ea97d2 #288: fixed compilation error with GnuTLS
and added unit test for rar3 encrypted files
2016-10-03 15:05:29 +02:00
Andrey Prygunkov 2cc38a85df #288: fixed incompatibility with older compilers 2016-10-03 01:35:46 +02:00
Andrey Prygunkov ccd509b70c #288: reading rar3 encrypted archives
Rar-renamer now supports renaming of rar3 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-10-02 23:10:31 +02:00
Andrey Prygunkov bab43d8d30 #288: reading rar5 encrypted archives
Rar-renamer now supports renaming of rar5 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-09-30 21:39:35 +02:00
Andrey Prygunkov ed4761db37 #288: unit and functional tests for encrypted rar files 2016-09-30 19:32:55 +02:00
Andrey Prygunkov db7bc4314f #288: rar-rename as a separate post-processing stage
Made rar-rename a separate post-processing stage which runs after
par-repair, in order to restore archive file names after possible
repair stage (which could rename files back to obfuscated names if
rar-rename were run before par-repair).
2016-09-28 22:22:18 +02:00
Andrey Prygunkov 8a21626bf6 #288: sophisticated renaming for rar-files 2016-09-27 23:34:42 +02:00
Andrey Prygunkov 804f8ab085 #288: integrated rar-renamer into post-processing
also implemented initial simple renaming for rar-files
2016-09-26 21:55:41 +02:00
Andrey Prygunkov 3593990dd6 #288: new module "Rename" to handle par-rename and rar-rename 2016-09-26 21:16:27 +02:00
Andrey Prygunkov 3a38a2c7c6 #288: refactor: moved parts of module "RarRenamer" into new module "RarReader"
Also added new unit test for module “RarReader”.
2016-09-26 21:01:15 +02:00
Andrey Prygunkov 16aef2e7a8 #288: functional tests for par-renamer and rar-renamer
Rar-renamer tests are currently failing as the feature isn’t completed
yet.
2016-09-25 21:08:45 +02:00
Andrey Prygunkov bf992195e8 #288: fixed unit test for rar-renamer 2016-09-25 21:07:23 +02:00
Andrey Prygunkov 2fb0fd1113 #288: reading rar5-files 2016-09-24 22:43:20 +02:00
Andrey Prygunkov 709c9856c9 #288: reading rar3-files 2016-09-24 22:39:45 +02:00
Andrey Prygunkov 9e9ef3120f #288: added module "RarRenamer" with unit tests
The tests are failing at the moment as the module is not fully
implemented yet.
2016-09-24 18:37:02 +02:00
guaguasi a7525ae6f9 #274, #285: password-badge for nzbs with passwords 2016-09-23 21:28:12 +02:00
Andrey Prygunkov 5e0e91f671 #249: do not cleanup if unpack disabled
If parameter "unpack" is disabled for an nzb-file the cleanup isn't
performed for it.
2016-09-19 23:40:00 +02:00
Andrey Prygunkov 09f863f3af #275: nZEDb attributes in rss feeds
Added support for nZEDb attributes in rss feeds.
2016-09-19 23:37:51 +02:00
Andrey Prygunkov 87e8d479d8 #281: improved error reporting on feed parse errors 2016-09-19 22:22:29 +02:00
Andrey Prygunkov fb8c6b9cd3 #279: size of test files
can now be adjusted via pytest.ini; reduced default sizes to speed up
tests.
2016-09-19 22:04:49 +02:00
Andrey Prygunkov bc98b0582c #279: corrected nntp error reporting 2016-09-19 21:44:38 +02:00
guaguasi 84388785e7 #276 , #280: highlight selected rows with alternative colors 2016-09-19 06:43:07 +02:00
Andrey Prygunkov 4c519cf646 #279: fixed compilation error on Windows 2016-09-17 23:45:03 +02:00
Andrey Prygunkov f28c049c12 fixed #284: API-method "append" and dupe check
method “append” returns nzb-id also for items added straight to history.
2016-09-17 20:38:57 +02:00
Andrey Prygunkov 33dbbcb0b5 Merge branch '279-nserv' into develop 2016-09-17 13:49:45 +02:00
Andrey Prygunkov 7f6cbd137f #279: updated .gitignore 2016-09-17 13:49:09 +02:00
Andrey Prygunkov facd8cd41f #279: fixed file permissions 2016-09-17 13:37:56 +02:00
guaguasi e269d8f062 #29, #278: additional options in "custom pause dialog" 2016-09-16 06:40:55 +02:00
Andrey Prygunkov dd141e4cf5 #279: initial collection of functional tests 2016-09-13 22:05:10 +02:00
Andrey Prygunkov f18ee92a23 #279: built-in nntp server for functional testing
- implemented nserv - built-in simple nntp server to be used for
functional testing;
- nserv is part of nzbget executable and can be easily used on all
platforms;
- to start nzbget in nserv mode use command “nzbget --nserv”.
2016-09-13 21:31:20 +02:00
Andrey Prygunkov b25a88683b #271: do not close files on daemonizing 2016-09-09 20:12:10 +02:00
guaguasi 9dc2b8c71b #49, #260: fields containing passwords are now password fields
- fields containing passwords are now password fields with option to view;
- excluding post-processing/archive password.
2016-09-09 19:43:46 +02:00
Andrey Prygunkov 2ef393531a updated version string to 18.0-testing 2016-09-09 19:45:46 +02:00
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 032a7a4770 update version string to "17.0" 2016-07-27 14:23:43 +02:00
Andrey Prygunkov b2dcc59845 Merge branch 'develop' for v17.0 2016-07-27 13:40:50 +02:00
Andrey Prygunkov 1d77852bea updated version string to "16.4" 2015-12-05 12:24:20 +01:00
Andrey Prygunkov 15a5d056ed #118: merge 80653a8dad: fixed resource (socket) leak
, which may cause “too many open files” errors with a possible crash.
2015-12-05 11:51:30 +01:00
Andrey Prygunkov 050dc8d55f updated version string to "16.3" 2015-10-29 20:59:27 +01:00
Andrey Prygunkov 4a5063c14e #100: fixed: downloads may sometimes fail due to incorrect decoding of articles 2015-10-28 18:09:29 +01:00
Andrey Prygunkov 665645b510 updated version string to "16.2" 2015-10-24 15:26:50 +02:00
Andrey Prygunkov eb87111204 fixed #100: workaround to deal with malformed responses
…which still may contain useful data.
2015-10-24 15:13:04 +02:00
Andrey Prygunkov 94aa547a85 updated version string to "16.1" 2015-10-19 22:17:18 +02:00
Andrey Prygunkov dfa18b50a4 corrected file permissions 2015-10-19 22:01:52 +02:00
Andrey Prygunkov 2820ee4bc5 fixed #95: starting nzbget from setup (Windows)
When launching NZBGet at the end of setup the program is now started
with regular user permissions.
2015-10-19 21:55:21 +02:00
Andrey Prygunkov c9ff56cc7e #89: fixed unpack failure on certain CPUs
unrar shipped with nzbget was compiled in optimization mode O3. In that
mode GCC activates loop vectorization. On x86_64 architecture that
caused the usage of CPU commands from extended set SSSE3. Some older
AMD processors doesn’t support SSSE3, which lead to abortion with
“illegal instruction”-message. Now using O2-mode; that solves the
issues.
2015-10-19 21:53:06 +02:00
Andrey Prygunkov 9ea9da8d33 #77: fixed issues with reverse proxies (3)
merging into 16.x-branch: when very long headers were sent from the
proxy, in particular if htdigest authorization were used.
2015-10-19 21:52:33 +02:00
Andrey Prygunkov 297a966da3 #77: fixed issues with reverse proxies
merging into 16.x-branch: removing of authorization-header wasn’t such
a good idea.
2015-10-19 21:51:23 +02:00
206 changed files with 13826 additions and 3819 deletions

No files matched your search

Executable → Regular
+3
View File
@@ -61,3 +61,6 @@ ipch/
# NZBGet specific
nzbget
code_revision.cpp
*.temp
*.pyc
pytest.ini
+24 -5
View File
@@ -4,6 +4,7 @@ language: cpp
matrix:
include:
- compiler: gcc
addons:
apt:
@@ -11,7 +12,10 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-5
env: COMPILER=g++-5
- unrar
env:
- COMPILER=g++-5
- compiler: gcc
addons:
apt:
@@ -19,7 +23,20 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
env: COMPILER=g++-4.9
- unrar
env:
- COMPILER=g++-4.9
- compiler: gcc
addons:
apt:
packages:
- unrar
env:
- COMPILER=g++-4.8
- CXXFLAGS="-std=c++11 -O2 -s"
- CONFIGUREOPTS="--disable-cpp-check"
- compiler: clang
addons:
apt:
@@ -28,9 +45,11 @@ matrix:
- llvm-toolchain-precise-3.6
packages:
- clang-3.6
env: COMPILER=clang++-3.6
- unrar
env:
- COMPILER=clang++-3.6
script:
- $COMPILER --version
- CXX=$COMPILER ./configure --enable-tests && make
- ./nzbget --tests exclude:[DupeMatcher]
- CXX=$COMPILER ./configure $CONFIGUREOPTS --enable-tests && make
- ./nzbget --tests
+111 -2
View File
@@ -1,6 +1,115 @@
nzbget-19.0:
- please see repository change log at
https://github.com/nzbget/nzbget/commits/develop
nzbget-18.1:
- fixed: crash during download caused by a race condition;
- fixed: sleep mode did not work on Windows;
- fixed: queue was not saved after deleting of queued post-jobs;
- fixed: possible crash at the end of post-processing;
- fixed: "undefined" in reorder extension scripts.
nzbget-18.0:
- automatic deobfuscation of rar-archives without par-files:
- obfuscated downloads not having par-files can now be successfully
unpacked;
- also helps with downloads where rar-files were obfuscated before
creating par-files;
- new options "RarRename" and "UnpackIgnoreExt";
- multi post-processing:
- in addition to classic post-processing strategy where items are
processed one after another it is now possible to post-process
multiple items at the same time;
- new option "PostStrategy" to choose from four: sequential, balanced,
aggressive, rocket;
- in "balanced" strategy downloads needing repair do not block other
items which are processed sequentially but simultaneously with
repairing item;
- in "aggressive" mode up to three items are post-processed at the same
time and in "rocket" mode up to six items (including up to two repair
tasks);
- unified extension scripts settings:
- options "PostScript", "QueueScript", "ScanScript" and "FeedScript"
were replaced with one option "Extensions";
- users don't need to know the technical details os extension scripts as
all scripts are now can be selected at one place;
- easier activation of complex extension scripts which previously needed
to be selected in multiple options for their proper work;
- reordering download queue with drag and drop in web-interface:
- new actions "GroupMoveBefore" and "GroupMoveAfter" in API-method
"editqueue";
- priorities are now displayed as a column instead of badge; that makes it
possible to manually sort on priority;
- removed vertical lines in tables; looks better in combination with new
priority column;
- keyboard shortcuts in web-interface;
- improved UI to prevent accidental deletion of many items:
- visual indication of records selected on other pages;
- extra warning when deleting many records from history;
- additional options in "custom pause dialog";
- better handing of damaged par2-files in par-renamer:
- if par-renamer can't load a (damaged) par2-file then another par2-file
is downloaded and par-renamer tries again;
- reverted non-strict par2-filename matching to handle article subjects
with non-parseable filenames;
- better handling of obfuscated par-files;
- splitted option "Retries" into "ArticleRetries" and "UrlRetries"; option
"RetryInterval" into "ArticleInterval" and "UrlInterval";
- scheduler tasks can be started at program launch:
- use asterisk as TaskX.Time;
- graceful termination of scheduler scripts:
- scripts receive signal SIGINT (CTRL+BREAK on Windows) before
termination;
- added support for nZEDb attributes in rss feeds;
- better cleanup handling: if parameter "unpack" is disabled for an nzb-file
the cleanup isn't performed for it;
- fields containing passwords are now displayed as protected fields;
- showing password-badge for nzbs with passwords;
- allow control of what tab is shown when opening web-interface:
add "#downloads", "#history", "#messages" or "#settings" to the URL,
for example "http://localhost:6789/#history" or
"http://localhost:6789/index.html#history";
- functional testing to ensure program quality:
- implemented built-in simple nntp server to be used for functional
testing;
- created a number of tests;
- new features come with additional tests;
- improved API-method "append" in combination with duplicate check; method
returns nzb-id also for items added straight to history;
- removed parameter "offset" from api-method "editqueue":
- when needed the "offset" is now passed within parameter "Args" as
string;
- old method signature is supported for compatibility;
- improved error reporting on feed parse errors;
- highlighting selected rows with alternative colors;
- improved selecting of par2-file for repair;
- splitted config section "Download Queue" and moved many options into new
section "Connection";
- disabled SSLv3 in built-in web-server;
- multiple recipients in the example pp-script "EMail.py";
- added compatibility with openssl 1.1.0;
- fixed TLS handshake error when using GnuTLS;
- fixed: sorting of selected items may give wrong results;
- fixed: search box filter in feed view were not reset.
nzbget-17.1:
- please see repository change log at
https://github.com/nzbget/nzbget/commits/develop
- 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:
+55 -10
View File
@@ -28,6 +28,8 @@ nzbget_SOURCES = \
daemon/connect/WebDownloader.h \
daemon/extension/FeedScript.cpp \
daemon/extension/FeedScript.h \
daemon/extension/CommandScript.cpp \
daemon/extension/CommandScript.h \
daemon/extension/NzbScript.cpp \
daemon/extension/NzbScript.h \
daemon/extension/PostScript.cpp \
@@ -90,16 +92,26 @@ 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/postprocess/DirectUnpack.cpp \
daemon/postprocess/DirectUnpack.h \
daemon/queue/DirectRenamer.cpp \
daemon/queue/DirectRenamer.h \
daemon/queue/DiskState.cpp \
daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp \
@@ -146,6 +158,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 +196,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 +225,7 @@ AM_CPPFLAGS = \
-I$(srcdir)/daemon/queue \
-I$(srcdir)/daemon/remote \
-I$(srcdir)/daemon/util \
-I$(srcdir)/daemon/nserv \
-I$(srcdir)/lib/par2
if WITH_TESTS
@@ -218,6 +239,9 @@ nzbget_SOURCES += \
tests/main/OptionsTest.cpp \
tests/feed/FeedFilterTest.cpp \
tests/postprocess/DupeMatcherTest.cpp \
tests/postprocess/RarRenamerTest.cpp \
tests/postprocess/RarReaderTest.cpp \
tests/postprocess/DirectUnpackTest.cpp \
tests/queue/NzbFileTest.cpp \
tests/nntp/ServerPoolTest.cpp \
tests/util/FileSystemTest.cpp \
@@ -366,7 +390,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)
@@ -417,21 +462,21 @@ uninstall-conf:
# we create new file "code_revision.c" with empty revision number.
code_revision.cpp: FORCE
@ if test -d ./.git ; then \
B="$(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')"; \
M="$(shell git status --porcelain)" ; \
B=`git branch | sed -n -e 's/^\* \(.*\)/\1/p'`; \
M=`git status --porcelain` ; \
if test "$$M" != "" ; then \
M="M" ; \
fi ; \
if test "$$B" = "master" ; then \
V="$$M" ; \
elif test "$$B" = "develop" ; then \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M" ; \
else \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M ($$B)" ; \
fi ; \
H="$(shell test -f ./code_revision.cpp && head -n 1 code_revision.cpp)"; \
H=`test -f ./code_revision.cpp && head -n 1 code_revision.cpp`; \
if test "/* $$V */" != "$$H" ; then \
( \
echo "/* $$V */" ;\
Vendored
+378 -117
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,21 @@ 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/postprocess/DirectUnpackTest.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
@@ -151,9 +154,12 @@ am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
daemon/connect/Connection.h daemon/connect/TlsSocket.cpp \
daemon/connect/TlsSocket.h daemon/connect/WebDownloader.cpp \
daemon/connect/WebDownloader.h daemon/extension/FeedScript.cpp \
daemon/extension/FeedScript.h daemon/extension/NzbScript.cpp \
daemon/extension/NzbScript.h daemon/extension/PostScript.cpp \
daemon/extension/PostScript.h daemon/extension/QueueScript.cpp \
daemon/extension/FeedScript.h \
daemon/extension/CommandScript.cpp \
daemon/extension/CommandScript.h \
daemon/extension/NzbScript.cpp daemon/extension/NzbScript.h \
daemon/extension/PostScript.cpp daemon/extension/PostScript.h \
daemon/extension/QueueScript.cpp \
daemon/extension/QueueScript.h daemon/extension/ScanScript.cpp \
daemon/extension/ScanScript.h \
daemon/extension/SchedulerScript.cpp \
@@ -191,15 +197,22 @@ 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/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/postprocess/DirectUnpack.cpp \
daemon/postprocess/DirectUnpack.h \
daemon/queue/DirectRenamer.cpp daemon/queue/DirectRenamer.h \
daemon/queue/DiskState.cpp daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \
daemon/queue/DupeCoordinator.cpp \
@@ -223,21 +236,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 +266,20 @@ 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/postprocess/DirectUnpackTest.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,16 +291,21 @@ 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@ RarRenamerTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ RarReaderTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ DirectUnpackTest.$(OBJEXT) \
@WITH_TESTS_TRUE@ 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) \
CommandScript.$(OBJEXT) NzbScript.$(OBJEXT) \
PostScript.$(OBJEXT) QueueScript.$(OBJEXT) \
ScanScript.$(OBJEXT) SchedulerScript.$(OBJEXT) \
ScriptConfig.$(OBJEXT) FeedCoordinator.$(OBJEXT) \
FeedFile.$(OBJEXT) FeedFilter.$(OBJEXT) FeedInfo.$(OBJEXT) \
@@ -294,20 +318,23 @@ 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) \
DupeCoordinator.$(OBJEXT) HistoryCoordinator.$(OBJEXT) \
NzbFile.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) Scanner.$(OBJEXT) \
UrlCoordinator.$(OBJEXT) BinRpc.$(OBJEXT) \
ParChecker.$(OBJEXT) ParParser.$(OBJEXT) ParRenamer.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) RarRenamer.$(OBJEXT) \
RarReader.$(OBJEXT) Rename.$(OBJEXT) Repair.$(OBJEXT) \
Unpack.$(OBJEXT) DirectUnpack.$(OBJEXT) \
DirectRenamer.$(OBJEXT) DiskState.$(OBJEXT) \
DownloadInfo.$(OBJEXT) DupeCoordinator.$(OBJEXT) \
HistoryCoordinator.$(OBJEXT) NzbFile.$(OBJEXT) \
QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \
Scanner.$(OBJEXT) UrlCoordinator.$(OBJEXT) BinRpc.$(OBJEXT) \
RemoteClient.$(OBJEXT) RemoteServer.$(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 +470,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@
@@ -464,9 +493,12 @@ nzbget_SOURCES = daemon/connect/Connection.cpp \
daemon/connect/Connection.h daemon/connect/TlsSocket.cpp \
daemon/connect/TlsSocket.h daemon/connect/WebDownloader.cpp \
daemon/connect/WebDownloader.h daemon/extension/FeedScript.cpp \
daemon/extension/FeedScript.h daemon/extension/NzbScript.cpp \
daemon/extension/NzbScript.h daemon/extension/PostScript.cpp \
daemon/extension/PostScript.h daemon/extension/QueueScript.cpp \
daemon/extension/FeedScript.h \
daemon/extension/CommandScript.cpp \
daemon/extension/CommandScript.h \
daemon/extension/NzbScript.cpp daemon/extension/NzbScript.h \
daemon/extension/PostScript.cpp daemon/extension/PostScript.h \
daemon/extension/QueueScript.cpp \
daemon/extension/QueueScript.h daemon/extension/ScanScript.cpp \
daemon/extension/ScanScript.h \
daemon/extension/SchedulerScript.cpp \
@@ -504,15 +536,22 @@ 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/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/postprocess/DirectUnpack.cpp \
daemon/postprocess/DirectUnpack.h \
daemon/queue/DirectRenamer.cpp daemon/queue/DirectRenamer.h \
daemon/queue/DiskState.cpp daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp daemon/queue/DownloadInfo.h \
daemon/queue/DupeCoordinator.cpp \
@@ -536,14 +575,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 +720,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
@@ -812,8 +878,12 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ColoredFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CommandLineParser.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CommandLineParserTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CommandScript.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Connection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DirectRenamer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DirectUnpack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DirectUnpackTest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DiskService.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DiskState.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadInfo.Po@am__quote@
@@ -834,19 +904,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 +928,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 +958,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 +972,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@
@@ -972,6 +1051,20 @@ FeedScript.obj: daemon/extension/FeedScript.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 FeedScript.obj `if test -f 'daemon/extension/FeedScript.cpp'; then $(CYGPATH_W) 'daemon/extension/FeedScript.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/extension/FeedScript.cpp'; fi`
CommandScript.o: daemon/extension/CommandScript.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT CommandScript.o -MD -MP -MF "$(DEPDIR)/CommandScript.Tpo" -c -o CommandScript.o `test -f 'daemon/extension/CommandScript.cpp' || echo '$(srcdir)/'`daemon/extension/CommandScript.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/CommandScript.Tpo" "$(DEPDIR)/CommandScript.Po"; else rm -f "$(DEPDIR)/CommandScript.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/extension/CommandScript.cpp' object='CommandScript.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 CommandScript.o `test -f 'daemon/extension/CommandScript.cpp' || echo '$(srcdir)/'`daemon/extension/CommandScript.cpp
CommandScript.obj: daemon/extension/CommandScript.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT CommandScript.obj -MD -MP -MF "$(DEPDIR)/CommandScript.Tpo" -c -o CommandScript.obj `if test -f 'daemon/extension/CommandScript.cpp'; then $(CYGPATH_W) 'daemon/extension/CommandScript.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/extension/CommandScript.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/CommandScript.Tpo" "$(DEPDIR)/CommandScript.Po"; else rm -f "$(DEPDIR)/CommandScript.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/extension/CommandScript.cpp' object='CommandScript.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 CommandScript.obj `if test -f 'daemon/extension/CommandScript.cpp'; then $(CYGPATH_W) 'daemon/extension/CommandScript.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/extension/CommandScript.cpp'; fi`
NzbScript.o: daemon/extension/NzbScript.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT NzbScript.o -MD -MP -MF "$(DEPDIR)/NzbScript.Tpo" -c -o NzbScript.o `test -f 'daemon/extension/NzbScript.cpp' || echo '$(srcdir)/'`daemon/extension/NzbScript.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/NzbScript.Tpo" "$(DEPDIR)/NzbScript.Po"; else rm -f "$(DEPDIR)/NzbScript.Tpo"; exit 1; fi
@@ -1406,20 +1499,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 +1541,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
@@ -1476,6 +1611,34 @@ Unpack.obj: daemon/postprocess/Unpack.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 Unpack.obj `if test -f 'daemon/postprocess/Unpack.cpp'; then $(CYGPATH_W) 'daemon/postprocess/Unpack.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/Unpack.cpp'; fi`
DirectUnpack.o: daemon/postprocess/DirectUnpack.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectUnpack.o -MD -MP -MF "$(DEPDIR)/DirectUnpack.Tpo" -c -o DirectUnpack.o `test -f 'daemon/postprocess/DirectUnpack.cpp' || echo '$(srcdir)/'`daemon/postprocess/DirectUnpack.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectUnpack.Tpo" "$(DEPDIR)/DirectUnpack.Po"; else rm -f "$(DEPDIR)/DirectUnpack.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/DirectUnpack.cpp' object='DirectUnpack.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 DirectUnpack.o `test -f 'daemon/postprocess/DirectUnpack.cpp' || echo '$(srcdir)/'`daemon/postprocess/DirectUnpack.cpp
DirectUnpack.obj: daemon/postprocess/DirectUnpack.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectUnpack.obj -MD -MP -MF "$(DEPDIR)/DirectUnpack.Tpo" -c -o DirectUnpack.obj `if test -f 'daemon/postprocess/DirectUnpack.cpp'; then $(CYGPATH_W) 'daemon/postprocess/DirectUnpack.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/DirectUnpack.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectUnpack.Tpo" "$(DEPDIR)/DirectUnpack.Po"; else rm -f "$(DEPDIR)/DirectUnpack.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/postprocess/DirectUnpack.cpp' object='DirectUnpack.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 DirectUnpack.obj `if test -f 'daemon/postprocess/DirectUnpack.cpp'; then $(CYGPATH_W) 'daemon/postprocess/DirectUnpack.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/postprocess/DirectUnpack.cpp'; fi`
DirectRenamer.o: daemon/queue/DirectRenamer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectRenamer.o -MD -MP -MF "$(DEPDIR)/DirectRenamer.Tpo" -c -o DirectRenamer.o `test -f 'daemon/queue/DirectRenamer.cpp' || echo '$(srcdir)/'`daemon/queue/DirectRenamer.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectRenamer.Tpo" "$(DEPDIR)/DirectRenamer.Po"; else rm -f "$(DEPDIR)/DirectRenamer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/queue/DirectRenamer.cpp' object='DirectRenamer.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 DirectRenamer.o `test -f 'daemon/queue/DirectRenamer.cpp' || echo '$(srcdir)/'`daemon/queue/DirectRenamer.cpp
DirectRenamer.obj: daemon/queue/DirectRenamer.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectRenamer.obj -MD -MP -MF "$(DEPDIR)/DirectRenamer.Tpo" -c -o DirectRenamer.obj `if test -f 'daemon/queue/DirectRenamer.cpp'; then $(CYGPATH_W) 'daemon/queue/DirectRenamer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/queue/DirectRenamer.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectRenamer.Tpo" "$(DEPDIR)/DirectRenamer.Po"; else rm -f "$(DEPDIR)/DirectRenamer.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='daemon/queue/DirectRenamer.cpp' object='DirectRenamer.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 DirectRenamer.obj `if test -f 'daemon/queue/DirectRenamer.cpp'; then $(CYGPATH_W) 'daemon/queue/DirectRenamer.cpp'; else $(CYGPATH_W) '$(srcdir)/daemon/queue/DirectRenamer.cpp'; fi`
DiskState.o: daemon/queue/DiskState.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DiskState.o -MD -MP -MF "$(DEPDIR)/DiskState.Tpo" -c -o DiskState.o `test -f 'daemon/queue/DiskState.cpp' || echo '$(srcdir)/'`daemon/queue/DiskState.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DiskState.Tpo" "$(DEPDIR)/DiskState.Po"; else rm -f "$(DEPDIR)/DiskState.Tpo"; exit 1; fi
@@ -1784,6 +1947,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 +2171,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 +2353,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 +2367,48 @@ 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`
DirectUnpackTest.o: tests/postprocess/DirectUnpackTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectUnpackTest.o -MD -MP -MF "$(DEPDIR)/DirectUnpackTest.Tpo" -c -o DirectUnpackTest.o `test -f 'tests/postprocess/DirectUnpackTest.cpp' || echo '$(srcdir)/'`tests/postprocess/DirectUnpackTest.cpp; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectUnpackTest.Tpo" "$(DEPDIR)/DirectUnpackTest.Po"; else rm -f "$(DEPDIR)/DirectUnpackTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/DirectUnpackTest.cpp' object='DirectUnpackTest.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 DirectUnpackTest.o `test -f 'tests/postprocess/DirectUnpackTest.cpp' || echo '$(srcdir)/'`tests/postprocess/DirectUnpackTest.cpp
DirectUnpackTest.obj: tests/postprocess/DirectUnpackTest.cpp
@am__fastdepCXX_TRUE@ if $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT DirectUnpackTest.obj -MD -MP -MF "$(DEPDIR)/DirectUnpackTest.Tpo" -c -o DirectUnpackTest.obj `if test -f 'tests/postprocess/DirectUnpackTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/DirectUnpackTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/DirectUnpackTest.cpp'; fi`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/DirectUnpackTest.Tpo" "$(DEPDIR)/DirectUnpackTest.Po"; else rm -f "$(DEPDIR)/DirectUnpackTest.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='tests/postprocess/DirectUnpackTest.cpp' object='DirectUnpackTest.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 DirectUnpackTest.obj `if test -f 'tests/postprocess/DirectUnpackTest.cpp'; then $(CYGPATH_W) 'tests/postprocess/DirectUnpackTest.cpp'; else $(CYGPATH_W) '$(srcdir)/tests/postprocess/DirectUnpackTest.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 +2478,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 +2612,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 \
@@ -2629,21 +2890,21 @@ uninstall-conf:
# we create new file "code_revision.c" with empty revision number.
code_revision.cpp: FORCE
@ if test -d ./.git ; then \
B="$(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')"; \
M="$(shell git status --porcelain)" ; \
B=`git branch | sed -n -e 's/^\* \(.*\)/\1/p'`; \
M=`git status --porcelain` ; \
if test "$$M" != "" ; then \
M="M" ; \
fi ; \
if test "$$B" = "master" ; then \
V="$$M" ; \
elif test "$$B" = "develop" ; then \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M" ; \
else \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M ($$B)" ; \
fi ; \
H="$(shell test -f ./code_revision.cpp && head -n 1 code_revision.cpp)"; \
H=`test -f ./code_revision.cpp && head -n 1 code_revision.cpp`; \
if test "/* $$V */" != "$$H" ; then \
( \
echo "/* $$V */" ;\
+1 -1
View File
@@ -14,5 +14,5 @@ the program can be compiled from sources.
- [Home page (nzbget.net)](http://nzbget.net) - for first time visitors, learn more about NZBGet;
- [Downloads](http://nzbget.net/download) - get the binaries and sources;
- [Documentation](https://github.com/nzbget/nzbget/wiki) - installation manuals, HOW-TOs, API;
- [Documentation](http://nzbget.net/documentation) - installation manuals, HOW-TOs, API;
- [Forum](http://forum.nzbget.net) - get support, share your ideas, scripts, add-ons.
+7 -1
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. */
@@ -131,6 +134,9 @@
/* Define to 1 if variadic macros are supported */
#undef HAVE_VARIADIC_MACROS
/* Define to 1 if OpenSSL supports function "X509_check_host". */
#undef HAVE_X509_CHECK_HOST
/* Define to 1 to exclude debug-code */
#undef NDEBUG
Vendored
+509 -35
View File
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 17.1-testing.
# Generated by GNU Autoconf 2.61 for nzbget 19.0-testing.
#
# Report bugs to <hugbug@users.sourceforge.net>.
#
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='nzbget'
PACKAGE_TARNAME='nzbget'
PACKAGE_VERSION='17.1-testing'
PACKAGE_STRING='nzbget 17.1-testing'
PACKAGE_VERSION='19.0-testing'
PACKAGE_STRING='nzbget 19.0-testing'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="daemon/main/nzbget.cpp"
@@ -721,6 +721,8 @@ openssl_CFLAGS
openssl_LIBS
gnutls_CFLAGS
gnutls_LIBS
nettle_CFLAGS
nettle_LIBS
zlib_CFLAGS
zlib_LIBS
WITH_TESTS_TRUE
@@ -747,6 +749,8 @@ openssl_CFLAGS
openssl_LIBS
gnutls_CFLAGS
gnutls_LIBS
nettle_CFLAGS
nettle_LIBS
zlib_CFLAGS
zlib_LIBS'
@@ -1251,7 +1255,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures nzbget 17.1-testing to adapt to many kinds of systems.
\`configure' configures nzbget 19.0-testing to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1322,7 +1326,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 17.1-testing:";;
short | recursive ) echo "Configuration of nzbget 19.0-testing:";;
esac
cat <<\_ACEOF
@@ -1369,6 +1373,10 @@ Optional Packages:
GnuTLS include directory
--with-libgnutls-libraries=DIR
GnuTLS library directory
--with-libnettle-includes=DIR
Nettle include directory
--with-libnettle-libraries=DIR
Nettle library directory
--with-zlib-includes=DIR
zlib include directory
--with-zlib-libraries=DIR
@@ -1399,6 +1407,9 @@ Some influential environment variables:
gnutls_CFLAGS
C compiler flags for gnutls, overriding pkg-config
gnutls_LIBS linker flags for gnutls, overriding pkg-config
nettle_CFLAGS
C compiler flags for nettle, overriding pkg-config
nettle_LIBS linker flags for nettle, overriding pkg-config
zlib_CFLAGS C compiler flags for zlib, overriding pkg-config
zlib_LIBS linker flags for zlib, overriding pkg-config
@@ -1466,7 +1477,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 17.1-testing
nzbget configure 19.0-testing
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1480,7 +1491,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by nzbget $as_me 17.1-testing, which was
It was created by nzbget $as_me 19.0-testing, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2276,7 +2287,7 @@ fi
# Define the identity of the package.
PACKAGE='nzbget'
VERSION='17.1-testing'
VERSION='19.0-testing'
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
@@ -9978,6 +9989,93 @@ cat >>confdefs.h <<\_ACEOF
#define HAVE_OPENSSL 1
_ACEOF
{ echo "$as_me:$LINENO: checking for library containing X509_check_host" >&5
echo $ECHO_N "checking for library containing X509_check_host... $ECHO_C" >&6; }
if test "${ac_cv_search_X509_check_host+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 X509_check_host ();
int
main ()
{
return X509_check_host ();
;
return 0;
}
_ACEOF
for ac_lib in '' crypto; 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_X509_check_host=$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_X509_check_host+set}" = set; then
break
fi
done
if test "${ac_cv_search_X509_check_host+set}" = set; then
:
else
ac_cv_search_X509_check_host=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_X509_check_host" >&5
echo "${ECHO_T}$ac_cv_search_X509_check_host" >&6; }
ac_res=$ac_cv_search_X509_check_host
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
cat >>confdefs.h <<\_ACEOF
#define HAVE_X509_CHECK_HOST 1
_ACEOF
fi
fi
fi
fi
@@ -10630,6 +10728,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 +12297,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by nzbget $as_me 17.1-testing, which was
This file was extended by nzbget $as_me 19.0-testing, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -11878,7 +12350,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 17.1-testing
nzbget config.status 19.0-testing
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -12203,6 +12675,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 +12685,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
+40 -5
View File
@@ -1,7 +1,7 @@
#
# This file is part of nzbget. See <http://nzbget.net>.
#
# Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -21,7 +21,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 17.1-testing, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 19.0-testing, 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,9 @@ 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.])
AC_SEARCH_LIBS([X509_check_host], [crypto],
AC_DEFINE([HAVE_X509_CHECK_HOST],1,[Define to 1 if OpenSSL supports function "X509_check_host".]))
fi
fi
fi
@@ -457,6 +459,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
+7 -8
View File
@@ -117,7 +117,6 @@ void Connection::Final()
Connection::Connection(const char* host, int port, bool tls) :
m_host(host), m_port(port), m_tls(tls)
{
debug("Creating Connection");
@@ -214,7 +213,7 @@ bool Connection::Bind()
struct addrinfo addr_hints, *addr_list, *addr;
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_family = m_ipVersion == ipV4 ? AF_INET : m_ipVersion == ipV6 ? AF_INET6 : AF_UNSPEC;
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
@@ -374,7 +373,7 @@ char* Connection::ReadLine(char* buffer, int size, int* bytesReadOut)
bufAvail = recv(m_socket, m_readBuf, m_readBuf.Size() - 1, 0);
if (bufAvail < 0)
{
ReportError("Could not receive data on socket", nullptr, true);
ReportError("Could not receive data on socket from %s", m_host, true);
m_broken = true;
break;
}
@@ -446,7 +445,7 @@ std::unique_ptr<Connection> Connection::Accept()
SOCKET socket = accept(m_socket, nullptr, nullptr);
if (socket == INVALID_SOCKET && m_status != csCancelled)
{
ReportError("Could not accept connection", nullptr, true);
ReportError("Could not accept connection for %s", m_host, true);
}
if (socket == INVALID_SOCKET)
{
@@ -466,7 +465,7 @@ int Connection::TryRecv(char* buffer, int size)
if (received < 0)
{
ReportError("Could not receive data on socket", nullptr, true);
ReportError("Could not receive data on socket from %s", m_host, true);
}
return received;
@@ -498,7 +497,7 @@ bool Connection::Recv(char * buffer, int size)
// Did the recv succeed?
if (received <= 0)
{
ReportError("Could not receive data on socket", nullptr, true);
ReportError("Could not receive data on socket from %s", m_host, true);
return false;
}
bufPtr += received;
@@ -518,7 +517,7 @@ bool Connection::DoConnect()
struct addrinfo addr_hints, *addr_list, *addr;
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
addr_hints.ai_family = m_ipVersion == ipV4 ? AF_INET : m_ipVersion == ipV6 ? AF_INET6 : AF_UNSPEC;
addr_hints.ai_socktype = SOCK_STREAM;
BString<100> portStr("%d", m_port);
@@ -813,7 +812,7 @@ void Connection::Cancel()
int r = shutdown(m_socket, SHUT_RDWR);
if (r == -1)
{
ReportError("Could not shutdown connection", nullptr, true);
ReportError("Could not shutdown connection for %s", m_host, true);
}
}
}
+9
View File
@@ -44,6 +44,13 @@ public:
csCancelled
};
enum EIPVersion
{
ipAuto,
ipV4,
ipV6
};
Connection(const char* host, int port, bool tls);
Connection(SOCKET socket, bool tls);
virtual ~Connection();
@@ -65,6 +72,7 @@ public:
const char* GetCipher() { return m_cipher; }
void SetCipher(const char* cipher) { m_cipher = cipher; }
void SetTimeout(int timeout) { m_timeout = timeout; }
void SetIPVersion(EIPVersion ipVersion) { m_ipVersion = ipVersion; }
EStatus GetStatus() { return m_status; }
void SetSuppressErrors(bool suppressErrors);
bool GetSuppressErrors() { return m_suppressErrors; }
@@ -80,6 +88,7 @@ protected:
CString m_host;
int m_port;
bool m_tls;
EIPVersion m_ipVersion = ipAuto;
SOCKET m_socket = INVALID_SOCKET;
CString m_cipher;
CharBuffer m_readBuf;
+214 -36
View File
@@ -25,6 +25,8 @@
#include "TlsSocket.h"
#include "Thread.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
class TlsSocketFinalizer
{
@@ -36,6 +38,7 @@ public:
};
std::unique_ptr<TlsSocketFinalizer> m_tlsSocketFinalizer;
CString TlsSocket::m_certStore;
#ifdef HAVE_LIBGNUTLS
#ifdef NEED_GCRYPT_LOCKING
@@ -89,6 +92,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 +117,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 +141,7 @@ static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const
}
}
#endif /* NEED_CRYPTO_LOCKING */
#endif /* HAVE_OPENSSL */
@@ -172,20 +171,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 */
@@ -204,11 +205,11 @@ TlsSocket::~TlsSocket()
Close();
}
void TlsSocket::ReportError(const char* errMsg)
void TlsSocket::ReportError(const char* errMsg, bool suppressable)
{
#ifdef HAVE_LIBGNUTLS
const char* errstr = gnutls_strerror(m_retCode);
if (m_suppressErrors)
if (suppressable && m_suppressErrors)
{
debug("%s: %s", errMsg, errstr);
}
@@ -219,16 +220,14 @@ void TlsSocket::ReportError(const char* errMsg)
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
int errcode;
int errcode = ERR_get_error();
do
{
errcode = ERR_get_error();
char errstr[1024];
ERR_error_string_n(errcode, errstr, sizeof(errstr));
errstr[1024-1] = '\0';
if (m_suppressErrors)
if (suppressable && m_suppressErrors)
{
debug("%s: %s", errMsg, errstr);
}
@@ -240,6 +239,8 @@ void TlsSocket::ReportError(const char* errMsg)
{
PrintError(errMsg);
}
errcode = ERR_get_error();
} while (errcode);
#endif /* HAVE_OPENSSL */
}
@@ -256,7 +257,7 @@ bool TlsSocket::Start()
m_retCode = gnutls_certificate_allocate_credentials(&cred);
if (m_retCode != 0)
{
ReportError("Could not create TLS context");
ReportError("Could not create TLS context", false);
return false;
}
@@ -268,7 +269,7 @@ bool TlsSocket::Start()
m_certFile, m_keyFile, GNUTLS_X509_FMT_PEM);
if (m_retCode != 0)
{
ReportError("Could not load certificate or key file");
ReportError("Could not load certificate or key file", false);
Close();
return false;
}
@@ -278,7 +279,7 @@ bool TlsSocket::Start()
m_retCode = gnutls_init(&sess, m_isClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
if (m_retCode != 0)
{
ReportError("Could not create TLS session");
ReportError("Could not create TLS session", false);
Close();
return false;
}
@@ -287,12 +288,13 @@ 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)
{
ReportError("Could not select cipher for TLS");
ReportError("Could not select cipher for TLS", false);
Close();
return false;
}
@@ -302,7 +304,7 @@ bool TlsSocket::Start()
m_retCode = gnutls_server_name_set((gnutls_session_t)m_session, GNUTLS_NAME_DNS, m_host, m_host.Length());
if (m_retCode != 0)
{
ReportError("Could not set host name for TLS");
ReportError("Could not set hostname for TLS");
Close();
return false;
}
@@ -312,7 +314,7 @@ bool TlsSocket::Start()
(gnutls_certificate_credentials_t*)m_context);
if (m_retCode != 0)
{
ReportError("Could not initialize TLS session");
ReportError("Could not initialize TLS session", false);
Close();
return false;
}
@@ -322,7 +324,13 @@ bool TlsSocket::Start()
m_retCode = gnutls_handshake((gnutls_session_t)m_session);
if (m_retCode != 0)
{
ReportError("TLS handshake failed");
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
Close();
return false;
}
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
{
Close();
return false;
}
@@ -336,7 +344,7 @@ bool TlsSocket::Start()
if (!m_context)
{
ReportError("Could not create TLS context");
ReportError("Could not create TLS context", false);
return false;
}
@@ -344,29 +352,65 @@ bool TlsSocket::Start()
{
if (SSL_CTX_use_certificate_chain_file((SSL_CTX*)m_context, m_certFile) != 1)
{
ReportError("Could not load certificate file");
ReportError("Could not load certificate file", false);
Close();
return false;
}
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_context, m_keyFile, SSL_FILETYPE_PEM) != 1)
{
ReportError("Could not load key file");
ReportError("Could not load key file", false);
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", false);
Close();
return false;
}
// For ECC certificates
EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (!ecdh)
{
ReportError("Could not generate ecdh parameters for TLS", false);
Close();
return false;
}
if (!SSL_CTX_set_tmp_ecdh((SSL_CTX*)m_context, ecdh))
{
ReportError("Could not set ecdh parameters for TLS", false);
EC_KEY_free(ecdh);
Close();
return false;
}
EC_KEY_free(ecdh);
}
if (m_isClient && !m_certStore.Empty())
{
// Enable certificate validation
if (SSL_CTX_load_verify_locations((SSL_CTX*)m_context, m_certStore, nullptr) != 1)
{
ReportError("Could not set certificate store location", false);
Close();
return false;
}
SSL_CTX_set_verify((SSL_CTX*)m_context, SSL_VERIFY_PEER, nullptr);
}
m_session = SSL_new((SSL_CTX*)m_context);
if (!m_session)
{
ReportError("Could not create TLS session");
ReportError("Could not create TLS session", false);
Close();
return false;
}
if (!m_cipher.Empty() && !SSL_set_cipher_list((SSL*)m_session, m_cipher))
{
ReportError("Could not select cipher for TLS");
ReportError("Could not select cipher for TLS", false);
Close();
return false;
}
@@ -388,7 +432,23 @@ bool TlsSocket::Start()
int error_code = m_isClient ? SSL_connect((SSL*)m_session) : SSL_accept((SSL*)m_session);
if (error_code < 1)
{
ReportError("TLS handshake failed");
long verifyRes = SSL_get_verify_result((SSL*)m_session);
if (verifyRes != X509_V_OK)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
" For more info visit http://nzbget.net/certificate-verification",
*m_host, X509_verify_cert_error_string(verifyRes)));
}
else
{
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
}
Close();
return false;
}
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
{
Close();
return false;
}
@@ -398,6 +458,124 @@ bool TlsSocket::Start()
#endif /* HAVE_OPENSSL */
}
bool TlsSocket::ValidateCert()
{
#ifdef HAVE_LIBGNUTLS
#if GNUTLS_VERSION_NUMBER >= 0x030104
#if GNUTLS_VERSION_NUMBER >= 0x030306
if (FileSystem::DirectoryExists(m_certStore))
{
if (gnutls_certificate_set_x509_trust_dir((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
{
ReportError("Could not set certificate store location");
return false;
}
}
else
#endif
{
if (gnutls_certificate_set_x509_trust_file((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
{
ReportError("Could not set certificate store location");
return false;
}
}
unsigned int status = 0;
if (gnutls_certificate_verify_peers3((gnutls_session_t)m_session, m_host, &status) != 0 ||
gnutls_certificate_type_get((gnutls_session_t)m_session) != GNUTLS_CRT_X509)
{
ReportError("Could not verify TLS certificate");
return false;
}
if (status != 0)
{
if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
{
// Extracting hostname from the certificate
unsigned int cert_list_size = 0;
const gnutls_datum_t* cert_list = gnutls_certificate_get_peers((gnutls_session_t)m_session, &cert_list_size);
if (cert_list_size > 0)
{
gnutls_x509_crt_t cert;
gnutls_x509_crt_init(&cert);
gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
char dn[256];
size_t size = sizeof(dn);
if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &size) == 0)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
" For more info visit http://nzbget.net/certificate-verification", *m_host, dn));
gnutls_x509_crt_deinit(cert);
return false;
}
gnutls_x509_crt_deinit(cert);
}
}
gnutls_datum_t msgdata;
if (gnutls_certificate-verification_status_print(status, GNUTLS_CRT_X509, &msgdata, 0) == 0)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
" For more info visit http://nzbget.net/certificate-verification", *m_host, msgdata.data));
gnutls_free(&msgdata);
}
else
{
ReportError(BString<1024>("TLS certificate verification failed for %s."
" For more info visit http://nzbget.net/certificate-verification", *m_host));
}
return false;
}
#endif
return true;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
// verify a server certificate was presented during the negotiation
X509* cert = SSL_get_peer_certificate((SSL*)m_session);
if (!cert)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: no certificate provided by server."
" For more info visit http://nzbget.net/certificate-verification", *m_host));
return false;
}
#ifdef HAVE_X509_CHECK_HOST
// hostname verification
if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
{
char* certHost = nullptr;
// Find the position of the CN field in the Subject field of the certificate
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
if (common_name_loc >= 0)
{
// Extract the CN field
X509_NAME_ENTRY* common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
if (common_name_entry != nullptr)
{
// Convert the CN field to a C string
ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
if (common_name_asn1 != nullptr)
{
certHost = (char*)ASN1_STRING_data(common_name_asn1);
}
}
}
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
" For more info visit http://nzbget.net/certificate-verification", *m_host, certHost));
X509_free(cert);
return false;
}
#endif
X509_free(cert);
return true;
#endif /* HAVE_OPENSSL */
}
void TlsSocket::Close()
{
if (m_session)
+5 -2
View File
@@ -33,6 +33,7 @@ public:
m_certFile(certFile), m_keyFile(keyFile), m_cipher(cipher) {}
virtual ~TlsSocket();
static void Init();
static void InitOptions(const char* certStore) { m_certStore = certStore; }
bool Start();
void Close();
int Send(const char* buffer, int size);
@@ -41,7 +42,7 @@ public:
protected:
virtual void PrintError(const char* errMsg);
private:
bool m_isClient;
CString m_host;
@@ -53,12 +54,14 @@ private:
bool m_initialized = false;
bool m_connected = false;
int m_retCode;
static CString m_certStore;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TlsSocket.h
void* m_context = nullptr;
void* m_session = nullptr;
void ReportError(const char* errMsg);
void ReportError(const char* errMsg, bool suppressable = true);
bool ValidateCert();
static void Final();
friend class TlsSocketFinalizer;
+3 -3
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);
+131
View File
@@ -0,0 +1,131 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 "CommandScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
static const int COMMANDPROCESS_SUCCESS = 93;
static const int COMMANDPROCESS_ERROR = 94;
bool CommandScriptController::StartScript(const char* scriptName, const char* command,
std::unique_ptr<Options::OptEntries> modifiedOptions)
{
CommandScriptController* scriptController = new CommandScriptController();
scriptController->m_script = scriptName;
scriptController->m_command = command;
scriptController->m_logId = g_CommandScriptLog->Reset();
scriptController->m_modifiedOptions = std::move(modifiedOptions);
scriptController->SetAutoDestroy(true);
scriptController->Start();
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
return false;
}
void CommandScriptController::Run()
{
ExecuteScriptList(m_script);
}
void CommandScriptController::ExecuteScript(ScriptConfig::Script* script)
{
PrintMessage(Message::mkInfo, "Executing script %s with command %s", script->GetName(), *m_command);
SetArgs({script->GetLocation()});
BString<1024> infoName("script %s with command %s", script->GetName(), *m_command);
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
PrepareParams(script->GetName());
int exitCode = Execute();
infoName[0] = 'S'; // uppercase
SetLogPrefix(nullptr);
switch (exitCode)
{
case COMMANDPROCESS_SUCCESS:
PrintMessage(Message::mkInfo, "%s successful", *infoName);
break;
case COMMANDPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", *infoName);
break;
default:
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", *infoName);
break;
}
}
void CommandScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetEnvVar("NZBCP_COMMAND", m_command);
PrepareEnvScript(nullptr, scriptName);
}
const char* CommandScriptController::GetOptValue(const char* name, const char* value)
{
Options::OptEntry* entry = m_modifiedOptions->FindOption(name);
return entry ? entry->GetValue() : value;
}
void CommandScriptController::AddMessage(Message::EKind kind, const char * text)
{
NzbScriptController::AddMessage(kind, text);
g_CommandScriptLog->AddMessage(m_logId, kind, text);
}
int CommandScriptLog::Reset()
{
Guard guard(m_logMutex);
m_messages.clear();
return ++m_idScriptGen;
}
void CommandScriptLog::AddMessage(int scriptId, Message::EKind kind, const char * text)
{
Guard guard(m_logMutex);
// save only messages from the last started script
if (scriptId == m_idScriptGen)
{
m_messages.emplace_back(++m_idMessageGen, kind, Util::CurrentTime(), text);
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 COMMANDSCRIPT_H
#define COMMANDSCRIPT_H
#include "NzbScript.h"
#include "Log.h"
class CommandScriptController : public Thread, public NzbScriptController
{
public:
virtual void Run();
static bool StartScript(const char* scriptName, const char* command, std::unique_ptr<Options::OptEntries> modifiedOptions);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
virtual void AddMessage(Message::EKind kind, const char* text);
virtual const char* GetOptValue(const char* name, const char* value);
private:
CString m_script;
CString m_command;
int m_logId;
std::unique_ptr<Options::OptEntries> m_modifiedOptions;
void PrepareParams(const char* scriptName);
};
class CommandScriptLog
{
public:
GuardedMessageList GuardMessages() { return GuardedMessageList(&m_messages, &m_logMutex); }
int Reset();
void AddMessage(int scriptId, Message::EKind kind, const char* text);
private:
MessageList m_messages;
Mutex m_logMutex;
int m_idMessageGen;
int m_idScriptGen;
};
extern CommandScriptLog* g_CommandScriptLog;
#endif
+1 -1
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;
}
+8 -8
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;
}
}
+1 -1
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
+9 -22
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,6 +32,7 @@ static const char* QUEUE_EVENT_NAMES[] = {
"URL_COMPLETED",
"NZB_MARKED",
"NZB_ADDED",
"NZB_NAMED",
"NZB_DOWNLOADED",
"NZB_DELETED" };
@@ -112,7 +113,7 @@ void QueueScriptController::Run()
{
nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
nzbInfo->SetDeleteStatus(NzbInfo::dsBad);
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, 0, nullptr);
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, nullptr);
}
}
@@ -336,21 +337,7 @@ bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo*
return false;
}
// check queue-scripts
const char* queueScript = g_Options->GetQueueScript();
if (!Util::EmptyStr(queueScript))
{
Tokenizer tok(queueScript, ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
}
// check post-processing-scripts assigned for that nzb
// check extension scripts assigned for that nzb
for (NzbParameter& parameter : nzbInfo->GetParameters())
{
const char* varname = parameter.GetName();
@@ -368,17 +355,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();
}
}
+2 -1
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@ public:
qeUrlCompleted,
qeNzbMarked,
qeNzbAdded,
qeNzbNamed,
qeNzbDownloaded,
qeNzbDeleted // highest priority
};
+28 -1
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)
+1
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);
+8 -2
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());
+137 -70
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;
}
}
}
}
}
+12 -8
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);
};
+23 -20
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");
+2
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;
+15 -14
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
+2 -1
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
+6 -2
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 : "";
}
+3 -3
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;
+1 -1
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));
}
}
+1 -1
View File
@@ -738,7 +738,7 @@ void NCursesFrontend::PrintFilename(FileInfo * fileInfo, int row, bool selected)
BString<1024> nzbNiceName;
if (m_showNzbname)
{
nzbNiceName.Format("%s%c", fileInfo->GetNzbInfo()->GetName(), (int)PATH_SEPARATOR);
nzbNiceName.Format("%s%c", fileInfo->GetNzbInfo()->GetName(), PATH_SEPARATOR);
}
BString<1024> text("%s%i%s%s%s %s%s (%s%s)%s", Brace1, fileInfo->GetId(),
+7 -3
View File
@@ -517,6 +517,10 @@ void CommandLineParser::InitCommandLine(int argc, const char* const_argv[])
{
m_editQueueAction = DownloadQueue::eaGroupParkDelete;
}
else if (!strcasecmp(optarg, "SF"))
{
m_editQueueAction = DownloadQueue::eaGroupSortFiles;
}
else if (!strcasecmp(optarg, "C") || !strcasecmp(optarg, "K") || !strcasecmp(optarg, "CP"))
{
// switch "K" is provided for compatibility with v. 0.8.0 and can be removed in future versions
@@ -741,11 +745,11 @@ void CommandLineParser::PrintUsage(const char* com)
" S Print only server status\n"
" <RegEx> Regular expression (only with options \"FR\", \"GR\")\n"
" using POSIX Extended Regular Expression Syntax\n"
" -P, --pause [D|O|S] Pause server\n"
" -P, --pause [D|O|S] Pause server\n"
" D Pause download queue (default)\n"
" O Pause post-processor queue\n"
" S Pause scan of incoming nzb-directory\n"
" -U, --unpause [D|O|S] Unpause server\n"
" -U, --unpause [D|O|S] Unpause server\n"
" D Unpause download queue (default)\n"
" O Unpause post-processor queue\n"
" S Unpause scan of incoming nzb-directory\n"
@@ -772,7 +776,6 @@ void CommandLineParser::PrintUsage(const char* com)
" T Move to top of queue\n"
" B Move to bottom of queue\n"
" D Delete\n"
" - for files (F) and groups (G):\n"
" P Pause\n"
" U Resume (unpause)\n"
" - for groups (G):\n"
@@ -784,6 +787,7 @@ void CommandLineParser::PrintUsage(const char* com)
" CP <name> Set category and apply post-process parameters\n"
" N <name> Rename\n"
" M Merge\n"
" SF Sort inner files for optimal order\n"
" S <name> Split - create new group from selected files\n"
" O <name>=<value> Set post-process parameter\n"
" - for post-jobs (O):\n"
+1 -1
View File
@@ -373,7 +373,7 @@ bool Signature::ReadSignature()
{
hexSig[sigLen - 2] = '\0'; // trim trailing ",
}
for (; *hexSig && *(hexSig+1);)
while (*hexSig && *(hexSig+1) && output != m_signature + sizeof(m_signature))
{
uchar c1 = *hexSig++;
uchar c2 = *hexSig++;
+286 -70
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -59,10 +59,13 @@ static const char* OPTION_RESTRICTEDUSERNAME = "RestrictedUsername";
static const char* OPTION_RESTRICTEDPASSWORD = "RestrictedPassword";
static const char* OPTION_ADDUSERNAME = "AddUsername";
static const char* OPTION_ADDPASSWORD = "AddPassword";
static const char* OPTION_FORMAUTH = "FormAuth";
static const char* OPTION_SECURECONTROL = "SecureControl";
static const char* OPTION_SECUREPORT = "SecurePort";
static const char* OPTION_SECURECERT = "SecureCert";
static const char* OPTION_SECUREKEY = "SecureKey";
static const char* OPTION_CERTSTORE = "CertStore";
static const char* OPTION_CERTCHECK = "CertCheck";
static const char* OPTION_AUTHORIZEDIP = "AuthorizedIP";
static const char* OPTION_ARTICLETIMEOUT = "ArticleTimeout";
static const char* OPTION_URLTIMEOUT = "UrlTimeout";
@@ -72,8 +75,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 +92,14 @@ 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_FILENAMING = "FileNaming";
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_DIRECTRENAME = "DirectRename";
static const char* OPTION_UMASK = "UMask";
static const char* OPTION_UPDATEINTERVAL = "UpdateInterval";
static const char* OPTION_CURSESNZBNAME = "CursesNzbName";
@@ -105,7 +111,8 @@ static const char* OPTION_WRITEBUFFER = "WriteBuffer";
static const char* OPTION_NZBDIRINTERVAL = "NzbDirInterval";
static const char* OPTION_NZBDIRFILEAGE = "NzbDirFileAge";
static const char* OPTION_DISKSPACE = "DiskSpace";
static const char* OPTION_DUMPCORE = "DumpCore";
static const char* OPTION_CRASHTRACE = "CrashTrace";
static const char* OPTION_CRASHDUMP = "CrashDump";
static const char* OPTION_PARPAUSEQUEUE = "ParPauseQueue";
static const char* OPTION_SCRIPTPAUSEQUEUE = "ScriptPauseQueue";
static const char* OPTION_NZBCLEANUPDISK = "NzbCleanupDisk";
@@ -113,15 +120,17 @@ static const char* OPTION_PARTIMELIMIT = "ParTimeLimit";
static const char* OPTION_KEEPHISTORY = "KeepHistory";
static const char* OPTION_ACCURATERATE = "AccurateRate";
static const char* OPTION_UNPACK = "Unpack";
static const char* OPTION_DIRECTUNPACK = "DirectUnpack";
static const char* OPTION_UNPACKCLEANUPDISK = "UnpackCleanupDisk";
static const char* OPTION_UNRARCMD = "UnrarCmd";
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";
@@ -132,6 +141,7 @@ static const char* OPTION_SHELLOVERRIDE = "ShellOverride";
static const char* OPTION_MONTHLYQUOTA = "MonthlyQuota";
static const char* OPTION_QUOTASTARTDAY = "QuotaStartDay";
static const char* OPTION_DAILYQUOTA = "DailyQuota";
static const char* OPTION_REORDERFILES = "ReorderFiles";
// obsolete options
static const char* OPTION_POSTLOGKIND = "PostLogKind";
@@ -155,6 +165,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 };
@@ -188,10 +201,13 @@ bool Options::OptEntry::Restricted()
bool restricted = !strcasecmp(m_name, OPTION_CONTROLIP) ||
!strcasecmp(m_name, OPTION_CONTROLPORT) ||
!strcasecmp(m_name, OPTION_FORMAUTH) ||
!strcasecmp(m_name, OPTION_SECURECONTROL) ||
!strcasecmp(m_name, OPTION_SECUREPORT) ||
!strcasecmp(m_name, OPTION_SECURECERT) ||
!strcasecmp(m_name, OPTION_SECUREKEY) ||
!strcasecmp(m_name, OPTION_CERTSTORE) ||
!strcasecmp(m_name, OPTION_CERTCHECK) ||
!strcasecmp(m_name, OPTION_AUTHORIZEDIP) ||
!strcasecmp(m_name, OPTION_DAEMONUSERNAME) ||
!strcasecmp(m_name, OPTION_UMASK) ||
@@ -325,6 +341,7 @@ void Options::Init(const char* exeName, const char* configFilename, bool noConfi
return;
}
ConvertOldOptions(&m_optEntries);
InitOptions();
CheckOptions();
@@ -416,10 +433,13 @@ void Options::InitDefaults()
SetOption(OPTION_ADDUSERNAME, "");
SetOption(OPTION_ADDPASSWORD, "");
SetOption(OPTION_CONTROLPORT, "6789");
SetOption(OPTION_FORMAUTH, "no");
SetOption(OPTION_SECURECONTROL, "no");
SetOption(OPTION_SECUREPORT, "6791");
SetOption(OPTION_SECURECERT, "");
SetOption(OPTION_SECUREKEY, "");
SetOption(OPTION_CERTSTORE, "");
SetOption(OPTION_CERTCHECK, "no");
SetOption(OPTION_AUTHORIZEDIP, "");
SetOption(OPTION_ARTICLETIMEOUT, "60");
SetOption(OPTION_URLTIMEOUT, "60");
@@ -429,8 +449,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 +466,16 @@ void Options::InitDefaults()
SetOption(OPTION_PARREPAIR, "yes");
SetOption(OPTION_PARSCAN, "extended");
SetOption(OPTION_PARQUICK, "yes");
SetOption(OPTION_POSTSTRATEGY, "sequential");
SetOption(OPTION_FILENAMING, "article");
SetOption(OPTION_PARRENAME, "yes");
SetOption(OPTION_PARBUFFER, "16");
SetOption(OPTION_PARTHREADS, "1");
SetOption(OPTION_RARRENAME, "yes");
SetOption(OPTION_HEALTHCHECK, "none");
SetOption(OPTION_DIRECTRENAME, "no");
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");
@@ -465,7 +488,8 @@ void Options::InitDefaults()
SetOption(OPTION_NZBDIRINTERVAL, "5");
SetOption(OPTION_NZBDIRFILEAGE, "60");
SetOption(OPTION_DISKSPACE, "250");
SetOption(OPTION_DUMPCORE, "no");
SetOption(OPTION_CRASHTRACE, "no");
SetOption(OPTION_CRASHDUMP, "no");
SetOption(OPTION_PARPAUSEQUEUE, "no");
SetOption(OPTION_SCRIPTPAUSEQUEUE, "no");
SetOption(OPTION_NZBCLEANUPDISK, "no");
@@ -473,6 +497,7 @@ void Options::InitDefaults()
SetOption(OPTION_KEEPHISTORY, "7");
SetOption(OPTION_ACCURATERATE, "no");
SetOption(OPTION_UNPACK, "no");
SetOption(OPTION_DIRECTUNPACK, "no");
SetOption(OPTION_UNPACKCLEANUPDISK, "no");
#ifdef WIN32
SetOption(OPTION_UNRARCMD, "unrar.exe");
@@ -485,6 +510,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");
@@ -495,6 +521,7 @@ void Options::InitDefaults()
SetOption(OPTION_MONTHLYQUOTA, "0");
SetOption(OPTION_QUOTASTARTDAY, "1");
SetOption(OPTION_DAILYQUOTA, "0");
SetOption(OPTION_REORDERFILES, "no");
}
void Options::InitOptFile()
@@ -641,10 +668,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);
@@ -654,6 +678,7 @@ void Options::InitOptions()
m_addPassword = GetOption(OPTION_ADDPASSWORD);
m_secureCert = GetOption(OPTION_SECURECERT);
m_secureKey = GetOption(OPTION_SECUREKEY);
m_certStore = GetOption(OPTION_CERTSTORE);
m_authorizedIp = GetOption(OPTION_AUTHORIZEDIP);
m_lockFile = GetOption(OPTION_LOCKFILE);
m_daemonUsername = GetOption(OPTION_DAEMONUSERNAME);
@@ -663,14 +688,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 +738,8 @@ 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_directRename = (bool)ParseEnumValue(OPTION_DIRECTRENAME, BoolCount, BoolNames, BoolValues);
m_reloadQueue = (bool)ParseEnumValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, BoolValues);
m_cursesNzbName = (bool)ParseEnumValue(OPTION_CURSESNZBNAME, BoolCount, BoolNames, BoolValues);
m_cursesTime = (bool)ParseEnumValue(OPTION_CURSESTIME, BoolCount, BoolNames, BoolValues);
@@ -717,16 +747,21 @@ void Options::InitOptions()
m_crcCheck = (bool)ParseEnumValue(OPTION_CRCCHECK, BoolCount, BoolNames, BoolValues);
m_directWrite = (bool)ParseEnumValue(OPTION_DIRECTWRITE, BoolCount, BoolNames, BoolValues);
m_decode = (bool)ParseEnumValue(OPTION_DECODE, BoolCount, BoolNames, BoolValues);
m_dumpCore = (bool)ParseEnumValue(OPTION_DUMPCORE, BoolCount, BoolNames, BoolValues);
m_crashTrace = (bool)ParseEnumValue(OPTION_CRASHTRACE, BoolCount, BoolNames, BoolValues);
m_crashDump = (bool)ParseEnumValue(OPTION_CRASHDUMP, BoolCount, BoolNames, BoolValues);
m_parPauseQueue = (bool)ParseEnumValue(OPTION_PARPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_scriptPauseQueue = (bool)ParseEnumValue(OPTION_SCRIPTPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_nzbCleanupDisk = (bool)ParseEnumValue(OPTION_NZBCLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_accurateRate = (bool)ParseEnumValue(OPTION_ACCURATERATE, BoolCount, BoolNames, BoolValues);
m_formAuth = (bool)ParseEnumValue(OPTION_FORMAUTH, BoolCount, BoolNames, BoolValues);
m_secureControl = (bool)ParseEnumValue(OPTION_SECURECONTROL, BoolCount, BoolNames, BoolValues);
m_unpack = (bool)ParseEnumValue(OPTION_UNPACK, BoolCount, BoolNames, BoolValues);
m_directUnpack = (bool)ParseEnumValue(OPTION_DIRECTUNPACK, BoolCount, BoolNames, BoolValues);
m_unpackCleanupDisk = (bool)ParseEnumValue(OPTION_UNPACKCLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_unpackPauseQueue = (bool)ParseEnumValue(OPTION_UNPACKPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_urlForce = (bool)ParseEnumValue(OPTION_URLFORCE, BoolCount, BoolNames, BoolValues);
m_certCheck = (bool)ParseEnumValue(OPTION_CERTCHECK, BoolCount, BoolNames, BoolValues);
m_reorderFiles = (bool)ParseEnumValue(OPTION_REORDERFILES, BoolCount, BoolNames, BoolValues);
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
@@ -743,6 +778,16 @@ 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* FileNamingNames[] = { "auto", "article", "nzb" };
const int FileNamingValues[] = { nfAuto, nfArticle, nfNzb };
const int FileNamingCount = 4;
m_fileNaming = (EFileNaming)ParseEnumValue(OPTION_FILENAMING, FileNamingCount, FileNamingNames, FileNamingValues);
const char* HealthCheckNames[] = { "pause", "delete", "park", "none" };
const int HealthCheckValues[] = { hcPause, hcDelete, hcPark, hcNone };
const int HealthCheckCount = 4;
@@ -957,13 +1002,24 @@ 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
m_tls |= tls;
}
const char* nipversion = GetOption(BString<100>("Server%i.IpVersion", n));
int ipversion = 0;
if (nipversion)
{
const char* IpVersionNames[] = {"auto", "ipv4", "ipv6"};
const int IpVersionValues[] = {0, 4, 6};
const int IpVersionCount = 3;
ipversion = ParseEnumValue(BString<100>("Server%i.IpVersion", n), IpVersionCount, IpVersionNames, IpVersionValues);
}
const char* ncipher = GetOption(BString<100>("Server%i.Cipher", n));
const char* nconnections = GetOption(BString<100>("Server%i.Connections", n));
const char* nretention = GetOption(BString<100>("Server%i.Retention", n));
@@ -984,6 +1040,7 @@ void Options::InitServers()
m_extender->AddNewsServer(n, active, nname,
nhost,
nport ? atoi(nport) : 119,
ipversion,
nusername, npassword,
joinGroup, tls, ncipher,
nconnections ? atoi(nconnections) : 1,
@@ -1017,10 +1074,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 +1093,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 +1124,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 +1144,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 +1157,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 +1226,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 +1256,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 +1338,7 @@ bool Options::ParseTime(const char* time, int* hours, int* minutes)
if (time[0] == '*')
{
*hours = -1;
*hours = -2;
}
else
{
@@ -1463,7 +1537,8 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
!strcasecmp(p, ".password") || !strcasecmp(p, ".joingroup") ||
!strcasecmp(p, ".encryption") || !strcasecmp(p, ".connections") ||
!strcasecmp(p, ".cipher") || !strcasecmp(p, ".group") ||
!strcasecmp(p, ".retention") || !strcasecmp(p, ".optional")))
!strcasecmp(p, ".retention") || !strcasecmp(p, ".optional") ||
!strcasecmp(p, ".notes") || !strcasecmp(p, ".ipversion")))
{
return true;
}
@@ -1485,7 +1560,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 +1573,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 +1606,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 +1680,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,10 +1711,25 @@ 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";
}
if (!strcasecmp(option, "DumpCore"))
{
option = OPTION_CRASHDUMP;
}
}
void Options::CheckOptions()
@@ -1643,6 +1745,11 @@ void Options::CheckOptions()
LocateOptionSrcPos(OPTION_PARRENAME);
ConfigError("Invalid value for option \"%s\": program was compiled without parcheck-support", OPTION_PARRENAME);
}
if (m_directRename)
{
LocateOptionSrcPos(OPTION_DIRECTRENAME);
ConfigError("Invalid value for option \"%s\": program was compiled without parcheck-support", OPTION_DIRECTRENAME);
}
#endif
#ifdef DISABLE_CURSES
@@ -1659,8 +1766,43 @@ void Options::CheckOptions()
LocateOptionSrcPos(OPTION_SECURECONTROL);
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", OPTION_SECURECONTROL);
}
if (m_certCheck)
{
LocateOptionSrcPos(OPTION_CERTCHECK);
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", OPTION_CERTCHECK);
}
#endif
#ifdef HAVE_OPENSSL
#ifndef HAVE_X509_CHECK_HOST
if (m_certCheck)
{
LocateOptionSrcPos(OPTION_CERTCHECK);
ConfigWarn("TLS certificate verification (option \"%s\") is limited because the program "
"was compiled with older OpenSSL version not supporting hostname validation (at least OpenSSL 1.0.2d is required)", OPTION_CERTCHECK);
}
#endif
#endif
#ifdef HAVE_LIBGNUTLS
#if GNUTLS_VERSION_NUMBER < 0x030104
if (m_certCheck)
{
LocateOptionSrcPos(OPTION_CERTCHECK);
ConfigWarn("TLS certificate verification (option \"%s\") is disabled because the program "
"was compiled with older GnuTLS version not supporting certificate validation (at least GnuTLS 3.1.4 is required)", OPTION_CERTCHECK);
}
#endif
#endif
if (m_certCheck && m_certStore.Empty())
{
LocateOptionSrcPos(OPTION_CERTCHECK);
ConfigError("Option \"%s\" requires proper configuration of option \"%s\"", OPTION_CERTCHECK, OPTION_CERTSTORE);
m_certCheck = false;
}
if (!m_decode)
{
m_directWrite = false;
@@ -1695,7 +1837,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 +1846,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;
};
+60 -22
View File
@@ -85,6 +85,19 @@ public:
scDeactivateServer,
scFetchFeed
};
enum EPostStrategy
{
ppSequential,
ppBalanced,
ppAggressive,
ppRocket
};
enum EFileNaming
{
nfAuto,
nfArticle,
nfNzb
};
class OptEntry
{
@@ -126,19 +139,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;
};
@@ -154,12 +167,12 @@ public:
{
public:
virtual void AddNewsServer(int id, bool active, const char* name, const char* host,
int port, const char* user, const char* pass, bool joinGroup,
int port, int ipVersion, const char* user, const char* pass, bool joinGroup,
bool tls, const char* cipher, int maxConnections, int retention,
int level, int group, bool optional) = 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 +183,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 +216,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; }
@@ -213,10 +231,13 @@ public:
const char* GetAddUsername() { return m_addUsername; }
const char* GetAddPassword() { return m_addPassword; }
int GetControlPort() { return m_controlPort; }
bool GetFormAuth() { return m_formAuth; }
bool GetSecureControl() { return m_secureControl; }
int GetSecurePort() { return m_securePort; }
const char* GetSecureCert() { return m_secureCert; }
const char* GetSecureKey() { return m_secureKey; }
const char* GetCertStore() { return m_certStore; }
bool GetCertCheck() { return m_certCheck; }
const char* GetAuthorizedIp() { return m_authorizedIp; }
const char* GetLockFile() { return m_lockFile; }
const char* GetDaemonUsername() { return m_daemonUsername; }
@@ -231,15 +252,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; }
@@ -252,7 +272,8 @@ public:
int GetNzbDirFileAge() { return m_nzbDirFileAge; }
int GetDiskSpace() { return m_diskSpace; }
bool GetTls() { return m_tls; }
bool GetDumpCore() { return m_dumpCore; }
bool GetCrashTrace() { return m_crashTrace; }
bool GetCrashDump() { return m_crashDump; }
bool GetParPauseQueue() { return m_parPauseQueue; }
bool GetScriptPauseQueue() { return m_scriptPauseQueue; }
bool GetNzbCleanupDisk() { return m_nzbCleanupDisk; }
@@ -260,6 +281,7 @@ public:
int GetKeepHistory() { return m_keepHistory; }
bool GetAccurateRate() { return m_accurateRate; }
bool GetUnpack() { return m_unpack; }
bool GetDirectUnpack() { return m_directUnpack; }
bool GetUnpackCleanupDisk() { return m_unpackCleanupDisk; }
const char* GetUnrarCmd() { return m_unrarCmd; }
const char* GetSevenZipCmd() { return m_sevenZipCmd; }
@@ -267,6 +289,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; }
@@ -277,6 +300,9 @@ public:
int GetMonthlyQuota() { return m_monthlyQuota; }
int GetQuotaStartDay() { return m_quotaStartDay; }
int GetDailyQuota() { return m_dailyQuota; }
bool GetDirectRename() { return m_directRename; }
bool GetReorderFiles() { return m_reorderFiles; }
EFileNaming GetFileNaming() { return m_fileNaming; }
Categories* GetCategories() { return &m_categories; }
Category* FindCategory(const char* name, bool searchAliases) { return m_categories.FindCategory(name, searchAliases); }
@@ -343,8 +369,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;
@@ -355,11 +383,14 @@ private:
CString m_restrictedPassword;
CString m_addUsername;
CString m_addPassword;
bool m_formAuth = false;
int m_controlPort = 0;
bool m_secureControl = false;
int m_securePort = 0;
CString m_secureCert;
CString m_secureKey;
CString m_certStore;
bool m_certCheck = false;
CString m_authorizedIp;
CString m_lockFile;
CString m_daemonUsername;
@@ -374,15 +405,15 @@ 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;
bool m_directRename = 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;
@@ -395,7 +426,8 @@ private:
int m_nzbDirFileAge = 0;
int m_diskSpace = 0;
bool m_tls = false;
bool m_dumpCore = false;
bool m_crashTrace = false;
bool m_crashDump = false;
bool m_parPauseQueue = false;
bool m_scriptPauseQueue = false;
bool m_nzbCleanupDisk = false;
@@ -403,6 +435,7 @@ private:
int m_keepHistory = 0;
bool m_accurateRate = false;
bool m_unpack = false;
bool m_directUnpack = false;
bool m_unpackCleanupDisk = false;
CString m_unrarCmd;
CString m_sevenZipCmd;
@@ -410,6 +443,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;
@@ -420,6 +454,8 @@ private:
int m_monthlyQuota = 0;
int m_quotaStartDay = 0;
int m_dailyQuota = 0;
bool m_reorderFiles = false;
EFileNaming m_fileNaming = nfArticle;
// Current state
bool m_serverMode = false;
@@ -461,7 +497,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;
+12 -8
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
@@ -93,7 +93,10 @@ void Scheduler::CheckTasks()
for (Task* task : &m_taskList)
{
task->m_lastExecuted = 0;
if (task->m_hours != Task::STARTUP_TASK)
{
task->m_lastExecuted = 0;
}
}
}
@@ -133,9 +136,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 +164,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 +194,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 +208,7 @@ void Scheduler::ExecuteTask(Task* task)
break;
case scFetchFeed:
if (m_executeProcess)
if (executeProcess)
{
FetchFeed(task->m_param);
break;
+2 -2
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;
+16 -10
View File
@@ -147,17 +147,20 @@ LONG __stdcall ExceptionFilter(EXCEPTION_POINTERS* exPtrs)
#ifdef DEBUG
PrintBacktrace(exPtrs->ContextRecord);
#else
info("Detailed exception information can be printed by debug version of NZBGet (available from download page)");
#endif
ExitProcess(-1);
return EXCEPTION_CONTINUE_SEARCH;
#else
info("Detailed crash information can be printed by debug version of NZBGet."
" For more info visit http://nzbget.net/crash-dump");
return EXCEPTION_EXECUTE_HANDLER;
#endif
}
void InstallErrorHandler()
{
SetUnhandledExceptionFilter(ExceptionFilter);
if (g_Options->GetCrashTrace())
{
SetUnhandledExceptionFilter(ExceptionFilter);
}
}
#else
@@ -171,7 +174,7 @@ std::vector<sighandler> SignalProcList;
/**
* activates the creation of core-files
*/
void EnableDumpCore()
void EnableCoreDump()
{
rlimit rlim;
rlim.rlim_cur= RLIM_INFINITY;
@@ -248,9 +251,9 @@ void SignalProc(int signum)
void InstallErrorHandler()
{
#ifdef HAVE_SYS_PRCTL_H
if (g_Options->GetDumpCore())
if (g_Options->GetCrashDump())
{
EnableDumpCore();
EnableCoreDump();
}
#endif
@@ -258,7 +261,10 @@ void InstallErrorHandler()
signal(SIGTERM, SignalProc);
signal(SIGPIPE, SIG_IGN);
#ifdef DEBUG
signal(SIGSEGV, SignalProc);
if (g_Options->GetCrashTrace())
{
signal(SIGSEGV, SignalProc);
}
#endif
#ifdef SIGCHLD_HANDLER
// it could be necessary on some systems to activate a handler for SIGCHLD
+60 -38
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -39,7 +39,6 @@
#include "PrePostProcessor.h"
#include "HistoryCoordinator.h"
#include "DupeCoordinator.h"
#include "ParChecker.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "FeedCoordinator.h"
@@ -52,6 +51,7 @@
#include "Util.h"
#include "FileSystem.h"
#include "StackTrace.h"
#include "CommandScript.h"
#ifdef WIN32
#include "WinService.h"
#include "WinConsole.h"
@@ -60,6 +60,9 @@
#ifdef ENABLE_TESTS
#include "TestMain.h"
#endif
#ifndef DISABLE_NSERV
#include "NServMain.h"
#endif
// Prototypes
void RunMain();
@@ -82,6 +85,7 @@ ArticleCache* g_ArticleCache;
QueueScriptCoordinator* g_QueueScriptCoordinator;
ServiceCoordinator* g_ServiceCoordinator;
ScriptConfig* g_ScriptConfig;
CommandScriptLog* g_CommandScriptLog;
#ifdef WIN32
WinConsole* g_WinConsole;
#endif
@@ -127,6 +131,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 +152,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;
}
@@ -160,7 +175,7 @@ public:
// Options::Extender
virtual void AddNewsServer(int id, bool active, const char* name, const char* host,
int port, const char* user, const char* pass, bool joinGroup,
int port, int ipVersion, const char* user, const char* pass, bool joinGroup,
bool tls, const char* cipher, int maxConnections, int retention,
int level, int group, bool optional);
virtual void AddFeed(int id, const char* name, const char* url, int interval,
@@ -191,6 +206,7 @@ private:
std::unique_ptr<QueueScriptCoordinator> m_queueScriptCoordinator;
std::unique_ptr<ServiceCoordinator> m_serviceCoordinator;
std::unique_ptr<ScriptConfig> m_scriptConfig;
std::unique_ptr<CommandScriptLog> m_commandScriptLog;
#ifdef WIN32
std::unique_ptr<WinConsole> m_winConsole;
#endif
@@ -266,6 +282,9 @@ void NZBGet::Init()
m_scanner->InitOptions();
m_queueScriptCoordinator->InitOptions();
#ifndef DISABLE_TLS
TlsSocket::InitOptions(g_Options->GetCertCheck() ? g_Options->GetCertStore() : nullptr);
#endif
if (m_commandLineParser->GetDaemonMode())
{
@@ -351,6 +370,9 @@ void NZBGet::CreateGlobals()
m_scriptConfig = std::make_unique<ScriptConfig>();
g_ScriptConfig = m_scriptConfig.get();
m_commandScriptLog = std::make_unique<CommandScriptLog>();
g_CommandScriptLog = m_commandScriptLog.get();
m_scheduler = std::make_unique<Scheduler>();
m_diskService = std::make_unique<DiskService>();
@@ -368,7 +390,7 @@ void NZBGet::BootConfig()
if (m_commandLineParser->GetPrintUsage() || m_commandLineParser->GetErrors() || g_ArgumentCount <= 1)
{
m_commandLineParser->PrintUsage(((const char**)(*g_Arguments))[0]);
exit(0);
exit(m_commandLineParser->GetPrintUsage() ? 0 : 1);
}
debug("Reading options");
@@ -394,7 +416,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();
}
@@ -423,6 +445,7 @@ void NZBGet::Cleanup()
g_QueueScriptCoordinator = nullptr;
g_Maintenance = nullptr;
g_StatMeter = nullptr;
g_CommandScriptLog = nullptr;
#ifdef WIN32
g_WinConsole = nullptr;
#endif
@@ -680,106 +703,109 @@ void NZBGet::Run(bool reload)
void NZBGet::ProcessClientRequest()
{
RemoteClient Client;
bool ok = false;
switch (m_commandLineParser->GetClientOperation())
{
case CommandLineParser::opClientRequestListFiles:
Client.RequestServerList(true, false, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
ok = Client.RequestServerList(true, false, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
break;
case CommandLineParser::opClientRequestListGroups:
Client.RequestServerList(false, true, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
ok = Client.RequestServerList(false, true, m_commandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? m_commandLineParser->GetEditQueueText() : nullptr);
break;
case CommandLineParser::opClientRequestListStatus:
Client.RequestServerList(false, false, nullptr);
ok = Client.RequestServerList(false, false, nullptr);
break;
case CommandLineParser::opClientRequestDownloadPause:
Client.RequestServerPauseUnpause(true, rpDownload);
ok = Client.RequestServerPauseUnpause(true, rpDownload);
break;
case CommandLineParser::opClientRequestDownloadUnpause:
Client.RequestServerPauseUnpause(false, rpDownload);
ok = Client.RequestServerPauseUnpause(false, rpDownload);
break;
case CommandLineParser::opClientRequestSetRate:
Client.RequestServerSetDownloadRate(m_commandLineParser->GetSetRate());
ok = Client.RequestServerSetDownloadRate(m_commandLineParser->GetSetRate());
break;
case CommandLineParser::opClientRequestDumpDebug:
Client.RequestServerDumpDebug();
ok = Client.RequestServerDumpDebug();
break;
case CommandLineParser::opClientRequestEditQueue:
Client.RequestServerEditQueue((DownloadQueue::EEditAction)m_commandLineParser->GetEditQueueAction(),
ok = Client.RequestServerEditQueue((DownloadQueue::EEditAction)m_commandLineParser->GetEditQueueAction(),
m_commandLineParser->GetEditQueueOffset(), m_commandLineParser->GetEditQueueText(),
m_commandLineParser->GetEditQueueIdList(), m_commandLineParser->GetEditQueueNameList(),
(ERemoteMatchMode)m_commandLineParser->GetMatchMode());
break;
case CommandLineParser::opClientRequestLog:
Client.RequestServerLog(m_commandLineParser->GetLogLines());
ok = Client.RequestServerLog(m_commandLineParser->GetLogLines());
break;
case CommandLineParser::opClientRequestShutdown:
Client.RequestServerShutdown();
ok = Client.RequestServerShutdown();
break;
case CommandLineParser::opClientRequestReload:
Client.RequestServerReload();
ok = Client.RequestServerReload();
break;
case CommandLineParser::opClientRequestDownload:
Client.RequestServerDownload(m_commandLineParser->GetAddNzbFilename(), m_commandLineParser->GetArgFilename(),
ok = Client.RequestServerDownload(m_commandLineParser->GetAddNzbFilename(), m_commandLineParser->GetArgFilename(),
m_commandLineParser->GetAddCategory(), m_commandLineParser->GetAddTop(), m_commandLineParser->GetAddPaused(), m_commandLineParser->GetAddPriority(),
m_commandLineParser->GetAddDupeKey(), m_commandLineParser->GetAddDupeMode(), m_commandLineParser->GetAddDupeScore());
break;
case CommandLineParser::opClientRequestVersion:
Client.RequestServerVersion();
ok = Client.RequestServerVersion();
break;
case CommandLineParser::opClientRequestPostQueue:
Client.RequestPostQueue();
ok = Client.RequestPostQueue();
break;
case CommandLineParser::opClientRequestWriteLog:
Client.RequestWriteLog(m_commandLineParser->GetWriteLogKind(), m_commandLineParser->GetLastArg());
ok = Client.RequestWriteLog(m_commandLineParser->GetWriteLogKind(), m_commandLineParser->GetLastArg());
break;
case CommandLineParser::opClientRequestScanAsync:
Client.RequestScan(false);
ok = Client.RequestScan(false);
break;
case CommandLineParser::opClientRequestScanSync:
Client.RequestScan(true);
ok = Client.RequestScan(true);
break;
case CommandLineParser::opClientRequestPostPause:
Client.RequestServerPauseUnpause(true, rpPostProcess);
ok = Client.RequestServerPauseUnpause(true, rpPostProcess);
break;
case CommandLineParser::opClientRequestPostUnpause:
Client.RequestServerPauseUnpause(false, rpPostProcess);
ok = Client.RequestServerPauseUnpause(false, rpPostProcess);
break;
case CommandLineParser::opClientRequestScanPause:
Client.RequestServerPauseUnpause(true, rpScan);
ok = Client.RequestServerPauseUnpause(true, rpScan);
break;
case CommandLineParser::opClientRequestScanUnpause:
Client.RequestServerPauseUnpause(false, rpScan);
ok = Client.RequestServerPauseUnpause(false, rpScan);
break;
case CommandLineParser::opClientRequestHistory:
case CommandLineParser::opClientRequestHistoryAll:
Client.RequestHistory(m_commandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
ok = Client.RequestHistory(m_commandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
break;
case CommandLineParser::opClientNoOperation:
break;
return;
}
exit(ok ? 0 : 1);
}
void NZBGet::ProcessWebGet()
@@ -865,16 +891,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;
@@ -928,10 +950,10 @@ void NZBGet::Daemonize()
#endif
void NZBGet::AddNewsServer(int id, bool active, const char* name, const char* host,
int port, const char* user, const char* pass, bool joinGroup, bool tls,
int port, int ipVersion, const char* user, const char* pass, bool joinGroup, bool tls,
const char* cipher, int maxConnections, int retention, int level, int group, bool optional)
{
m_serverPool->AddServer(std::make_unique<NewsServer>(id, active, name, host, port, user, pass, joinGroup,
m_serverPool->AddServer(std::make_unique<NewsServer>(id, active, name, host, port, ipVersion, user, pass, joinGroup,
tls, cipher, maxConnections, retention, level, group, optional));
}
+13
View File
@@ -40,6 +40,9 @@
//#define HAVE_LIBGNUTLS
#endif
/* Define to 1 if OpenSSL supports function "X509_check_host". */
#define HAVE_X509_CHECK_HOST 1
/* Define to the name of macro which returns the name of function being
compiled */
#define FUNCTION_MACRO_NAME __FUNCTION__
@@ -196,6 +199,7 @@ using namespace MSXML;
#include <iostream>
#include <fstream>
#include <memory>
#include <functional>
#ifdef HAVE_LIBGNUTLS
#ifdef WIN32
@@ -204,12 +208,16 @@ typedef SSIZE_T ssize_t;
typedef int pid_t;
#endif
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#if GNUTLS_VERSION_NUMBER <= 0x020b00
#define NEED_GCRYPT_LOCKING
#endif
#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
@@ -218,6 +226,7 @@ typedef int pid_t;
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#endif /* HAVE_OPENSSL */
#ifdef HAVE_REGEX_H
@@ -318,12 +327,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;
+14 -11
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());
@@ -296,6 +296,11 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
m_writingStarted = false;
m_articleInfo->SetCrc(0);
if (m_contentAnalyzer)
{
m_contentAnalyzer->Reset();
}
if (m_connection->GetNewsServer()->GetJoinGroup())
{
// change group
@@ -316,14 +321,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
}
// retrieve article
for (int retry = 3; retry > 0; retry--)
{
response = m_connection->Request(BString<1024>("ARTICLE %s\r\n", m_articleInfo->GetMessageId()));
if ((response && !strncmp(response, "2", 1)) || m_connection->GetAuthError())
{
break;
}
}
response = m_connection->Request(BString<1024>("ARTICLE %s\r\n", m_articleInfo->GetMessageId()));
status = CheckResponse(response, "could not fetch article");
if (status != adFinished)
@@ -548,6 +546,11 @@ bool ArticleDownloader::Write(char* line, int len)
bool ok = len == 0 || m_articleWriter.Write(line, len);
if (m_contentAnalyzer)
{
m_contentAnalyzer->Append(line, len);
}
return ok;
}
+12
View File
@@ -29,6 +29,15 @@
#include "NntpConnection.h"
#include "Decoder.h"
#include "ArticleWriter.h"
#include "Util.h"
class ArticleContentAnalyzer
{
public:
virtual ~ArticleContentAnalyzer() {};
virtual void Reset() = 0;
virtual void Append(const void* buffer, int len) = 0;
};
class ArticleDownloader : public Thread, public Subject
{
@@ -77,6 +86,8 @@ public:
void SetConnection(NntpConnection* connection) { m_connection = connection; }
void CompleteFileParts() { m_articleWriter.CompleteFileParts(); }
int GetDownloadedSize() { return m_downloadedSize; }
void SetContentAnalyzer(std::unique_ptr<ArticleContentAnalyzer> contentAnalyzer) { m_contentAnalyzer = std::move(contentAnalyzer); }
ArticleContentAnalyzer* GetContentAnalyzer() { return m_contentAnalyzer.get(); }
void LogDebugInfo();
@@ -97,6 +108,7 @@ private:
ServerStatList m_serverStats;
bool m_writingStarted;
int m_downloadedSize = 0;
std::unique_ptr<ArticleContentAnalyzer> m_contentAnalyzer;
EStatus Download();
EStatus DecodeCheck();
+42 -20
View File
@@ -84,7 +84,7 @@ bool ArticleWriter::Start(Decoder::EFormat format, const char* filename, int64 f
}
if (!outputInitialized && filename &&
FileSystem::FileExists(m_fileInfo->GetNzbInfo()->GetDestDir(), filename))
FileSystem::FileExists(BString<1024>("%s%c%s", m_fileInfo->GetNzbInfo()->GetDestDir(), PATH_SEPARATOR, filename)))
{
m_duplicate = true;
return false;
@@ -275,7 +275,7 @@ void ArticleWriter::BuildOutputFilename()
else
{
filename.Format("%s%c%i.out.tmp", m_fileInfo->GetNzbInfo()->GetDestDir(),
(int)PATH_SEPARATOR, m_fileInfo->GetId());
PATH_SEPARATOR, m_fileInfo->GetId());
m_fileInfo->SetOutputFilename(filename);
}
@@ -292,14 +292,16 @@ void ArticleWriter::CompleteFileParts()
BString<1024> nzbName;
BString<1024> nzbDestDir;
BString<1024> filename;
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
nzbName = m_fileInfo->GetNzbInfo()->GetName();
nzbDestDir = m_fileInfo->GetNzbInfo()->GetDestDir();
filename = m_fileInfo->GetFilename();
}
BString<1024> infoFilename("%s%c%s", *nzbName, (int)PATH_SEPARATOR, m_fileInfo->GetFilename());
BString<1024> infoFilename("%s%c%s", *nzbName, PATH_SEPARATOR, *filename);
bool cached = m_fileInfo->GetCachedArticles() > 0;
@@ -332,11 +334,11 @@ void ArticleWriter::CompleteFileParts()
CString ofn;
if (m_fileInfo->GetForceDirectWrite())
{
ofn.Format("%s%c%s", *nzbDestDir, PATH_SEPARATOR, m_fileInfo->GetFilename());
ofn.Format("%s%c%s", *nzbDestDir, PATH_SEPARATOR, *filename);
}
else
{
ofn = FileSystem::MakeUniqueFilename(nzbDestDir, m_fileInfo->GetFilename());
ofn = FileSystem::MakeUniqueFilename(nzbDestDir, *filename);
}
DiskFile outfile;
@@ -436,14 +438,14 @@ void ArticleWriter::CompleteFileParts()
m_fileInfo->SetFailedArticles(m_fileInfo->GetFailedArticles() + 1);
m_fileInfo->SetSuccessArticles(m_fileInfo->GetSuccessArticles() - 1);
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not find file %s for %s%c%s [%i/%i]",
pa->GetResultFilename(), *nzbName, (int)PATH_SEPARATOR, m_fileInfo->GetFilename(),
pa->GetPartNumber(), (int)m_fileInfo->GetArticles()->size());
"Could not find file %s for %s [%i/%i]",
pa->GetResultFilename(), *infoFilename, pa->GetPartNumber(),
(int)m_fileInfo->GetArticles()->size());
}
}
else if (!g_Options->GetDecode())
{
BString<1024> dstFileName("%s%c%03i", *ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
BString<1024> dstFileName("%s%c%03i", *ofn, PATH_SEPARATOR, pa->GetPartNumber());
if (!FileSystem::MoveFile(pa->GetResultFilename(), dstFileName))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
@@ -523,11 +525,11 @@ void ArticleWriter::CompleteFileParts()
if (g_Options->GetBrokenLog())
{
BString<1024> brokenLogName("%s%c_brokenlog.txt", *nzbDestDir, (int)PATH_SEPARATOR);
BString<1024> brokenLogName("%s%c_brokenlog.txt", *nzbDestDir, PATH_SEPARATOR);
DiskFile file;
if (file.Open(brokenLogName, DiskFile::omAppend))
{
file.Print("%s (%i/%i)%s", m_fileInfo->GetFilename(), m_fileInfo->GetSuccessArticles(),
file.Print("%s (%i/%i)%s", *filename, m_fileInfo->GetSuccessArticles(),
m_fileInfo->GetTotalArticles(), LINE_ENDING);
file.Close();
}
@@ -538,11 +540,25 @@ void ArticleWriter::CompleteFileParts()
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkInfo, "Partially downloaded %s", *infoFilename);
}
m_fileInfo->SetCrc(crc);
m_fileInfo->SetOutputFilename(ofn);
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_fileInfo->SetCrc(crc);
m_fileInfo->SetOutputFilename(ofn);
if (strcmp(m_fileInfo->GetFilename(), filename))
{
// file was renamed during completion, need to move the file
ofn = FileSystem::MakeUniqueFilename(nzbDestDir, m_fileInfo->GetFilename());
if (!FileSystem::MoveFile(m_fileInfo->GetOutputFilename(), ofn))
{
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not rename file %s to %s: %s", m_fileInfo->GetOutputFilename(),
*ofn, *FileSystem::GetLastErrorMessage());
}
m_fileInfo->SetOutputFilename(ofn);
}
if (strcmp(m_fileInfo->GetNzbInfo()->GetDestDir(), nzbDestDir))
{
// destination directory was changed during completion, need to move the file
@@ -593,6 +609,9 @@ void ArticleWriter::FlushCache()
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not open file %s: %s", m_fileInfo->GetOutputFilename(),
*FileSystem::GetLastErrorMessage());
// prevent multiple error messages
pa->DiscardSegment();
flushedArticles++;
break;
}
needBufFile = true;
@@ -608,6 +627,9 @@ void ArticleWriter::FlushCache()
m_fileInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Could not create file %s: %s", *destFile,
*FileSystem::GetLastErrorMessage());
// prevent multiple error messages
pa->DiscardSegment();
flushedArticles++;
break;
}
needBufFile = true;
@@ -674,14 +696,14 @@ bool ArticleWriter::MoveCompletedFiles(NzbInfo* nzbInfo, const char* oldDestDir)
// move already downloaded files to new destination
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
BString<1024> oldFileName("%s%c%s", oldDestDir, (int)PATH_SEPARATOR, completedFile.GetFileName());
BString<1024> newFileName("%s%c%s", nzbInfo->GetDestDir(), (int)PATH_SEPARATOR, completedFile.GetFileName());
BString<1024> oldFileName("%s%c%s", oldDestDir, PATH_SEPARATOR, completedFile.GetFilename());
BString<1024> newFileName("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, completedFile.GetFilename());
// check if file was not moved already
if (strcmp(oldFileName, newFileName))
{
// prevent overwriting of existing files
newFileName = FileSystem::MakeUniqueFilename(nzbInfo->GetDestDir(), completedFile.GetFileName());
newFileName = FileSystem::MakeUniqueFilename(nzbInfo->GetDestDir(), completedFile.GetFilename());
detail("Moving file %s to %s", *oldFileName, *newFileName);
if (!FileSystem::MoveFile(oldFileName, newFileName))
@@ -695,10 +717,10 @@ bool ArticleWriter::MoveCompletedFiles(NzbInfo* nzbInfo, const char* oldDestDir)
// move brokenlog.txt
if (g_Options->GetBrokenLog())
{
BString<1024> oldBrokenLogName("%s%c_brokenlog.txt", oldDestDir, (int)PATH_SEPARATOR);
BString<1024> oldBrokenLogName("%s%c_brokenlog.txt", oldDestDir, PATH_SEPARATOR);
if (FileSystem::FileExists(oldBrokenLogName))
{
BString<1024> brokenLogName("%s%c_brokenlog.txt", nzbInfo->GetDestDir(), (int)PATH_SEPARATOR);
BString<1024> brokenLogName("%s%c_brokenlog.txt", nzbInfo->GetDestDir(), PATH_SEPARATOR);
detail("Moving file %s to %s", *oldBrokenLogName, *brokenLogName);
if (FileSystem::FileExists(brokenLogName))
@@ -871,7 +893,7 @@ bool ArticleCache::CheckFlush(bool flushEverything)
if (fileInfo->GetCachedArticles() > 0 && (fileInfo->GetActiveDownloads() == 0 || flushEverything))
{
m_fileInfo = fileInfo;
infoName.Format("%s%c%s", m_fileInfo->GetNzbInfo()->GetName(), (int)PATH_SEPARATOR, m_fileInfo->GetFilename());
infoName.Format("%s%c%s", m_fileInfo->GetNzbInfo()->GetName(), PATH_SEPARATOR, m_fileInfo->GetFilename());
break;
}
}
+2 -2
View File
@@ -22,10 +22,10 @@
#include "nzbget.h"
#include "NewsServer.h"
NewsServer::NewsServer(int id, bool active, const char* name, const char* host, int port,
NewsServer::NewsServer(int id, bool active, const char* name, const char* host, int port, int ipVersion,
const char* user, const char* pass, bool joinGroup, bool tls, const char* cipher,
int maxConnections, int retention, int level, int group, bool optional) :
m_id(id), m_active(active), m_port(port), m_level(level), m_normLevel(level),
m_id(id), m_active(active), m_port(port), m_ipVersion(ipVersion), m_level(level), m_normLevel(level),
m_group(group), m_maxConnections(maxConnections), m_joinGroup(joinGroup), m_tls(tls),
m_name(name), m_host(host ? host : ""), m_user(user ? user : ""), m_password(pass ? pass : ""),
m_cipher(cipher ? cipher : ""), m_retention(retention), m_optional(optional)
+3 -1
View File
@@ -32,7 +32,7 @@
class NewsServer
{
public:
NewsServer(int id, bool active, const char* name, const char* host, int port,
NewsServer(int id, bool active, const char* name, const char* host, int port, int ipVersion,
const char* user, const char* pass, bool joinGroup,
bool tls, const char* cipher, int maxConnections, int retention,
int level, int group, bool optional);
@@ -45,6 +45,7 @@ public:
int GetGroup() { return m_group; }
const char* GetHost() { return m_host; }
int GetPort() { return m_port; }
int GetIpVersion() { return m_ipVersion; }
const char* GetUser() { return m_user; }
const char* GetPassword() { return m_password; }
int GetMaxConnections() { return m_maxConnections; }
@@ -67,6 +68,7 @@ private:
int m_group;
CString m_host;
int m_port;
int m_ipVersion;
CString m_user;
CString m_password;
int m_maxConnections;
+4 -1
View File
@@ -27,10 +27,13 @@
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NntpConnection::NntpConnection(NewsServer* newsServer) : Connection(newsServer->GetHost(), newsServer->GetPort(), newsServer->GetTls()), m_newsServer(newsServer)
NntpConnection::NntpConnection(NewsServer* newsServer) :
Connection(newsServer->GetHost(), newsServer->GetPort(), newsServer->GetTls()), m_newsServer(newsServer)
{
m_lineBuf.Reserve(CONNECTION_LINEBUFFER_SIZE);
SetCipher(newsServer->GetCipher());
SetIPVersion(newsServer->GetIpVersion() == 4 ? Connection::ipV4 :
newsServer->GetIpVersion() == 6 ? Connection::ipV6 : Connection::ipAuto);
}
const char* NntpConnection::Request(const char* req)
+146
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
}
+48
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
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
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
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
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
+131
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");
}
+43
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
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
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
-2
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);
}
+546
View File
@@ -0,0 +1,546 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 "DirectUnpack.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
#include "Options.h"
void DirectUnpack::StartJob(NzbInfo* nzbInfo)
{
DirectUnpack* directUnpack = new DirectUnpack();
directUnpack->m_nzbId = nzbInfo->GetId();
directUnpack->SetAutoDestroy(true);
nzbInfo->SetUnpackThread(directUnpack);
nzbInfo->SetDirectUnpackStatus(NzbInfo::nsRunning);
directUnpack->Start();
}
void DirectUnpack::Run()
{
debug("Entering DirectUnpack-loop for %i", m_nzbId);
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
if (!nzbInfo)
{
debug("Could not find NzbInfo for %i", m_nzbId);
return;
}
m_name = nzbInfo->GetName();
m_destDir = nzbInfo->GetDestDir();
m_finalDir = nzbInfo->BuildFinalDirName();
NzbParameter* parameter = nzbInfo->GetParameters()->Find("*Unpack:Password", false);
if (parameter)
{
m_password = parameter->GetValue();
}
}
m_infoName.Format("direct unpack for %s", *m_name);
m_infoNameUp.Format("Direct unpack for %s", *m_name); // first letter in upper case
FindArchiveFiles();
SetInfoName(m_infoName);
SetWorkingDir(m_destDir);
while (!IsStopped())
{
CString archive;
{
Guard guard(m_volumeMutex);
if (!m_archives.empty())
{
archive = std::move(m_archives.front());
m_archives.pop_front();
}
}
if (archive)
{
if (!m_processed)
{
PrintMessage(Message::mkInfo, "Directly unpacking %s", *m_name);
Cleanup();
CreateUnpackDir();
m_processed = true;
}
ExecuteUnrar(archive);
if (!m_unpackOk)
{
break;
}
}
else
{
if (m_nzbCompleted)
{
break;
}
usleep(100 * 1000);
}
}
if (!m_unpackOk)
{
Cleanup();
}
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
if (!nzbInfo || nzbInfo->GetUnpackThread() != this)
{
debug("Could not find NzbInfo for %s", *m_infoName);
return;
}
nzbInfo->SetUnpackThread(nullptr);
nzbInfo->SetDirectUnpackStatus(m_processed ? m_unpackOk && !IsStopped() ? NzbInfo::nsSuccess : NzbInfo::nsFailure : NzbInfo::nsNone);
if (nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsSuccess)
{
nzbInfo->AddMessage(Message::mkInfo, BString<1024>("%s successful", *m_infoNameUp));
}
else if (nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsFailure)
{
nzbInfo->AddMessage(Message::mkWarning, BString<1024>("%s failed", *m_infoNameUp));
}
if (nzbInfo->GetPostInfo() && nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsSuccess)
{
std::move(m_extractedArchives.begin(), m_extractedArchives.end(),
std::back_inserter(*nzbInfo->GetPostInfo()->GetExtractedArchives()));
}
AddExtraTime(nzbInfo);
if (nzbInfo->GetPostInfo())
{
nzbInfo->GetPostInfo()->SetWorking(false);
}
}
debug("Exiting DirectUnpack-loop for %i", m_nzbId);
}
void DirectUnpack::ExecuteUnrar(const char* archiveName)
{
// Format:
// unrar x -y -p- -o+ -vp <archive.part0001.rar> <dest-dir>/_unpack/
ParamList params;
if (!PrepareCmdParams(g_Options->GetUnrarCmd(), &params, "unrar"))
{
return;
}
if (!params.Exists("x") && !params.Exists("e"))
{
params.emplace_back("x");
}
params.emplace_back("-y");
if (!m_password.Empty())
{
params.push_back(CString::FormatStr("-p%s", *m_password));
}
else
{
params.emplace_back("-p-");
}
if (!params.Exists("-o+") && !params.Exists("-o-"))
{
params.emplace_back("-o+");
}
params.emplace_back("-vp");
params.emplace_back(archiveName);
m_unpackExtendedDir = FileSystem::MakeExtendedPath(m_unpackDir, true);
params.push_back(*BString<1024>("%s%c", *m_unpackExtendedDir, PATH_SEPARATOR));
SetArgs(std::move(params));
SetLogPrefix("Unrar");
ResetEnv();
m_allOkMessageReceived = false;
SetNeedWrite(true);
m_unpacking = true;
int exitCode = Execute();
m_unpacking = false;
SetLogPrefix(nullptr);
m_unpackOk = exitCode == 0 && m_allOkMessageReceived && !GetTerminated();
if (!m_unpackOk && exitCode > 0)
{
PrintMessage(Message::mkError, "Unrar error code: %i", exitCode);
}
}
bool DirectUnpack::PrepareCmdParams(const char* command, ParamList* params, const char* infoName)
{
if (FileSystem::FileExists(command))
{
params->emplace_back(command);
return true;
}
std::vector<CString> cmdArgs = Util::SplitCommandLine(command);
if (cmdArgs.empty())
{
PrintMessage(Message::mkError, "Could not start %s, failed to parse command line: %s", infoName, command);
m_unpackOk = false;
return false;
}
std::move(cmdArgs.begin(), cmdArgs.end(), std::back_inserter(*params));
return true;
}
void DirectUnpack::CreateUnpackDir()
{
bool useInterDir = !Util::EmptyStr(g_Options->GetInterDir()) &&
!strncmp(m_destDir, g_Options->GetInterDir(), strlen(g_Options->GetInterDir())) &&
m_destDir[strlen(g_Options->GetInterDir())] == PATH_SEPARATOR;
m_finalDirCreated = useInterDir && !FileSystem::DirectoryExists(m_finalDir);
const char* destDir = useInterDir && !m_finalDir.Empty() ? *m_finalDir : *m_destDir;
m_unpackDir.Format("%s%c%s", destDir, PATH_SEPARATOR, "_unpack");
detail("Unpacking into %s", *m_unpackDir);
CString errmsg;
if (!FileSystem::ForceDirectories(m_unpackDir, errmsg))
{
PrintMessage(Message::mkError, "Could not create directory %s: %s", *m_unpackDir, *errmsg);
}
}
void DirectUnpack::FindArchiveFiles()
{
Guard guard(m_volumeMutex);
DirBrowser dir(m_destDir);
while (const char* filename = dir.Next())
{
if (IsMainArchive(filename))
{
BString<1024> fullFilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
if (!FileSystem::DirectoryExists(fullFilename))
{
m_archives.emplace_back(filename);
}
}
}
}
bool DirectUnpack::IsMainArchive(const char* filename)
{
RegEx regExRarPart(".*\\.part([0-9]+)\\.rar$");
bool mainPart = Util::EndsWith(filename, ".rar", false) &&
(!regExRarPart.Match(filename) || atoi(filename + regExRarPart.GetMatchStart(1)) == 1);
return mainPart;
}
/**
* Unrar prints "Insert disk"-message without new line terminator.
* In order to become the message we analyze the output after every char.
*/
bool DirectUnpack::ReadLine(char* buf, int bufSize, FILE* stream)
{
int i = 0;
bool backspace = false;
for (; i < bufSize - 1; i++)
{
int ch = fgetc(stream);
if (ch == '\b')
{
backspace = true;
}
if (!backspace)
{
buf[i] = ch;
buf[i+1] = '\0';
}
if (ch == EOF)
{
break;
}
if (ch == '\n')
{
i++;
break;
}
if (i > 35 && ch == ' ' &&
!strncmp(buf, "Insert disk with", 16) && strstr(buf, " [C]ontinue, [Q]uit "))
{
i++;
break;
}
}
// skip unnecessary progress messages
if (!strncmp(buf, "...", 3))
{
buf[0] = '\0';
}
buf[i] = '\0';
return i > 0;
}
void DirectUnpack::AddMessage(Message::EKind kind, const char* text)
{
debug("%s", text);
BString<1024> msgText = text;
int len = strlen(text);
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (!strncmp(text, "Unrar: Extracting ", 19) &&
!strncmp(text + 19, m_unpackExtendedDir, strlen(m_unpackExtendedDir)))
{
msgText.Format("Unrar: Extracting %s", text + 19 + strlen(m_unpackExtendedDir) + 1);
}
if (!strncmp(text, "Unrar: Insert disk with", 23) && strstr(text, " [C]ontinue, [Q]uit"))
{
BString<1024> filename;
filename.Set(text + 24, strstr(text, " [C]ontinue, [Q]uit") - text - 24);
WaitNextVolume(filename);
return;
}
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
if (nzbInfo)
{
nzbInfo->AddMessage(kind, msgText);
}
else
{
ScriptController::AddMessage(kind, msgText);
}
if (!strncmp(msgText, "Unrar: Extracting ", 18) && nzbInfo)
{
SetProgressLabel(nzbInfo, msgText + 7);
}
if (!strncmp(text, "Unrar: Extracting from ", 23) && nzbInfo)
{
SetProgressLabel(nzbInfo, text + 7);
m_extractedArchives.emplace_back(text + 23);
}
if (!IsStopped() && (
!strncmp(text, "Unrar: Checksum error in the encrypted file", 42) ||
!strncmp(text, "Unrar: CRC failed in the encrypted file", 39) ||
!strncmp(text, "Unrar: The specified password is incorrect.", 43) ||
strstr(text, " : packed data CRC failed in volume") ||
strstr(text, " : packed data checksum error in volume") ||
(len > 13 && !strncmp(text + len - 13, " - CRC failed", 13)) ||
(len > 18 && !strncmp(text + len - 18, " - checksum failed", 18)) ||
!strncmp(text, "Unrar: WARNING: You need to start extraction from a previous volume", 67)))
{
Stop(downloadQueue, nzbInfo);
}
if (!strncmp(text, "Unrar: All OK", 13))
{
m_allOkMessageReceived = true;
}
}
void DirectUnpack::Stop(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
debug("Stopping direct unpack for %s", *m_infoName);
if (m_processed)
{
if (nzbInfo)
{
nzbInfo->AddMessage(Message::mkWarning, BString<1024>("Cancelling %s", *m_infoName));
}
else
{
warn("Cancelling %s", *m_infoName);
}
}
AddExtraTime(nzbInfo);
if (nzbInfo->GetPostInfo())
{
nzbInfo->GetPostInfo()->SetWorking(false);
}
Thread::Stop();
if (m_unpacking)
{
Terminate();
}
}
void DirectUnpack::WaitNextVolume(const char* filename)
{
debug("WaitNextVolume for %s", filename);
BString<1024> fullFilename("%s%c%s", *m_destDir, PATH_SEPARATOR, filename);
if (FileSystem::FileExists(fullFilename))
{
Write("\n"); // emulating click on Enter-key for "continue"
}
else
{
Guard guard(m_volumeMutex);
m_waitingFile = filename;
if (m_nzbCompleted)
{
// nzb completed but unrar waits for another volume
PrintMessage(Message::mkWarning, "Could not find volume %s", filename);
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
Stop(downloadQueue, nzbInfo);
}
}
}
void DirectUnpack::FileDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo)
{
debug("FileDownloaded for %s/%s", fileInfo->GetNzbInfo()->GetName(), fileInfo->GetFilename());
if (fileInfo->GetNzbInfo()->GetFailedArticles() > 0)
{
Stop(downloadQueue, fileInfo->GetNzbInfo());
return;
}
Guard guard(m_volumeMutex);
if (m_waitingFile && !strcasecmp(fileInfo->GetFilename(), m_waitingFile))
{
m_waitingFile = nullptr;
Write("\n"); // emulating click on Enter-key for "continue"
}
if (IsMainArchive(fileInfo->GetFilename()))
{
m_archives.emplace_back(fileInfo->GetFilename());
}
}
void DirectUnpack::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
debug("NzbDownloaded for %s", nzbInfo->GetName());
Guard guard(m_volumeMutex);
m_nzbCompleted = true;
if (m_waitingFile)
{
// nzb completed but unrar waits for another volume
nzbInfo->AddMessage(Message::mkWarning, BString<1024>("Unrar: Could not find volume %s", *m_waitingFile));
Stop(downloadQueue, nzbInfo);
return;
}
m_extraStartTime = Util::CurrentTime();
if (nzbInfo->GetPostInfo())
{
nzbInfo->GetPostInfo()->SetProgressLabel(m_progressLabel);
}
}
void DirectUnpack::NzbDeleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
debug("NzbDeleted for %s", nzbInfo->GetName());
nzbInfo->SetUnpackThread(nullptr);
nzbInfo->SetDirectUnpackStatus(NzbInfo::nsFailure);
Stop(downloadQueue, nzbInfo);
}
// Remove _unpack-dir
void DirectUnpack::Cleanup()
{
debug("Cleanup for %s", *m_infoName);
CString errmsg;
if (FileSystem::DirectoryExists(m_unpackDir) &&
!FileSystem::DeleteDirectoryWithContent(m_unpackDir, errmsg))
{
PrintMessage(Message::mkError, "Could not delete temporary directory %s: %s", *m_unpackDir, *errmsg);
}
if (m_finalDirCreated)
{
FileSystem::DeleteDirectory(m_finalDir);
}
}
void DirectUnpack::SetProgressLabel(NzbInfo* nzbInfo, const char* progressLabel)
{
m_progressLabel = progressLabel;
if (nzbInfo->GetPostInfo())
{
nzbInfo->GetPostInfo()->SetProgressLabel(progressLabel);
}
}
void DirectUnpack::AddExtraTime(NzbInfo* nzbInfo)
{
if (m_extraStartTime)
{
time_t extraTime = Util::CurrentTime() - m_extraStartTime;
nzbInfo->SetUnpackSec(nzbInfo->GetUnpackSec() + extraTime);
nzbInfo->SetPostTotalSec(nzbInfo->GetPostTotalSec() + extraTime);
m_extraStartTime = 0;
}
}
bool DirectUnpack::IsArchiveFilename(const char* filename)
{
if (Util::EndsWith(filename, ".rar", false))
{
return true;
}
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
return regExRarMultiSeq.Match(filename);
}
+86
View File
@@ -0,0 +1,86 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 DIRECTUNPACK_H
#define DIRECTUNPACK_H
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "Script.h"
class DirectUnpack : public Thread, public ScriptController
{
public:
virtual void Run();
static void StartJob(NzbInfo* nzbInfo);
void Stop(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void FileDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo);
void NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void NzbDeleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
static bool IsArchiveFilename(const char* filename);
protected:
virtual bool ReadLine(char* buf, int bufSize, FILE* stream);
virtual void AddMessage(Message::EKind kind, const char* text);
private:
class ParamList : public std::vector<CString>
{
public:
bool Exists(const char* param) { return std::find(begin(), end(), param) != end(); }
};
typedef std::deque<CString> ArchiveList;
int m_nzbId;
CString m_name;
CString m_infoName;
CString m_infoNameUp;
CString m_destDir;
CString m_finalDir;
CString m_unpackDir;
CString m_unpackExtendedDir;
CString m_password;
CString m_waitingFile;
CString m_progressLabel;
bool m_allOkMessageReceived = false;
bool m_unpackOk = false;
bool m_finalDirCreated = false;
bool m_nzbCompleted = false;
Mutex m_volumeMutex;
ArchiveList m_archives;
bool m_processed = false;
bool m_unpacking = false;
time_t m_extraStartTime = 0;
ArchiveList m_extractedArchives;
void CreateUnpackDir();
void FindArchiveFiles();
void ExecuteUnrar(const char* archiveName);
bool PrepareCmdParams(const char* command, ParamList* params, const char* infoName);
void WaitNextVolume(const char* filename);
void Cleanup();
bool IsMainArchive(const char* filename);
void SetProgressLabel(NzbInfo* nzbInfo, const char* progressLabel);
void AddExtraTime(NzbInfo* nzbInfo);
};
#endif
+16 -29
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,23 +400,22 @@ 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);
BString<1024> fullParFilename( "%s%c%s", *m_destDir, PATH_SEPARATOR, *parFilename);
int baseLen = 0;
ParParser::ParseParFilename(parFilename, &baseLen, nullptr);
ParParser::ParseParFilename(parFilename, true, &baseLen, nullptr);
BString<1024> infoName;
infoName.Set(parFilename, baseLen);
BString<1024> parInfoName("%s%c%s", *m_nzbName, (int)PATH_SEPARATOR, *infoName);
BString<1024> parInfoName("%s%c%s", *m_nzbName, PATH_SEPARATOR, *infoName);
SetInfoName(parInfoName);
EStatus status = RunParCheck(fullParFilename);
@@ -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,13 +1187,12 @@ void ParChecker::CheckEmptyFiles()
void ParChecker::Cancel()
{
GetRepairer()->cancelled = true;
m_cancelled = true;
QueueChanged();
}
void ParChecker::WriteBrokenLog(EStatus status)
{
BString<1024> brokenLogName("%s%c_brokenlog.txt", *m_destDir, (int)PATH_SEPARATOR);
BString<1024> brokenLogName("%s%c_brokenlog.txt", *m_destDir, PATH_SEPARATOR);
if (status != psRepairNotNeeded || FileSystem::FileExists(brokenLogName))
{
@@ -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);
}
+5 -5
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.
+13 -10
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)
{
@@ -44,7 +44,7 @@ bool ParParser::FindMainPars(const char* path, ParFileList* fileList)
bool exists = false;
for (CString& filename2 : fileList)
{
exists = SameParCollection(filename, filename2);
exists = SameParCollection(filename, filename2, true);
if (exists)
{
break;
@@ -59,16 +59,16 @@ bool ParParser::FindMainPars(const char* path, ParFileList* fileList)
return fileList && !fileList->empty();
}
bool ParParser::SameParCollection(const char* filename1, const char* filename2)
bool ParParser::SameParCollection(const char* filename1, const char* filename2, bool confirmedFilenames)
{
int baseLen1 = 0, baseLen2 = 0;
return ParseParFilename(filename1, &baseLen1, nullptr) &&
ParseParFilename(filename2, &baseLen2, nullptr) &&
return ParseParFilename(filename1, confirmedFilenames, &baseLen1, nullptr) &&
ParseParFilename(filename2, confirmedFilenames, &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)
+2 -2
View File
@@ -30,8 +30,8 @@ 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 SameParCollection(const char* filename1, const char* filename2);
static bool ParseParFilename(const char* parFilename, bool confirmedFilename, int* baseNameLen, int* blocks);
static bool SameParCollection(const char* filename1, const char* filename2, bool confirmedFilenames);
};
#endif
+145 -67
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
+36 -27
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
+438 -229
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,16 +29,18 @@
#include "FileSystem.h"
#include "Unpack.h"
#include "Cleanup.h"
#include "Rename.h"
#include "Repair.h"
#include "NzbFile.h"
#include "QueueScript.h"
#include "ParParser.h"
#include "DirectUnpack.h"
PrePostProcessor::PrePostProcessor()
{
debug("Creating PrePostProcessor");
m_downloadQueueObserver.m_owner = this;
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
DownloadQueue::Guard()->Attach(this);
}
void PrePostProcessor::Run()
@@ -57,49 +59,144 @@ 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 post-processing 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 post-processing 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;
}
}
}
// wait 5 seconds until direct unpack threads gracefully finish
waitStart = Util::CurrentTime();
while (Util::CurrentTime() < waitStart + 5)
{
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (std::find_if(downloadQueue->GetQueue()->begin(),
downloadQueue->GetQueue()->end(),
[](const std::unique_ptr<NzbInfo>& nzbInfo)
{
return nzbInfo->GetUnpackThread() != nullptr;
}) == downloadQueue->GetQueue()->end())
{
break;
}
}
usleep(200 * 1000);
}
// disconnect remaining direct unpack jobs
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
{
nzbInfo->SetUnpackThread(nullptr);
}
}
debug("PrePostProcessor: Jobs are completed");
}
void PrePostProcessor::Stop()
{
Thread::Stop();
GuardedDownloadQueue guard = DownloadQueue::Guard();
GuardedDownloadQueue downloadQueue = 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();
}
}
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
{
if (nzbInfo->GetUnpackThread())
{
((DirectUnpack*)nzbInfo->GetUnpackThread())->Stop(downloadQueue, nzbInfo);
}
}
}
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);
@@ -108,6 +205,10 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
{
NzbAdded(queueAspect->downloadQueue, queueAspect->nzbInfo);
}
else if (queueAspect->action == DownloadQueue::eaNzbNamed)
{
g_QueueScriptCoordinator->EnqueueScript(queueAspect->nzbInfo, QueueScriptCoordinator::qeNzbNamed);
}
else if (queueAspect->action == DownloadQueue::eaNzbDeleted &&
queueAspect->nzbInfo->GetDeleting() &&
!queueAspect->nzbInfo->GetPostInfo() &&
@@ -122,15 +223,23 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
else if ((queueAspect->action == DownloadQueue::eaFileCompleted ||
queueAspect->action == DownloadQueue::eaFileDeleted))
{
if (queueAspect->action == DownloadQueue::eaFileCompleted && !queueAspect->nzbInfo->GetPostInfo())
if (queueAspect->action == DownloadQueue::eaFileCompleted)
{
g_QueueScriptCoordinator->EnqueueScript(queueAspect->nzbInfo, QueueScriptCoordinator::qeFileDownloaded);
FileDownloaded(queueAspect->downloadQueue, queueAspect->nzbInfo, queueAspect->fileInfo);
}
#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
@@ -138,7 +247,7 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
queueAspect->fileInfo->GetDupeDeleted()) &&
queueAspect->fileInfo->GetNzbInfo()->GetDeleteStatus() != NzbInfo::dsHealth &&
!queueAspect->nzbInfo->GetPostInfo() &&
IsNzbFileCompleted(queueAspect->nzbInfo, true))
queueAspect->nzbInfo->IsDownloadCompleted(true))
{
queueAspect->nzbInfo->PrintMessage(Message::mkInfo,
"Collection %s completely downloaded", queueAspect->nzbInfo->GetName());
@@ -149,7 +258,7 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
(queueAspect->action == DownloadQueue::eaFileCompleted &&
queueAspect->fileInfo->GetNzbInfo()->GetDeleteStatus() > NzbInfo::dsNone)) &&
!queueAspect->nzbInfo->GetPostInfo() &&
IsNzbFileCompleted(queueAspect->nzbInfo, false))
queueAspect->nzbInfo->IsDownloadCompleted(false))
{
queueAspect->nzbInfo->PrintMessage(Message::mkInfo,
"Collection %s deleted from queue", queueAspect->nzbInfo->GetName());
@@ -170,7 +279,13 @@ 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 (g_Options->GetReorderFiles() && nzbInfo->GetDeleteStatus() == NzbInfo::dsNone)
{
nzbInfo->PrintMessage(Message::mkInfo, "Reordering files for %s", nzbInfo->GetName());
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupSortFiles, nullptr);
}
if (nzbInfo->GetDeleteStatus() == NzbInfo::dsDupe ||
@@ -199,7 +314,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,21 +323,33 @@ void PrePostProcessor::NzbDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbI
nzbInfo->SetParStatus(NzbInfo::psSkipped);
}
if (nzbInfo->GetRenameStatus() == NzbInfo::rsNone && !g_Options->GetParRename())
if (nzbInfo->GetUnpackThread())
{
nzbInfo->SetRenameStatus(NzbInfo::rsSkipped);
nzbInfo->GetPostInfo()->SetWorking(true);
m_activeJobs.push_back(nzbInfo);
((DirectUnpack*)nzbInfo->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbInfo);
}
downloadQueue->Save();
}
else
{
if (nzbInfo->GetUnpackThread())
{
((DirectUnpack*)nzbInfo->GetUnpackThread())->NzbDownloaded(downloadQueue, nzbInfo);
}
NzbCompleted(downloadQueue, nzbInfo, true);
}
}
void PrePostProcessor::NzbDeleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
if (nzbInfo->GetUnpackThread())
{
((DirectUnpack*)nzbInfo->GetUnpackThread())->NzbDeleted(downloadQueue, nzbInfo);
}
if (nzbInfo->GetDeleteStatus() == NzbInfo::dsNone)
{
nzbInfo->SetDeleteStatus(NzbInfo::dsManual);
@@ -291,10 +418,10 @@ void PrePostProcessor::DeleteCleanup(NzbInfo* nzbInfo)
// download was cancelled, deleting already downloaded files from disk
for (CompletedFile& completedFile: nzbInfo->GetCompletedFiles())
{
BString<1024> fullFileName("%s%c%s", nzbInfo->GetDestDir(), (int)PATH_SEPARATOR, completedFile.GetFileName());
BString<1024> fullFileName("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, completedFile.GetFilename());
if (FileSystem::FileExists(fullFileName))
{
detail("Deleting file %s", completedFile.GetFileName());
detail("Deleting file %s", completedFile.GetFilename());
FileSystem::DeleteFile(fullFileName);
}
}
@@ -313,97 +440,144 @@ void PrePostProcessor::DeleteCleanup(NzbInfo* nzbInfo)
}
// delete old directory (if empty)
if (FileSystem::DirEmpty(nzbInfo->GetDestDir()))
{
FileSystem::RemoveDirectory(nzbInfo->GetDestDir());
}
FileSystem::DeleteDirectory(nzbInfo->GetDestDir());
}
}
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() &&
!(postInfo->GetPostThread() && postInfo->GetPostThread()->IsRunning()))
{
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) &&
nzbInfo1->GetDirectUnpackStatus() != NzbInfo::nsRunning &&
(!nzbInfo || nzbInfo1->GetPriority() > nzbInfo->GetPriority()) &&
(!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()))
(!g_Options->GetPausePostProcess() || nzbInfo1->GetForcePriority()) &&
(allowPar || !nzbInfo1->GetPostInfo()->GetNeedParCheck()) &&
(std::find(m_activeJobs.begin(), m_activeJobs.end(), nzbInfo1) == m_activeJobs.end()) &&
nzbInfo1->IsDownloadCompleted(true))
{
nzbInfo = nzbInfo1;
}
@@ -412,73 +586,92 @@ NzbInfo* PrePostProcessor::GetNextJob(DownloadQueue* downloadQueue)
return nzbInfo;
}
/**
* Reset the state of items after reloading from disk and
* delete items which could not be resumed.
* Also count the number of post-jobs.
*/
void PrePostProcessor::SanitisePostQueue()
void PrePostProcessor::CheckPostQueue()
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
size_t countBefore = m_activeJobs.size();
CheckRequestPar(downloadQueue);
CleanupJobs(downloadQueue);
bool changed = m_activeJobs.size() != countBefore;
bool allowPar;
while (CanRunMoreJobs(&allowPar) && !IsStopped())
{
PostInfo* postInfo = nzbInfo->GetPostInfo();
if (postInfo)
NzbInfo* postJob = PickNextJob(downloadQueue, allowPar);
if (!postJob)
{
m_jobCount++;
if (postInfo->GetStage() == PostInfo::ptExecutingScript ||
!FileSystem::DirectoryExists(nzbInfo->GetDestDir()))
{
postInfo->SetStage(PostInfo::ptFinished);
}
else
{
postInfo->SetStage(PostInfo::ptQueued);
}
break;
}
m_activeJobs.push_back(postJob);
PostInfo* postInfo = postJob->GetPostInfo();
if (postInfo->GetStage() == PostInfo::ptQueued &&
(!g_Options->GetPausePostProcess() || postInfo->GetNzbInfo()->GetForcePriority()))
{
StartJob(downloadQueue, postInfo, allowPar);
CheckRequestPar(downloadQueue);
CleanupJobs(downloadQueue);
changed = true;
}
}
if (changed)
{
downloadQueue->Save();
UpdatePauseState();
}
Util::SetStandByMode(m_activeJobs.empty());
}
void PrePostProcessor::DeletePostThread(PostInfo* postInfo)
{
delete postInfo->GetPostThread();
postInfo->SetPostThread(nullptr);
}
void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo)
void PrePostProcessor::StartJob(DownloadQueue* downloadQueue, PostInfo* postInfo, bool allowPar)
{
if (!postInfo->GetStartTime())
{
postInfo->SetStartTime(Util::CurrentTime());
}
postInfo->SetStageTime(Util::CurrentTime());
postInfo->SetStageProgress(0);
postInfo->SetFileProgress(0);
postInfo->SetProgressLabel("");
#ifndef DISABLE_PARCHECK
if (postInfo->GetNzbInfo()->GetRenameStatus() == NzbInfo::rsNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone)
if (postInfo->GetNzbInfo()->GetParRenameStatus() == NzbInfo::rsNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone &&
g_Options->GetParRename())
{
UpdatePauseState(g_Options->GetParPauseQueue(), "par-rename");
m_parCoordinator.StartParRenameJob(postInfo);
EnterStage(downloadQueue, postInfo, PostInfo::ptParRenaming);
RenameController::StartJob(postInfo, RenameController::jkPar);
return;
}
else if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone &&
#ifndef DISABLE_PARCHECK
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psNone &&
postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsNone)
{
if (ParParser::FindMainPars(postInfo->GetNzbInfo()->GetDestDir(), nullptr))
{
UpdatePauseState(g_Options->GetParPauseQueue(), "par-check");
m_parCoordinator.StartParCheckJob(postInfo);
if (!allowPar)
{
postInfo->SetNeedParCheck(true);
return;
}
EnterStage(downloadQueue, postInfo, PostInfo::ptLoadingPars);
postInfo->SetNeedParCheck(false);
RepairController::StartJob(postInfo);
}
else
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Nothing to par-check for %s", postInfo->GetNzbInfo()->GetName());
postInfo->GetNzbInfo()->SetParStatus(NzbInfo::psSkipped);
postInfo->SetWorking(false);
postInfo->SetStage(PostInfo::ptQueued);
}
return;
}
else if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
if (postInfo->GetNzbInfo()->GetParStatus() == NzbInfo::psSkipped &&
((g_Options->GetParScan() != Options::psDupe &&
postInfo->GetNzbInfo()->CalcHealth() < postInfo->GetNzbInfo()->CalcCriticalHealth(false) &&
postInfo->GetNzbInfo()->CalcCriticalHealth(false) < 1000) ||
@@ -500,7 +693,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 +707,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 +750,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,123 +759,94 @@ 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))
if (nzbInfo->IsDownloadCompleted(true))
{
NzbCompleted(downloadQueue, nzbInfo, false);
}
if (nzbInfo == m_curJob)
{
m_curJob = nullptr;
}
m_jobCount--;
downloadQueue->Save();
m_queuedJobs--;
}
bool PrePostProcessor::IsNzbFileCompleted(NzbInfo* nzbInfo, bool ignorePausedPars)
void PrePostProcessor::UpdatePauseState()
{
if (nzbInfo->GetActiveDownloads())
bool needPause = false;
for (NzbInfo* postJob : m_activeJobs)
{
return false;
}
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if ((!fileInfo->GetPaused() || !ignorePausedPars || !fileInfo->GetParFile()) &&
!fileInfo->GetDeleted())
switch (postJob->GetPostInfo()->GetStage())
{
return false;
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 true;
}
bool PrePostProcessor::IsNzbFileDownloading(NzbInfo* nzbInfo)
{
if (nzbInfo->GetActiveDownloads())
{
return true;
}
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (!fileInfo->GetPaused())
{
return true;
}
}
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,22 +875,17 @@ 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;
}
else if (postInfo->GetNzbInfo()->GetUnpackThread())
{
((DirectUnpack*)postInfo->GetNzbInfo()->GetUnpackThread())->NzbDeleted(downloadQueue, postInfo->GetNzbInfo());
ok = true;
}
else
{
error("Internal error in PrePostProcessor::QueueDelete");
@@ -729,7 +895,16 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* downloadQueue, IdList* idL
{
postInfo->GetNzbInfo()->PrintMessage(Message::mkInfo,
"Deleting queued post-job %s", postInfo->GetNzbInfo()->GetName());
JobCompleted(downloadQueue, postInfo);
m_activeJobs.erase(std::remove_if(m_activeJobs.begin(), m_activeJobs.end(),
[postInfo](NzbInfo* postJob)
{
return postInfo == postJob->GetPostInfo();
}),
m_activeJobs.end());
ok = true;
}
break;
@@ -737,5 +912,39 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* downloadQueue, IdList* idL
}
}
if (ok)
{
downloadQueue->Save();
}
return ok;
}
void PrePostProcessor::FileDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, FileInfo* fileInfo)
{
if (!nzbInfo->GetPostInfo())
{
g_QueueScriptCoordinator->EnqueueScript(nzbInfo, QueueScriptCoordinator::qeFileDownloaded);
}
if (g_Options->GetDirectUnpack() && g_Options->GetDecode())
{
bool allowPar;
if (nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsNone &&
nzbInfo->GetDirectRenameStatus() != NzbInfo::tsRunning &&
DirectUnpack::IsArchiveFilename(fileInfo->GetFilename()) &&
CanRunMoreJobs(&allowPar))
{
NzbParameter* unpackParameter = nzbInfo->GetParameters()->Find("*Unpack:", false);
bool wantUnpack = !(unpackParameter && !strcasecmp(unpackParameter->GetValue(), "no"));
if (wantUnpack && nzbInfo->GetFailedArticles() == 0)
{
DirectUnpack::StartJob(nzbInfo);
}
}
else if (nzbInfo->GetDirectUnpackStatus() == NzbInfo::nsRunning)
{
((DirectUnpack*)nzbInfo->GetUnpackThread())->FileDownloaded(downloadQueue, fileInfo);
}
}
}
+20 -25
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);
void WaitJobs();
void FileDownloaded(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, FileInfo* fileInfo);
};
extern PrePostProcessor* g_PrePostProcessor;
+730
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;
}
+114
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
+325
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;
}
+81
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
+221
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++;
}
+98
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
@@ -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,14 +67,14 @@ 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;
for (CompletedFile& completedFile2 : m_postInfo->GetNzbInfo()->GetCompletedFiles())
{
if (!strcasecmp(completedFile2.GetFileName(), filename))
if (!strcasecmp(completedFile2.GetFilename(), filename))
{
completedFile = &completedFile2;
break;
@@ -85,7 +85,7 @@ ParChecker::EFileStatus ParCoordinator::PostParChecker::FindFileCrc(const char*
return ParChecker::fsUnknown;
}
debug("Found completed file: %s, CRC: %.8x, Status: %i", FileSystem::BaseFileName(completedFile->GetFileName()), completedFile->GetCrc(), (int)completedFile->GetStatus());
debug("Found completed file: %s, CRC: %.8x, Status: %i", FileSystem::BaseFileName(completedFile->GetFilename()), completedFile->GetCrc(), (int)completedFile->GetStatus());
*crc = completedFile->GetCrc();
@@ -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,130 +184,69 @@ 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());
BString<1024> fullFilename("%s%c%s", fileInfo->GetNzbInfo()->GetDestDir(), PATH_SEPARATOR, fileInfo->GetFilename());
m_parChecker.AddParFile(fullFilename);
}
else
@@ -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(),
[](const BlockInfo& block1, const 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());
m_parChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", nzbInfo->GetName(), 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,14 +403,15 @@ 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;
if (exactParName)
{
useFile = ParParser::SameParCollection(fileInfo->GetFilename(), FileSystem::BaseFileName(parFilename));
useFile = ParParser::SameParCollection(fileInfo->GetFilename(),
FileSystem::BaseFileName(parFilename), fileInfo->GetFilenameConfirmed());
}
else if (strictParName)
{
@@ -566,7 +455,7 @@ void ParCoordinator::FindPars(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, co
}
}
void ParCoordinator::UpdateParCheckProgress()
void RepairController::UpdateParCheckProgress()
{
PostInfo* postInfo;
@@ -600,7 +489,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 +510,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 +528,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 +556,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
@@ -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);
+55 -53
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-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
@@ -50,17 +50,7 @@ void UnpackController::StartJob(PostInfo* postInfo)
void UnpackController::Run()
{
time_t start = Util::CurrentTime();
m_cleanedUpDisk = false;
m_finalDirCreated = false;
m_unpackOk = true;
m_unpackStartError = false;
m_unpackSpaceError = false;
m_unpackDecryptError = false;
m_unpackPasswordError = false;
m_autoTerminated = false;
m_passListTried = false;
bool unpack;
{
@@ -87,16 +77,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()))
@@ -106,8 +93,7 @@ void UnpackController::Run()
m_postInfo->GetLastUnpackStatus() == (int)NzbInfo::usPassword ?
"%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);
m_postInfo->GetNzbInfo()->SetUnpackStatus((NzbInfo::EPostUnpackStatus)m_postInfo->GetLastUnpackStatus());
}
else if (unpack && hasFiles)
{
@@ -115,9 +101,20 @@ void UnpackController::Run()
CreateUnpackDir();
if (m_hasRarFiles || m_hasNonStdRarFiles)
if (m_hasRarFiles)
{
UnpackArchives(upUnrar, false);
if (m_hasUnpackedRarFiles)
{
if (m_postInfo->GetNzbInfo()->GetDirectUnpackStatus() == NzbInfo::nsSuccess)
{
PrintMessage(Message::mkInfo, "Found archive files not processed by direct unpack, unpacking all files again");
}
UnpackArchives(upUnrar, false);
}
else
{
PrintMessage(Message::mkInfo, "Using directly unpacked files");
}
}
if (m_hasSevenZipFiles && m_unpackOk)
@@ -139,23 +136,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,8 +277,9 @@ void UnpackController::ExecuteUnrar(const char* password)
params.emplace_back("-o+");
}
params.emplace_back(m_hasNonStdRarFiles ? "*.*" : "*.rar");
params.push_back(FileSystem::MakeExtendedPath(BString<1024>("%s%c", *m_unpackDir, PATH_SEPARATOR), true));
params.emplace_back("*.rar");
m_unpackExtendedDir = FileSystem::MakeExtendedPath(m_unpackDir, true);
params.push_back(*BString<1024>("%s%c", *m_unpackExtendedDir, PATH_SEPARATOR));
SetArgs(std::move(params));
SetLogPrefix("Unrar");
ResetEnv();
@@ -532,9 +542,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 +567,6 @@ void UnpackController::Completed()
m_unpackSpaceError ? NzbInfo::usSpace :
m_unpackPasswordError || m_unpackDecryptError ? NzbInfo::usPassword :
NzbInfo::usFailure);
m_postInfo->SetStage(PostInfo::ptQueued);
}
}
}
@@ -569,7 +577,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 +616,12 @@ void UnpackController::CreateUnpackDir()
}
}
void UnpackController::CheckArchiveFiles(bool scanNonStdFiles)
void UnpackController::CheckArchiveFiles()
{
m_hasRarFiles = false;
m_hasNonStdRarFiles = 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);
@@ -637,6 +637,10 @@ void UnpackController::CheckArchiveFiles(bool scanNonStdFiles)
if (regExRar.Match(filename))
{
m_hasRarFiles = true;
m_hasUnpackedRarFiles |= std::find(
m_postInfo->GetExtractedArchives()->begin(),
m_postInfo->GetExtractedArchives()->end(),
filename) == m_postInfo->GetExtractedArchives()->end();
}
else if (regExSevenZip.Match(filename))
{
@@ -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;
}
}
}
}
@@ -727,7 +731,7 @@ bool UnpackController::Cleanup()
if (!m_unpackOk && m_finalDirCreated)
{
FileSystem::RemoveDirectory(m_finalDir);
FileSystem::DeleteDirectory(m_finalDir);
}
if (m_unpackOk && ok && g_Options->GetUnpackCleanupDisk())
@@ -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);
@@ -837,9 +839,9 @@ void UnpackController::AddMessage(Message::EKind kind, const char* text)
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (m_unpacker == upUnrar && !strncmp(text, "Unrar: Extracting ", 19) &&
!strncmp(text + 19, m_unpackDir, strlen(m_unpackDir)))
!strncmp(text + 19, m_unpackExtendedDir, strlen(m_unpackExtendedDir)))
{
msgText.Format("Unrar: Extracting %s", text + 19 + strlen(m_unpackDir) + 1);
msgText.Format("Unrar: Extracting %s", text + 19 + strlen(m_unpackExtendedDir) + 1);
}
m_postInfo->GetNzbInfo()->AddMessage(kind, msgText);
@@ -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;
}
+22 -20
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-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
@@ -65,27 +65,29 @@ private:
CString m_destDir;
CString m_finalDir;
CString m_unpackDir;
CString m_unpackExtendedDir;
CString m_password;
bool m_interDir;
bool m_allOkMessageReceived;
bool m_noFilesMessageReceived;
bool m_hasParFiles;
bool m_hasRarFiles;
bool m_hasNonStdRarFiles;
bool m_hasSevenZipFiles;
bool m_hasSevenZipMultiFiles;
bool m_hasSplittedFiles;
bool m_unpackOk;
bool m_unpackStartError;
bool m_unpackSpaceError;
bool m_unpackDecryptError;
bool m_unpackPasswordError;
bool m_cleanedUpDisk;
bool m_autoTerminated;
EUnpacker m_unpacker;
bool m_finalDirCreated;
bool m_interDir = false;
bool m_allOkMessageReceived = false;
bool m_noFilesMessageReceived = false;
bool m_hasParFiles = false;
bool m_hasRarFiles = false;
bool m_hasUnpackedRarFiles = false;
bool m_hasRenamedArchiveFiles = false;
bool m_hasSevenZipFiles = false;
bool m_hasSevenZipMultiFiles = false;
bool m_hasSplittedFiles = false;
bool m_unpackOk = false;
bool m_unpackStartError = false;
bool m_unpackSpaceError = false;
bool m_unpackDecryptError = false;
bool m_unpackPasswordError = false;
bool m_cleanedUpDisk = false;
bool m_autoTerminated = false;
bool m_finalDirCreated = false;
bool m_passListTried = false;
FileList m_joinedFiles;
bool m_passListTried;
void ExecuteUnpack(EUnpacker unpacker, const char* password, bool multiVolumes);
void ExecuteUnrar(const char* password);
@@ -96,7 +98,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);
+546
View File
@@ -0,0 +1,546 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 "DirectRenamer.h"
#include "Options.h"
#include "FileSystem.h"
#include "ParParser.h"
#ifndef DISABLE_PARCHECK
#include "par2cmdline.h"
#include "par2fileformat.h"
#include "md5.h"
#endif
class RenameContentAnalyzer : public ArticleContentAnalyzer
{
public:
virtual void Reset();
virtual void Append(const void* buffer, int len);
void Finish();
const char* GetHash16k() { return m_hash16k; }
bool GetParFile() { return m_parFile; }
const char* GetParSetId() { return m_parSetId; }
private:
#ifndef DISABLE_PARCHECK
Par2::MD5Context m_md5Context;
char m_signature[sizeof(Par2::PACKET_HEADER)];
#endif
int m_dataSize = 0;
CString m_hash16k;
CString m_parSetId;
bool m_parFile = false;
};
class DirectParRepairer : public Par2::Par2Repairer
{
public:
DirectParRepairer() : Par2::Par2Repairer(m_nout, m_nout) {};
friend class DirectParLoader;
private:
class NullStreamBuf : public std::streambuf {};
NullStreamBuf m_nullbuf;
std::ostream m_nout{&m_nullbuf};
};
class DirectParLoader : public Thread
{
public:
static void StartLoader(DirectRenamer* owner, NzbInfo* nzbInfo);
virtual void Run();
private:
typedef std::vector<CString> ParFiles;
DirectRenamer* m_owner;
ParFiles m_parFiles;
DirectRenamer::FileHashList m_parHashes;
int m_nzbId;
void LoadParFile(const char* parFile);
};
void DirectParLoader::StartLoader(DirectRenamer* owner, NzbInfo* nzbInfo)
{
nzbInfo->PrintMessage(Message::mkInfo, "Directly checking renamed files for %s", nzbInfo->GetName());
DirectParLoader* directParLoader = new DirectParLoader();
directParLoader->m_owner = owner;
directParLoader->m_nzbId = nzbInfo->GetId();
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
if (completedFile.GetParFile())
{
directParLoader->m_parFiles.emplace_back(BString<1024>("%s%c%s",
nzbInfo->GetDestDir(), PATH_SEPARATOR, completedFile.GetFilename()));
}
}
directParLoader->SetAutoDestroy(true);
directParLoader->Start();
}
void DirectParLoader::Run()
{
debug("Started DirectParLoader");
for (CString& parFile : m_parFiles)
{
LoadParFile(parFile);
}
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
if (nzbInfo)
{
// nzb is still in queue
m_owner->RenameFiles(downloadQueue, nzbInfo, &m_parHashes);
}
}
void DirectParLoader::LoadParFile(const char* parFile)
{
DirectParRepairer repairer;
if (!repairer.LoadPacketsFromFile(parFile))
{
warn("Could not load par2-file %s", parFile);
return;
}
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_nzbId);
if (!nzbInfo)
{
// nzb isn't in queue anymore
return;
}
nzbInfo->PrintMessage(Message::mkInfo, "Loaded par2-file %s for direct-rename", FileSystem::BaseFileName(parFile));
for (std::pair<const Par2::MD5Hash, Par2::Par2RepairerSourceFile*>& entry : repairer.sourcefilemap)
{
if (IsStopped())
{
break;
}
Par2::Par2RepairerSourceFile* sourceFile = entry.second;
if (!sourceFile || !sourceFile->GetDescriptionPacket())
{
nzbInfo->PrintMessage(Message::mkWarning, "Damaged par2-file detected: %s", FileSystem::BaseFileName(parFile));
return;
}
std::string filename = Par2::DiskFile::TranslateFilename(sourceFile->GetDescriptionPacket()->FileName());
std::string hash = sourceFile->GetDescriptionPacket()->Hash16k().print();
debug("file: %s, hash-16k: %s", filename.c_str(), hash.c_str());
m_parHashes.emplace_back(filename.c_str(), hash.c_str());
}
}
std::unique_ptr<ArticleContentAnalyzer> DirectRenamer::MakeArticleContentAnalyzer()
{
return std::make_unique<RenameContentAnalyzer>();
}
void DirectRenamer::ArticleDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo,
ArticleInfo* articleInfo, ArticleContentAnalyzer* articleContentAnalyzer)
{
debug("Applying analyzer data %s for ", fileInfo->GetFilename());
RenameContentAnalyzer* contentAnalyzer = (RenameContentAnalyzer*)articleContentAnalyzer;
contentAnalyzer->Finish();
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
// we don't support analyzing of files split into articles smaller than 16KB
if (articleInfo->GetSize() >= 16 * 1024 || fileInfo->GetArticles()->size() == 1)
{
fileInfo->SetHash16k(contentAnalyzer->GetHash16k());
debug("file: %s; article-hash16k: %s", fileInfo->GetFilename(), fileInfo->GetHash16k());
}
detail("Detected %s %s", (contentAnalyzer->GetParFile() ? "par2-file" : "non-par2-file"), fileInfo->GetFilename());
if (fileInfo->GetParFile() != contentAnalyzer->GetParFile())
{
debug("Changing par2-flag for %s", fileInfo->GetFilename());
fileInfo->SetParFile(contentAnalyzer->GetParFile());
int delta = fileInfo->GetParFile() ? 1 : -1;
nzbInfo->SetParSize(nzbInfo->GetParSize() + fileInfo->GetSize() * delta);
nzbInfo->SetParCurrentSuccessSize(nzbInfo->GetParCurrentSuccessSize() + fileInfo->GetSuccessSize() * delta);
nzbInfo->SetParCurrentFailedSize(nzbInfo->GetParCurrentFailedSize() +
fileInfo->GetFailedSize() * delta + fileInfo->GetMissedSize() * delta);
nzbInfo->SetRemainingParCount(nzbInfo->GetRemainingParCount() + 1 * delta);
downloadQueue->Save();
}
if (fileInfo->GetParFile())
{
fileInfo->SetParSetId(contentAnalyzer->GetParSetId());
debug("file: %s; setid: %s", fileInfo->GetFilename(), fileInfo->GetParSetId());
}
CheckState(downloadQueue, nzbInfo);
}
void DirectRenamer::FileDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo)
{
CheckState(downloadQueue, fileInfo->GetNzbInfo());
}
void DirectRenamer::CheckState(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
if (nzbInfo->GetDirectRenameStatus() > NzbInfo::tsRunning)
{
return;
}
// check if all first articles are successfully downloaded (1)
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (Util::EmptyStr(fileInfo->GetHash16k()) ||
(fileInfo->GetParFile() && Util::EmptyStr(fileInfo->GetParSetId())))
{
return;
}
}
// check if all first articles are successfully downloaded (2)
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
if (Util::EmptyStr(completedFile.GetHash16k()) ||
(completedFile.GetParFile() && Util::EmptyStr(completedFile.GetParSetId())))
{
return;
}
}
if (!nzbInfo->GetWaitingPar())
{
// all first articles downloaded
UnpausePars(nzbInfo);
nzbInfo->SetWaitingPar(true);
downloadQueue->Save();
}
if (nzbInfo->GetWaitingPar() && !nzbInfo->GetLoadingPar())
{
// check if all par2-files scheduled for downloading already completed
FileList::iterator pos = std::find_if(
nzbInfo->GetFileList()->begin(), nzbInfo->GetFileList()->end(),
[](std::unique_ptr<FileInfo>& fileInfo)
{
return fileInfo->GetExtraPriority();
});
if (pos == nzbInfo->GetFileList()->end())
{
// all wanted par2-files are downloaded
nzbInfo->SetLoadingPar(true);
DirectParLoader::StartLoader(this, nzbInfo);
return;
}
}
}
// Unpause smallest par-files from each par-set
void DirectRenamer::UnpausePars(NzbInfo* nzbInfo)
{
ParFileList parFiles;
CollectPars(nzbInfo, &parFiles);
std::vector<CString> parsets;
// sort by size
std::sort(parFiles.begin(), parFiles.end(),
[nzbInfo](const ParFile& parFile1, const ParFile& parFile2)
{
FileInfo* fileInfo1 = nzbInfo->GetFileList()->Find(const_cast<ParFile&>(parFile1).GetId());
FileInfo* fileInfo2 = nzbInfo->GetFileList()->Find(const_cast<ParFile&>(parFile2).GetId());
return (!fileInfo1 && fileInfo2) ||
(fileInfo1 && fileInfo2 && fileInfo1->GetSize() < fileInfo2->GetSize());
});
// 1. count already downloaded files
for (ParFile& parFile : parFiles)
{
if (parFile.GetCompleted())
{
parsets.emplace_back(parFile.GetSetId());
}
}
// 2. find smallest par-file from each par-set from not yet completely downloaded files
for (ParFile& parFile : parFiles)
{
std::vector<CString>::iterator pos = std::find(parsets.begin(), parsets.end(), parFile.GetSetId());
if (pos == parsets.end())
{
// this par-set is not yet downloaded
parsets.emplace_back(parFile.GetSetId());
FileInfo* fileInfo = nzbInfo->GetFileList()->Find(parFile.GetId());
if (fileInfo)
{
nzbInfo->PrintMessage(Message::mkDetail, "Increasing priority for par2-file %s", fileInfo->GetFilename());
fileInfo->SetPaused(false);
fileInfo->SetExtraPriority(true);
}
}
}
}
void DirectRenamer::CollectPars(NzbInfo* nzbInfo, ParFileList* parFiles)
{
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (fileInfo->GetParFile())
{
parFiles->emplace_back(fileInfo->GetId(), fileInfo->GetFilename(), fileInfo->GetParSetId(), false);
}
}
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
if (completedFile.GetParFile())
{
parFiles->emplace_back(completedFile.GetId(), completedFile.GetFilename(), completedFile.GetParSetId(), true);
}
}
}
void DirectRenamer::RenameFiles(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, FileHashList* parHashes)
{
int renamedCount = 0;
bool renamePars = NeedRenamePars(nzbInfo);
int vol = 1;
// rename in-progress files
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
CString newName;
if (fileInfo->GetParFile() && renamePars)
{
newName = BuildNewParName(fileInfo->GetFilename(), nzbInfo->GetDestDir(), fileInfo->GetParSetId(), vol);
}
else if (!fileInfo->GetParFile())
{
newName = BuildNewRegularName(fileInfo->GetFilename(), parHashes, fileInfo->GetHash16k());
}
if (newName)
{
bool written = fileInfo->GetOutputFilename() &&
!Util::EndsWith(fileInfo->GetOutputFilename(), ".out.tmp", true);
if (!written)
{
nzbInfo->PrintMessage(Message::mkInfo, "Renaming in-progress file %s to %s",
fileInfo->GetFilename(), *newName);
fileInfo->SetFilename(newName);
fileInfo->SetFilenameConfirmed(true);
renamedCount++;
}
else if (RenameCompletedFile(nzbInfo, fileInfo->GetFilename(), newName))
{
fileInfo->SetFilename(newName);
fileInfo->SetFilenameConfirmed(true);
renamedCount++;
}
}
}
// rename completed files
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
CString newName;
if (completedFile.GetParFile() && renamePars)
{
newName = BuildNewParName(completedFile.GetFilename(), nzbInfo->GetDestDir(), completedFile.GetParSetId(), vol);
}
else if (!completedFile.GetParFile())
{
newName = BuildNewRegularName(completedFile.GetFilename(), parHashes, completedFile.GetHash16k());
}
if (newName && RenameCompletedFile(nzbInfo, completedFile.GetFilename(), newName))
{
completedFile.SetFilename(newName);
renamedCount++;
}
}
if (renamedCount > 0)
{
nzbInfo->PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", renamedCount, nzbInfo->GetName());
}
else
{
nzbInfo->PrintMessage(Message::mkInfo, "No renamed files found for %s", nzbInfo->GetName());
}
RenameCompleted(downloadQueue, nzbInfo);
}
CString DirectRenamer::BuildNewRegularName(const char* oldName, FileHashList* parHashes, const char* hash16k)
{
if (Util::EmptyStr(hash16k))
{
return nullptr;
}
FileHashList::iterator pos = std::find_if(parHashes->begin(), parHashes->end(),
[hash16k](FileHash& parHash)
{
return !strcmp(parHash.GetHash(), hash16k);
});
if (pos != parHashes->end())
{
FileHash& parHash = *pos;
if (strcasecmp(oldName, parHash.GetFilename()))
{
return parHash.GetFilename();
}
}
return nullptr;
}
CString DirectRenamer::BuildNewParName(const char* oldName, const char* destDir, const char* setId, int& vol)
{
BString<1024> newName;
BString<1024> destFileName;
// trying to reuse file suffix
const char* suffix = strstr(oldName, ".vol");
const char* extension = suffix ? strrchr(suffix, '.') : nullptr;
if (suffix && extension && !strcasecmp(extension, ".par2"))
{
newName.Format("%s%s", setId, suffix);
destFileName.Format("%s%c%s", destDir, PATH_SEPARATOR, *newName);
}
while (destFileName.Empty() || FileSystem::FileExists(destFileName))
{
newName.Format("%s.vol%03i+01.PAR2", setId, vol);
destFileName.Format("%s%c%s", destDir, PATH_SEPARATOR, *newName);
vol++;
}
return *newName;
}
bool DirectRenamer::NeedRenamePars(NzbInfo* nzbInfo)
{
// renaming is needed if par2-files from same par-set have different base names
// or if any par2-file has non .par2-extension
ParFileList parFiles;
CollectPars(nzbInfo, &parFiles);
for (ParFile& parFile : parFiles)
{
if (!Util::EndsWith(parFile.GetFilename(), ".par2", false))
{
return true;
}
for (ParFile& parFile2 : parFiles)
{
if (&parFile != &parFile2 && !strcmp(parFile.GetSetId(), parFile2.GetSetId()) &&
!ParParser::SameParCollection(parFile.GetFilename(), parFile2.GetFilename(), false))
{
return true;
}
}
}
return false;
}
bool DirectRenamer::RenameCompletedFile(NzbInfo* nzbInfo, const char* oldName, const char* newName)
{
BString<1024> oldFullFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, oldName);
BString<1024> newFullFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, newName);
nzbInfo->PrintMessage(Message::mkInfo, "Renaming completed file %s to %s", oldName, newName);
if (!FileSystem::MoveFile(oldFullFilename, newFullFilename))
{
nzbInfo->PrintMessage(Message::mkError, "Could not rename completed file %s to %s: %s",
*oldFullFilename, *newFullFilename, *FileSystem::GetLastErrorMessage());
return false;
}
return true;
}
void RenameContentAnalyzer::Reset()
{
#ifndef DISABLE_PARCHECK
m_md5Context.Reset();
#endif
m_dataSize = 0;
}
void RenameContentAnalyzer::Append(const void* buffer, int len)
{
#ifndef DISABLE_PARCHECK
if (m_dataSize < sizeof(m_signature))
{
memcpy(m_signature + m_dataSize, buffer, std::min((size_t)len, sizeof(m_signature) - m_dataSize));
}
if (m_dataSize >= sizeof(m_signature) && (*(Par2::MAGIC*)m_signature) == Par2::packet_magic)
{
m_parFile = true;
m_parSetId = ((Par2::PACKET_HEADER*)m_signature)->setid.print().c_str();
}
int rem16kSize = std::min(len, 16 * 1024 - m_dataSize);
if (rem16kSize > 0)
{
m_md5Context.Update(buffer, rem16kSize);
}
m_dataSize += len;
#endif
}
// Must be called with locked DownloadQueue
void RenameContentAnalyzer::Finish()
{
#ifndef DISABLE_PARCHECK
Par2::MD5Hash hash;
m_md5Context.Final(hash);
m_hash16k = hash.print().c_str();
#endif
}
+84
View File
@@ -0,0 +1,84 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 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
* 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 DIRECTRENAMER_H
#define DIRECTRENAMER_H
#include "ArticleDownloader.h"
class DirectRenamer
{
public:
class FileHash
{
public:
FileHash(const char* filename, const char* hash) :
m_filename(filename), m_hash(hash) {}
const char* GetFilename() { return m_filename; }
const char* GetHash() { return m_hash; }
private:
CString m_filename;
CString m_hash;
};
typedef std::deque<FileHash> FileHashList;
class ParFile
{
public:
ParFile(int id, const char* filename, const char* setId, bool completed) :
m_id(id), m_filename(filename), m_setId(setId), m_completed(completed) {}
int GetId() { return m_id; }
const char* GetFilename() { return m_filename; }
const char* GetSetId() { return m_setId; }
bool GetCompleted() { return m_completed; }
private:
int m_id;
CString m_filename;
CString m_setId;
bool m_completed = false;
};
typedef std::deque<ParFile> ParFileList;
std::unique_ptr<ArticleContentAnalyzer> MakeArticleContentAnalyzer();
void ArticleDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo,
ArticleInfo* articleInfo, ArticleContentAnalyzer* articleContentAnalyzer);
void FileDownloaded(DownloadQueue* downloadQueue, FileInfo* fileInfo);
protected:
virtual void RenameCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo) = 0;
private:
void CheckState(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void UnpausePars(NzbInfo* nzbInfo);
void RenameFiles(DownloadQueue* downloadQueue, NzbInfo* nzbInfo, FileHashList* parHashes);
bool RenameCompletedFile(NzbInfo* nzbInfo, const char* oldName, const char* newName);
bool NeedRenamePars(NzbInfo* nzbInfo);
void CollectPars(NzbInfo* nzbInfo, ParFileList* parFiles);
CString BuildNewRegularName(const char* oldName, FileHashList* parHashes, const char* hash16k);
CString BuildNewParName(const char* oldName, const char* destDir, const char* setId, int& vol);
friend class DirectParLoader;
};
#endif
+123 -27
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -265,7 +265,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* downloadQueue, bool saveHistory
bool ok = true;
{
StateFile stateFile("queue", 57, true);
StateFile stateFile("queue", 60, 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", 60, 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", 60, 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", 60, true);
if (stateFile.FileExists())
{
StateDiskFile* infile = stateFile.BeginRead();
@@ -428,9 +428,10 @@ 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,%i", (int)nzbInfo->GetParStatus(), (int)nzbInfo->GetUnpackStatus(),
(int)nzbInfo->GetMoveStatus(), (int)nzbInfo->GetParRenameStatus(), (int)nzbInfo->GetRarRenameStatus(),
(int)nzbInfo->GetDirectRenameStatus(), (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(),
@@ -466,8 +467,11 @@ void DiskState::SaveNzbInfo(NzbInfo* nzbInfo, StateDiskFile& outfile)
outfile.PrintLine("%i", (int)nzbInfo->GetCompletedFiles()->size());
for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles())
{
outfile.PrintLine("%i,%i,%u,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
completedFile.GetCrc(), completedFile.GetFileName());
outfile.PrintLine("%i,%i,%u,%i,%s,%s,%s", completedFile.GetId(), (int)completedFile.GetStatus(),
completedFile.GetCrc(), (int)completedFile.GetParFile(),
completedFile.GetHash16k() ? completedFile.GetHash16k() : "",
completedFile.GetParSetId() ? completedFile.GetParSetId() : "",
completedFile.GetFilename());
}
outfile.PrintLine("%i", (int)nzbInfo->GetParameters()->size());
@@ -556,17 +560,45 @@ 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,
directRenameStatus, deleteStatus, markStatus, urlStatus;
if (formatVersion >= 60)
{
if (infile.ScanLine("%i,%i,%i,%i,%i,%i,%i,%i,%i", &parStatus,
&unpackStatus, &moveStatus, &parRenameStatus, &rarRenameStatus, &directRenameStatus,
&deleteStatus, &markStatus, &urlStatus) != 9) goto error;
}
else if (formatVersion >= 58)
{
directRenameStatus = 0;
if (infile.ScanLine("%i,%i,%i,%i,%i,%i,%i,%i", &parStatus,
&unpackStatus, &moveStatus, &parRenameStatus, &rarRenameStatus, &deleteStatus,
&markStatus, &urlStatus) != 8) goto error;
}
else
{
rarRenameStatus = directRenameStatus = 0;
if (infile.ScanLine("%i,%i,%i,%i,%i,%i,%i", &parStatus, &unpackStatus, &moveStatus,
&parRenameStatus, &deleteStatus, &markStatus, &urlStatus) != 7) goto error;
}
nzbInfo->SetParStatus((NzbInfo::EParStatus)parStatus);
nzbInfo->SetUnpackStatus((NzbInfo::EUnpackStatus)unpackStatus);
nzbInfo->SetUnpackStatus((NzbInfo::EPostUnpackStatus)unpackStatus);
nzbInfo->SetMoveStatus((NzbInfo::EMoveStatus)moveStatus);
nzbInfo->SetRenameStatus((NzbInfo::ERenameStatus)renameStatus);
nzbInfo->SetParRenameStatus((NzbInfo::EPostRenameStatus)parRenameStatus);
nzbInfo->SetRarRenameStatus((NzbInfo::EPostRenameStatus)rarRenameStatus);
nzbInfo->SetDirectRenameStatus((NzbInfo::EDirectRenameStatus)directRenameStatus);
nzbInfo->SetDeleteStatus((NzbInfo::EDeleteStatus)deleteStatus);
nzbInfo->SetMarkStatus((NzbInfo::EMarkStatus)markStatus);
if (nzbInfo->GetKind() == NzbInfo::nkNzb ||
@@ -678,10 +710,31 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
char* fileName = buf;
int status = 0;
uint32 crc = 0;
int parFile = 0;
char* hash16k = nullptr;
char* parSetId = nullptr;
if (formatVersion >= 49)
{
if (formatVersion >= 50)
if (formatVersion >= 60)
{
if (sscanf(buf, "%i,%i,%u,%i", &id, &status, &crc, &parFile) != 4) goto error;
hash16k = strchr(buf, ',');
if (hash16k) hash16k = strchr(hash16k+1, ',');
if (hash16k) hash16k = strchr(hash16k+1, ',');
if (hash16k) hash16k = strchr(hash16k+1, ',');
if (hash16k)
{
parSetId = strchr(++hash16k, ',');
if (parSetId)
{
*parSetId++ = '\0';
fileName = strchr(parSetId, ',');
if (fileName) *fileName = '\0';
}
}
}
else if (formatVersion >= 50)
{
if (sscanf(buf, "%i,%i,%u", &id, &status, &crc) != 3) goto error;
fileName = strchr(buf, ',');
@@ -699,7 +752,10 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
}
}
nzbInfo->GetCompletedFiles()->emplace_back(id, fileName, (CompletedFile::EStatus)status, crc);
nzbInfo->GetCompletedFiles()->emplace_back(id, fileName,
(CompletedFile::EStatus)status, crc, (bool)parFile,
Util::EmptyStr(hash16k) ? nullptr : hash16k,
Util::EmptyStr(parSetId) ? nullptr : parSetId);
}
int parameterCount;
@@ -772,7 +828,7 @@ bool DiskState::LoadNzbInfo(NzbInfo* nzbInfo, Servers* servers, StateDiskFile& i
{
fileInfo->SetTime(time);
}
fileInfo->SetExtraPriority(extraPriority != 0);
fileInfo->SetExtraPriority((bool)extraPriority);
fileInfo->SetNzbInfo(nzbInfo);
nzbInfo->GetFileList()->Add(std::move(fileInfo));
}
@@ -828,7 +884,7 @@ bool DiskState::SaveFile(FileInfo* fileInfo)
debug("Saving FileInfo %i to disk", fileInfo->GetId());
BString<100> filename("%i", fileInfo->GetId());
StateFile stateFile(filename, 4, false);
StateFile stateFile(filename, 5, false);
StateDiskFile* outfile = stateFile.BeginWrite();
if (!outfile)
@@ -844,7 +900,7 @@ bool DiskState::SaveFileInfo(FileInfo* fileInfo, StateDiskFile& outfile)
outfile.PrintLine("%s", fileInfo->GetSubject());
outfile.PrintLine("%s", fileInfo->GetFilename());
outfile.PrintLine("%i", (int)fileInfo->GetTime());
outfile.PrintLine("%i,%i", (int)fileInfo->GetFilenameConfirmed(), (int)fileInfo->GetTime());
uint32 High, Low;
Util::SplitInt64(fileInfo->GetSize(), &High, &Low);
@@ -882,7 +938,7 @@ bool DiskState::LoadFile(FileInfo* fileInfo, bool fileSummary, bool articles)
debug("Loading FileInfo %i from disk", fileInfo->GetId());
BString<100> filename("%i", fileInfo->GetId());
StateFile stateFile(filename, 4, false);
StateFile stateFile(filename, 5, false);
StateDiskFile* infile = stateFile.BeginRead();
if (!infile)
@@ -903,7 +959,14 @@ bool DiskState::LoadFileInfo(FileInfo* fileInfo, StateDiskFile& infile, int form
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
if (fileSummary) fileInfo->SetFilename(buf);
if (formatVersion >= 4)
if (formatVersion >= 5)
{
int time, filenameConfirmed;
if (infile.ScanLine("%i,%i", &filenameConfirmed, &time) != 2) goto error;
if (fileSummary) fileInfo->SetFilenameConfirmed((bool)filenameConfirmed);
if (fileSummary) fileInfo->SetTime((time_t)time);
}
else if (formatVersion >= 4)
{
int time;
if (infile.ScanLine("%i", &time) != 1) goto error;
@@ -967,7 +1030,7 @@ bool DiskState::SaveFileState(FileInfo* fileInfo, bool completed)
debug("Saving FileState %i to disk", fileInfo->GetId());
BString<100> filename("%i%s", fileInfo->GetId(), completed ? "c" : "s");
StateFile stateFile(filename, 4, false);
StateFile stateFile(filename, 5, false);
StateDiskFile* outfile = stateFile.BeginWrite();
if (!outfile)
@@ -989,6 +1052,8 @@ bool DiskState::SaveFileState(FileInfo* fileInfo, StateDiskFile& outfile, bool c
outfile.PrintLine("%u,%u,%u,%u,%u,%u", High1, Low1, High2, Low2, High3, Low3);
outfile.PrintLine("%s", fileInfo->GetFilename());
outfile.PrintLine("%s", fileInfo->GetHash16k());
outfile.PrintLine("%i", (int)fileInfo->GetParFile());
SaveServerStats(fileInfo->GetServerStats(), outfile);
@@ -1008,7 +1073,7 @@ bool DiskState::LoadFileState(FileInfo* fileInfo, Servers* servers, bool complet
debug("Loading FileInfo %i from disk", fileInfo->GetId());
BString<100> filename("%i%s", fileInfo->GetId(), completed ? "c" : "s");
StateFile stateFile(filename, 4, false);
StateFile stateFile(filename, 5, false);
StateDiskFile* infile = stateFile.BeginRead();
if (!infile)
@@ -1034,13 +1099,23 @@ bool DiskState::LoadFileState(FileInfo* fileInfo, Servers* servers, StateDiskFil
fileInfo->SetSuccessSize(Util::JoinInt64(High2, Low2));
fileInfo->SetFailedSize(Util::JoinInt64(High3, Low3));
char buf[1024];
if (formatVersion >= 4)
{
char buf[1024];
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
fileInfo->SetFilename(buf);
}
if (formatVersion >= 5)
{
if (!infile.ReadLine(buf, sizeof(buf))) goto error;
fileInfo->SetHash16k(*buf ? buf : nullptr);
int parFile = 0;
if (infile.ScanLine("%i", &parFile) != 1) goto error;
fileInfo->SetParFile((bool)parFile);
}
if (!LoadServerStats(fileInfo->GetServerStats(), servers, infile)) goto error;
int completedArticles;
@@ -1079,6 +1154,13 @@ bool DiskState::LoadFileState(FileInfo* fileInfo, Servers* servers, StateDiskFil
status = ArticleInfo::aiUndefined;
}
if (status == ArticleInfo::aiFinished && !g_Options->GetDirectWrite() &&
!fileInfo->GetForceDirectWrite() && !pa->GetResultFilename())
{
pa->SetResultFilename(BString<1024>("%s%c%i.%03i", g_Options->GetTempDir(),
PATH_SEPARATOR, fileInfo->GetId(), pa->GetPartNumber()));
}
// don't allow all articles be completed or the file will stuck.
// such states should never be saved on disk but just in case.
if (completedArticles == size - 1 && !completed)
@@ -1318,9 +1400,23 @@ void DiskState::CleanupTempDir(DownloadQueue* downloadQueue)
DirBrowser dir(g_Options->GetTempDir());
while (const char* filename = dir.Next())
{
bool garbage = strstr(filename, ".tmp") || strstr(filename, ".dec");
int id, part;
if (strstr(filename, ".tmp") || strstr(filename, ".dec") ||
(sscanf(filename, "%i.%i", &id, &part) == 2))
if (!garbage && sscanf(filename, "%i.%i", &id, &part) == 2)
{
garbage = true;
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
{
if (nzbInfo->GetFileList()->Find(id))
{
garbage = false;
break;
}
}
}
if (garbage)
{
BString<1024> fullFilename("%s%c%s", g_Options->GetTempDir(), PATH_SEPARATOR, filename);
FileSystem::DeleteFile(fullFilename);
+31 -5
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -470,13 +470,20 @@ void NzbInfo::SetActiveDownloads(int activeDownloads)
if (activeDownloads > 0)
{
m_downloadStartTime = Util::CurrentTime();
m_downloadStartSec = m_downloadSec;
}
else
{
m_downloadSec += Util::CurrentTime() - m_downloadStartTime;
m_downloadSec = m_downloadStartSec + (Util::CurrentTime() - m_downloadStartTime);
m_downloadStartTime = 0;
m_changed = true;
}
}
else if (activeDownloads > 0)
{
m_downloadSec = m_downloadStartSec + (Util::CurrentTime() - m_downloadStartTime);
m_changed = true;
}
m_activeDownloads = activeDownloads;
}
@@ -725,6 +732,24 @@ void NzbInfo::UpdateDeletedStats(FileInfo* fileInfo)
m_currentServerStats.ListOp(fileInfo->GetServerStats(), ServerStatList::soSubtract);
}
bool NzbInfo::IsDownloadCompleted(bool ignorePausedPars)
{
if (m_activeDownloads)
{
return false;
}
for (FileInfo* fileInfo : &m_fileList)
{
if ((!fileInfo->GetPaused() || !ignorePausedPars || !fileInfo->GetParFile()) &&
!fileInfo->GetDeleted())
{
return false;
}
}
return true;
}
void ArticleInfo::AttachSegment(std::unique_ptr<SegmentData> content, int64 offset, int size)
{
@@ -791,9 +816,10 @@ void FileInfo::SetActiveDownloads(int activeDownloads)
}
CompletedFile::CompletedFile(int id, const char* fileName, EStatus status, uint32 crc) :
m_id(id), m_fileName(fileName), m_status(status), m_crc(crc)
CompletedFile::CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
bool parFile, const char* hash16k, const char* parSetId) :
m_id(id), m_filename(filename), m_status(status), m_crc(crc), m_parFile(parFile),
m_hash16k(hash16k), m_parSetId(parSetId)
{
if (FileInfo::m_idMax < m_id)
{
+89 -19
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -192,6 +192,11 @@ public:
void SetPartialState(EPartialState partialState) { m_partialState = partialState; }
uint32 GetCrc() { return m_crc; }
void SetCrc(uint32 crc) { m_crc = crc; }
const char* GetHash16k() { return m_hash16k; }
void SetHash16k(const char* hash16k) { m_hash16k = hash16k; }
const char* GetParSetId() { return m_parSetId; }
void SetParSetId(const char* parSetId) { m_parSetId = parSetId; }
ServerStatList* GetServerStats() { return &m_serverStats; }
private:
@@ -228,6 +233,8 @@ private:
bool m_forceDirectWrite = false;
EPartialState m_partialState = psNone;
uint32 m_crc = 0;
CString m_hash16k;
CString m_parSetId;
static int m_idGen;
static int m_idMax;
@@ -249,18 +256,27 @@ public:
cfFailure
};
CompletedFile(int id, const char* fileName, EStatus status, uint32 crc);
CompletedFile(int id, const char* filename, EStatus status, uint32 crc,
bool parFile, const char* hash16k, const char* parSetId);
int GetId() { return m_id; }
void SetFileName(const char* fileName) { m_fileName = fileName; }
const char* GetFileName() { return m_fileName; }
void SetFilename(const char* filename) { m_filename = filename; }
const char* GetFilename() { return m_filename; }
bool GetParFile() { return m_parFile; }
EStatus GetStatus() { return m_status; }
uint32 GetCrc() { return m_crc; }
const char* GetHash16k() { return m_hash16k; }
void SetHash16k(const char* hash16k) { m_hash16k = hash16k; }
const char* GetParSetId() { return m_parSetId; }
void SetParSetId(const char* parSetId) { m_parSetId = parSetId; }
private:
int m_id;
CString m_fileName;
CString m_filename;
EStatus m_status;
uint32 m_crc;
bool m_parFile;
CString m_hash16k;
CString m_parSetId;
};
typedef std::deque<CompletedFile> CompletedFileList;
@@ -332,11 +348,19 @@ enum EDupeMode
class NzbInfo
{
public:
enum ERenameStatus
enum EDirectRenameStatus
{
tsNone,
tsRunning,
tsFailure,
tsSuccess
};
enum EPostRenameStatus
{
rsNone,
rsSkipped,
rsFailure,
rsNothing,
rsSuccess
};
@@ -350,7 +374,15 @@ public:
psManual
};
enum EUnpackStatus
enum EDirectUnpackStatus
{
nsNone,
nsRunning,
nsFailure,
nsSuccess
};
enum EPostUnpackStatus
{
usNone,
usSkipped,
@@ -485,12 +517,18 @@ public:
void BuildDestDirName();
CString BuildFinalDirName();
CompletedFileList* GetCompletedFiles() { return &m_completedFiles; }
ERenameStatus GetRenameStatus() { return m_renameStatus; }
void SetRenameStatus(ERenameStatus renameStatus) { m_renameStatus = renameStatus; }
void SetDirectRenameStatus(EDirectRenameStatus renameStatus) { m_directRenameStatus = renameStatus; }
EDirectRenameStatus GetDirectRenameStatus() { return m_directRenameStatus; }
EPostRenameStatus GetParRenameStatus() { return m_parRenameStatus; }
void SetParRenameStatus(EPostRenameStatus renameStatus) { m_parRenameStatus = renameStatus; }
EPostRenameStatus GetRarRenameStatus() { return m_rarRenameStatus; }
void SetRarRenameStatus(EPostRenameStatus renameStatus) { m_rarRenameStatus = renameStatus; }
EParStatus GetParStatus() { return m_parStatus; }
void SetParStatus(EParStatus parStatus) { m_parStatus = parStatus; }
EUnpackStatus GetUnpackStatus() { return m_unpackStatus; }
void SetUnpackStatus(EUnpackStatus unpackStatus) { m_unpackStatus = unpackStatus; }
EDirectUnpackStatus GetDirectUnpackStatus() { return m_directUnpackStatus; }
void SetDirectUnpackStatus(EDirectUnpackStatus directUnpackStatus) { m_directUnpackStatus = directUnpackStatus; }
EPostUnpackStatus GetUnpackStatus() { return m_unpackStatus; }
void SetUnpackStatus(EPostUnpackStatus unpackStatus) { m_unpackStatus = unpackStatus; }
ECleanupStatus GetCleanupStatus() { return m_cleanupStatus; }
void SetCleanupStatus(ECleanupStatus cleanupStatus) { m_cleanupStatus = cleanupStatus; }
EMoveStatus GetMoveStatus() { return m_moveStatus; }
@@ -554,6 +592,8 @@ public:
void SetUnpackSec(int unpackSec) { m_unpackSec = unpackSec; }
time_t GetDownloadStartTime() { return m_downloadStartTime; }
void SetDownloadStartTime(time_t downloadStartTime) { m_downloadStartTime = downloadStartTime; }
bool GetChanged() { return m_changed; }
void SetChanged(bool changed) { m_changed = changed; }
void SetReprocess(bool reprocess) { m_reprocess = reprocess; }
bool GetReprocess() { return m_reprocess; }
time_t GetQueueScriptTime() { return m_queueScriptTime; }
@@ -575,9 +615,18 @@ public:
void SetMessageCount(int messageCount) { m_messageCount = messageCount; }
int GetCachedMessageCount() { return m_cachedMessageCount; }
GuardedMessageList GuardCachedMessages() { return GuardedMessageList(&m_messages, &m_logMutex); }
bool GetAllFirst() { return m_allFirst; }
void SetAllFirst(bool allFirst) { m_allFirst = allFirst; }
bool GetWaitingPar() { return m_waitingPar; }
void SetWaitingPar(bool waitingPar) { m_waitingPar = waitingPar; }
bool GetLoadingPar() { return m_loadingPar; }
void SetLoadingPar(bool loadingPar) { m_loadingPar = loadingPar; }
Thread* GetUnpackThread() { return m_unpackThread; }
void SetUnpackThread(Thread* unpackThread) { m_unpackThread = unpackThread; }
void UpdateCurrentStats();
void UpdateCompletedStats(FileInfo* fileInfo);
void UpdateDeletedStats(FileInfo* fileInfo);
bool IsDownloadCompleted(bool ignorePausedPars);
static const int FORCE_PRIORITY = 900;
@@ -616,9 +665,12 @@ private:
time_t m_maxTime = 0;
int m_priority = 0;
CompletedFileList m_completedFiles;
ERenameStatus m_renameStatus = rsNone;
EDirectRenameStatus m_directRenameStatus = tsNone;
EPostRenameStatus m_parRenameStatus = rsNone;
EPostRenameStatus m_rarRenameStatus = rsNone;
EParStatus m_parStatus = psNone;
EUnpackStatus m_unpackStatus = usNone;
EDirectUnpackStatus m_directUnpackStatus = nsNone;
EPostUnpackStatus m_unpackStatus = usNone;
ECleanupStatus m_cleanupStatus = csNone;
EMoveStatus m_moveStatus = msNone;
EDeleteStatus m_deleteStatus = dsNone;
@@ -652,17 +704,23 @@ private:
std::unique_ptr<PostInfo> m_postInfo;
int64 m_downloadedSize = 0;
time_t m_downloadStartTime = 0;
int m_downloadStartSec = 0;
int m_downloadSec = 0;
int m_postTotalSec = 0;
int m_parSec = 0;
int m_repairSec = 0;
int m_unpackSec = 0;
bool m_reprocess = false;
bool m_changed = false;
time_t m_queueScriptTime = 0;
bool m_parFull = false;
int m_messageCount = 0;
int m_cachedMessageCount = 0;
int m_feedId = 0;
bool m_allFirst = false;
bool m_waitingPar = false;
bool m_loadingPar = false;
Thread* m_unpackThread = nullptr;
static int m_idGen;
static int m_idMax;
@@ -685,14 +743,17 @@ public:
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptRenaming,
ptParRenaming,
ptRarRenaming,
ptUnpacking,
ptCleaningUp,
ptMoving,
ptExecutingScript,
ptFinished
};
typedef std::vector<CString> ParredFiles;
typedef std::vector<CString> ExtractedArchives;
NzbInfo* GetNzbInfo() { return m_nzbInfo; }
void SetNzbInfo(NzbInfo* nzbInfo) { m_nzbInfo = nzbInfo; }
@@ -726,9 +787,12 @@ 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; }
ExtractedArchives* GetExtractedArchives() { return &m_extractedArchives; }
private:
NzbInfo* m_nzbInfo = nullptr;
@@ -741,6 +805,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;
@@ -749,6 +814,7 @@ private:
time_t m_stageTime = 0;
Thread* m_postThread = nullptr;
ParredFiles m_parredFiles;
ExtractedArchives m_extractedArchives;
};
typedef std::vector<int> IdList;
@@ -842,6 +908,7 @@ public:
eaNzbFound,
eaNzbAdded,
eaNzbDeleted,
eaNzbNamed,
eaFileCompleted,
eaFileDeleted,
eaUrlCompleted
@@ -864,12 +931,14 @@ public:
eaFileResume, // resume (unpause) files
eaFileDelete, // delete files
eaFilePauseAllPars, // pause only (all) pars (does not affect other files)
eaFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eaFilePauseExtraPars, // pause (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
@@ -888,6 +957,7 @@ public:
eaGroupSetDupeScore, // set duplicate score
eaGroupSetDupeMode, // set duplicate mode
eaGroupSort, // sort groups
eaGroupSortFiles, // sort files for optimal download order
eaPostDelete, // cancel post-processing
eaHistoryDelete, // hide history-item
eaHistoryFinalDelete, // delete history-item
@@ -918,8 +988,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);
+1 -1
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;
}
}
+27 -13
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -130,8 +130,8 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
nzbInfo->UpdateCompletedStats(fileInfo);
nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(),
fileInfo->GetFilename(), CompletedFile::cfNone, 0);
nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(), fileInfo->GetFilename(),
CompletedFile::cfNone, 0, fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
}
// Cleaning up parked files if par-check was successful or unpack was successful or
@@ -169,7 +169,7 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
(completedFile.GetStatus() == CompletedFile::cfPartial &&
&completedFile == &*nzbInfo->GetCompletedFiles()->rbegin()))
{
nzbInfo->PrintMessage(Message::mkDetail, "Parking file %s", completedFile.GetFileName());
nzbInfo->PrintMessage(Message::mkDetail, "Parking file %s", completedFile.GetFilename());
nzbInfo->SetParkedFileCount(nzbInfo->GetParkedFileCount() + 1);
}
}
@@ -178,6 +178,11 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* downloadQueue, NzbInfo* nzb
nzbInfo->SetRemainingParCount(0);
nzbInfo->SetParking(false);
if (nzbInfo->GetDirectRenameStatus() == NzbInfo::tsRunning)
{
nzbInfo->SetDirectRenameStatus(NzbInfo::tsFailure);
}
nzbInfo->PrintMessage(Message::mkInfo, "Collection %s added to history", nzbInfo->GetName());
}
@@ -232,7 +237,8 @@ void HistoryCoordinator::PrepareEdit(DownloadQueue* downloadQueue, IdList* idLis
}
}
bool HistoryCoordinator::EditList(DownloadQueue* downloadQueue, IdList* idList, DownloadQueue::EEditAction action, int offset, const char* text)
bool HistoryCoordinator::EditList(DownloadQueue* downloadQueue, IdList* idList,
DownloadQueue::EEditAction action, const char* args)
{
bool ok = false;
PrepareEdit(downloadQueue, idList, action);
@@ -270,22 +276,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:
@@ -378,8 +384,10 @@ void HistoryCoordinator::MoveToQueue(DownloadQueue* downloadQueue, HistoryList::
if (!nzbInfo->GetUnpackCleanedUpDisk())
{
nzbInfo->SetUnpackStatus(NzbInfo::usNone);
nzbInfo->SetDirectUnpackStatus(NzbInfo::nsNone);
nzbInfo->SetCleanupStatus(NzbInfo::csNone);
nzbInfo->SetRenameStatus(NzbInfo::rsNone);
nzbInfo->SetParRenameStatus(NzbInfo::rsNone);
nzbInfo->SetRarRenameStatus(NzbInfo::rsNone);
nzbInfo->SetPostTotalSec(nzbInfo->GetPostTotalSec() - nzbInfo->GetUnpackSec());
nzbInfo->SetUnpackSec(0);
@@ -489,7 +497,10 @@ 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->SetDirectRenameStatus(NzbInfo::tsNone);
nzbInfo->SetDirectUnpackStatus(NzbInfo::nsNone);
nzbInfo->SetDownloadedSize(0);
nzbInfo->SetDownloadSec(0);
nzbInfo->SetPostTotalSec(0);
@@ -497,6 +508,9 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* downloadQueue, History
nzbInfo->SetRepairSec(0);
nzbInfo->SetUnpackSec(0);
nzbInfo->SetExtraParBlocks(0);
nzbInfo->SetAllFirst(false);
nzbInfo->SetWaitingPar(false);
nzbInfo->SetLoadingPar(false);
nzbInfo->GetCompletedFiles()->clear();
nzbInfo->GetServerStats()->clear();
nzbInfo->GetCurrentServerStats()->clear();
@@ -584,7 +598,7 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
g_DiskState->LoadFileState(fileInfo.get(), g_ServerPool->GetServers(), true) &&
(resetFailed || fileInfo->GetRemainingSize() > 0))))
{
fileInfo->SetFilename(completedFile.GetFileName());
fileInfo->SetFilename(completedFile.GetFilename());
fileInfo->SetNzbInfo(nzbInfo);
BString<1024> outputFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, fileInfo->GetFilename());
@@ -631,7 +645,7 @@ void HistoryCoordinator::HistoryRetry(DownloadQueue* downloadQueue, HistoryList:
if (g_Options->GetParCheck() != Options::pcForce)
{
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, 0, nullptr);
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, nullptr);
}
}
+1 -1
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);
+281 -59
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -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)
{
@@ -63,6 +63,11 @@ void QueueCoordinator::CoordinatorDownloadQueue::Save()
g_DiskState->SaveDownloadQueue(this, m_historyChanged);
}
for (NzbInfo* nzbInfo : GetQueue())
{
nzbInfo->SetChanged(false);
}
m_wantSave = false;
m_historyChanged = false;
}
@@ -71,7 +76,6 @@ QueueCoordinator::QueueCoordinator()
{
debug("Creating QueueCoordinator");
m_downloadQueue.m_owner = this;
CoordinatorDownloadQueue::Init(&m_downloadQueue);
}
@@ -225,7 +229,7 @@ void QueueCoordinator::Run()
wasStandBy = standBy;
if (standBy)
{
SavePartialState();
SaveAllPartialState();
}
}
@@ -248,7 +252,7 @@ void QueueCoordinator::Run()
ResetHangingDownloads();
if (!standBy)
{
SavePartialState();
SaveAllPartialState();
}
resetCounter = 0;
g_StatMeter->IntervalCheck();
@@ -256,23 +260,31 @@ void QueueCoordinator::Run()
}
}
WaitJobs();
SaveAllPartialState();
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 +383,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();
@@ -489,6 +504,14 @@ bool QueueCoordinator::GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &f
break;
}
if (g_Options->GetDirectRename() &&
fileInfo->GetNzbInfo()->GetDirectRenameStatus() <= NzbInfo::tsRunning &&
!fileInfo->GetNzbInfo()->GetAllFirst() &&
GetNextFirstArticle(fileInfo->GetNzbInfo(), fileInfo, articleInfo))
{
return true;
}
if (fileInfo->GetArticles()->empty() && g_Options->GetSaveQueue() && g_Options->GetServerMode())
{
g_DiskState->LoadArticles(fileInfo);
@@ -501,8 +524,7 @@ bool QueueCoordinator::GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &f
if (article->GetStatus() == ArticleInfo::aiUndefined)
{
articleInfo = article;
ok = true;
break;
return true;
}
}
@@ -514,7 +536,39 @@ bool QueueCoordinator::GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &f
}
}
return ok;
return false;
}
bool QueueCoordinator::GetNextFirstArticle(NzbInfo* nzbInfo, FileInfo* &fileInfo, ArticleInfo* &articleInfo)
{
// find a file not renamed yet
for (FileInfo* fileInfo1 : nzbInfo->GetFileList())
{
if (!fileInfo1->GetFilenameConfirmed())
{
if (fileInfo1->GetArticles()->empty() && g_Options->GetSaveQueue() && g_Options->GetServerMode())
{
g_DiskState->LoadArticles(fileInfo1);
LoadPartialState(fileInfo1);
}
if (!fileInfo1->GetArticles()->empty())
{
ArticleInfo* article = fileInfo1->GetArticles()->at(0).get();
if (article->GetStatus() == ArticleInfo::aiUndefined)
{
fileInfo = fileInfo1;
articleInfo = article;
nzbInfo->SetDirectRenameStatus(NzbInfo::tsRunning);
return true;
}
}
}
}
// no more files for renaming remained
nzbInfo->SetAllFirst(true);
return false;
}
void QueueCoordinator::StartArticleDownload(FileInfo* fileInfo, ArticleInfo* articleInfo, NntpConnection* connection)
@@ -528,7 +582,12 @@ void QueueCoordinator::StartArticleDownload(FileInfo* fileInfo, ArticleInfo* art
articleDownloader->SetArticleInfo(articleInfo);
articleDownloader->SetConnection(connection);
BString<1024> infoName("%s%c%s [%i/%i]", fileInfo->GetNzbInfo()->GetName(), (int)PATH_SEPARATOR, fileInfo->GetFilename(), articleInfo->GetPartNumber(), (int)fileInfo->GetArticles()->size());
if (articleInfo->GetPartNumber() == 1 && g_Options->GetDirectRename() && g_Options->GetDecode())
{
articleDownloader->SetContentAnalyzer(m_directRenamer.MakeArticleContentAnalyzer());
}
BString<1024> infoName("%s%c%s [%i/%i]", fileInfo->GetNzbInfo()->GetName(), PATH_SEPARATOR, fileInfo->GetFilename(), articleInfo->GetPartNumber(), (int)fileInfo->GetArticles()->size());
articleDownloader->SetInfoName(infoName);
articleInfo->SetStatus(ArticleInfo::aiRunning);
@@ -557,12 +616,14 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
debug("Article downloaded");
FileInfo* fileInfo = articleDownloader->GetFileInfo();
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
ArticleInfo* articleInfo = articleDownloader->GetArticleInfo();
bool retry = false;
bool fileCompleted = false;
bool completeFileParts = false;
{
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
ArticleInfo* articleInfo = articleDownloader->GetArticleInfo();
bool retry = false;
bool fileCompleted = false;
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
if (articleDownloader->GetStatus() == ArticleDownloader::adFinished)
@@ -587,6 +648,10 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
{
articleInfo->SetStatus(ArticleInfo::aiUndefined);
retry = true;
if (articleInfo->GetPartNumber() == 1)
{
nzbInfo->SetAllFirst(false);
}
}
if (!retry)
@@ -608,13 +673,22 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
articleDownloader->GetStatus() == ArticleDownloader::adFinished &&
articleDownloader->GetArticleFilename())
{
fileInfo->SetFilename(articleDownloader->GetArticleFilename());
fileInfo->MakeValidFilename();
// in "FileNaming=auto"-mode prefer filename from nzb-file to filename read from article
// if the name from article seems to be obfuscated
bool useFilenameFromArticle = g_Options->GetFileNaming() == Options::nfArticle ||
(g_Options->GetFileNaming() == Options::nfAuto &&
!Util::AlphaNum(articleDownloader->GetArticleFilename()) &&
!nzbInfo->GetManyDupeFiles());
if (useFilenameFromArticle)
{
fileInfo->SetFilename(articleDownloader->GetArticleFilename());
fileInfo->MakeValidFilename();
}
fileInfo->SetFilenameConfirmed(true);
if (g_Options->GetDupeCheck() &&
nzbInfo->GetDupeMode() != dmForce &&
!nzbInfo->GetManyDupeFiles() &&
FileSystem::FileExists(nzbInfo->GetDestDir(), fileInfo->GetFilename()))
FileSystem::FileExists(BString<1024>("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, fileInfo->GetFilename())))
{
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", fileInfo->GetFilename());
fileCompleted = false;
@@ -623,6 +697,11 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
}
}
if (articleDownloader->GetContentAnalyzer() && articleDownloader->GetStatus() == ArticleDownloader::adFinished)
{
m_directRenamer.ArticleDownloaded(downloadQueue, fileInfo, articleInfo, articleDownloader->GetContentAnalyzer());
}
nzbInfo->SetDownloadedSize(nzbInfo->GetDownloadedSize() + articleDownloader->GetDownloadedSize());
CheckHealth(downloadQueue, fileInfo);
@@ -631,35 +710,44 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* articleDownloader)
{
fileCompleted = true;
}
completeFileParts = fileCompleted && (!fileInfo->GetDeleted() || nzbInfo->GetParking());
if (!completeFileParts)
{
DeleteDownloader(downloadQueue, articleDownloader, false);
}
}
bool deleteFileObj = false;
if (fileCompleted && (!fileInfo->GetDeleted() || nzbInfo->GetParking()))
if (completeFileParts)
{
// all jobs done
articleDownloader->CompleteFileParts();
fileInfo->SetPartialChanged(false);
deleteFileObj = true;
}
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
DeleteDownloader(downloadQueue, articleDownloader, true);
}
}
bool hasOtherDownloaders = fileInfo->GetActiveDownloads() > 1;
deleteFileObj |= fileInfo->GetDeleted() && !hasOtherDownloaders;
void QueueCoordinator::DeleteDownloader(DownloadQueue* downloadQueue,
ArticleDownloader* articleDownloader, bool fileCompleted)
{
FileInfo* fileInfo = articleDownloader->GetFileInfo();
NzbInfo* nzbInfo = fileInfo->GetNzbInfo();
bool hasOtherDownloaders = fileInfo->GetActiveDownloads() > 1;
bool deleteFileObj = fileCompleted || (fileInfo->GetDeleted() && !hasOtherDownloaders);
// remove downloader from downloader list
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), articleDownloader));
// remove downloader from downloader list
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), articleDownloader));
fileInfo->SetActiveDownloads(fileInfo->GetActiveDownloads() - 1);
nzbInfo->SetActiveDownloads(nzbInfo->GetActiveDownloads() - 1);
fileInfo->SetActiveDownloads(fileInfo->GetActiveDownloads() - 1);
nzbInfo->SetActiveDownloads(nzbInfo->GetActiveDownloads() - 1);
if (deleteFileObj)
{
DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
downloadQueue->Save();
}
if (deleteFileObj)
{
DeleteFileInfo(downloadQueue, fileInfo, fileCompleted);
downloadQueue->Save();
}
}
@@ -712,7 +800,19 @@ void QueueCoordinator::DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fi
completed && fileInfo->GetOutputFilename() ?
FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(),
fileStatus,
fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0);
fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0,
fileInfo->GetParFile(), fileInfo->GetHash16k(), fileInfo->GetParSetId());
}
if (g_Options->GetDirectRename())
{
m_directRenamer.FileDownloaded(downloadQueue, fileInfo);
}
if (nzbInfo->GetDirectRenameStatus() == NzbInfo::tsRunning &&
!nzbInfo->GetDeleting() && nzbInfo->IsDownloadCompleted(true))
{
DiscardDirectRename(downloadQueue, nzbInfo);
}
std::unique_ptr<FileInfo> srcFileInfo = nzbInfo->GetFileList()->Remove(fileInfo);
@@ -745,30 +845,45 @@ void QueueCoordinator::DiscardTempFiles(FileInfo* fileInfo)
}
}
void QueueCoordinator::SavePartialState()
void QueueCoordinator::SaveAllPartialState()
{
if (!(g_Options->GetServerMode() && g_Options->GetSaveQueue() && g_Options->GetContinuePartial()))
if (!(g_Options->GetServerMode() && g_Options->GetSaveQueue()))
{
return;
}
bool hasUnsavedData = false;
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
for (NzbInfo* nzbInfo : downloadQueue->GetQueue())
{
for (FileInfo* fileInfo : nzbInfo->GetFileList())
if (g_Options->GetContinuePartial())
{
if (fileInfo->GetPartialChanged())
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
debug("Saving partial state for %s", fileInfo->GetFilename());
if (fileInfo->GetPartialState() == FileInfo::psCompleted)
{
g_DiskState->DiscardFile(fileInfo->GetId(), false, false, true);
}
g_DiskState->SaveFileState(fileInfo, false);
fileInfo->SetPartialChanged(false);
fileInfo->SetPartialState(FileInfo::psPartial);
SavePartialState(fileInfo);
}
}
hasUnsavedData |= nzbInfo->GetChanged();
}
if (hasUnsavedData)
{
downloadQueue->Save();
}
}
void QueueCoordinator::SavePartialState(FileInfo* fileInfo)
{
if (fileInfo->GetPartialChanged())
{
debug("Saving partial state for %s", fileInfo->GetFilename());
if (fileInfo->GetPartialState() == FileInfo::psCompleted)
{
g_DiskState->DiscardFile(fileInfo->GetId(), false, false, true);
}
g_DiskState->SaveFileState(fileInfo, false);
fileInfo->SetPartialChanged(false);
fileInfo->SetPartialState(FileInfo::psPartial);
}
}
@@ -808,7 +923,7 @@ void QueueCoordinator::CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileI
warn("Pausing %s due to health %.1f%% below critical %.1f%%", fileInfo->GetNzbInfo()->GetName(),
fileInfo->GetNzbInfo()->CalcHealth() / 10.0, fileInfo->GetNzbInfo()->CalcCriticalHealth(true) / 10.0);
fileInfo->GetNzbInfo()->SetHealthPaused(true);
downloadQueue->EditEntry(fileInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupPause, 0, nullptr);
downloadQueue->EditEntry(fileInfo->GetNzbInfo()->GetId(), DownloadQueue::eaGroupPause, nullptr);
}
else if (g_Options->GetHealthCheck() == Options::hcDelete ||
g_Options->GetHealthCheck() == Options::hcPark)
@@ -820,7 +935,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);
}
}
@@ -1172,3 +1287,110 @@ bool QueueCoordinator::SplitQueueEntries(DownloadQueue* downloadQueue, RawFileLi
return true;
}
void QueueCoordinator::DirectRenameCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() && !fileInfo->GetArticles()->empty())
{
// save new file name into disk state file
g_DiskState->SaveFile(fileInfo);
}
}
DiscardDirectRename(downloadQueue, nzbInfo);
nzbInfo->SetDirectRenameStatus(NzbInfo::tsSuccess);
if (g_Options->GetParCheck() != Options::pcForce)
{
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupResume, nullptr);
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseAllPars, nullptr);
}
if (g_Options->GetReorderFiles())
{
nzbInfo->PrintMessage(Message::mkInfo, "Reordering files for %s", nzbInfo->GetName());
downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupSortFiles, nullptr);
}
downloadQueue->Save();
DownloadQueue::Aspect namedAspect = { DownloadQueue::eaNzbNamed, downloadQueue, nzbInfo, nullptr };
downloadQueue->Notify(&namedAspect);
}
void QueueCoordinator::DiscardDirectRename(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{
int64 discardedSize = 0;
int discardedCount = 0;
for (FileInfo* fileInfo : nzbInfo->GetFileList())
{
if (fileInfo->GetParFile() && fileInfo->GetCompletedArticles() == 1 && fileInfo->GetActiveDownloads() == 0)
{
// discard downloaded articles from partially downloaded par-files
discardedSize += fileInfo->GetSuccessSize();
discardedCount++;
nzbInfo->SetCurrentSuccessArticles(nzbInfo->GetCurrentSuccessArticles() - fileInfo->GetSuccessArticles());
nzbInfo->SetCurrentSuccessSize(nzbInfo->GetCurrentSuccessSize() - fileInfo->GetSuccessSize());
nzbInfo->SetParCurrentSuccessSize(nzbInfo->GetParCurrentSuccessSize() - fileInfo->GetSuccessSize());
fileInfo->SetSuccessSize(0);
fileInfo->SetSuccessArticles(0);
nzbInfo->SetCurrentFailedArticles(nzbInfo->GetCurrentFailedArticles() - fileInfo->GetFailedArticles());
nzbInfo->SetCurrentFailedSize(nzbInfo->GetCurrentFailedSize() - fileInfo->GetFailedSize());
nzbInfo->SetParCurrentFailedSize(nzbInfo->GetParCurrentFailedSize() - fileInfo->GetFailedSize());
fileInfo->SetFailedSize(0);
fileInfo->SetFailedArticles(0);
fileInfo->SetCompletedArticles(0);
fileInfo->SetRemainingSize(fileInfo->GetSize() - fileInfo->GetMissedSize());
// discard temporary files
DiscardTempFiles(fileInfo);
g_DiskState->DiscardFile(fileInfo->GetId(), false, true, false);
fileInfo->SetOutputFilename(nullptr);
fileInfo->SetOutputInitialized(false);
fileInfo->SetCachedArticles(0);
fileInfo->SetPartialChanged(false);
fileInfo->SetPartialState(FileInfo::psNone);
if (g_Options->GetSaveQueue() && g_Options->GetServerMode())
{
// free up memory used by articles if possible
fileInfo->GetArticles()->clear();
}
else
{
// reset article states if discarding isn't possible
for (ArticleInfo* articleInfo : fileInfo->GetArticles())
{
articleInfo->SetStatus(ArticleInfo::aiUndefined);
articleInfo->SetResultFilename(nullptr);
articleInfo->DiscardSegment();
}
}
}
if (g_Options->GetSaveQueue() && g_Options->GetServerMode() &&
!fileInfo->GetArticles()->empty() && g_Options->GetContinuePartial() &&
fileInfo->GetActiveDownloads() == 0 && fileInfo->GetCachedArticles() == 0)
{
// discard article infos to free up memory if possible
debug("Discarding article infos for %s/%s", nzbInfo->GetName(), fileInfo->GetFilename());
fileInfo->SetPartialChanged(true);
SavePartialState(fileInfo);
fileInfo->GetArticles()->clear();
}
}
if (discardedSize > 0)
{
nzbInfo->PrintMessage(Message::mkDetail, "Discarded %s from %i files used for direct renaming",
*Util::FormatSize(discardedSize), discardedCount);
}
}
+25 -5
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -30,6 +30,7 @@
#include "Observer.h"
#include "QueueEditor.h"
#include "NntpConnection.h"
#include "DirectRenamer.h"
class QueueCoordinator : public Thread, public Observer, public Debuggable
{
@@ -60,9 +61,10 @@ private:
class CoordinatorDownloadQueue : public DownloadQueue
{
public:
virtual bool EditEntry(int ID, EEditAction action, int offset, const char* text);
CoordinatorDownloadQueue(QueueCoordinator* owner) : m_owner(owner) {}
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:
@@ -73,23 +75,41 @@ private:
friend class QueueCoordinator;
};
CoordinatorDownloadQueue m_downloadQueue;
class CoordinatorDirectRenamer : public DirectRenamer
{
public:
CoordinatorDirectRenamer(QueueCoordinator* owner) : m_owner(owner) {}
protected:
virtual void RenameCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo)
{ m_owner->DirectRenameCompleted(downloadQueue, nzbInfo); }
private:
QueueCoordinator* m_owner;
};
CoordinatorDownloadQueue m_downloadQueue{this};
ActiveDownloads m_activeDownloads;
QueueEditor m_queueEditor;
CoordinatorDirectRenamer m_directRenamer{this};
bool m_hasMoreJobs = true;
int m_downloadsLimit;
int m_serverConfigGeneration = 0;
bool GetNextArticle(DownloadQueue* downloadQueue, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
bool GetNextFirstArticle(NzbInfo* nzbInfo, FileInfo* &fileInfo, ArticleInfo* &articleInfo);
void StartArticleDownload(FileInfo* fileInfo, ArticleInfo* articleInfo, NntpConnection* connection);
void ArticleCompleted(ArticleDownloader* articleDownloader);
void DeleteDownloader(DownloadQueue* downloadQueue, ArticleDownloader* articleDownloader, bool fileCompleted);
void DeleteFileInfo(DownloadQueue* downloadQueue, FileInfo* fileInfo, bool completed);
void DirectRenameCompleted(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void DiscardDirectRename(DownloadQueue* downloadQueue, NzbInfo* nzbInfo);
void CheckHealth(DownloadQueue* downloadQueue, FileInfo* fileInfo);
void ResetHangingDownloads();
void AdjustDownloadsLimit();
void Load();
void SavePartialState();
void SaveAllPartialState();
void SavePartialState(FileInfo* fileInfo);
void LoadPartialState(FileInfo* fileInfo);
void WaitJobs();
};
extern QueueCoordinator* g_QueueCoordinator;
+224 -90
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include "PrePostProcessor.h"
#include "HistoryCoordinator.h"
#include "UrlCoordinator.h"
#include "ParParser.h"
const int MAX_ID = 1000000000;
@@ -62,8 +63,6 @@ private:
QueueEditor::ItemList* m_sortItemList;
ESortCriteria m_sortCriteria;
ESortOrder m_sortOrder;
void AlignSelectedGroups();
};
bool GroupSorter::Execute(const char* sort)
@@ -112,8 +111,6 @@ bool GroupSorter::Execute(const char* sort)
m_sortOrder = soAuto;
}
AlignSelectedGroups();
RawNzbList tempList;
for (NzbInfo* nzbInfo : m_nzbList)
{
@@ -126,7 +123,7 @@ bool GroupSorter::Execute(const char* sort)
m_sortOrder = soDescending;
}
std::sort(m_nzbList->begin(), m_nzbList->end(), *this);
std::stable_sort(m_nzbList->begin(), m_nzbList->end(), *this);
if (origSortOrder == soAuto &&
std::equal(tempList.begin(), tempList.end(), m_nzbList->begin(),
@@ -136,7 +133,7 @@ bool GroupSorter::Execute(const char* sort)
}))
{
m_sortOrder = m_sortOrder == soDescending ? soAscending : soDescending;
std::sort(m_nzbList->begin(), m_nzbList->end(), *this);
std::stable_sort(m_nzbList->begin(), m_nzbList->end(), *this);
}
return true;
@@ -200,44 +197,6 @@ bool GroupSorter::operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const
return ret;
}
void GroupSorter::AlignSelectedGroups()
{
NzbInfo* lastNzbInfo = nullptr;
uint32 lastNum = 0;
uint32 num = 0;
while (num < m_nzbList->size())
{
std::unique_ptr<NzbInfo>& nzbInfo = m_nzbList->at(num);
bool selected = false;
for (QueueEditor::EditItem& item : m_sortItemList)
{
if (item.m_nzbInfo == nzbInfo.get())
{
selected = true;
break;
}
}
if (selected)
{
if (lastNzbInfo && num - lastNum > 1)
{
std::unique_ptr<NzbInfo> movedNzbInfo = std::move(*(m_nzbList->begin() + num));
m_nzbList->erase(m_nzbList->begin() + num);
m_nzbList->insert(m_nzbList->begin() + lastNum + 1, std::move(movedNzbInfo));
lastNum++;
}
else
{
lastNum = num;
}
lastNzbInfo = nzbInfo.get();
}
num++;
}
}
FileInfo* QueueEditor::FindFileInfo(int id)
{
@@ -252,17 +211,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 +227,6 @@ void QueueEditor::DeleteEntry(FileInfo* fileInfo)
}
}
/*
* Moves entry in the queue
*/
void QueueEditor::MoveEntry(FileInfo* fileInfo, int offset)
{
int entry = 0;
@@ -309,9 +259,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 +291,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 +323,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 +331,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 +353,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 +390,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 +422,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,9 +435,13 @@ bool QueueEditor::InternEditList(ItemList* itemList,
}
else
{
EditGroup(item.m_nzbInfo, action, offset, text);
EditGroup(item.m_nzbInfo, action, args);
}
break;
case DownloadQueue::eaGroupSortFiles:
SortGroupFiles(item.m_nzbInfo);
break;
default:
// suppress compiler warning "enumeration not handled in switch"
@@ -566,15 +523,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 +713,7 @@ bool QueueEditor::BuildIdListFromNameList(IdList* idList, NameList* nameList, Do
return true;
}
bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, int offset, const char* text)
bool QueueEditor::EditGroup(NzbInfo* nzbInfo, DownloadQueue::EEditAction action, const char* args)
{
ItemList itemList;
bool allPaused = true;
@@ -803,6 +760,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 +775,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 +909,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 +931,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 +950,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 +970,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 +1043,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)
@@ -1178,6 +1264,54 @@ void QueueEditor::SetNzbDupeParam(NzbInfo* nzbInfo, DownloadQueue::EEditAction a
}
}
void QueueEditor::SortGroupFiles(NzbInfo* nzbInfo)
{
debug("QueueEditor: sorting inner files for '%s'", nzbInfo->GetName());
std::sort(nzbInfo->GetFileList()->begin(), nzbInfo->GetFileList()->end(),
[](const std::unique_ptr<FileInfo>& fileInfo1, const std::unique_ptr<FileInfo>& fileInfo2)
{
if (!fileInfo1->GetParFile() && !fileInfo2->GetParFile())
{
// ".rar"-files are ordered before ".r01", etc. files
int len1 = strlen(fileInfo1->GetFilename());
int len2 = strlen(fileInfo2->GetFilename());
const char* ext1 = strrchr(fileInfo1->GetFilename(), '.');
const char* ext2 = strrchr(fileInfo2->GetFilename(), '.');
int ext1len = ext1 ? strlen(ext1) : 0;
int ext2len = ext2 ? strlen(ext2) : 0;
bool sameBaseName = len1 == len2 && ext1len == 4 && ext2len == 4 &&
!strncmp(fileInfo1->GetFilename(), fileInfo2->GetFilename(), len1 - 4);
if (sameBaseName && !strcmp(ext1, ".rar") && ext2[1] == 'r' &&
isdigit(ext2[2]) && isdigit(ext2[3]))
{
return true;
}
else if (sameBaseName && !strcmp(ext2, ".rar") && ext1[1] == 'r' &&
isdigit(ext1[2]) && isdigit(ext1[3]))
{
return false;
}
}
else if (fileInfo1->GetParFile() && fileInfo2->GetParFile() &&
ParParser::SameParCollection(fileInfo1->GetFilename(), fileInfo2->GetFilename(),
fileInfo1->GetFilenameConfirmed() && fileInfo2->GetFilenameConfirmed()))
{
return fileInfo1->GetSize() < fileInfo2->GetSize();
}
else if (!fileInfo1->GetParFile() && fileInfo2->GetParFile())
{
return true;
}
else if (fileInfo1->GetParFile() && !fileInfo2->GetParFile())
{
return false;
}
return strcmp(fileInfo1->GetFilename(), fileInfo2->GetFilename()) < 0;
});
}
bool QueueEditor::DeleteUrl(NzbInfo* nzbInfo, DownloadQueue::EEditAction action)
{
return g_UrlCoordinator->DeleteQueueEntry(m_downloadQueue, nzbInfo, action == DownloadQueue::eaGroupFinalDelete);
+10 -6
View File
@@ -1,7 +1,7 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -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,19 @@ 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);
void SortGroupFiles(NzbInfo* nzbInfo);
bool ItemListContainsItem(ItemList* itemList, int id);
friend class GroupSorter;
};
+21 -13
View File
@@ -28,6 +28,8 @@
#include "Util.h"
#include "FileSystem.h"
int Scanner::m_idGen = 0;
Scanner::QueueData::QueueData(const char* filename, const char* nzbName, const char* category,
int priority, const char* dupeKey, int dupeScore, EDupeMode dupeMode,
NzbParameterList* parameters, bool addTop, bool addPaused, NzbInfo* urlInfo,
@@ -72,8 +74,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 +97,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 +106,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 +348,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 +356,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 +373,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");
}
}
}
}
}
@@ -487,8 +495,8 @@ Scanner::EAddStatus Scanner::AddExternalFile(const char* nzbName, const char* ca
}
else
{
int num = 1;
while (num == 1 || FileSystem::FileExists(tempFileName))
int num = ++m_idGen;
while (tempFileName.Empty() || FileSystem::FileExists(tempFileName))
{
tempFileName.Format("%s%cnzb-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, num);
num++;
+1
View File
@@ -114,6 +114,7 @@ private:
QueueList m_queueList;
bool m_scanning = false;
Mutex m_scanMutex;
static int m_idGen;
void CheckIncomingNzbs(const char* directory, const char* category, bool checkStat);
bool AddFileToQueue(const char* filename, const char* nzbName, const char* category,
+14 -5
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()
+1
View File
@@ -56,6 +56,7 @@ private:
void StartUrlDownload(NzbInfo* nzbInfo);
void UrlCompleted(UrlDownloader* urlDownloader);
void ResetHangingDownloads();
void WaitJobs();
};
extern UrlCoordinator* g_UrlCoordinator;
+3 -1
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)
{
+2 -2
View File
@@ -2,7 +2,7 @@
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32 NZBMESSAGE_SIGNATURE = 0x6E7A6228; // = "nzb-XX" (protocol version)
static const int32 NZBMESSAGE_SIGNATURE = 0x6E7A6231; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
+3 -1
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);
+47 -16
View File
@@ -90,7 +90,10 @@ void WebProcessor::Execute()
ParseUrl();
if (!CheckCredentials())
m_rpcRequest = XmlRpcProcessor::IsRpcRequest(m_url);
m_authorized = CheckCredentials();
if ((!g_Options->GetFormAuth() || m_rpcRequest) && !m_authorized)
{
SendAuthResponse();
return;
@@ -124,11 +127,12 @@ void WebProcessor::ParseHeaders()
{
if (char* pe = strrchr(p, '\r')) *pe = '\0';
debug("header=%s", p);
if (!strncasecmp(p, "Content-Length: ", 16))
{
m_contentLen = atoi(p + 16);
}
if (!strncasecmp(p, "Authorization: Basic ", 21))
else if (!strncasecmp(p, "Authorization: Basic ", 21) && Util::EmptyStr(m_authInfo))
{
char* authInfo64 = p + 21;
if (strlen(authInfo64) > sizeof(m_authInfo))
@@ -138,20 +142,39 @@ void WebProcessor::ParseHeaders()
}
m_authInfo[WebUtil::DecodeBase64(authInfo64, 0, m_authInfo)] = '\0';
}
if (!strncasecmp(p, "Accept-Encoding: ", 17))
else if (!strncasecmp(p, "X-Authorization: Basic ", 23))
{
char* authInfo64 = p + 23;
if (strlen(authInfo64) > sizeof(m_authInfo))
{
error("Invalid-request: auth-info too big");
return;
}
m_authInfo[WebUtil::DecodeBase64(authInfo64, 0, m_authInfo)] = '\0';
}
else if (!strncasecmp(p, "Accept-Encoding: ", 17))
{
m_gzip = strstr(p, "gzip");
}
if (!strncasecmp(p, "Origin: ", 8))
else if (!strncasecmp(p, "Origin: ", 8))
{
m_origin = p + 8;
}
if (!strncasecmp(p, "X-Auth-Token: ", 14))
else if (!strncasecmp(p, "Cookie: ", 8))
{
strncpy(m_authToken, p + 14, sizeof(m_authToken)-1);
m_authToken[sizeof(m_authToken)-1] = '\0';
debug("%s", p);
const char* tok = strstr(p, "Auth-Token=");
if (tok && tok[11] != ';' && tok[11] != '\0')
{
strncpy(m_authToken, tok + 11, sizeof(m_authToken)-1);
m_authToken[sizeof(m_authToken)-1] = '\0';
}
}
if (*p == '\0')
else if (!strncasecmp(p, "X-Forwarded-For: ", 17))
{
m_forwardedFor = p + 17;
}
else if (*p == '\0')
{
break;
}
@@ -159,7 +182,7 @@ void WebProcessor::ParseHeaders()
debug("URL=%s", *m_url);
debug("Authorization=%s", m_authInfo);
debug("X-Auth-Token=%s", m_authToken);
debug("Auth-Token=%s", m_authToken);
}
void WebProcessor::ParseUrl()
@@ -245,8 +268,10 @@ bool WebProcessor::CheckCredentials()
}
else
{
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
g_Options->GetControlPort(), m_connection->GetRemoteAddr(), m_authInfo, pw);
warn("Request received on port %i from %s%s, but username or password invalid (%s:%s)",
g_Options->GetControlPort(), m_connection->GetRemoteAddr(),
!m_forwardedFor.Empty() ? (char*)BString<1024>(" (forwarded for: %s)", *m_forwardedFor) : "",
m_authInfo, pw);
return false;
}
}
@@ -281,7 +306,7 @@ void WebProcessor::Dispatch()
return;
}
if (XmlRpcProcessor::IsRpcRequest(m_url))
if (m_rpcRequest)
{
XmlRpcProcessor processor;
processor.SetRequest(m_request);
@@ -333,12 +358,15 @@ void WebProcessor::SendAuthResponse()
{
const char* AUTH_RESPONSE_HEADER =
"HTTP/1.0 401 Unauthorized\r\n"
"WWW-Authenticate: Basic realm=\"NZBGet\"\r\n"
"%s"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
BString<1024> responseHeader(AUTH_RESPONSE_HEADER, Util::VersionRevision());
BString<1024> responseHeader(AUTH_RESPONSE_HEADER,
g_Options->GetFormAuth() ? "" : "WWW-Authenticate: Basic realm=\"NZBGet\"\r\n",
Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", *responseHeader);
@@ -417,7 +445,8 @@ void WebProcessor::SendBodyResponse(const char* body, int bodyLen, const char* c
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Max-Age: 86400\r\n"
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
"X-Auth-Token: %s\r\n"
"Set-Cookie: Auth-Type=%s\r\n"
"Set-Cookie: Auth-Token=%s\r\n"
"Content-Length: %i\r\n"
"%s" // Content-Type: xxx
"%s" // Content-Encoding: gzip
@@ -454,7 +483,9 @@ void WebProcessor::SendBodyResponse(const char* body, int bodyLen, const char* c
BString<1024> responseHeader(RESPONSE_HEADER,
m_origin.Str(),
m_serverAuthToken[m_userAccess], bodyLen, *contentTypeHeader,
g_Options->GetFormAuth() ? "form" : "http",
m_authorized ? m_serverAuthToken[m_userAccess] : "",
bodyLen, *contentTypeHeader,
gzip ? "Content-Encoding: gzip\r\n" : "",
Util::VersionRevision());
Loaded 100 of 206 files, more files were not shown because too many files have changed in this diff. Show more