Compare commits

...

509 Commits

Author SHA1 Message Date
Andrey Prygunkov
aa78a360fe version 10.1 2013-03-31 20:12:14 +00:00
Andrey Prygunkov
d014407ba4 updated ChangeLog 2013-03-31 14:37:57 +00:00
Andrey Prygunkov
2e8bfa16f9 fixed: articles with decoding errors (incomplete or damaged posts) caused infinite retry-loop in downloader 2013-03-31 14:36:15 +00:00
Andrey Prygunkov
bf34713b0c updated version string to 10.1 2013-03-31 14:10:57 +00:00
Andrey Prygunkov
c46c1a96cd updated version string (preparing to release 10.0) 2013-03-29 21:07:24 +00:00
Andrey Prygunkov
2693b62de4 if an obsolete option is found in the config file a warning is printed instead of an error and the program is not paused anymore 2013-03-23 15:07:25 +00:00
Andrey Prygunkov
18387f6d98 fixed: when the option <ContinuePartial> is active and there are partially downloaded files in queue, after reloading/restarting of the program the file may stuck with status <downloading>; trying to reload or quit the program in this state resulted in a crash (bug introduced in r599) 2013-03-22 22:07:23 +00:00
Andrey Prygunkov
08b7356184 adding of local files via web-interface now works in IE10 2013-03-18 21:43:01 +00:00
Andrey Prygunkov
e30cdfc176 addition to r602: fixed: if news servers from different levels were defined with the same group (bad config actually), download could hang when waiting for a free connection to a higher level server 2013-03-17 20:24:25 +00:00
Andrey Prygunkov
cc0ed38e68 refactor: removed dead code 2013-03-17 12:43:11 +00:00
Andrey Prygunkov
5ec0d20286 improvement in news-server/connection management: new option <ServerX.Group> allows more flexible configuration of news servers when using multiple accounts on the same server; with this option it's also possible to imitate the old server management behavior regarding levels as it was before r599 2013-03-17 12:21:46 +00:00
Andrey Prygunkov
e0aa69f605 improvement in news-server/connection management: do not reconnect on <article/group not found> errors since this doesn't help but unnecessary increases CPU load and network traffic 2013-03-16 15:24:01 +00:00
Andrey Prygunkov
c859f39036 addition to r599: fixed: download could be cancelled when waiting for a free connection to news server 2013-03-16 13:32:35 +00:00
Andrey Prygunkov
5251f62665 major improvement in news-server/connection management (main and fill servers): if download of article fails, the program tries all servers of the same level before trying higher level servers; this ensures that fill servers are used only if all main servers fail; this makes the configuring of multiple servers much easier than before: in most cases the simple configuration of level 0 for all main servers and level 1 for all fill servers suffices; in previous versions the level was increased immediately after the first tried server of the level failed; to make sure all main servers were tried before downloading from fill servers it was required to create complex server configurations with duplicates; these configurations were still not as effective as now 2013-03-14 22:30:59 +00:00
Andrey Prygunkov
2b87e2b221 fixed: download could be cancelled when waiting for a free connection on a second-(or higher)-level news server 2013-03-12 22:24:29 +00:00
Andrey Prygunkov
c185e9b487 removed unneeded code from configure-script 2013-03-12 21:33:45 +00:00
Andrey Prygunkov
b5d9a99f10 fixed: special characters (quotation marks, etc.) in unpack password and in configuration options were not displayed properly and could be discarded on saving 2013-03-12 20:28:03 +00:00
Andrey Prygunkov
fec67fe0ea fixed: some characters with codes below 32 were not properly encoded in JSON-RPC; sometimes output from unrar contained such characters and could break web-interface 2013-03-12 20:22:08 +00:00
Andrey Prygunkov
987997a986 fixed: when option <UnpackCleanupDisk> is active the unpacked archive-files (second level archives) could be deleled too (mostly affected 7-Zip archives but sometimes also rar-archives if the second level rar-files had same names as the first level rars) 2013-03-11 20:03:47 +00:00
Andrey Prygunkov
27ef79ca27 immediately clearing post-process progress label after unpack to avoid status update lag in web-interface 2013-03-11 20:00:49 +00:00
Andrey Prygunkov
bcc1932a37 addition to r586/r591: added automatic speed meter recalibration to recover after possible synchronisation errors which can occur when the option <AccurateRate> is not active; this makes the default (less accurate but fast) speed meter almost as good as the accurate one; important when speed throttling is active 2013-03-11 19:54:14 +00:00
Andrey Prygunkov
2e163e9986 reverted r586 <automatic recovery after synchronisation errors>: needs more testing 2013-03-10 22:50:34 +00:00
Andrey Prygunkov
5473e57b10 fixed: if an external program (unrar, pp-script, etc.) could not be started, the execute-function has returned code 255 although the code -1 were expected in this case; this could break designed post-processing flow 2013-03-10 22:19:46 +00:00
Andrey Prygunkov
b970bad058 imporved configure-script: 1) libs which are added via pkgconfig are now put into LIBS instead of LDFLAGS - improves compatibility with newer Linux linkers; 2) OpenSSL libs/includes are now added using pkgconfig to better handle dependencies; 3) additional check for libcrypto (part of OpenSSL) ensures the library is added to linker command even if pkgconfig is not used 2013-03-10 15:20:41 +00:00
Andrey Prygunkov
bea1814cb9 removed a line of unused code (introduced in r579) 2013-03-09 22:29:18 +00:00
Andrey Prygunkov
45e58f29b4 corrections in code formatting; no actual code changes 2013-03-09 22:23:58 +00:00
Andrey Prygunkov
f7a3df635a added automatic recovery after synchronisation errors which can occur in speed meter when the option <AccurateRate> is not active; this makes the <inaccurate/fast/default> speed meter much more reliable; important when speed throttling is active; a warning is printed to log to indicate detected error and reset of speed meter 2013-03-09 22:21:44 +00:00
Andrey Prygunkov
3f67984929 changed default value for option <ServerX.JoinGroup> to <no>; most news servers nowadays do not require joining the group and many servers do not keep headers for many groups making the join-command fail even if the articles still can be successfully downloaded 2013-03-09 14:51:48 +00:00
Andrey Prygunkov
bf82171baa removed hint <Post-processing script may have moved files elsewhere> from history dialog since it caused more questions than helped 2013-03-08 19:21:43 +00:00
Andrey Prygunkov
4c49a7f003 added link to wiki-article <Performance tips> to settings tab on web-interface 2013-03-08 19:10:34 +00:00
Andrey Prygunkov
57b5d40851 when post-processing-parameters are passed to the post-processing script a second version of each parameter with a normalized parameter-name is passed in addition to the original parameter name; in the normalized name the special characters <*> and <:> are replaced with <_> and all characters are passed in upper case; this is important for internal post-processing-parameters (*Unpack:=yes/no) which includes special characters 2013-03-07 16:45:41 +00:00
Andrey Prygunkov
ecde2d1627 improved post-processing script: better handling of nzb-files not having archive files 2013-03-07 16:45:08 +00:00
Andrey Prygunkov
b48e9a31f6 updated description of option <ServerX.Cipher> and added link to wiki article <Choosing a cipher> 2013-03-06 21:52:15 +00:00
Andrey Prygunkov
f64e5241ed improved the handling of hanging connections: if a connection hangs longer than defined by option <ConnectionTimeout> the program tries to gracefully close connection first (this is new); if it still hangs after <TerminateTimeout> the download thread is terminated as a last resort (as in previous versions) 2013-03-06 21:36:09 +00:00
Andrey Prygunkov
4e9d01055a news servers configuration is now less error-prone: 1) the option <ServerX.Level> is not required to start from <0> and when several news servers are configured the Levels can be any integers - the program sorts the servers and corrects the Levels to 0,1,2,etc. automatically if needed; 2) when option <ServerX.Connections> is set to <0> the server is ignored (in previous version such a server could cause hanging when the program was trying to go to the next level); 3) if no news servers are defined (or all definitions are invalid) a warning is printed to inform that the download is not possible 2013-03-06 21:35:31 +00:00
Andrey Prygunkov
9d4ca25499 updated VC++ project file 2013-03-04 22:16:25 +00:00
Andrey Prygunkov
f1ddf9dc2b addition to r568: corrected saving of diskstate for post-processor queue 2013-03-04 22:14:15 +00:00
Andrey Prygunkov
e99b790d58 added new option <ServerX.Cipher> to manually select cipher for encrypted communication with news server; manually choosing a faster cipher (such as <RC4>) can significantly improve performance (if CPU is a limiting factor) 2013-03-04 22:01:14 +00:00
Andrey Prygunkov
5e4a99c1ad addition to r572: changed the log-messages for deleting of 7-zip-files to <info> too 2013-03-04 21:28:37 +00:00
Andrey Prygunkov
c89824bf25 the log-messages <deleting file *file*> (when option <UnpackCleanupDisk> is active) and <moving file *file* to *destination*> are now printed as <info> instead of <detail> (since <detail> is for article related messages whereas <info> is more suitable for file related messages) 2013-03-04 20:28:27 +00:00
Andrey Prygunkov
1230d9cdd4 added fast renaming of intentionally misnamed (rar-) files; the new renaming algorithm doesn't require full par-scan and restores original filenames in just a few seconds, even on very slow computers (NAS, media players, etc.); the fast renaming is performed automatically when requested by the built-in unpacker (option <Unpack> must be active) 2013-03-04 19:55:36 +00:00
Andrey Prygunkov
d3dd8dc686 fixed a compilation warning 2013-03-03 20:48:14 +00:00
Andrey Prygunkov
382faa49cb added new option <InterDir> to put intermediate files during download into a separate directory (instead of storing them directly in destination directory (option <DestDir>); when nzb-file is completely (successfully) downloaded, repaired (if neccessary) and unpacked the files are moved to destination directory (option <DestDir> or <CategoryX.DestDir>); intermediate directory can significantly improve unpack performance if it is located on a separate physical hard drive 2013-03-01 20:32:17 +00:00
Andrey Prygunkov
5cf4b4663f addition to r568: corrected diskstate version check 2013-02-28 20:47:30 +00:00
Andrey Prygunkov
749b4d3083 when a history item is post-processed again and the archive files were previously deleted because of option <UnpackCleanupDisk> the post-processing goes directly to script stage; if the archive files were kept, the full post-processing including unpack is performed instead 2013-02-28 20:23:50 +00:00
Andrey Prygunkov
02835d057e fixed: remote commands <--list/-L> and <--connect/-C> showed download speed and speed limit in Bytes instead of KiloBytes (bug introduced in r544) 2013-02-15 08:04:23 +00:00
Andrey Prygunkov
f5aaaecc48 fixed: RPC-method <history> returned incorrect Par-Status (bug introduced in r563) 2013-02-14 15:06:10 +00:00
Andrey Prygunkov
184ff84f92 small change in example post-processing script: message <Deleting source ts-files> are now printed only if ts-files really existed 2013-02-13 19:47:48 +00:00
Andrey Prygunkov
ef56bc1f55 fixed: par-status <FAILED> was not correctly checked in the example post-processing script (bug introduced in r558) 2013-02-13 17:05:26 +00:00
Andrey Prygunkov
9846f7509e fixed: parameter <NZBPP_PARSTATUS> was not correctly passed to post-processing script when the download was repaired 2013-02-13 16:54:13 +00:00
Andrey Prygunkov
1cf7cefe83 updated VC++ project file 2013-02-12 22:12:43 +00:00
Andrey Prygunkov
b5f1dbc47b changed formatting of remaining time for post-processing to short format (as used for remaining download time) 2013-02-12 14:10:44 +00:00
Andrey Prygunkov
37b85491c3 fixed: RPC-method <history> returned bad results for URLs; impacts history tab in web-interface (bug introduced in r555) 2013-02-10 21:35:46 +00:00
Andrey Prygunkov
30d792b35b when running external programs (such as unrar or post-processing script) the full path to the program is not neccessary since the search in system PATH is now performed 2013-02-09 10:58:14 +00:00
Andrey Prygunkov
ac29412b2f updated example post-processing script: added check for nzbget version (at least 10.0) and option <Unpack>, small other corrections 2013-02-08 13:16:29 +00:00
Andrey Prygunkov
01c170afaf fixed a compilation warning 2013-02-07 19:32:07 +00:00
Andrey Prygunkov
539e0811c9 fixed compilation error on Linux 2013-02-07 19:30:11 +00:00
Andrey Prygunkov
940448ffae added built-in unpack: 1) rar and 7-zip formats are supported (via external Unrar and 7-Zip executables); 2) new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>, <UnrarCmd>, <SevenZipCmd>; 3) web-interface now shows progress and estimated time during unpack (rar only; for 7-Zip progress is not available due to limitations of 7-Zip) 4) when built-in unpack is enabled, the post-processing script is called after unpack and possibly par-check/repair (if needed); 5) for nzb-files containing multiple collections (par-sets) the post-processing script is called only once, after the last par-set; 6) new parameter <NZBPP_UNPACKSTATUS> passed to post-processing script; 7) if the option <AllowReProcess> is enabled the post-processing-script is called after each par-set (as in previous versions); 8) example post-processing script updated: removed unrar-code, added check for unpack status; 9) new field <UnpackStatus> in result of RPC-method <history>; 10) history-dialog in web-interface shows three status: par-status, unpack-status, script-status; 11) with two built-in special post-processing parameters <*Unpack:> and <*Unpack:Password> the unpack can be disabled for individual nzb-file or the password can be set; 12) built-in special post-processing parameters can be set via web-interface on page <PP-Parameters> (when built-in unpack is enabled). 2013-02-06 22:04:50 +00:00
Andrey Prygunkov
68a73f96c4 warning <Non-nzbget request received> now is not printed when the connection was aborted before the request signature was read 2013-01-31 20:49:52 +00:00
Andrey Prygunkov
87a93745cb when the par-checked requests more par-files, they get an extra priority and are downloaded before other files regardless of their priorities; this is needed to avoid hanging of par-checker-job if a file with a higher priority gets added to queue during par-check 2013-01-28 23:22:15 +00:00
Andrey Prygunkov
3b08abca10 refactor: extracted par-related code from module <PrePostProcessor> into new module <ParCoordinator> 2013-01-23 21:32:36 +00:00
Andrey Prygunkov
5e68096a2e added validation for option <CategoryX.DestDir>; removed a superfluous slash in the generated destination path 2013-01-22 22:16:18 +00:00
Andrey Prygunkov
e3ef11ceae improved error reporting for connection errors (especially on Windows) 2013-01-21 21:29:45 +00:00
Andrey Prygunkov
575fe8379f improved error reporting for connection errors when using OpenSSL 2013-01-21 21:28:51 +00:00
Andrey Prygunkov
60feae7e5b new feature <Pause for X Minutes> in web-interface; new XML-/JSON-RPC method <scheduleresume> 2013-01-21 21:19:04 +00:00
Andrey Prygunkov
2b45ecaea1 fixed: some XML-/JSON-RPC methods may return negative values for file sizes between 2-4GB; this had also influence on web-interface 2013-01-21 20:44:30 +00:00
Andrey Prygunkov
7a3a430137 fixed warning <file glyphicons-halflings.png not found> 2013-01-20 11:19:10 +00:00
Andrey Prygunkov
11c0563fe5 refactor: download speed and speed limit are now internally integers (Bytes) instead of floats (KB) 2013-01-18 21:36:17 +00:00
Andrey Prygunkov
00018b3e89 improved the automatic par-scan (option <ParScan=auto>) to significantly reduce the verify-time in some common cases with renamed rar-files: 1) the extra files are scanned in an optimized order; 2) the scan stops when all missings files are found 2013-01-17 22:08:01 +00:00
Andrey Prygunkov
de787a069d added support for HTTPS to the built-in web-server (web-interface and XML/JSON-RPC); new options <SecureControl>, <SecurePort>, <SecureCert> and <SecureKey>; Module <TLS.c/h> completely rewritten with support for servers-side sockets, newer versions of GnuTLS, proper thread lockings in OpenSSL 2013-01-17 19:07:13 +00:00
Andrey Prygunkov
5f33ea6013 merge from 9-branch: improved the post-processing script to better handle renamed rar-files 2013-01-15 22:00:44 +00:00
Andrey Prygunkov
ee74c4c17f refactor: reformatted TLS.c/.h 2013-01-13 17:46:11 +00:00
Andrey Prygunkov
b031f52ee2 replaced a browser error message when trying to add local files in IE9 with a better message dialog 2013-01-06 19:50:30 +00:00
Andrey Prygunkov
77bb01b18c small changes in libpar2-patches for compatibility with optware 2013-01-06 13:00:13 +00:00
Andrey Prygunkov
d2fdc28c85 refactor: reworked Connection-class: fully encapsulted sockets; better read/write methods 2012-12-30 15:27:38 +00:00
Andrey Prygunkov
d34e985a92 addition to r525: updated config.h.in 2012-12-20 22:04:24 +00:00
Andrey Prygunkov
f0c2c834c3 fixed: segfault if a category didn't have a destination directory defined (bug introduced in r524) 2012-12-20 22:00:46 +00:00
Andrey Prygunkov
ca0cce9401 addition to r525: corrected and updated configure-script 2012-12-19 21:52:24 +00:00
Andrey Prygunkov
2a0e211daf fixed: the reported line numbers for configuration errors were sometimes inaccurate 2012-12-19 20:37:07 +00:00
Andrey Prygunkov
ea89983e45 added full par-scan feature needed to par-check/repair files which were renamed after creation of par-files; new option <ParScan> to activate full par-scan (always or automatic); the automatic full par-scan activates if missing files are detected during par-check, this avoids unnecessary full scan for normal (not renamed) par sets 2012-12-19 20:16:17 +00:00
Andrey Prygunkov
e4ed1c8fd7 categories can now have their own destination directories 2012-12-18 22:29:24 +00:00
Andrey Prygunkov
d46155bf32 updated version string to 10.0-testing 2012-12-18 22:09:09 +00:00
Andrey Prygunkov
9bdf0d8937 updated version string in windows version 2012-12-18 21:45:45 +00:00
Andrey Prygunkov
f56c1226b6 corrected file properties 2012-12-09 13:21:18 +00:00
Andrey Prygunkov
bc3e4742f0 updated version string (preparing to release 9.0) 2012-12-09 13:15:23 +00:00
Andrey Prygunkov
891b16ac76 fixed: saving of file properties (priority or category) failed if a post-processing script having post-processing parameters were not used (bug introduced in r476) 2012-11-25 19:38:09 +00:00
Andrey Prygunkov
11a32c3537 updated Changelog 2012-11-25 15:57:28 +00:00
Andrey Prygunkov
8afc96e1ad updated Changelog 2012-11-23 22:28:48 +00:00
Andrey Prygunkov
55d2c9e49c updated README 2012-11-23 21:34:38 +00:00
Andrey Prygunkov
9baabee3fd fixed an issue on mobile safari where the click on time-label (which should bring the statistics dialog) was often registered as a click on speed-label (and showed the time limit dialog instead) 2012-11-21 21:05:45 +00:00
Andrey Prygunkov
ad20cb6644 improved the startup script <nzbgetd> so it can be directly used in </etc/init.d> without modifications 2012-11-21 20:24:17 +00:00
Andrey Prygunkov
04d2d92524 implemented function <Clear messages> in web-interface; added RPC-method <clearlog> 2012-11-20 20:42:00 +00:00
Andrey Prygunkov
6630a8c2a5 renamed subcommand <K> of command <--edit/-E> to <C> (the old subcommand is still supported for compatibility) 2012-11-16 21:27:37 +00:00
Andrey Prygunkov
67ee86eaeb made all command-line subcommands case insensitive (like it already was in <--edit/-E>-command) (example: <nzbget -L g> and <nzbget -L G> is the same) 2012-11-16 21:12:28 +00:00
Andrey Prygunkov
2fcfbc2e1a added new option <NzbAddedProcess> to setup a script called after a nzb-file is added to queue 2012-11-16 20:50:56 +00:00
Andrey Prygunkov
2bb1162adf corrected the help screen (nzbget --help) 2012-11-12 21:21:26 +00:00
Andrey Prygunkov
f5e0b67305 extended remote command <--append/-A> with optional parameters: <T> - adds the file/URL to the top of queue; <P> - pauses added files; <C category-name> - sets category for added nzb-file/URL; <N nzb-name> - sets nzb filename for added URL; the old switches <--category/-K> and <--top/-T> are deprecated but still supported for compatibility 2012-11-12 21:11:42 +00:00
Andrey Prygunkov
0b25fb5771 fixed: if the loading of settings tab were cancelled (by clicking on other tab) an error could appear 2012-11-12 19:22:15 +00:00
Andrey Prygunkov
27ff29329f added debug messages for speed meter 2012-11-12 19:21:34 +00:00
Andrey Prygunkov
b52cfbb602 fixed: version number wasn't displayed in about dialog 2012-11-12 19:21:01 +00:00
Andrey Prygunkov
c5a1c64a35 fixed: switching between tabs didn't work in IE10 2012-11-12 19:20:34 +00:00
Andrey Prygunkov
cbedd9bec5 extended browser check for IE<9 with a tip about compatibility mode 2012-11-12 19:19:52 +00:00
Andrey Prygunkov
7a90844970 fixed: edit commands for group/post/history didn't work properly (bug introduced in r487) 2012-11-12 19:18:55 +00:00
Andrey Prygunkov
45dcb72178 fixed compilation error on windows (bug introduced in r499) 2012-11-12 19:18:32 +00:00
Andrey Prygunkov
d23b5bb58b addition: fixed: the loading of configuration in web-interface failed if the program was started with parameter <-c> using relative path to config filename 2012-11-11 13:35:38 +00:00
Andrey Prygunkov
bf7de99182 fixed: the loading of configuration in web-interface failed if the program was started with parameter <-c> using relative path to config filename 2012-11-09 16:38:22 +00:00
Andrey Prygunkov
8ddfab4b47 now using minified versions of libraries for better performance and smaller size 2012-11-07 14:34:14 +00:00
Andrey Prygunkov
57c2dc2d65 updated README 2012-11-07 14:30:37 +00:00
Andrey Prygunkov
62236a38f3 added javascript error reporting - should help users to easily see browser compatibility issues 2012-11-07 14:28:14 +00:00
Andrey Prygunkov
25ab7bba02 small improvements in speed meter: 1) eliminated unneeded calls of time-function in standby mode (might help with hibernation issue on Synology NAS); 2) better speed metering on high CPU load caused by other programs (if nzbget has less CPU time) 2012-11-07 14:25:34 +00:00
Andrey Prygunkov
fe14f3ee0e fixed: RPC-method <listfiles> didn't work correctly if the parameter <NZBID> was set to <0> (bug introduced in r487) 2012-11-07 03:16:54 +00:00
Andrey Prygunkov
425120de94 fixed: trailing spaces were not discarded when the config file were loaded in web-interface 2012-11-07 03:12:25 +00:00
Andrey Prygunkov
2520c8d173 fixed compilation warning 2012-11-07 03:07:35 +00:00
Andrey Prygunkov
7b4ee1c44b fixed compilation error on older systems (bug introduced in r411) 2012-11-07 03:01:27 +00:00
Andrey Prygunkov
58a1dcd141 fixed compilation error on recent linux versions 2012-11-04 20:36:12 +00:00
Andrey Prygunkov
3b4f44f276 refactor: restructured the entire web-interface code 2012-11-03 07:41:44 +00:00
Andrey Prygunkov
ebcc06686c added editing of individual files of the group in web-interface (pause/resume/delete/reorder); added new command <FileReorder> to RPC-method <editqueue> to set the order of individual files in the group 2012-10-20 11:06:45 +00:00
Andrey Prygunkov
45b3a7dbcd temporary pausing the play animation if any modal dialog is shown (to avoid artifacts in safari) 2012-10-18 20:37:15 +00:00
Andrey Prygunkov
16f04f2255 fixed: the lockfile (option <LockFile>) was deleted after reloading (bug introduced in r463) 2012-10-18 20:31:32 +00:00
Andrey Prygunkov
a23fcbd095 addition: added processing of URLs starting with path <nzbget> (e.g. <http://localhost:6789/nzbget/>) as alias to the root path (e.g. <http://localhost:6789/>) in internal web-server to support reverse proxies lacking the ability to rewrite URL 2012-10-16 15:49:18 +00:00
Andrey Prygunkov
7491c0f7c4 added processing of URLs starting with path <nzbget> (e.g. <http://localhost:6789/nzbget/>) as alias to the root path (e.g. <http://localhost:6789/>) in internal web-server to support reverse proxies lacking the ability to rewrite URL 2012-10-15 20:40:32 +00:00
Andrey Prygunkov
83da75a5e5 fixed: error in GnuTLS-support on certain systems (bug introduced in r463) 2012-10-15 18:04:25 +00:00
Andrey Prygunkov
2474c32f60 addition: added indication of soft-pause state via orange border on play/pause button 2012-10-14 08:18:01 +00:00
Andrey Prygunkov
6910f1f0b7 added indication of soft-pause state via orange border on play/pause button 2012-10-14 07:32:37 +00:00
Andrey Prygunkov
c426aeac6a fixed: the web-interface was trying to load the post-processing configuration template file even if no post-processing script was used or when the script doesn't have a config file at all; this lead to warnings in the log (although harmless) (bug introduced in r476) 2012-10-08 03:51:45 +00:00
Andrey Prygunkov
0e2716ba31 fixed: the settings page failed to load when a post-procesing script with a config file was used and the post-processing configuration template file was not present in webui-directory (bug introduced in r476) 2012-10-07 16:40:41 +00:00
Andrey Prygunkov
011239d45c renamed options <ServerIP>, <ServerPort> and <ServerPassword> to <ControlIP>, <ControlPort> and <ControlPassword> to avoid confusion with news-server options <ServerX.Host>, <ServerX.Port> and <ServerX.Password>; the old option names are still recognized and are automatically renamed when the configuration is saved from web-interface; also renamed option <> to <MainDir> 2012-10-05 18:57:09 +00:00
Andrey Prygunkov
5bb3d1a9e1 added support for post-processing parameters in web-interface 2012-10-05 18:13:13 +00:00
Andrey Prygunkov
43766c7ab9 fixed: the status of active post-processing download was displayed as <PP-QUEUED> if the download has multiple par-sets 2012-10-02 15:23:33 +00:00
Andrey Prygunkov
e12eeed65d fixed: <make install> failed on BSD due to different syntax in <sed>-command 2012-10-01 20:15:05 +00:00
Andrey Prygunkov
711ecb4025 fixed: the size of small downloads (less than 100 MB) was not printed properly in web-interface 2012-10-01 19:50:19 +00:00
Andrey Prygunkov
88957699c5 fixed: unrar failure was not always properly detected causing the post-processing to delete not yet unpacked rar-files 2012-10-01 19:41:13 +00:00
Andrey Prygunkov
fcd6c51d55 categories available in web-interface are now configured in program configuration file (nzbget.conf) instead of a separate file <webui/categories.txt> and can therefore be added and changed via web-interface on settings page 2012-09-30 19:58:58 +00:00
Andrey Prygunkov
adda02dd0d updated descriptions in example configuration file 2012-09-29 19:56:19 +00:00
Andrey Prygunkov
ab1cff2a7d added <free disk space> to dialog <statistics and status> in web-interface 2012-09-29 19:54:54 +00:00
Andrey Prygunkov
0716a743d8 fixed: free disk space reported incorrectly on some OSes 2012-09-29 19:52:18 +00:00
Andrey Prygunkov
d0e17fde77 the status of post-processing and directory scan is now displayed as <disabled> if the related options in config file are not set 2012-09-28 19:29:03 +00:00
Andrey Prygunkov
d6c0aa8a80 the priority of nzb-file can now be set when adding local-file via web-interface; JSON/XML-RPC method <append> extended with parameter <priority> 2012-09-28 19:21:34 +00:00
Andrey Prygunkov
815bf9b390 fixed: added workaround for bug in iOS 6 safari caching POST-requests 2012-09-28 19:04:11 +00:00
Andrey Prygunkov
0aa6e0a8b2 all images are now provided with HiDPI versions in addition to standard versions; the HiDPI images are activated automatically on retina displays (requires webkit browser) 2012-09-27 20:59:47 +00:00
Andrey Prygunkov
8b1aff33fe added remote command <--reload/-O> and JSON/XML-RPC method <reload> to reload configuration from disk and reintialize the program; the reload can be performed from web-interface 2012-09-27 20:13:25 +00:00
Andrey Prygunkov
fdc9464576 added subcommand <W> to remote command <-S/--scan> to scan syncronously (wait until scan completed); added parameter <SyncMode> to XML/JSON-RPC method <scan>; the command <Scan> in web-interface now waits for completing of scan before reporting the status 2012-09-19 18:42:13 +00:00
Andrey Prygunkov
dc6c1a0fe1 with active option <AllowReProcess> the NZB considered completed even if there are paused non-par-files (the paused non-par-files are treated the same way as paused par-files): as a result the reprocessable script is called 2012-09-18 02:47:44 +00:00
Andrey Prygunkov
78a73ac15f added missing turtle icon 2012-09-18 02:32:39 +00:00
Andrey Prygunkov
48891ed7c7 many improvements in web-interface UI: main tabs are better distinguishable; separate tab headers removed; handbrake button moved to navbar and renamed to pause/resume-button; animation on pause/resume-button better shows current state; two other important info-elements <current speed> and <remaining time> moved to the navbar as well; the search-edit moved to navbar too; the refresh-button has animation; the navbar is now fixed to the top on big screens; the speed limit is now set via click on <current speed> info; <statistics and status> are accessible via click on <remaining time>; the scan-button moved to add-dialog; due to reduced number of toolbar buttons on the downloads-tab the ability to hide buttons on the toolbar were removed (not neccessary anymore); the phone-theme is now less cluttered; added editing of nzbget and post-processing script settings; the settings-tab is searchable like other tabs; added new XML/JSON-RPC methods <config>, <loadconfig> and <saveconfig>; 2012-09-16 11:38:44 +00:00
Andrey Prygunkov
d3fd5ba9ac fixed: url-downloads could fail when compiled with gzip-support (bug introduced in r440) 2012-09-08 06:40:59 +00:00
Andrey Prygunkov
2b6f575802 set svn keywords 2012-08-11 10:37:14 +00:00
Andrey Prygunkov
f604460d56 set svn keywords 2012-08-06 20:32:48 +00:00
Andrey Prygunkov
7472893e8e added built-in web-interface; new option <WebDir> 2012-08-04 13:13:49 +00:00
Andrey Prygunkov
eff074faae <index.html> is now returned by web-server for every directory-request, not only for the root one (</>) 2012-07-31 18:34:50 +00:00
Andrey Prygunkov
07c04b40b1 </index.html> is now returned by web-server when the root path </> is requested 2012-07-30 20:46:35 +00:00
Andrey Prygunkov
754adb545e eliminated few compiler warnings 2012-07-28 13:37:37 +00:00
Andrey Prygunkov
5fc04277c1 updated VC-project 2012-07-28 13:36:44 +00:00
Andrey Prygunkov
78f5fd3f71 fixed compilation error on linux (bug introduced in r449) 2012-07-18 21:27:03 +00:00
Andrey Prygunkov
0277c6b9bd improved handling of configuration errors: the program now does not terminate on errors but rather logs all of them and uses default option values 2012-07-16 20:42:33 +00:00
Andrey Prygunkov
e34b4b8ae7 fixed: RPC-method <log(0, IdFrom)> could return wrong results if the log was filtered with options <XXXTarget> 2012-07-16 20:10:21 +00:00
Andrey Prygunkov
c0de18f3aa fixed line endings 2012-07-16 19:41:33 +00:00
Andrey Prygunkov
4b78918347 fixed incompatibility with OpenSLL 1.0 (thanks to OpenWRT team for the patch) 2012-07-15 12:10:19 +00:00
Andrey Prygunkov
6606a883c5 improved the automatic installation (<make install>) to install all necessary files (not only the binary as it was before) 2012-07-14 20:04:11 +00:00
Andrey Prygunkov
30c1a64d31 renamed example configuration file and postprocessing script to make the installation easier 2012-07-14 13:53:28 +00:00
Andrey Prygunkov
91dbcc40aa fixed: remote command <-E/--edit> with option <GN> or <FN> did not work 2012-07-11 19:48:58 +00:00
Andrey Prygunkov
b0f5119ec0 added authorization via URL in RPC-server (example: http://localhost:6789/username:password/jsonrpc) 2012-07-10 20:04:13 +00:00
Andrey Prygunkov
ed9aba18b8 added processing of http-request <OPTIONS> in RPC-server for better support of cross domain requests 2012-07-10 20:00:13 +00:00
Andrey Prygunkov
d507325378 added gzip-support to URL-downloader 2012-07-09 20:56:28 +00:00
Andrey Prygunkov
0a0546168b fixed memory leak in gzip-support (bug introduced in r436) 2012-07-05 19:09:28 +00:00
Andrey Prygunkov
5abbbe80d1 refactor: reordered classes 2012-07-05 16:47:29 +00:00
Andrey Prygunkov
4a6413f654 fixed error in configure script (bug introduced in r436) 2012-07-04 18:12:51 +00:00
Andrey Prygunkov
6c60244b26 added gzip-support to built-in web-server 2012-07-03 20:34:36 +00:00
Andrey Prygunkov
a384f0e6e9 prevent duplicate nzb-entries in the history 2012-07-03 20:21:11 +00:00
Andrey Prygunkov
2a56410543 improved performance of RPC-command <listgroups> 2012-07-02 20:04:35 +00:00
Andrey Prygunkov
7ef22fc1e0 fixed compilation error on linux (bug introduced in r432) 2012-07-01 19:54:45 +00:00
Andrey Prygunkov
5051d698c0 implemented built-in web-server 2012-07-01 19:31:06 +00:00
Andrey Prygunkov
1a87c08bc2 changed version naming scheme by removing the leading zero: current version is now called 9.0 instead of 0.9.0 (it's really the 9th major version of the program) 2012-07-01 17:23:08 +00:00
Andrey Prygunkov
ed0c5908ce fixed few compiler warnings 2012-07-01 14:56:28 +00:00
Andrey Prygunkov
f571ced9c5 when adding url via RPC the supplied filename (if not empty) has precedence over the original file name 2012-06-24 17:00:51 +00:00
Andrey Prygunkov
3110181a9f fixed: when adding url the nzb name was not set properly (bug introduced in r419) 2012-06-24 16:07:16 +00:00
Andrey Prygunkov
fcb7966f70 fixed: segfault in remote command <--list/-L> when used without subcommands (bug introduced in r422) 2012-06-23 21:18:47 +00:00
Andrey Prygunkov
be2945a16f fixed a resource leak (socket) which could occur when an active download was deleted from queue 2012-06-23 19:29:43 +00:00
Andrey Prygunkov
6b3326ad42 restored accidental change of Connection.cpp in r423 (should be commited as a separate changeset) 2012-06-23 19:27:36 +00:00
Andrey Prygunkov
3e81a03087 refactor: corrected inconsistent include of <config.h> 2012-06-23 18:58:56 +00:00
Andrey Prygunkov
3778430ead in remote command <--list/-l> with subcommands <GR> and <FR> the regex-matching is now performed on the server; that ensures the list-command selects the same records as the edit-command (when server and client have different implementations of POSIX ERE) 2012-06-22 18:30:14 +00:00
Andrey Prygunkov
31bd251f37 added support for regular expressions (POSIX ERE Syntax) in remote commands <--list/-L> and <--edit/-E> using new subcommands <GR> and <FR> 2012-06-20 22:53:03 +00:00
Andrey Prygunkov
f49f01ec85 refactor: splitted class <Util> into <Util> and <Webtil> 2012-06-20 22:15:09 +00:00
Andrey Prygunkov
1bd6721af9 added options <GN> and <FN> for remote command <--edit/-E>. With these options the name of group or file can be used in edit-command instead of file ID 2012-06-19 15:08:23 +00:00
Andrey Prygunkov
4a069266d8 added new field <name> to nzb-info-object. It is initially set to the cleaned up name of the nzb-file. The renaming of the group changes this field. All RPC-methods related to nzb-object return the new field, the old field <NZBNicename> is now deprecated. The option <MergeNZB> now checks the <name>-field instead of <nzbfilename> (the latter is not changed when the nzb is renamed). New env-var-parameter <NZBPP_NZBNAME> for post-processing script. 2012-06-11 15:09:03 +00:00
Andrey Prygunkov
d10d7f3f02 fixed: RPC-Command <history> were not returning the UrlStatus correctly in JSON-RPC (bug introduced in r414) 2012-06-08 11:06:00 +00:00
Andrey Prygunkov
05adbb1325 fixed: by adding a failed URL to the history it was added to the end instead of the top of the history (bug introduced in r414) 2012-06-08 11:04:23 +00:00
Andrey Prygunkov
ca8719b42b fixed: after renaming of a group, the new name was not displayed by remote commands <-L G> and <-C in curses mode> 2012-05-27 12:59:51 +00:00
Andrey Prygunkov
ec80850e76 improved error reporting when trying to download a HTTPS-URL and the program was compiled without TLS/SSL support 2012-05-04 14:55:49 +00:00
Andrey Prygunkov
ab75a8b3e5 added the ability to queue URLs. The program automatically downloads nzb-files from given URLs and put them to download queue. When multiple URLs are added in a short time, they are put into a special URL-queue. The number of simultaneous URL-downloads are controlled via new option UrlConnections. With the new option ReloadUrlQueue can be controlled if the URL-queue should be reloaded after the program is restarted (if the URL-queue was not empty). New switch <-U> for remote-command <--append/-A> to queue an URL. New subcommand <-U> in the remote command <--list/-L> prints the current URL-queue. If URL-download fails, the URL is moved into history. With subcommand <-R> of command <--edit> the failed URL can be returned to URL-queue for redownload. The remote command <--list/-L> for history can now print the infos for URL history items. New XML/JSON-RPC command <appendurl> to add an URL or multiple URLs for download. New XML/JSON-RPC command <urlqueue> returns the items from the URL-queue. The XML/JSON-RPC command <history> was exteneded to provide infos about URL history items. The URL-queue obeys the pause-state of download queue. The URL-downloads support HTTP and HTTPS protocols. 2012-05-03 13:47:44 +00:00
Andrey Prygunkov
d00c8119fa removed references to <NetAddress.c/h> from VS-Project 2012-05-03 11:00:54 +00:00
Andrey Prygunkov
2b7d188677 removed NetAddress.cpp/h 2012-05-03 10:53:18 +00:00
Andrey Prygunkov
12c09693bd refactoring: removed class <NetAddress>. That makes <Connection>-class more transparent and easier to use. The TLS-initializing moved from <NNTPConnection> to <Connection> 2012-05-03 10:51:13 +00:00
Andrey Prygunkov
87793b3dc3 updated version string to 0.9.0-testing 2012-05-03 10:23:00 +00:00
Andrey Prygunkov
7ce8c0b966 updated version string, ChangeLog and README (preparing to release 0.8.0) 2012-04-29 16:15:25 +00:00
Andrey Prygunkov
a831944b14 added the automatic configuring of required signal handling logic to better support BSD without breaking the compatibility with certain Linux systems 2012-03-16 22:03:55 +00:00
Andrey Prygunkov
17533d2c61 fixed a compatibility issue with OpenBSD (and possibly other BSD based systems) 2012-01-14 20:06:53 +00:00
Andrey Prygunkov
bee1d6beed fixed the incorrect displaying of sizes bigger than 4 GB on 64 bit systems 2012-01-12 21:25:08 +00:00
Andrey Prygunkov
a49ae076a5 improved the parsing of filename from article subject 2012-01-01 20:51:23 +00:00
Andrey Prygunkov
4856503a33 fixed: par-repair could fail when the filenames were not correctly parsed from article subjects 2011-12-29 21:30:25 +00:00
Andrey Prygunkov
0d4560f54e fixed a compilation error on some windows versions 2011-12-29 14:04:11 +00:00
Andrey Prygunkov
c5f7c2ace7 fixed spelling errors in the example configuration file 2011-12-29 14:02:10 +00:00
Andrey Prygunkov
f8c03fa48c fixed a bug causing error on decoding of input data in JSON-RPC 2011-11-18 20:46:48 +00:00
Andrey Prygunkov
8b6b34d7c0 fixed incorrect displaying of group sizes between 2GB and 4GB on many 64-bit OSes 2011-06-15 07:27:44 +00:00
Andrey Prygunkov
1fd7001e0c corrected a spelling error 2011-05-24 12:57:47 +00:00
Andrey Prygunkov
2631550c2f corrected the address of Free Software Foundation in copyright notice; corrected the spelling of authors name (caused by new rules for translating of cyrillic names to latin alphabet / english spelling) 2011-05-24 12:52:41 +00:00
Andrey Prygunkov
01734fadcf fixed incorrect displaying of group sizes bigger than 4GB on many 64-bit OSes 2011-03-30 21:04:58 +00:00
Andrey Prygunkov
33cead0b03 updated descriptions in example config file 2011-03-24 08:36:24 +00:00
Andrey Prygunkov
08f86cbcf9 added priorities; new action <I> for remote command <--edit/-E> to set priorities for groups or individual files; new actions <SetGroupPriority> and <SetFilePriority> of RPC-command <EditQueue>; remote command <--list/-L> prints priorities and indicates files or groups being downloaded; ncurses-frontend prints priorities and indicates files or groups being download; new command <PRIORITY> to set priority of nzb-file from nzbprocess-script; RPC-commands <ListGroups> and <ListFiles> return priorities and indicate files or groups being downloaded 2011-03-12 11:48:13 +00:00
Andrey Prygunkov
a83b96c74d fixed compilation error on Posix (bug introduced in r391) 2011-03-04 11:11:35 +00:00
Andrey Prygunkov
7cf0ddc81b eliminated small memory leak (bug introduced in r391) 2011-03-01 11:56:26 +00:00
Andrey Prygunkov
41640b5215 added new option <AccurateRate>, which enables syncronisation in speed meter; that makes the indicated speed more accurate by eliminating measurement errors possible due thread conflicts; thanks to anonymous nzbget user for the patch 2011-02-05 15:48:59 +00:00
Andrey Prygunkov
e1abedbfa4 fixed: article IDs containing special xml-characters were not parsed correctly (bug introduced in r386) 2010-12-07 16:22:31 +00:00
Andrey Prygunkov
a1482b9781 added renaming of groups; new subcommand <N> for command <--edit/-E>; new action <SetName> for RPC-method <editqueue> 2010-08-11 13:30:34 +00:00
Andrey Prygunkov
8a6d6ae771 option <DirectWrite> is now efficiently works on Windows with NTFS partitions 2010-07-23 14:42:16 +00:00
Andrey Prygunkov
a67e8314af added URL-based-authentication as alternative to HTTP-header authentication for XML- and JSON-RPC 2010-07-18 12:46:28 +00:00
Andrey Prygunkov
f5e7497913 fixed: nzb-files containing umlauts and other special characters could not be parsed - replaced XML-Reader with SAX-Parser; the issue was fixed only on POSIX 2010-07-05 14:52:42 +00:00
Andrey Prygunkov
e4c7c601d7 updated version string to 0.8.0-testing 2010-06-10 15:23:42 +00:00
Andrey Prygunkov
02fdc78066 updated version string in other file (preparing to release 0.7.0) 2010-04-26 21:04:08 +00:00
Andrey Prygunkov
d1a4521396 updated version string, ChangeLog and README (preparing to release 0.7.0) 2010-04-25 13:38:08 +00:00
Andrey Prygunkov
646ddb4ddb fixed: elapsed time for active post-processing job was calculated incorrectly if the post-processor queue was paused; that could cause the interruption of active par-job when using the option <ParTimeLimit> 2010-02-08 16:08:10 +00:00
Andrey Prygunkov
8578078f7c added second pause register, which is independent of main pause-state and therfore is intended for usage from external scripts; that allows to pause download without interferring with options <ParPauseQueue> and <PostPauseQueue> and scheduler tasks <PauseDownload> and <UnpauseDownload> - they all work with first (default) pause register; new subcommand <D2> for commands <--pause/-P> and <--unpause/-U>; new RPC-command <pausedownload2> and <resumedownload2>; existing RPC-commands <pause> und <resume> renamed to <pausedownload> and <resumedownload>; new field <Download2Paused> in result struct for RPC-command <status>; existing fields <ServerPaused> and <ParJobCount> renamed to <DownloadPaused> and <PostJobCount>; old RPC-commands and fields still exist for compatibility; the status output of command <--list/-L> indicates the state of second pause register; key <P> in curses-frontend can unpause second pause-register 2010-01-30 14:43:58 +00:00
Andrey Prygunkov
ab204281ed fixed: script-status was not reset by <post-process again> 2010-01-29 09:53:57 +00:00
Andrey Prygunkov
31940d8f58 nzbprocess-script (option <NZBProcess>) can now set category and post-processing parameters for nzb-file 2010-01-29 09:34:44 +00:00
Andrey Prygunkov
98874790fc corrected line endings in one source file 2010-01-29 08:54:20 +00:00
Andrey Prygunkov
5cd476687e fixed: command <list history> (nzbget -L H) didn't work (bug introduced in r373) 2010-01-29 08:53:04 +00:00
Andrey Prygunkov
73449e4407 updated README 2010-01-28 19:11:17 +00:00
Andrey Prygunkov
eae06a4145 fixed: nothing was downloaded when the option <Retries> was set to <0> 2010-01-18 10:59:26 +00:00
Andrey Prygunkov
5a8d56c2b4 fixed: when option <DaemonUserName> was specified and nzbget was started as root, the lockfile was not removed 2009-12-29 13:13:12 +00:00
Andrey Prygunkov
da6ccb6310 added files <nzbgetd> and <nzbget-shell.bat> to makefile 2009-12-27 21:11:38 +00:00
Andrey Prygunkov
b0a8d04f97 added debian style init script; thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script 2009-12-27 21:07:04 +00:00
Andrey Prygunkov
99acfc9641 added shell batch file for windows (thanks to orbisvicis <orbisvicis@users.sourceforge.net>) for the script 2009-12-26 22:48:12 +00:00
Andrey Prygunkov
a731b606c5 fixed: RPC-method <append> did not work properly on Posix systems (it worked only on Windows) 2009-12-10 22:43:54 +00:00
Andrey Prygunkov
c99b369b56 fixed line endings 2009-12-08 22:26:21 +00:00
Andrey Prygunkov
0f41e6053d fixed: base64 decoding function used by RPC-method <append> sometimes failed, in particular when called from Ruby-language 2009-12-08 22:25:23 +00:00
Andrey Prygunkov
b2ffddd84d changed the sleep-time during the throttling of bandwidth from 200ms to 10ms in order to achieve better uniformity 2009-12-08 21:55:39 +00:00
Andrey Prygunkov
1a9451fe61 command <pause post-processing> now not only pauses the post-processing queue but also pauses the current post-processing job (par-job or script-job); however the script-job can be paused only after the next line printed to screen 2009-12-08 21:50:53 +00:00
Andrey Prygunkov
0859eef869 fixed line endings in two source files 2009-10-16 11:55:47 +00:00
Andrey Prygunkov
1ad28cb9ac added the returning of a proper HTTP error code if the authorization was failed on RPC-calls (thanks to jdembski <jdembski@users.sourceforge.net> for the patch) 2009-10-16 11:54:15 +00:00
Andrey Prygunkov
0f8aa5d7e7 added support for JSON-P (extension of JSON-RPC) 2009-10-16 11:26:45 +00:00
Andrey Prygunkov
5bf680a1cf eliminated few compiler warnings on GCC 2009-10-09 20:18:30 +00:00
Andrey Prygunkov
e9d599ab26 fixed: JSON-RPC-commands failed, if parameters were placed before method name in the request 2009-10-09 20:14:33 +00:00
Andrey Prygunkov
dc83e66f72 improvement in example post-processing script: added check for existence of <unrar> and command <wc> 2009-10-09 19:22:31 +00:00
Andrey Prygunkov
5cf16dc9fd in <curses> and <colored> output-modes the download speed is now printed with one decimal digit when the speed is lower than 10 KB/s 2009-09-30 20:26:47 +00:00
Andrey Prygunkov
e076ac1a9c improved example post-processing script: added the check for existence of destination directory to return a proper ERROR-code (important for reprocessing of history items) 2009-09-27 11:24:56 +00:00
Andrey Prygunkov
7a28932569 fixed: RPC-commands <HistoryReturn> and <HistoryProcess> were processed as <HistoryDelete> 2009-09-26 20:44:11 +00:00
Andrey Prygunkov
5567c5abf6 fixed: reprocessing of a history item, which doesn't have any files left, caused a seg. fault 2009-09-26 19:54:53 +00:00
Andrey Prygunkov
b2e2c5c173 clearing the list of completeded files when adding a nzb-file to history if the cleaning up of queue (option <ParCleanupQueue>) is enabled (since there no more files in the nzb-file left, it cannot be returned to download queue; the list of completed files is not needed anymore) 2009-09-26 16:01:44 +00:00
Andrey Prygunkov
e41731ce23 by saving the queue to disk now using relative paths for the list of compeled files to reduce the file's size 2009-09-26 15:21:11 +00:00
Andrey Prygunkov
b1b6d6ace8 added field <PostTime> to result of RPC-Command <listfiles> and fields <MinPostTime> and <MaxPostTime> for command <listgroups> 2009-09-22 20:40:40 +00:00
Andrey Prygunkov
97b5a4c304 corrected few spelling errors in the example configuration file 2009-09-20 15:57:11 +00:00
Andrey Prygunkov
0722417e0b added a new return code <95/POSTPROCESS_NONE> for post-processing scripts for cases when pp-script skips all post-processing work (typically upon a user's request via a pp-parameter); modified the example post-processing script to return the new code instead of a error code when a pp-parameter <PostProcess> was set to <no> 2009-09-20 15:24:19 +00:00
Andrey Prygunkov
dc9e938510 Added message log to history items; new field <Log> is returned by RPC-command <history> for each item 2009-09-19 15:49:08 +00:00
Andrey Prygunkov
c37b8f37e2 fixed: the discarding of download queue did not work (bug introduced in r346) 2009-09-19 14:33:41 +00:00
Andrey Prygunkov
80116b6687 renamed ParStatus constant <FAILED> to <FAILURE> for a consistence with ScriptStatus constant <FAILURE>, that also affects the results of RPC-command <history> 2009-08-23 12:32:46 +00:00
Andrey Prygunkov
7d5225b2ba added field <RemainingFileCount> to history items returned via RPC-command <history> 2009-08-23 12:18:44 +00:00
Andrey Prygunkov
f2f318b11e fixed: returning the postprocessing exit code <92 - par-check all collections> when there were no par-files results in endless calling of postprocessing script 2009-08-22 13:31:03 +00:00
Andrey Prygunkov
6bfe1b0cfd added actions <R> (return history item) and <P> (postprocess history item) for subcommand <H> of command <-E/--edit>; added actions <HistoryReturn> and <HistoryProcess> for remote RPC-command <editqueue> 2009-08-22 12:05:25 +00:00
Andrey Prygunkov
f807cca8c7 modified example postprocessing script to not use the command <dirname>, which is not always available (thanks to Ger Teunis for the patch) 2009-07-26 11:35:38 +00:00
Andrey Prygunkov
feadf59fa0 added history: new option <KeepHistory>, new remote subcommand <H> for commands <L> (list history entries) and <E> (delete history entries), new RPC-command <History> and subcommand <HistoryDelete> for command <EditQueue>; changed the result code returning status <ERROR> for postprocessing script from <1> to <94> 2009-06-30 16:08:41 +00:00
Andrey Prygunkov
10e64e04fe fixed: option <ContinuePartial> did not work 2009-06-24 17:09:19 +00:00
Andrey Prygunkov
c2cbd502ea fixed: remote command <-S/--scan> caused the processing of already scanned files (bug introduced in r328) 2009-06-20 20:58:22 +00:00
Andrey Prygunkov
cf5fd8064b debug-messages generated on early stages during initializing are now printed to screen/log-file; messages about obsolete options are now printed to screen/log-file 2009-06-14 15:57:01 +00:00
Andrey Prygunkov
1a417b9d63 fixed: discarding of download queue (option <reloadqueue=no>) didn't work (bug introduced in r320 2009-06-13 16:47:26 +00:00
Andrey Prygunkov
123cfe6a38 added <binary> flag by opening of all files to explicitly set binary mode for compatibility with OSes which do not do that by default (like Posix does) 2009-06-11 21:13:37 +00:00
Andrey Prygunkov
43c0681d35 added the detection of <socklen_t> type to configure script (to remove OS2-specific code from <nzbget.h>); changed configure script to avoid unneded tests when debugging is disabled 2009-06-11 12:39:06 +00:00
Andrey Prygunkov
2ec5784a44 improvements in example postprocessing script: 1) if download contains only par2-files the script do not delete them during cleanup; 2) if download contains only nzb-files the script moves them to incoming nzb-directory for further download 2009-06-10 16:28:54 +00:00
Andrey Prygunkov
d59f4229d6 set the default compiler in configure script to <C++> instead of <C> for better error detection and because nzbget does not need C-compiler 2009-06-09 19:29:39 +00:00
Andrey Prygunkov
69c8ac3942 fixed compatibility issues with OS/2 2009-06-07 18:32:24 +00:00
Andrey Prygunkov
acbc9370f5 added support for platforms without IPv6 (they do not have <getaddrinfo>) 2009-06-07 16:53:32 +00:00
Andrey Prygunkov
f3563e8b4a improved configure script for better detection of libpar2 2009-06-05 17:31:47 +00:00
Andrey Prygunkov
5484ccc0f0 added field <NZBID> to NZBInfo; the field is now also returned by XML-/JSON-RPC methods <listfiles>, <listgroups> and <postqueue> 2009-06-03 16:07:00 +00:00
Andrey Prygunkov
34fe0f1077 refactoring: NZBInfo-objects now holds the status of completed post-process jobs; that made the list of completed post jobs unnecessary 2009-06-03 16:00:35 +00:00
Andrey Prygunkov
5039cbb529 improved the detection of new files in incoming nzb directory: now the scanner does not rely on system datum, but tracks the changing of file sizes during a last few (<NzbDirFileAge>) seconds instead 2009-05-31 10:11:19 +00:00
Andrey Prygunkov
93a9876de0 removed unneeded time functions 2009-05-30 22:18:31 +00:00
Andrey Prygunkov
ae311718da corrected includes for <time.h> 2009-05-30 22:09:41 +00:00
Andrey Prygunkov
441945d7e6 improved formatting of groups and added time info in curses output mode 2009-05-30 16:36:49 +00:00
Andrey Prygunkov
419b7fcb2c refactoring: moved the code for checking the incoming nzb directory from <PrePostProcessor> into the new module <Scanner> 2009-05-29 22:37:51 +00:00
Andrey Prygunkov
0692547440 redesigned server pool and par-checker to avoid using of semaphores; updated makefile to include postprocess-example.conf 2009-05-27 21:09:08 +00:00
Andrey Prygunkov
6471928f91 added subcommand <S> to remote commands <--pause/-P> and <--unpause/-U> to pause/unpause the scanning of incoming nzb-directory; added commands <PauseScan> and <UnpauseScan> for scheduler option <TaskX.Command>; added remote commands <PauseScan> and <ResumeScan> for XML-/JSON-RPC 2009-05-25 20:18:20 +00:00
Andrey Prygunkov
1866295d5d refactoring: removed references to OS-specific includes and types from <Thread.h> in order to isolate other units from these specifics 2009-05-24 17:03:34 +00:00
Andrey Prygunkov
4b538b419a refactoring: extended the meaning of DowloadQueue-object, which now contains file queue, post-job queue, list of completed post-jobs and list of nzb-infos; post-jobs now have references to nzb-infos, what eiliminates duplicate infos; since file queue and post-job queue are now accessed via DownloadQueue only one locking mechanism for both queues is needed 2009-05-24 14:26:53 +00:00
Andrey Prygunkov
72c19c08b8 improved error reporting while parsing nzb-files 2009-05-18 22:38:56 +00:00
Andrey Prygunkov
57b5afa676 fixed: environment block was not passed correctly to child process, what could result in seg faults (windows only) 2009-05-16 18:46:51 +00:00
Andrey Prygunkov
a5b8dfc1a3 changed the command line syntax for requesting of post-processor queue from <-O> to <-L O> for consistency with other post-queue related commands (<-P O>, <-U O> and <-E O>) 2009-05-15 20:22:03 +00:00
Andrey Prygunkov
9b87a3b755 imporved example postprocessing script: added support for external configuration file, postprocessing parameters and configuration via web-interface 2009-05-15 16:45:16 +00:00
Andrey Prygunkov
191b8531be option <TaskX.Process> now can contain parameters which must be passed to the script 2009-05-08 16:57:12 +00:00
Andrey Prygunkov
b1f6735e87 added pausing/resuming for post-processor queue; added new modifier <O> to remote commands <--pause/-P> and <--unpause/-U>; added new commands <postpause> and <postresume> to XML-/JSON-RPC; extended output of remote command <--list/-L> to indicate paused state of post-processor queue; extended command <status> of XML-/JSON-RPC with field <PostPause> 2009-05-06 19:28:54 +00:00
Andrey Prygunkov
900968a91c corrected output redirections in example post-processing script 2009-05-04 18:09:54 +00:00
Andrey Prygunkov
1e25d93a05 fixed: seg. fault in service mode on program start (windows only) 2009-05-04 16:24:17 +00:00
Andrey Prygunkov
bbdfb2b4f2 added modifier <O> to command <-E/--edit> for editing of post-processor-queue; following subcommands are supported: <+/-offset>, <T>, <B>, <D>; subcommand <D> supports deletion of queued post-jobs and active job as well; deletion of active job means the cancelling of par-check/repair or terminating of post-processing-script (including child processes of the script); updated remote-server to support new edit-subcommands in XML/JSON-RPC 2009-05-03 17:14:52 +00:00
Andrey Prygunkov
93ebcbac6c made example post-processing script a little bit simpler and more readable 2009-04-27 17:23:38 +00:00
Andrey Prygunkov
8c261215d7 extended the syntax of option <TaskX.Time> in two ways: 1) it now accepts multiple comma-separated values; 2) an asterix as hours-part means <every hour> 2009-04-26 16:33:21 +00:00
Andrey Prygunkov
8059c4f1d5 improved example post-processing script: added support for delayed par-check (try unrar first, par-repair if unrar failed) 2009-04-25 12:05:30 +00:00
Andrey Prygunkov
e9f5cf2259 added check to prevent the changing of read-only options (ConfigFile, AppBin, AppDir, Version) 2009-04-24 16:49:37 +00:00
Andrey Prygunkov
86bec2943b added readonly option <Version> for usage in processing scripts (option is avaialble as environment variable <NZBOP_VERSION>) 2009-04-24 16:30:47 +00:00
Andrey Prygunkov
f812646267 updated ChangeLog 2009-04-24 16:21:53 +00:00
Andrey Prygunkov
390582e3f2 added readonly options <AppBin> and <ConfigFile> for usage in processing scripts (options are avaialble as environment variables <NZBOP_APPBIN> and <NZBOP_CONFIGFILE) 2009-04-24 16:10:58 +00:00
Andrey Prygunkov
5d5c6b06b9 commited changes missed in previous revision 2009-04-24 05:48:55 +00:00
Andrey Prygunkov
10269dc779 added new return code (93) for post-processing script to indicate successful processing; that results in cleaning up of download queue if option <ParCleanupQueue> is active 2009-04-23 20:39:07 +00:00
Andrey Prygunkov
b28c2ae735 added estimated remaining time and better distinguishing of server state in command <--list/-L> 2009-04-19 19:54:45 +00:00
Andrey Prygunkov
8810d18020 fixed compilation error on windows, caused by changes in revision 297 2009-04-19 19:00:17 +00:00
Andrey Prygunkov
9b8841174e fixed linking error on OpenSolaris when using GnuTLS 2009-04-19 17:27:58 +00:00
Andrey Prygunkov
a3a56b2ee9 fixed compilation warning on certain versions of gcc 2009-04-19 17:26:41 +00:00
Andrey Prygunkov
41db56c093 fixed compilation error when using native curses library on OpenSolaris 2009-04-19 16:44:03 +00:00
Andrey Prygunkov
c8e7cc856f added svn revision number to version string (commands <-v> and <-V>, startup log entry); svn revision is automatically read from svn-repository on each build 2009-04-17 21:53:00 +00:00
Andrey Prygunkov
18d3e0db42 updated version string (0.7.0-testing) 2009-04-13 19:06:37 +00:00
Andrey Prygunkov
865e5c85b8 updated README (preparing to release 0.6.0) 2009-03-20 18:16:47 +00:00
Andrey Prygunkov
e61175af6b updated version string and ChangeLog (preparing to release 0.6.0) 2009-03-20 17:32:57 +00:00
Andrey Prygunkov
4f1f72e1dc fixed: second scan of incoming nzb-directory, introduced in r288, actually did not work very well 2009-03-10 20:17:02 +00:00
Andrey Prygunkov
0da593a008 improved the speed of deleting of groups from download queue (by avoiding the saving of queue after the deleting of each individual file) 2009-03-04 22:46:56 +00:00
Andrey Prygunkov
8e78140259 added the second scan of incoming nzb-directory to ensure that files extracted by nzbprocess-script are scanned without a delay 2009-03-04 22:02:21 +00:00
Andrey Prygunkov
1376a05c6a fixed: not all necessary par2-files were unpaused on first request for par-blocks (although harmless, because additional files were unpaused later anyway) 2009-02-16 22:23:45 +00:00
Andrey Prygunkov
7d8ca6fdc7 fixed: command <--write> logged the superfluous info-message <nzbget version> (bug introduced in r280) 2009-02-10 22:18:26 +00:00
Andrey Prygunkov
f5ad09619f fixed critical memory allocation bug in thread management (bug introduced in r283) 2009-02-10 22:07:06 +00:00
Andrey Prygunkov
685d83bd1e remote command <scan> is now processed without checking of file timestamps (in contrast to automatic periodical checks) 2009-02-10 18:00:54 +00:00
Andrey Prygunkov
64a132808d fixed: named semaphores were not properly initialized in daemon mode (affects only Mac OS X) 2009-02-09 18:12:03 +00:00
Andrey Prygunkov
26e1f4001b fixed compatibility issues with Mac OS X (it does not support unnamed semaphores) 2009-02-08 18:39:43 +00:00
Andrey Prygunkov
1178138ad2 removed an unneeded field from the file-record in remote protocol 2009-02-07 23:33:20 +00:00
Andrey Prygunkov
9d3d075524 refactor: removed unused code 2009-02-07 23:25:10 +00:00
Andrey Prygunkov
d157bc4769 added the printing of nzbget version into the log-file on start 2009-02-07 22:45:10 +00:00
Andrey Prygunkov
ff72ede2f4 fixed: switch <-L> doesn't work without suboptions (bug introduced in r276) 2009-02-01 23:31:32 +00:00
Andrey Prygunkov
d2631a7586 fixed: configure-script could not detect the right syntax for function <ctime_r> on OpenSolaris 2009-02-01 23:05:01 +00:00
Andrey Prygunkov
d271acc67e added the printing of post-process-parameters for groups in command <--list G> 2009-01-30 19:22:21 +00:00
Andrey Prygunkov
503fb61ee1 added subcommands <F>, <G> and <S> to command line switch <-L/--list>, which prints list of files, groups or only status info respectively; extended binary communication protocol to transfer nzb-infos in addition to file-infos 2009-01-29 22:25:57 +00:00
Andrey Prygunkov
9dde5cd0b0 Improved compatibility of yenc-decoder 2009-01-26 22:14:50 +00:00
Andrey Prygunkov
9f96d171f7 Improved error reporting on decoding failures 2009-01-26 22:11:55 +00:00
Andrey Prygunkov
a69fc24b40 fixed: configuration file could not be found in common places (/etc/nzbget.conf, ..) (bug introduced in r260) 2009-01-15 19:44:20 +00:00
Andrey Prygunkov
82ab166b94 fixed: options <ParPauseQueue> and <PostPauseQueue> did not work properly if both were enabled (bug introduced in r232) 2009-01-15 18:11:39 +00:00
Andrey Prygunkov
8c4b5c7f6b fixed small memory leak appeared if process-script could not be started 2009-01-15 17:45:21 +00:00
Andrey Prygunkov
7f22f6d2a6 updated example configuration file 2009-01-15 17:43:14 +00:00
Andrey Prygunkov
d66c688910 the workaround for thread-related connection errors (introduced in r265) is now disabled by default (because it wasn't confirmed, that it helps), but can be activated by defining the symbol <THREADCONNECT_WORKAROUND> in any header file, for example <config.h> or <nzbget.h> 2008-12-18 21:34:13 +00:00
Andrey Prygunkov
986373c30d fixed: TLS/SSL didn't work in standalone mode 2008-12-11 21:56:43 +00:00
Andrey Prygunkov
2c45a20ca7 cleaning up of download queue (option <ParCleanupQueue>) and deletion of source nzb-file (option <NzbCleanupDisk>) after par-repair now works also if par-repair was cancelled (option <ParTimeLimit>); since required par-files were already downloaded the repair in an external tool is possible 2008-12-10 22:04:06 +00:00
Andrey Prygunkov
599f083fe2 updated configure-script for better compatibility with FreeBSD 2008-12-07 23:40:38 +00:00
Andrey Prygunkov
c572223147 improved the handling of running connect errors: if many attempts fail, download thread terminates to retry in a new thread; that should help on some systems 2008-12-05 23:27:41 +00:00
Andrey Prygunkov
a35fbc9de4 fixed: syntax error in sample postprocess script (although appeared not with all bash versions) (bug introduced in r258) 2008-12-05 22:13:12 +00:00
Andrey Prygunkov
4f4e7f8b61 fixed: the output of process-scripts was not always received (bug introduced in r250) 2008-12-02 22:32:02 +00:00
Andrey Prygunkov
5993408eff added command <Process> to scheduler option <TaskX.Command>; replaced options <PostLogKind> and <NzbLogKind> with new option <ProcessLogKind> which is now used by all scripts (PostProcess, NzbProcess, TaskX.Process) 2008-12-01 22:50:35 +00:00
Andrey Prygunkov
e9dfcfbb2f update nzbget.conf.example and reformatted it for better readability 2008-12-01 18:40:55 +00:00
Andrey Prygunkov
18e3dfb448 added the passing of nzbget-options to postprocess/nzbprocess scripts as environment variables 2008-12-01 18:38:54 +00:00
Andrey Prygunkov
b1e1b0f6ac fixed: download queue could not be discarded on program start (ReloadQueue=no) (bug introduced in r258) 2008-11-30 13:22:02 +00:00
Andrey Prygunkov
42718d3f7a added support for postprocess-parameters; new subcommand <O> of remote command <E> to add/modify pp-parameter for group (nzb-file); new XML-/JSON-RPC-subcommand <GroupSetParameter> of method <editqueue> for the same purpose; updated example configuration file and example postprocess-script to indicate new method of passing arguments via environment variables 2008-11-29 23:42:24 +00:00
Andrey Prygunkov
63cfccab40 fixed: sometimes program hanged on starting postprocess/nzbprocess in server (not daemon) mode (bug introduced in r250) 2008-11-25 18:26:53 +00:00
Andrey Prygunkov
583b36667f improved entering to paused state on connection errors (do not retry failed downloads if pause was activated) 2008-11-25 17:11:49 +00:00
Andrey Prygunkov
c7f55b88a6 implemented the normalizing of option names and values in option list; the command <-p> also prints normalized names and values now; that makes the parsing of output of command <-p> for external scripts easier 2008-11-23 12:20:02 +00:00
Andrey Prygunkov
df7503cb3b changed the method to pass arguments to postprocess/nzbprocess: now using environment variables (old method is still supported for compatibility with existing scripts) 2008-11-22 21:04:14 +00:00
Andrey Prygunkov
75066477cf refactor: removed unneeded code 2008-11-19 17:18:17 +00:00
Andrey Prygunkov
7be063f104 remote command <scan> is now executed asynchronously to prevent timeouts on client side 2008-11-15 14:57:57 +00:00
Andrey Prygunkov
5521fccf73 added command <--scan/-S> to execute the scan of nzb-directory on remote server 2008-11-13 19:40:18 +00:00
Andrey Prygunkov
b823535880 added workaround to avoid hangs in child processes (by starting of postprocess or nzbprocess), observed on uClibC based systems 2008-11-13 16:50:02 +00:00
Andrey Prygunkov
0971ba5cee updated makefile to include libpar2-patches in generated dist-archive 2008-11-01 19:29:09 +00:00
Andrey Prygunkov
ab584366f2 added option <ParTimeLimit>; added required patches for libpar2 2008-11-01 18:08:54 +00:00
Andrey Prygunkov
8bfb1fb348 fixed: the return value of postprocess-script were not recognized under Linux if compiled in release mode (but it worked in debug mode) 2008-10-30 22:14:51 +00:00
Andrey Prygunkov
acaa23fe88 changed exit codes for post-process-scripts (old values were too big for POSIX) 2008-10-25 21:44:43 +00:00
Andrey Prygunkov
6aea979ca5 added option <MergeNzb> to automatically merge nzb-files with the same filename (useful by adding pars from a different source) 2008-10-24 20:08:49 +00:00
Andrey Prygunkov
34c7eb612a added new subcommand <M> to edit-command <E> for merging of two (or more) groups (useful after adding pars from a separate nzb-file) 2008-10-23 18:29:40 +00:00
Andrey Prygunkov
b3d472f88d extended the communication between nzbget and post-process-script and improved delayed parcheck; collections are now detected even if parcheck is disabled; two return codes allow to repair current collection or all collections 2008-10-21 22:15:30 +00:00
Andrey Prygunkov
6801fea2b1 added support for delayed par-check/repair (post-process-script can request par-check/repair using special exit code) 2008-10-18 18:52:58 +00:00
Andrey Prygunkov
effe010223 added special meaning for extension <.nzb_processed> for files in incoming directory; those files are now not passed to nzbprocess-script 2008-10-16 17:10:55 +00:00
Andrey Prygunkov
394f4bbd49 fixed: deleting or renaming file in nzbprocess-script caused an error in nzbget when it tries to rename the file to .processed 2008-10-12 11:49:28 +00:00
Andrey Prygunkov
f48e5396e4 removed trailing slash in directory name passed to nzbprocess-script (to be similar to postprocess-script) 2008-10-12 11:21:34 +00:00
Andrey Prygunkov
08c6a9a73b added option <DeleteCleanupDisk> to automatically delete already downloaded files from disk if nzb-file was deleted from queue (the download was cancelled) 2008-10-11 17:34:35 +00:00
Andrey Prygunkov
3ad05c2424 reformatted the example configuration file 2008-10-08 20:32:34 +00:00
Andrey Prygunkov
d3b07cf012 fixed: empty string parameters did not always work in XML-RPC 2008-10-08 16:42:37 +00:00
Andrey Prygunkov
c6d2ba92d9 changed possible values for option <TaskX.Command> and added option <TaskX.DownloadRate> 2008-10-03 14:02:20 +00:00
Andrey Prygunkov
8569205297 fixed: files downloaded with disabled decoder (option decode=no) sometimes were malformed and could not be decoded 2008-10-01 17:58:03 +00:00
Andrey Prygunkov
8fd31eeb4b added script-processing of files in incoming directory to allow automatic unpacking and queueing of compressed nzb-files; new options <NzbProcess> and <NzbLogKind> 2008-09-29 21:57:28 +00:00
Andrey Prygunkov
5fea19aa88 added scheduler; new options <TaskX.Time>, <TaskX.WeekDays>, <TaskX.Command> 2008-09-25 21:19:24 +00:00
Andrey Prygunkov
b338a4bba3 updated version string 2008-09-24 16:45:24 +00:00
Andrey Prygunkov
a67eb5128f updated version string and ChangeLog (preparing to release 0.5.1) 2008-09-21 16:21:45 +00:00
Andrey Prygunkov
7ff36c1bdb fixed: parameter <category> in JSON/XML-RPC was not properly decoded by server, making the setting of a nested category (containing slash or backslash character) via nzbgetweb not possible 2008-09-20 10:20:46 +00:00
Andrey Prygunkov
2cf683fc0f improved the check of server responses to prevent unnecessary retrying if article does not exist on server 2008-09-20 09:50:28 +00:00
Andrey Prygunkov
b605619baf fixed: download speed indicator could report not-null values in standby-mode (when paused) 2008-09-20 09:37:35 +00:00
Andrey Prygunkov
57a6dc9225 fixed: seg.fault in standalone mode if used without specifying the category (e.g. without switch <-K>) 2008-09-11 20:42:08 +00:00
Andrey Prygunkov
dad80c023f updated version string 2008-09-11 20:27:23 +00:00
Andrey Prygunkov
d4bb7a14e3 updated version string and ChangeLog (preparing to release 0.5.0) 2008-09-08 20:58:08 +00:00
Andrey Prygunkov
f72c6d54a4 updated version numbers in Authors-file 2008-08-30 15:31:25 +00:00
Andrey Prygunkov
e679849568 added postprocess-example.sh to generated dist-file 2008-08-30 15:29:49 +00:00
Andrey Prygunkov
5ea312c64e changed VC project file to make build from command line possible 2008-08-29 16:21:00 +00:00
Andrey Prygunkov
f7ad051eef fixed: compilation error when building without TLS/SSL support 2008-08-29 14:56:53 +00:00
Andrey Prygunkov
8c5ef66865 added support for nested categories 2008-08-28 17:37:09 +00:00
Andrey Prygunkov
f130d0c3bf fixed: error by loading of download queue from disk if any of nzb-files was added via client request (not from incoming directory) - bug introduced in r211 2008-08-28 17:01:54 +00:00
Andrey Prygunkov
1c2d5300f1 switch <-P> can now be used together with switches <-s> and <-D> to start server/daemon in paused state 2008-08-23 11:19:19 +00:00
Andrey Prygunkov
66804e5a47 removed superfluous error messages if using TLS/SSL 2008-08-18 16:52:23 +00:00
Andrey Prygunkov
01c343a13a fixed: destination path for queue items contained superfluous slashes (harmless bug) 2008-08-17 14:02:55 +00:00
Andrey Prygunkov
932447aab6 fixed: subfolders in incoming directory were not scanned 2008-08-17 13:52:16 +00:00
Andrey Prygunkov
7394813151 added option <NzbCleanupDisk> to delete source nzb-file after successful download and parcheck 2008-08-16 22:07:26 +00:00
Andrey Prygunkov
01824c518f added detection of adjusting of system clock to correct uptime/download time (for NAS-devices, that do not have internal clock and set time from internet after booting, while nzbget may be already running) 2008-08-15 20:39:36 +00:00
Andrey Prygunkov
e38ab21d27 fixed: configure-script could not automatically find libsigc++ on 64-bit systems 2008-08-14 16:16:19 +00:00
Andrey Prygunkov
e9370792f5 avoided the short-time unpausing of queue between parcheck and postscript if both options <ParPauseQueue> and <PostPauseQueue> are enabled 2008-08-10 11:04:15 +00:00
Andrey Prygunkov
896e037662 updated README 2008-08-10 09:00:27 +00:00
Andrey Prygunkov
644cc5e06a fixed: TLS via GnuTLS did not work properly with multiple connections 2008-08-09 21:34:27 +00:00
Andrey Prygunkov
9f4d2d8924 added options <ParPauseQueue> and <PostPauseQueue> 2008-08-09 19:18:45 +00:00
Andrey Prygunkov
7ee0e867c8 now using OS-specific line-endings in log-file and brokenlog-file: LF on Posix and CRLF on Windows 2008-08-09 07:57:23 +00:00
Andrey Prygunkov
b9c5e5f077 better error reporting in connections 2008-08-05 21:48:26 +00:00
Andrey Prygunkov
8631d06ca5 suppressed two valgrind warnings 2008-08-05 21:18:54 +00:00
Andrey Prygunkov
e7d9ddd8e6 fixed small memory leak 2008-08-05 21:17:23 +00:00
Andrey Prygunkov
a264e8d0c6 added example post-process script (POSIX only) 2008-08-02 08:33:27 +00:00
Andrey Prygunkov
eb3a2e0dbf added option <DumpCore> for better debugging on Linux in a case of abnormal program termination 2008-07-31 20:52:57 +00:00
Andrey Prygunkov
b7ee6018ad added TLS/SSL support 2008-07-29 20:32:23 +00:00
Andrey Prygunkov
df151027cc updated the description of option <ThreadLimit> in the sample configuration file 2008-07-26 13:51:24 +00:00
Andrey Prygunkov
cf3e27431e added option <ServerX.JoinGroup> to connect to servers, that do not accept <GROUP>-command 2008-07-22 19:20:20 +00:00
Andrey Prygunkov
585cf775bd removed the check for <gethostbyname_r> (not needed anymore) 2008-07-22 17:18:17 +00:00
Andrey Prygunkov
b686cce21b added ipv6; thanks to Maik Nijhuis and Ian Cass for the patch 2008-07-21 21:52:26 +00:00
Andrey Prygunkov
512e9789d9 fixed: seg.fault by queueing the files from the top-level incoming directory (bug introduced in r191) 2008-07-21 20:49:07 +00:00
Andrey Prygunkov
09c27a433b added the printing of stack on segmentation faults (if configured with <--enable-debug>, POSIX only) 2008-07-21 18:48:07 +00:00
Andrey Prygunkov
e28aa2ee67 added the categories: new option <AppendCategoryDir>; new switch <-K>; new command <K> in switch <-E>; new parameter in XML-/JSON-RPC-command <editqueue>; new parameter to post-process-scripts; scan the subdirectories of incoming nzb-directory; automatic moving of already downloaded files after the changing of category 2008-07-20 13:47:50 +00:00
Andrey Prygunkov
73e87410ac changed version-string to <0.5.0-testing> 2008-07-18 15:44:18 +00:00
Andrey Prygunkov
baece5113b updated ChangeLog 2008-07-10 16:17:55 +00:00
Andrey Prygunkov
230db979ed updated version-string (preparing to release 0.4.1) 2008-07-10 16:03:07 +00:00
Andrey Prygunkov
0e31bbdbd4 fixed: seg.fault if a file listed in nzb-file does not have any segments (articles) 2008-07-09 16:12:17 +00:00
Andrey Prygunkov
8399322238 added the compatibility with gcc 4.3 2008-06-27 20:00:04 +00:00
Andrey Prygunkov
03b057be64 added the option <AllowReProcess> to help to post-process-scripts, which make par-check/-repair on it's own 2008-06-22 22:27:52 +00:00
Andrey Prygunkov
5bd621c540 fixed: edit-commands with negative offset did not work via XML-RPC (but worked via JSON-RPC) 2008-06-22 21:36:04 +00:00
Andrey Prygunkov
228a451284 updated the descriptions of several options in the example configuration file 2008-06-21 19:47:51 +00:00
Andrey Prygunkov
3c67755c6b fixed: on first screen-update the remote-client showed only one item of queue in curses-outputmode 2008-06-21 15:01:58 +00:00
Andrey Prygunkov
4d6ffa78f6 reduced CPU-usage in curses-outputmode 2008-06-16 17:47:59 +00:00
Andrey Prygunkov
a9be4a9860 improved speedmeters's responding after program's start and pause/unpause 2008-06-15 19:45:10 +00:00
Andrey Prygunkov
6275ddbcba fixed: umask-option did not allow to enable write-permissions for group and others 2008-06-14 21:38:55 +00:00
Andrey Prygunkov
365784bbed fixed: XML-RPC and JSON-RPC did not work on Big-Endian-CPUs 2008-06-13 20:51:17 +00:00
Andrey Prygunkov
c1623e412d improved download-speed-meter: uses less cpu and calculates the speed for the last 30 seconds (instead of 5 seconds), providing better accuracy 2008-06-06 20:09:22 +00:00
Andrey Prygunkov
13f6cca318 fixed: trailing spaces in nzb-filenames (before the file's extension) caused errors on windows (because windows does not allow trailing spaces in directory names). now they will be trimmed 2008-06-01 20:48:55 +00:00
Andrey Prygunkov
1f46214363 added the server's name to the detail-log-message, displayed on start of article's download 2008-05-29 22:16:07 +00:00
Andrey Prygunkov
c39a807766 options <username> and <password> in news-server's configuration are now optional 2008-05-29 22:00:16 +00:00
Andrey Prygunkov
3082c02fbe fixed: config-file with line-endings in windows-style (CR-LF) was not read properly 2008-05-29 21:49:54 +00:00
Andrey Prygunkov
96a134e69e Delete-key <D> in curses-frontend now must be pressed in uppercase to avoid an accidental deletion; added a hint-text under the status-line on a press of the lowercase-key <d> 2008-05-29 21:39:29 +00:00
Andrey Prygunkov
14fcebdde3 changed version-string to <0.4.1-testing> 2008-05-29 21:16:38 +00:00
Andrey Prygunkov
b4211a57e6 fixed compilation error on PC-BSD 2008-04-08 17:39:26 +00:00
Andrey Prygunkov
4a421c705a updated version-string (preparing to release 0.4.0) 2008-04-08 17:00:33 +00:00
Andrey Prygunkov
695e3a912c improved error-handling on start of post-process-script 2008-04-05 14:14:31 +00:00
Andrey Prygunkov
af190697c2 extended the description of parameter <PostProcess> 2008-03-27 21:46:23 +00:00
Andrey Prygunkov
814ffbd468 removed the accidentally commited debug-code 2008-03-26 18:54:05 +00:00
Andrey Prygunkov
cb10f39db3 fixed a deadlock in postprocessor (could hang the program) 2008-03-26 18:35:00 +00:00
Andrey Prygunkov
c8a496faaa fixed one warning under gcc 2008-03-26 18:23:23 +00:00
Andrey Prygunkov
7c858007b3 removed the logging from signal-handlers (they might cause the hangs, especially during the start/termination of post-process-scripts in debug-mode) 2008-03-25 23:35:13 +00:00
Andrey Prygunkov
3a9e8f1a79 set the proper progress-label for post-item during the loading of additional par-files 2008-03-23 15:39:27 +00:00
Andrey Prygunkov
dfa839c4e0 added the automatic termination (killing) of post-process-script on server's shutdown 2008-03-23 15:26:07 +00:00
Andrey Prygunkov
876fee3bc5 added field <ID> to post-queue-items (for feature usage) 2008-03-23 14:48:56 +00:00
Andrey Prygunkov
22ffd2e0bd added the check for existense of dest-directory after reloading of post-queue and the deletion of items which can not be continued (if directory was deleted) 2008-03-23 13:59:12 +00:00
Andrey Prygunkov
8cd59b9ab5 improved error-reporting by starting of post-process-script and on par-failures 2008-03-22 14:10:37 +00:00
Andrey Prygunkov
988714f92a fixed: negative integer parameters were not correctly parsed by JSON-RPC-server 2008-03-20 15:51:45 +00:00
Andrey Prygunkov
789d39bfb2 updated ChangeLog 2008-03-19 17:52:24 +00:00
Andrey Prygunkov
1c333e68c2 added the saving of script-output in post-job-object and the returning of the output by command <postqueue> via XML-RPC and JSON-RPC 2008-03-18 17:53:45 +00:00
Andrey Prygunkov
e9ff2e4fca fixed many warnings (regarding type conversion) on windows, instead of suppressing them with pragma-directive 2008-03-17 17:54:52 +00:00
Andrey Prygunkov
173622fea1 implemented the output-redirection for post-process-scripts on windows 2008-03-17 16:44:14 +00:00
Andrey Prygunkov
9881f2c39e corrected few output-messages in remote-client 2008-03-16 15:54:28 +00:00
Andrey Prygunkov
0970c0bc52 added the redirecting of post-process-script's output to log; new option <PostLogKind> to specify the default message-kind for unformatted log-messages 2008-03-14 22:42:55 +00:00
Andrey Prygunkov
cb2686dc3c eliminated one warning on GCC 2008-03-13 19:10:11 +00:00
Andrey Prygunkov
e0d79a4079 added option <DiskSpace> to automatically pause the download on low disk space 2008-03-13 19:07:13 +00:00
Andrey Prygunkov
2e54a182c9 fixed: stage-time was not set in post-processor (bug introduced in r143) 2008-03-09 08:48:21 +00:00
Andrey Prygunkov
83a405db8b fixed: by discarding of download-queue not all state-files were deleted from disk 2008-03-07 17:15:00 +00:00
Andrey Prygunkov
cdddecb834 added missed files and fixed the discarding of post-proccessor-queue 2008-03-07 16:45:28 +00:00
Andrey Prygunkov
faf528a94e added the saving and restoring of the post-processor-queue (if server was stopped before all items were processed); new option <ReloadPostQueue> 2008-03-07 16:20:27 +00:00
Andrey Prygunkov
aff3c978cb fixed: RPC-method <listfiles> does not work without parameters (they should be optional) 2008-03-06 17:59:48 +00:00
Andrey Prygunkov
c09c787507 fixed: command <write to log> was adding the current path at the beginning of the message text (bug appeared only on posix) 2008-03-06 17:42:06 +00:00
Andrey Prygunkov
9b82667fb5 added support for HTTP-method <GET> in remote server (XML-RPC and JSON-RPC) 2008-03-06 17:25:37 +00:00
Andrey Prygunkov
6a8d341ecc added remote command (switch -W/--write) to write messages to server's log 2008-03-06 17:21:47 +00:00
Andrey Prygunkov
743ce9f07c refactored a little 2008-03-06 16:59:50 +00:00
Andrey Prygunkov
fff550ca37 fixed few incompatibility-issues with unslung-platform on nslu2 (ARM) 2008-03-03 17:20:46 +00:00
Andrey Prygunkov
3c5c76eec4 added new command <-O/--post> to request the post-processor-queue from server 2008-02-28 17:04:14 +00:00
Andrey Prygunkov
ea05b09491 fixed: compile errors after configure with option <disable-parcheck> 2008-02-27 16:39:49 +00:00
Andrey Prygunkov
d6b8993eb3 fixed: not all characters were properly encoded in JSON-responses 2008-02-27 16:37:59 +00:00
Andrey Prygunkov
0d61926a7e added decoding of escaped strings in JSON-RPC, needed for the method <append> 2008-02-26 17:25:08 +00:00
Andrey Prygunkov
f8acf9278c changed version string to <0.4.0-testing> 2008-02-26 17:21:01 +00:00
Andrey Prygunkov
23f6a778a5 added support for JSON-RPC in remote-server 2008-02-25 20:21:00 +00:00
Andrey Prygunkov
e6e950e58c fixed: field <NZBNicename> was not properly encoded in XMLRPC-responses, producing invalid xml on certain filenames 2008-02-22 15:59:58 +00:00
Andrey Prygunkov
7162a19982 fixed: parameter <state of nzb-job> for post-process-script was not correct (bug introduced in r121) 2008-02-22 15:56:28 +00:00
Andrey Prygunkov
5a77dd8a96 fixed: some characters (in filenames) were not encoded properly in XML-RPC 2008-02-20 23:47:46 +00:00
Andrey Prygunkov
4e89546923 added new parameter to postprocess-script to indicate if any of par-jobs for the same nzb-file failed 2008-02-20 22:50:11 +00:00
Andrey Prygunkov
0429a689a4 fixed: StageTime was not set for script-jobs when par-check was skipped (affected only XML-RPC-method <postqueue>) 2008-02-20 22:19:39 +00:00
Andrey Prygunkov
b7239b611a extended the result of XML-RPC-method <listfiles> with one new field 2008-02-20 21:50:48 +00:00
Andrey Prygunkov
057df4d35c fixed: too many info-messages <Need more N blocks> could be printed during par-check (appeared on posix only) 2008-02-19 18:23:58 +00:00
Andrey Prygunkov
886a07896b fixed: the pausing of a group could cause the start of post-processing for that group 2008-02-19 17:48:30 +00:00
Andrey Prygunkov
60857b55f1 fixed: post-processor hung after par-check when no post-process-script was defined in options (bug introduced in 121) 2008-02-19 16:46:11 +00:00
Andrey Prygunkov
160590149f added the queueing of post-process-scripts and waiting for script's completion before starting of a next job in postprocessor (par-job or script); the purpose of changes was to provide more balanced cpu utilization; the state <executing script> is now viewable via XML-RPC method <postqueue> 2008-02-18 19:33:15 +00:00
Andrey Prygunkov
98c6ab5aac updated messages and progress-info in parchecker 2008-02-17 13:38:09 +00:00
Andrey Prygunkov
7c18cd1009 improved the parchecker: added the detection and processing of files splitted after parring 2008-02-17 11:56:36 +00:00
Andrey Prygunkov
27e53bc363 fixed: by registering the service on windows the fullpath to nzbget.exe was not always added to service's exename, making the registered service unusable 2008-02-14 18:31:22 +00:00
Andrey Prygunkov
f0e5c6efe7 fixed: nzb-files added from the monitoring-directory have double slashes at the end of pathname 2008-02-14 18:14:35 +00:00
Andrey Prygunkov
cbd86d3810 updated the description of option <decode> in nzbget.conf.example 2008-02-14 17:54:21 +00:00
Andrey Prygunkov
6f9a2dd57f added support for UU-format to internal decoder; removed support for uulib-decoder (it did not work well anyway); replaced the option <decoder (yenc, uulib, none)> with the option <decode (yes, no)> 2008-02-14 17:42:45 +00:00
Andrey Prygunkov
7f45bb22f5 fixed seg.fault in uulib-decoder on files in UU-format 2008-02-13 23:27:12 +00:00
Andrey Prygunkov
9137ed6278 improved the internal decoder: 1) added the support for yEnc-files without ypart-statement (sometimes used for small files); 2) added the detection of UU-format and the printing of a specific error-message instead of a general error-message; the download of article with detected unsupported format stops on early stage (after the first line of article's body), saving traffic and time 2008-02-13 21:42:01 +00:00
Andrey Prygunkov
3cda6109e9 improved the bugfix for a seg.fault in libpar2-patch (windows only) 2008-02-13 21:00:37 +00:00
Andrey Prygunkov
0ce6f07064 fixed: executing of post-process-script caused seg.fault if compiled in debug-mode on Linux 2008-02-13 20:34:07 +00:00
Andrey Prygunkov
6bbad0a399 fixed inconsistent line-endings in one of source files 2008-02-12 20:08:57 +00:00
Andrey Prygunkov
8c371cf8af added the gathering of progress-information during par-check; extended the XML-RPC-method <postqueue> with that info 2008-02-12 20:02:09 +00:00
Andrey Prygunkov
d74e484752 improved handling of nzb-files with multiple collections in par-checker 2008-02-11 18:55:11 +00:00
Andrey Prygunkov
8f315d9311 fixed: by dupe-checking of files contained in nzb-file the files with the same size were ignored (not deleted) 2008-02-11 18:50:47 +00:00
Andrey Prygunkov
4a63f14c75 added support for multicalls in XML-RPC-server 2008-02-10 16:14:16 +00:00
Andrey Prygunkov
e42f7426ee added option <DetailTarget> to allow to filter the (not so important) log-messages from articles' downloads (they have now the type <detail> instead of <info>) 2008-02-08 18:57:30 +00:00
Andrey Prygunkov
e96a1d6bde fixed: dupecheck could cause seg.faults when all articles for a file failed 2008-02-08 18:34:04 +00:00
Andrey Prygunkov
d08805dca6 fixed: articles with trailing text after binary data caused the decode failures and the reporting of CRC-errors 2008-02-08 18:28:57 +00:00
Andrey Prygunkov
1ca268e36a refactored: moved global functions in unit <Util> to new class <Util> to prevent possible conflicts with system functions; added error-reporting by moving completed files from tmp- to dst-directory and added code to move files across drives if renaming fails 2008-02-07 18:23:59 +00:00
Andrey Prygunkov
865afb2f9a added detection of errors <server busy> and <remote server not available> (special case for NNTPCache-server) to consider them as connect-errors (and therefore not count as retries); added check for incomplete articles (also mostly for NNTPCache-server) to such errors from CrcErrors (better error reporting) 2008-02-06 17:54:09 +00:00
Andrey Prygunkov
44f448493c fixed a small memory leak on windows 2008-02-06 17:03:44 +00:00
Andrey Prygunkov
cee5f769b6 updated libpar2-patch for msvc to fix a segfault in libpar2 (windows only) 2008-02-06 16:59:42 +00:00
Andrey Prygunkov
ea05d5635a added the automatic cleaning up of the download queue (deletion of unneeded paused par-files) after successful par-check/repair - new option <ParCleanupQueue> 2008-02-05 18:10:02 +00:00
Andrey Prygunkov
984f8b2dd9 fixed another warning on GCC (Linux) 2008-02-05 17:57:20 +00:00
Andrey Prygunkov
737f9b3d1e fixed few warnings under GCC on Linux 2008-02-05 17:45:33 +00:00
Andrey Prygunkov
8b3158de99 refactored: created new class NZBInfo and moved related fields from FileInfo into new class to eliminate the storing of duplicate data (nzb-summary) in each FileInfo-object; changed queue-fileformat to save/load NZBInfo and to prevent unneeded reload/-save of queue-files during parsing of nzb-file 2008-02-05 17:34:49 +00:00
Andrey Prygunkov
cf054c401e added XMLRPC-command <postqueue> 2008-02-02 15:34:22 +00:00
Andrey Prygunkov
3a34fa7a8f set svn-keywords on new files 2008-02-02 15:30:40 +00:00
Andrey Prygunkov
9438f91547 added XMLRPC-command <listgroups> 2008-02-02 15:26:07 +00:00
Andrey Prygunkov
157b694727 added the XMLRPC-support to easier control the server from other applications 2008-02-02 13:23:43 +00:00
Andrey Prygunkov
3caf2fc62e small corrections in text-files 2008-02-02 12:53:06 +00:00
Andrey Prygunkov
e3b144a374 added the memory leak detection on windows 2008-02-02 12:45:41 +00:00
Andrey Prygunkov
f7987b5c3e changed version-string to <0.3.2-testing> 2008-02-02 12:29:15 +00:00
125 changed files with 59500 additions and 9801 deletions

View File

@@ -1,4 +1,4 @@
nzbget:
Sven Henkel <sidddy@users.sourceforge.net> (versions 0.1.0 - ?)
Bo Cordes Petersen <placebodk@users.sourceforge.net> (versions ? - 0.2.3)
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 - 0.3.*)
Andrey Prygunkov <hugbug@users.sourceforge.net> (versions 0.3.0 and later)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -28,9 +28,6 @@
#define ARTICLEDOWNLOADER_H
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#include "Observer.h"
#include "DownloadInfo.h"
@@ -45,13 +42,16 @@ public:
{
adUndefined,
adRunning,
adWaiting,
adFinished,
adFailed,
adDecodeError,
adRetry,
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
@@ -67,16 +67,20 @@ private:
char* m_szInfoName;
char* m_szOutputFilename;
time_t m_tLastUpdateTime;
static const char* m_szJobStatus[];
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
EStatus Decode();
bool CreateOutputFile(int iSize);
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
public:
ArticleDownloader();
@@ -85,9 +89,7 @@ public:
FileInfo* GetFileInfo() { return m_pFileInfo; }
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
const char* GetStatusText() { return m_szJobStatus[m_eStatus]; }
virtual void Run();
virtual void Stop();
bool Terminate();
@@ -100,6 +102,7 @@ public:
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void CompleteFileParts();
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
void LogDebugInfo();
@@ -109,8 +112,8 @@ class DownloadSpeedMeter
{
public:
virtual ~DownloadSpeedMeter() {};
virtual float CalcCurrentDownloadSpeed() = 0;
virtual int CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};
#endif

1174
BinRpc.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

159
BinRpc.h Normal file
View File

@@ -0,0 +1,159 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2009 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef BINRPC_H
#define BINRPC_H
#include "Connection.h"
#include "MessageBase.h"
class BinRpcProcessor
{
private:
SNZBRequestBase m_MessageBase;
Connection* m_pConnection;
void Dispatch();
public:
BinRpcProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
};
class BinCommand
{
protected:
Connection* m_pConnection;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};
class DownloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ListBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class LogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PauseUnpauseBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class EditQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class SetDownloadRateBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DumpDebugBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ShutdownBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ReloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class VersionBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PostQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class WriteLogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ScanBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class HistoryBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DownloadUrlBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class UrlQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
#endif

1212
ChangeLog
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -41,6 +41,7 @@
#include "nzbget.h"
#include "ColoredFrontend.h"
#include "Util.h"
ColoredFrontend::ColoredFrontend()
{
@@ -73,47 +74,49 @@ void ColoredFrontend::PrintStatus()
char tmp[1024];
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = m_lRemainingSize / ((long long int)(fCurrentDownloadSpeed * 1024));
int h = remain_sec / 3600;
int m = (remain_sec % 3600) / 60;
int s = remain_sec % 60;
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
}
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
}
else
{
szDownloadLimit[0] = 0;
}
char szParStatus[128];
if (m_iParJobCount > 0)
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szParStatus, ", %i par", m_iParJobCount);
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
szParStatus[0] = 0;
szPostStatus[0] = 0;
}
#ifdef WIN32
char* szControlSeq = "";
#else
printf("\033[s");
char* szControlSeq = "\033[K";
const char* szControlSeq = "\033[K";
#endif
snprintf(tmp, 1024, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
timeString, szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, szControlSeq);
tmp[1024-1] = '\0';
printf("%s", tmp);
m_bNeedGoBack = true;
@@ -140,6 +143,10 @@ void ColoredFrontend::PrintMessage(Message * pMessage)
SetConsoleTextAttribute(m_hConsole, 2);
printf("[INFO]");
break;
case Message::mkDetail:
SetConsoleTextAttribute(m_hConsole, 2);
printf("[DETAIL]");
break;
}
SetConsoleTextAttribute(m_hConsole, 7);
char* msg = strdup(pMessage->GetText());
@@ -162,6 +169,9 @@ void ColoredFrontend::PrintMessage(Message * pMessage)
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
}

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,20 +25,28 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
// SKIP_DEFAULT_WINDOWS_HEADERS prevents the including of <windows.h>, which includes "winsock.h",
// but we need "winsock2.h" here (they conflicts with each other)
#define SKIP_DEFAULT_WINDOWS_HEADERS
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
@@ -48,10 +56,16 @@
#include "Log.h"
static const int CONNECTION_READBUFFER_SIZE = 1024;
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
Mutex* Connection::m_pMutexGetHostByName = NULL;
#endif
#endif
void Connection::Init()
{
debug("Intiializing global connection data");
debug("Initializing global connection data");
#ifdef WIN32
WSADATA wsaData;
@@ -68,6 +82,16 @@ void Connection::Init()
return;
}
#endif
#ifndef DISABLE_TLS
TLSSocket::Init();
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
m_pMutexGetHostByName = new Mutex();
#endif
#endif
}
void Connection::Final()
@@ -77,64 +101,146 @@ void Connection::Final()
#ifdef WIN32
WSACleanup();
#endif
#ifndef DISABLE_TLS
TLSSocket::Final();
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
delete m_pMutexGetHostByName;
#endif
#endif
}
Connection::Connection(NetAddress* pNetAddress)
Connection::Connection(const char* szHost, int iPort, bool bTLS)
{
debug("Creating Connection");
m_pNetAddress = pNetAddress;
m_szHost = NULL;
m_iPort = iPort;
m_bTLS = bTLS;
m_szCipher = NULL;
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_bTLSError = false;
#endif
if (szHost)
{
m_szHost = strdup(szHost);
}
}
Connection::Connection(SOCKET iSocket, bool bTLS)
{
debug("Creating Connection");
m_szHost = NULL;
m_iPort = 0;
m_bTLS = bTLS;
m_szCipher = NULL;
m_eStatus = csConnected;
m_iSocket = iSocket;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_bTLSError = false;
#endif
}
Connection::~Connection()
{
debug("Destroying Connection");
if (m_eStatus == csConnected)
Disconnect();
if (m_szHost)
{
Disconnect();
free(m_szHost);
}
if (m_szCipher)
{
free(m_szCipher);
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
if (m_pTLSSocket)
{
delete m_pTLSSocket;
}
#endif
}
int Connection::Connect()
void Connection::SetSuppressErrors(bool bSuppressErrors)
{
m_bSuppressErrors = bSuppressErrors;
#ifndef DISABLE_TLS
if (m_pTLSSocket)
{
m_pTLSSocket->SetSuppressErrors(bSuppressErrors);
}
#endif
}
void Connection::SetCipher(const char* szCipher)
{
if (m_szCipher)
{
free(m_szCipher);
}
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
bool Connection::Connect()
{
debug("Connecting");
if (m_eStatus == csConnected)
return 0;
{
return true;
}
int iRes = DoConnect();
bool bRes = DoConnect();
if (iRes >= 0)
if (bRes)
{
m_eStatus = csConnected;
}
else
{
Connection::DoDisconnect();
}
return iRes;
return bRes;
}
int Connection::Disconnect()
bool Connection::Disconnect()
{
debug("Disconnecting");
if (m_eStatus == csDisconnected)
return 0;
{
return true;
}
int iRes = DoDisconnect();
bool bRes = DoDisconnect();
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
return iRes;
return bRes;
}
int Connection::Bind()
@@ -156,7 +262,7 @@ int Connection::Bind()
return iRes;
}
int Connection::WriteLine(char* line)
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
@@ -165,23 +271,32 @@ int Connection::WriteLine(char* line)
return -1;
}
int iRes = DoWriteLine(line);
int iRes = DoWriteLine(pBuffer);
return iRes;
}
int Connection::Send(char* pBuffer, int iSize)
bool Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
if (m_eStatus != csConnected)
{
return -1;
return false;
}
int iRes = send(m_iSocket, pBuffer, iSize, 0);
int iBytesSent = 0;
while (iBytesSent < iSize)
{
int iRes = send(m_iSocket, pBuffer + iBytesSent, iSize-iBytesSent, 0);
if (iRes <= 0)
{
return false;
}
iBytesSent += iRes;
}
return iRes;
return true;
}
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
@@ -196,21 +311,27 @@ char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
return res;
}
SOCKET Connection::Accept()
Connection* Connection::Accept()
{
debug("Accepting connection");
if (m_eStatus != csListening)
{
return INVALID_SOCKET;
return NULL;
}
SOCKET iRes = DoAccept();
if (iRes == INVALID_SOCKET)
{
return NULL;
}
Connection* pCon = new Connection(iRes, m_bTLS);
return iRes;
return pCon;
}
int Connection::Recv(char* pBuffer, int iSize)
int Connection::TryRecv(char* pBuffer, int iSize)
{
debug("Receiving data");
@@ -220,13 +341,13 @@ int Connection::Recv(char* pBuffer, int iSize)
if (iReceived < 0)
{
ReportError("Could not receive data on socket", NULL, 0);
ReportError("Could not receive data on socket", NULL, true, 0);
}
return iReceived;
}
bool Connection::RecvAll(char * pBuffer, int iSize)
bool Connection::Recv(char * pBuffer, int iSize)
{
debug("Receiving data (full buffer)");
@@ -234,6 +355,17 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
char* pBufPtr = (char*)pBuffer;
int NeedBytes = iSize;
if (m_iBufAvail > 0)
{
int len = iSize > m_iBufAvail ? m_iBufAvail : iSize;
memcpy(pBufPtr, m_szBufPtr, len);
pBufPtr += len;
m_szBufPtr += len;
m_iBufAvail -= len;
NeedBytes -= len;
}
// Read from the socket until nothing remains
while (NeedBytes > 0)
{
@@ -241,7 +373,7 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
// Did the recv succeed?
if (iReceived <= 0)
{
ReportError("Could not receive data on socket", NULL, 0);
ReportError("Could not receive data on socket", NULL, true, 0);
return false;
}
pBufPtr += iReceived;
@@ -250,35 +382,90 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
return true;
}
int Connection::DoConnect()
bool Connection::DoConnect()
{
debug("Do connecting");
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
m_iSocket = INVALID_SOCKET;
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; //is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
addr_hints.ai_socktype = SOCK_STREAM,
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
return -1;
ReportError("Could not resolve hostname %s", m_szHost, true, 0);
return false;
}
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
bool bLastAddr = !addr->ai_next;
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
res = connect(m_iSocket , addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
if (bLastAddr)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
}
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
else if (bLastAddr)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
}
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
return -1;
return false;
}
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
sSocketAddress.sin_port = htons(m_iPort);
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return false;
}
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return false;
}
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
if (res < 0)
if (res == -1)
{
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), 0);
return -1;
ReportError("Connection to %s failed", m_szHost, true, 0);
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
return false;
}
#endif
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
@@ -291,71 +478,40 @@ int Connection::DoConnect()
#endif
if (err != 0)
{
ReportError("setsockopt failed", NULL, 0);
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
}
return 0;
}
unsigned int Connection::ResolveHostAddr(const char* szHost)
{
unsigned int uaddr = inet_addr(szHost);
if (uaddr == (unsigned int)-1)
#ifndef DISABLE_TLS
if (m_bTLS && !StartTLS(true, NULL, NULL))
{
struct hostent* hinfo;
bool err = false;
int h_errnop = 0;
#ifdef WIN32
hinfo = gethostbyname(szHost);
err = hinfo == NULL;
h_errnop = WSAGetLastError();
#else
struct hostent hinfobuf;
static const int strbuflen = 1024;
char* strbuf = (char*)malloc(strbuflen);
#ifdef HAVE_GETHOSTBYNAME_R_6
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
#else
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
err = hinfo == NULL;
#endif
#endif
if (err)
{
ReportError("Could not resolve hostname %s", szHost, h_errnop);
#ifndef WIN32
free(strbuf);
#endif
return (unsigned int)-1;
}
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
#ifndef WIN32
free(strbuf);
#endif
return false;
}
return uaddr;
#endif
return true;
}
int Connection::DoDisconnect()
bool Connection::DoDisconnect()
{
debug("Do disconnecting");
if (m_iSocket > 0)
if (m_iSocket != INVALID_SOCKET)
{
#ifndef DISABLE_TLS
CloseTLS();
#endif
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
m_eStatus = csDisconnected;
return 0;
return true;
}
int Connection::DoWriteLine(char* szText)
int Connection::DoWriteLine(const char* pBuffer)
{
//debug("Connection::doWrite()");
return send(m_iSocket, szText, strlen(szText), 0);
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
@@ -373,7 +529,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, 0);
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
@@ -388,7 +544,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = p - szBufPtr + 1;
len = (int)(p - szBufPtr + 1);
}
else
{
@@ -429,45 +585,106 @@ char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
return pBuffer;
}
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
{
*iBufLen = m_iBufAvail;
*pBuffer = m_szBufPtr;
m_iBufAvail = 0;
};
int Connection::DoBind()
{
debug("Do binding");
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
error("Could not resolve hostname %s", m_szHost);
return -1;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return -1;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
if (bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress)) < 0)
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), 0);
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return -1;
}
if (listen(m_iSocket, 10) < 0)
if (listen(m_iSocket, 100) < 0)
{
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return -1;
}
@@ -476,16 +693,11 @@ int Connection::DoBind()
SOCKET Connection::DoAccept()
{
struct sockaddr_in ClientAddress;
socklen_t SockLen;
SockLen = sizeof(ClientAddress);
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
SOCKET iSocket = accept(m_iSocket, NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, 0);
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
@@ -500,43 +712,201 @@ void Connection::Cancel()
int r = shutdown(m_iSocket, SHUT_RDWR);
if (r == -1)
{
ReportError("Could not shutdown connection", NULL, 0);
ReportError("Could not shutdown connection", NULL, true, 0);
}
}
}
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode)
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno)
{
if (ErrCode == 0)
#ifndef DISABLE_TLS
if (m_bTLSError)
{
#ifdef WIN32
ErrCode = WSAGetLastError();
#else
ErrCode = errno;
#endif
// TLS-Error was already reported
m_bTLSError = false;
return;
}
#endif
char szErrPrefix[1024];
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
szErrPrefix[1024-1] = '\0';
if (PrintErrCode)
{
#ifdef WIN32
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
int ErrCode = WSAGetLastError();
char szErrMsg[1024];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrCode, 0, szErrMsg, 1024, NULL);
szErrMsg[1024-1] = '\0';
#else
const char* szErrMsg = hstrerror(ErrCode);
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
const char *szErrMsg = NULL;
int ErrCode = herrno;
if (herrno == 0)
{
ErrCode = errno;
szErrMsg = strerror(ErrCode);
}
else
{
szErrMsg = hstrerror(ErrCode);
}
#endif
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
if (m_bSuppressErrors)
{
debug(szErrPrefix);
}
else
{
error(szErrPrefix);
}
}
#endif
}
#ifndef DISABLE_TLS
bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile)
{
debug("Starting TLS");
if (m_pTLSSocket)
{
delete m_pTLSSocket;
}
m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher);
m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors);
return m_pTLSSocket->Start();
}
void Connection::CloseTLS()
{
if (m_pTLSSocket)
{
m_pTLSSocket->Close();
delete m_pTLSSocket;
m_pTLSSocket = NULL;
}
}
int Connection::recv(SOCKET s, char* buf, int len, int flags)
{
int iReceived = 0;
if (m_pTLSSocket)
{
m_bTLSError = false;
iReceived = m_pTLSSocket->Recv(buf, len);
if (iReceived < 0)
{
m_bTLSError = true;
return -1;
}
}
else
{
iReceived = ::recv(s, buf, len, flags);
}
return iReceived;
}
int Connection::send(SOCKET s, const char* buf, int len, int flags)
{
int iSent = 0;
if (m_pTLSSocket)
{
m_bTLSError = false;
iSent = m_pTLSSocket->Send(buf, len);
if (iSent < 0)
{
m_bTLSError = true;
return -1;
}
return iSent;
}
else
{
iSent = ::send(s, buf, len, flags);
return iSent;
}
}
#endif
#ifndef HAVE_GETADDRINFO
unsigned int Connection::ResolveHostAddr(const char* szHost)
{
unsigned int uaddr = inet_addr(szHost);
if (uaddr == (unsigned int)-1)
{
struct hostent* hinfo;
bool err = false;
int h_errnop = 0;
#ifdef HAVE_GETHOSTBYNAME_R
struct hostent hinfobuf;
char strbuf[1024];
#ifdef HAVE_GETHOSTBYNAME_R_6
err = gethostbyname_r(szHost, &hinfobuf, strbuf, sizeof(strbuf), &hinfo, &h_errnop);
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
#endif
#ifdef HAVE_GETHOSTBYNAME_R_5
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, sizeof(strbuf), &h_errnop);
err = hinfo == NULL;
#endif
#ifdef HAVE_GETHOSTBYNAME_R_3
//NOTE: gethostbyname_r with three parameters were not tested
struct hostent_data hinfo_data;
hinfo = gethostbyname_r((char*)szHost, (struct hostent*)hinfobuf, &hinfo_data);
err = hinfo == NULL;
#endif
#else
m_pMutexGetHostByName->Lock();
hinfo = gethostbyname(szHost);
err = hinfo == NULL;
#endif
if (err)
{
#ifndef HAVE_GETHOSTBYNAME_R
m_pMutexGetHostByName->Unlock();
#endif
ReportError("Could not resolve hostname %s", szHost, true, h_errnop);
return (unsigned int)-1;
}
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
#ifndef HAVE_GETHOSTBYNAME_R
m_pMutexGetHostByName->Unlock();
#endif
}
return uaddr;
}
#endif
const char* Connection::GetRemoteAddr()
{
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
strncpy(m_szRemoteAddr, inet_ntoa(PeerName.sin_addr), sizeof(m_szRemoteAddr));
#else
inet_ntop(AF_INET, &PeerName.sin_addr, m_szRemoteAddr, sizeof(m_szRemoteAddr));
#endif
}
m_szRemoteAddr[sizeof(m_szRemoteAddr)-1] = '\0';
return m_szRemoteAddr;
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,7 +27,14 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "NetAddress.h"
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
#include "Thread.h"
#endif
#endif
#ifndef DISABLE_TLS
#include "TLS.h"
#endif
class Connection
{
@@ -39,47 +46,76 @@ public:
csListening,
csCancelled
};
protected:
NetAddress* m_pNetAddress;
char* m_szHost;
int m_iPort;
SOCKET m_iSocket;
bool m_bTLS;
char* m_szCipher;
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
int m_iTimeout;
bool m_bSuppressErrors;
char m_szRemoteAddr[20];
#ifndef DISABLE_TLS
TLSSocket* m_pTLSSocket;
bool m_bTLSError;
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
static Mutex* m_pMutexGetHostByName;
#endif
#endif
unsigned int ResolveHostAddr(const char* szHost);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
virtual int DoConnect();
virtual int DoDisconnect();
Connection(SOCKET iSocket, bool bTLS);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(char* text);
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
#endif
#ifndef DISABLE_TLS
int recv(SOCKET s, char* buf, int len, int flags);
int send(SOCKET s, const char* buf, int len, int flags);
void CloseTLS();
#endif
public:
Connection(NetAddress* pNetAddress);
Connection(const char* szHost, int iPort, bool bTLS);
virtual ~Connection();
static void Init();
static void Final();
int Connect();
int Disconnect();
bool Connect();
bool Disconnect();
int Bind();
int Send(char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
bool Send(const char* pBuffer, int iSize);
bool Recv(char* pBuffer, int iSize);
int TryRecv(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
int WriteLine(char* text);
SOCKET Accept();
void ReadBuffer(char** pBuffer, int *iBufLen);
int WriteLine(const char* pBuffer);
Connection* Accept();
void Cancel();
NetAddress* GetServer() { return m_pNetAddress; }
SOCKET GetSocket() { return m_iSocket; }
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
void SetCipher(const char* szCipher);
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
void SetSuppressErrors(bool bSuppressErrors);
bool GetSuppressErrors() { return m_bSuppressErrors; }
const char* GetRemoteAddr();
#ifndef DISABLE_TLS
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
#endif
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -40,19 +40,11 @@
#endif
#include "nzbget.h"
#ifdef ENABLE_UULIB
#ifndef PROTOTYPES
#define PROTOTYPES
#endif
#include <uudeview.h>
#endif
#include "Decoder.h"
#include "Log.h"
#include "Util.h"
Mutex UULibDecoder::m_mutexDecoder;
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
unsigned int YDecoder::crc_tab[256];
Decoder::Decoder()
@@ -62,7 +54,6 @@ Decoder::Decoder()
m_szSrcFilename = NULL;
m_szDestFilename = NULL;
m_szArticleFilename = NULL;
m_bCrcError = false;
}
Decoder::~ Decoder()
@@ -75,93 +66,51 @@ Decoder::~ Decoder()
}
}
/*
* UULibDecoder
*/
bool UULibDecoder::Execute()
void Decoder::Clear()
{
bool res = false;
#ifndef ENABLE_UULIB
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
#else
m_mutexDecoder.Lock();
UUInitialize();
UUSetOption(UUOPT_DESPERATE, 1, NULL);
// UUSetOption(UUOPT_DUMBNESS,1,NULL);
// UUSetOption( UUOPT_SAVEPATH, 1, szDestDir );
UULoadFile((char*) m_szSrcFilename, NULL, 0);
// choose right attachment
uulist* attachment = NULL;
for (int i = 0; ; i++)
if (m_szArticleFilename)
{
uulist* att_tmp = UUGetFileListItem(i);
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
if (!att_tmp)
{
break;
}
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
{
if (!strncmp(buffer, "=ybegin ", 8))
{
return efYenc;
}
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
return efUx;
}
if ((att_tmp) && (att_tmp->haveparts))
if (!strncmp(buffer, "begin ", 6))
{
bool bOK = true;
buffer += 6; //strlen("begin ")
while (*buffer && *buffer != ' ')
{
if (!attachment)
char ch = *buffer++;
if (ch < '0' || ch > '7')
{
attachment = att_tmp;
}
else
{
//f**k, multiple attachments!? Can't handle this.
attachment = NULL;
bOK = false;
break;
}
}
}
if (attachment)
{
// okay, we got only one attachment, perfect!
if ((attachment->haveparts) && (attachment->haveparts[0])) // && (!attachment->haveparts[1])) FUCK UULIB
if (bOK)
{
int r = UUDecodeFile(attachment, (char*)m_szDestFilename);
if (r == UURET_OK)
{
// we did it!
res = true;
m_szArticleFilename = strdup(attachment->filename);
}
}
else
{
error("[ERROR] Wrong number of parts!\n");
return efUx;
}
}
else
{
error("[ERROR] Wrong number of attachments!\n");
}
UUCleanUp();
m_mutexDecoder.Unlock();
#endif // ENABLE_UULIB
return res;
return efUnknown;
}
/**
* YDecoder
* Very primitive (but fast) implementation of yEnc-Decoder
* YDecoder: fast implementation of yEnc-Decoder
*/
void YDecoder::Init()
@@ -182,20 +131,22 @@ YDecoder::YDecoder()
void YDecoder::Clear()
{
Decoder::Clear();
m_bBody = false;
m_bBegin = false;
m_bPart = false;
m_bEnd = false;
m_bCrc = false;
m_lExpectedCRC = 0;
m_lCalculatedCRC = 0xFFFFFFFF;
m_iBegin = 0;
m_iEnd = 0;
m_iEnd = 0xFFFFFFFF;
m_iSize = 0;
m_iEndSize = 0;
m_bAutoSeek = false;
m_bNeedSetPos = false;
m_bCrcCheck = false;
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
@@ -254,16 +205,23 @@ unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, uns
unsigned int YDecoder::DecodeBuffer(char* buffer)
{
if (m_bBody)
if (m_bBody && !m_bEnd)
{
if (!strncmp(buffer, "=yend size=", 11))
if (!strncmp(buffer, "=yend ", 6))
{
m_bEnd = true;
char* pc = strstr(buffer, "pcrc32=");
if (pc)
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
if (pb)
{
pc += 7; //=strlen("pcrc32=")
m_lExpectedCRC = strtoul(pc, NULL, 16);
m_bCrc = true;
pb += 7 + (int)m_bPart; //=strlen(" crc32=") or strlen(" pcrc32=")
m_lExpectedCRC = strtoul(pb, NULL, 16);
}
pb = strstr(buffer, " size=");
if (pb)
{
pb += 6; //=strlen(" size=")
m_iEndSize = (int)atoi(pb);
}
return 0;
}
@@ -295,34 +253,19 @@ BreakLoop:
if (m_bCrcCheck)
{
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, optr - buffer);
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
}
return optr - buffer;
return (unsigned int)(optr - buffer);
}
else
else
{
if (!strncmp(buffer, "=ypart begin=", 13))
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
{
m_bBody = true;
char* pb = strstr(buffer, "begin=");
if (pb)
{
pb += 6; //=strlen("begin=")
m_iBegin = (int)atoi(pb);
}
pb = strstr(buffer, "end=");
if (pb)
{
pb += 4; //=strlen("end=")
m_iEnd = (int)atoi(pb);
}
}
else if (!strncmp(buffer, "=ybegin part=", 13))
{
char* pb = strstr(buffer, "name=");
m_bBegin = true;
char* pb = strstr(buffer, " name=");
if (pb)
{
pb += 5; //=strlen("name=")
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (m_szArticleFilename)
@@ -333,20 +276,50 @@ BreakLoop:
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
}
pb = strstr(buffer, " size=");
if (pb)
{
pb += 6; //=strlen(" size=")
m_iSize = (int)atoi(pb);
}
m_bPart = strstr(buffer, " part=");
if (!m_bPart)
{
m_bBody = true;
m_iBegin = 1;
m_iEnd = m_iSize;
}
}
else if (m_bPart && !strncmp(buffer, "=ypart ", 7))
{
m_bPart = true;
m_bBody = true;
char* pb = strstr(buffer, " begin=");
if (pb)
{
pb += 7; //=strlen(" begin=")
m_iBegin = (int)atoi(pb);
}
pb = strstr(buffer, " end=");
if (pb)
{
pb += 5; //=strlen(" end=")
m_iEnd = (int)atoi(pb);
}
}
}
return 0;
}
bool YDecoder::Write(char* buffer, FILE* outfile)
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer);
if (wcnt > 0)
{
if (m_bNeedSetPos)
{
if (m_iBegin == 0 || m_iEnd == 0 || !outfile)
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
{
return false;
}
@@ -361,13 +334,152 @@ bool YDecoder::Write(char* buffer, FILE* outfile)
return true;
}
bool YDecoder::Execute()
Decoder::EStatus YDecoder::Check()
{
m_lCalculatedCRC ^= 0xFFFFFFFF;
debug("Expected pcrc32=%x", m_lExpectedCRC);
debug("Calculated pcrc32=%x", m_lCalculatedCRC);
m_bCrcError = m_bCrcCheck && (m_lExpectedCRC != m_lCalculatedCRC);
debug("Expected crc32=%x", m_lExpectedCRC);
debug("Calculated crc32=%x", m_lCalculatedCRC);
return m_bBody && m_bEnd && !m_bCrcError;
if (!m_bBegin)
{
return eNoBinaryData;
}
else if (!m_bEnd)
{
return eArticleIncomplete;
}
else if (!m_bPart && m_iSize != m_iEndSize)
{
return eInvalidSize;
}
else if (m_bCrcCheck && m_bCrc && (m_lExpectedCRC != m_lCalculatedCRC))
{
return eCrcError;
}
return eFinished;
}
/**
* UDecoder: supports UU encoding formats
*/
UDecoder::UDecoder()
{
}
void UDecoder::Clear()
{
Decoder::Clear();
m_bBody = false;
m_bEnd = false;
}
/* DecodeBuffer-function uses portions of code from tool UUDECODE by Clem Dye
* UUDECODE.c (http://www.bastet.com/uue.zip)
* Copyright (C) 1998 Clem Dye
*
* Released under GPL (thanks)
*/
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
{
if (!m_bBody)
{
if (!strncmp(buffer, "begin ", 6))
{
char* pb = buffer;
pb += 6; //strlen("begin ")
// skip file-permissions
for (; *pb != ' ' && *pb != '\0' && *pb != '\n' && *pb != '\r'; pb++) ;
pb++;
// extracting filename
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
m_bBody = true;
return 0;
}
else if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
m_bBody = true;
}
}
if (m_bBody && (!strncmp(buffer, "end ", 4) || *buffer == '`'))
{
m_bEnd = true;
}
if (m_bBody && !m_bEnd)
{
int iEffLen = UU_DECODE_CHAR(buffer[0]);
if (iEffLen > len)
{
// error;
return 0;
}
char* iptr = buffer;
char* optr = buffer;
for (++iptr; iEffLen > 0; iptr += 4, iEffLen -= 3)
{
if (iEffLen >= 3)
{
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
*optr++ = UU_DECODE_CHAR (iptr[2]) << 6 | UU_DECODE_CHAR (iptr[3]);
}
else
{
if (iEffLen >= 1)
{
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
}
if (iEffLen >= 2)
{
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
}
}
}
return (unsigned int)(optr - buffer);
}
return 0;
}
bool UDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer, len);
if (wcnt > 0)
{
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus UDecoder::Check()
{
if (!m_bBody)
{
return eNoBinaryData;
}
return eFinished;
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,45 +27,60 @@
#ifndef DECODER_H
#define DECODER_H
#include "Thread.h"
class Decoder
{
public:
enum EStatus
{
eUnknownError,
eFinished,
eArticleIncomplete,
eCrcError,
eInvalidSize,
eNoBinaryData
};
enum EFormat
{
efUnknown,
efYenc,
efUx,
};
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
bool m_bCrcError;
public:
Decoder();
virtual ~Decoder();
virtual bool Execute() = 0;
virtual EStatus Check() = 0;
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
const char* GetArticleFilename() { return m_szArticleFilename; }
bool GetCrcError() { return m_bCrcError; }
};
class UULibDecoder: public Decoder
{
private:
static Mutex m_mutexDecoder;
public:
virtual bool Execute();
static EFormat DetectFormat(const char* buffer, int len);
};
class YDecoder: public Decoder
{
protected:
static unsigned int crc_tab[256];
bool m_bBegin;
bool m_bPart;
bool m_bBody;
bool m_bEnd;
bool m_bCrc;
unsigned long m_lExpectedCRC;
unsigned long m_lCalculatedCRC;
unsigned long m_iBegin;
unsigned long m_iEnd;
unsigned long m_iSize;
unsigned long m_iEndSize;
bool m_bAutoSeek;
bool m_bNeedSetPos;
bool m_bCrcCheck;
@@ -76,9 +91,9 @@ protected:
public:
YDecoder();
virtual bool Execute();
void Clear();
bool Write(char* buffer, FILE* outfile);
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
@@ -86,4 +101,19 @@ public:
static void Final();
};
class UDecoder: public Decoder
{
private:
bool m_bBody;
bool m_bEnd;
unsigned int DecodeBuffer(char* buffer, int len);
public:
UDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -31,17 +31,33 @@
class DiskState
{
private:
int ParseFormatVersion(const char* szFormatSignature);
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
void SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
void SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* outfile);
bool LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* infile, int iFormatVersion);
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
void SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile);
bool LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion);
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
public:
bool Exists();
bool Save(DownloadQueue* pDownloadQueue);
bool Load(DownloadQueue* pDownloadQueue);
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
bool Discard();
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -34,14 +34,384 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <map>
#include <sys/stat.h>
#include "nzbget.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
int NZBInfo::m_iIDGen = 0;
int PostInfo::m_iIDGen = 0;
int UrlInfo::m_iIDGen = 0;
int HistoryInfo::m_iIDGen = 0;
NZBParameter::NZBParameter(const char* szName)
{
m_szName = strdup(szName);
m_szValue = NULL;
}
NZBParameter::~NZBParameter()
{
if (m_szName)
{
free(m_szName);
}
if (m_szValue)
{
free(m_szValue);
}
}
void NZBParameter::SetValue(const char* szValue)
{
if (m_szValue)
{
free(m_szValue);
}
m_szValue = strdup(szValue);
}
void NZBParameterList::SetParameter(const char* szName, const char* szValue)
{
NZBParameter* pParameter = NULL;
bool bDelete = !szValue || !*szValue;
for (iterator it = begin(); it != end(); it++)
{
NZBParameter* pLookupParameter = *it;
if (!strcmp(pLookupParameter->GetName(), szName))
{
if (bDelete)
{
delete pLookupParameter;
erase(it);
return;
}
pParameter = pLookupParameter;
break;
}
}
if (bDelete)
{
return;
}
if (!pParameter)
{
pParameter = new NZBParameter(szName);
push_back(pParameter);
}
pParameter->SetValue(szValue);
}
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
m_szFilename = NULL;
m_szDestDir = NULL;
m_szCategory = strdup("");
m_szName = NULL;
m_iFileCount = 0;
m_iParkedFileCount = 0;
m_lSize = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_eRenameStatus = rsNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eMoveStatus = msNone;
m_eScriptStatus = srNone;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
m_bUnpackCleanedUpDisk = false;
m_szQueuedFilename = strdup("");
m_Owner = NULL;
m_Messages.clear();
m_iIDMessageGen = 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
NZBInfo::~NZBInfo()
{
debug("Destroying NZBInfo");
if (m_szFilename)
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szCategory)
{
free(m_szCategory);
}
if (m_szName)
{
free(m_szName);
}
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
ClearCompletedFiles();
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
{
delete *it;
}
m_ppParameters.clear();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
if (m_Owner)
{
m_Owner->Remove(this);
}
}
void NZBInfo::AddReference()
{
m_iRefCount++;
}
void NZBInfo::Release()
{
m_iRefCount--;
if (m_iRefCount <= 0)
{
delete this;
}
}
void NZBInfo::ClearCompletedFiles()
{
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
{
free(*it);
}
m_completedFiles.clear();
}
void NZBInfo::SetDestDir(const char* szDestDir)
{
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void NZBInfo::SetFilename(const char * szFilename)
{
if (m_szFilename)
{
free(m_szFilename);
}
m_szFilename = strdup(szFilename);
if (!m_szName)
{
char szNZBNicename[1024];
MakeNiceNZBName(m_szFilename, szNZBNicename, sizeof(szNZBNicename), true);
szNZBNicename[1024-1] = '\0';
SetName(szNZBNicename);
}
}
void NZBInfo::SetName(const char* szName)
{
if (m_szName)
{
free(m_szName);
}
m_szName = szName ? strdup(szName) : NULL;
}
void NZBInfo::SetCategory(const char* szCategory)
{
if (m_szCategory)
{
free(m_szCategory);
}
m_szCategory = strdup(szCategory);
}
void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
{
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
m_szQueuedFilename = strdup(szQueuedFilename);
}
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize, bool bRemoveExt)
{
char postname[1024];
const char* szBaseName = Util::BaseFileName(szNZBFilename);
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
if (bRemoveExt)
{
// wipe out ".nzb"
char* p = strrchr(postname, '.');
if (p && !strcasecmp(p, ".nzb")) *p = '\0';
}
Util::MakeValidFilename(postname, '_', false);
strncpy(szBuffer, postname, iSize);
szBuffer[iSize-1] = '\0';
}
void NZBInfo::BuildDestDirName()
{
char szDestDir[1024];
if (strlen(g_pOptions->GetInterDir()) == 0)
{
BuildFinalDirName(szDestDir, 1024);
}
else
{
snprintf(szDestDir, 1024, "%s%s", g_pOptions->GetInterDir(), GetName());
szDestDir[1024-1] = '\0';
}
SetDestDir(szDestDir);
}
void NZBInfo::BuildFinalDirName(char* szFinalDirBuf, int iBufSize)
{
char szBuffer[1024];
bool bUseCategory = m_szCategory && m_szCategory[0] != '\0';
snprintf(szFinalDirBuf, iBufSize, "%s", g_pOptions->GetDestDir());
szFinalDirBuf[iBufSize-1] = '\0';
if (bUseCategory)
{
Options::Category *pCategory = g_pOptions->FindCategory(m_szCategory);
if (pCategory && pCategory->GetDestDir() && pCategory->GetDestDir()[0] != '\0')
{
snprintf(szFinalDirBuf, iBufSize, "%s", pCategory->GetDestDir());
szFinalDirBuf[iBufSize-1] = '\0';
bUseCategory = false;
}
}
if (g_pOptions->GetAppendCategoryDir() && bUseCategory)
{
char szCategoryDir[1024];
strncpy(szCategoryDir, m_szCategory, 1024);
szCategoryDir[1024 - 1] = '\0';
Util::MakeValidFilename(szCategoryDir, '_', true);
snprintf(szBuffer, 1024, "%s%s%c", szFinalDirBuf, szCategoryDir, PATH_SEPARATOR);
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
if (g_pOptions->GetAppendNZBDir())
{
snprintf(szBuffer, 1024, "%s%s", szFinalDirBuf, GetName());
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
}
void NZBInfo::SetParameter(const char* szName, const char* szValue)
{
m_ppParameters.SetParameter(szName, szValue);
}
NZBInfo::Messages* NZBInfo::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void NZBInfo::UnlockMessages()
{
m_mutexLog.Unlock();
}
void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
{
if (tTime == 0)
{
tTime = time(NULL);
}
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_Messages.push_back(pMessage);
m_mutexLog.Unlock();
}
void NZBInfoList::Add(NZBInfo* pNZBInfo)
{
pNZBInfo->m_Owner = this;
push_back(pNZBInfo);
}
void NZBInfoList::Remove(NZBInfo* pNZBInfo)
{
for (iterator it = begin(); it != end(); it++)
{
NZBInfo* pNZBInfo2 = *it;
if (pNZBInfo2 == pNZBInfo)
{
erase(it);
break;
}
}
}
void NZBInfoList::ReleaseAll()
{
int i = 0;
for (iterator it = begin(); it != end(); )
{
NZBInfo* pNZBInfo = *it;
bool bObjDeleted = pNZBInfo->m_iRefCount == 1;
pNZBInfo->Release();
if (bObjDeleted)
{
it = begin() + i;
}
else
{
it++;
i++;
}
}
}
ArticleInfo::ArticleInfo()
{
@@ -73,6 +443,10 @@ void ArticleInfo::SetMessageID(const char * szMessageID)
void ArticleInfo::SetResultFilename(const char * v)
{
if (m_szResultFilename)
{
free(m_szResultFilename);
}
m_szResultFilename = strdup(v);
}
@@ -85,15 +459,19 @@ FileInfo::FileInfo()
m_Groups.clear();
m_szSubject = NULL;
m_szFilename = NULL;
m_szOutputFilename = NULL;
m_bFilenameConfirmed = false;
m_szDestDir = NULL;
m_szNZBFilename = NULL;
m_lSize = 0;
m_lRemainingSize = 0;
m_tTime = 0;
m_bPaused = false;
m_bDeleted = false;
m_iCompleted = 0;
m_bOutputInitialized = false;
m_pNZBInfo = NULL;
m_iPriority = 0;
m_bExtraPriority = false;
m_iActiveDownloads = 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
@@ -110,13 +488,9 @@ FileInfo::~ FileInfo()
{
free(m_szFilename);
}
if (m_szDestDir)
if (m_szOutputFilename)
{
free(m_szDestDir);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
free(m_szOutputFilename);
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
@@ -126,6 +500,11 @@ FileInfo::~ FileInfo()
m_Groups.clear();
ClearArticles();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void FileInfo::ClearArticles()
@@ -146,71 +525,21 @@ void FileInfo::SetID(int s)
}
}
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->AddReference();
}
void FileInfo::SetSubject(const char* szSubject)
{
m_szSubject = strdup(szSubject);
}
void FileInfo::SetDestDir(const char* szDestDir)
{
m_szDestDir = strdup(szDestDir);
}
void FileInfo::SetNZBFilename(const char * szNZBFilename)
{
m_szNZBFilename = strdup(szNZBFilename);
}
void FileInfo::GetNiceNZBName(char* szBuffer, int iSize)
{
MakeNiceNZBName(m_szNZBFilename, szBuffer, iSize);
}
void FileInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
{
char postname[1024];
const char* szBaseName = BaseFileName(szNZBFilename);
// if .nzb file has a certain structure, try to strip out certain elements
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
{
// OK, using stripped name
}
else
{
// using complete filename
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
}
// wipe out ".nzb"
if (char* p = strrchr(postname, '.')) *p = '\0';
::MakeValidFilename(postname, '_');
// if the resulting name is empty, use basename without cleaing up "msgid_"
if (strlen(postname) == 0)
{
// using complete filename
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
// wipe out ".nzb"
if (char* p = strrchr(postname, '.')) *p = '\0';
::MakeValidFilename(postname, '_');
// if the resulting name is STILL empty, use "noname"
if (strlen(postname) == 0)
{
strncpy(postname, "noname", 1024);
}
}
strncpy(szBuffer, postname, iSize);
szBuffer[iSize-1] = '\0';
}
void FileInfo::SetFilename(const char* szFilename)
{
if (m_szFilename)
@@ -222,7 +551,7 @@ void FileInfo::SetFilename(const char* szFilename)
void FileInfo::MakeValidFilename()
{
::MakeValidFilename(m_szFilename, '_');
Util::MakeValidFilename(m_szFilename, '_', false);
}
void FileInfo::LockOutputFile()
@@ -235,24 +564,383 @@ void FileInfo::UnlockOutputFile()
m_mutexOutputFile.Unlock();
}
void FileInfo::SetOutputFilename(const char* szOutputFilename)
{
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
m_szOutputFilename = strdup(szOutputFilename);
}
bool FileInfo::IsDupe(const char* szFilename)
{
struct stat buffer;
char fileName[1024];
snprintf(fileName, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
bool exists = !stat(fileName, &buffer);
if (exists)
if (Util::FileExists(fileName))
{
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
if (exists)
if (Util::FileExists(fileName))
{
return true;
}
return false;
}
GroupInfo::GroupInfo()
{
m_iFirstID = 0;
m_iLastID = 0;
m_iRemainingFileCount = 0;
m_iPausedFileCount = 0;
m_lRemainingSize = 0;
m_lPausedSize = 0;
m_iRemainingParCount = 0;
m_tMinTime = 0;
m_tMaxTime = 0;
m_iMinPriority = 0;
m_iMaxPriority = 0;
m_iActiveDownloads = 0;
}
GroupInfo::~GroupInfo()
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
PostInfo::PostInfo()
{
debug("Creating PostInfo");
m_pNZBInfo = NULL;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_bWorking = false;
m_bDeleted = false;
m_eRenameStatus = rsNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eRequestParCheck = rpNone;
m_bRequestParRename = false;
m_eScriptStatus = srNone;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
m_tStartTime = 0;
m_tStageTime = 0;
m_eStage = ptQueued;
m_pPostThread = NULL;
m_Messages.clear();
m_iIDMessageGen = 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
PostInfo::~ PostInfo()
{
debug("Destroying PostInfo");
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void PostInfo::SetNZBInfo(NZBInfo* pNZBInfo)
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->AddReference();
}
void PostInfo::SetParFilename(const char* szParFilename)
{
m_szParFilename = strdup(szParFilename);
}
void PostInfo::SetInfoName(const char* szInfoName)
{
m_szInfoName = strdup(szInfoName);
}
void PostInfo::SetProgressLabel(const char* szProgressLabel)
{
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
m_szProgressLabel = strdup(szProgressLabel);
}
PostInfo::Messages* PostInfo::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void PostInfo::UnlockMessages()
{
m_mutexLog.Unlock();
}
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
{
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
m_mutexLog.Unlock();
}
void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
{
std::map<int, GroupInfo*> groupMap;
for (FileQueue::iterator it = GetFileQueue()->begin(); it != GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo *&pGroupInfo = groupMap[pFileInfo->GetNZBInfo()->GetID()];
if (!pGroupInfo)
{
pGroupInfo = new GroupInfo();
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
pGroupInfo->m_pNZBInfo->AddReference();
pGroupInfo->m_iFirstID = pFileInfo->GetID();
pGroupInfo->m_iLastID = pFileInfo->GetID();
pGroupInfo->m_tMinTime = pFileInfo->GetTime();
pGroupInfo->m_tMaxTime = pFileInfo->GetTime();
pGroupInfo->m_iMinPriority = pFileInfo->GetPriority();
pGroupInfo->m_iMaxPriority = pFileInfo->GetPriority();
pGroupQueue->push_back(pGroupInfo);
}
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
{
pGroupInfo->m_iFirstID = pFileInfo->GetID();
}
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
{
pGroupInfo->m_iLastID = pFileInfo->GetID();
}
if (pFileInfo->GetTime() > 0)
{
if (pFileInfo->GetTime() < pGroupInfo->GetMinTime())
{
pGroupInfo->m_tMinTime = pFileInfo->GetTime();
}
if (pFileInfo->GetTime() > pGroupInfo->GetMaxTime())
{
pGroupInfo->m_tMaxTime = pFileInfo->GetTime();
}
}
if (pFileInfo->GetPriority() < pGroupInfo->GetMinPriority())
{
pGroupInfo->m_iMinPriority = pFileInfo->GetPriority();
}
if (pFileInfo->GetPriority() > pGroupInfo->GetMaxPriority())
{
pGroupInfo->m_iMaxPriority = pFileInfo->GetPriority();
}
pGroupInfo->m_iActiveDownloads += pFileInfo->GetActiveDownloads();
pGroupInfo->m_iRemainingFileCount++;
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
if (pFileInfo->GetPaused())
{
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
pGroupInfo->m_iPausedFileCount++;
}
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (strstr(szLoFileName, ".par2"))
{
pGroupInfo->m_iRemainingParCount++;
}
}
}
UrlInfo::UrlInfo()
{
//debug("Creating ArticleInfo");
m_szURL = NULL;
m_szNZBFilename = strdup("");
m_szCategory = strdup("");
m_iPriority = 0;
m_bAddTop = false;
m_bAddPaused = false;
m_eStatus = aiUndefined;
m_iIDGen++;
m_iID = m_iIDGen;
}
UrlInfo::~ UrlInfo()
{
if (m_szURL)
{
free(m_szURL);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szCategory)
{
free(m_szCategory);
}
}
void UrlInfo::SetURL(const char* szURL)
{
if (m_szURL)
{
free(m_szURL);
}
m_szURL = strdup(szURL);
}
void UrlInfo::SetID(int s)
{
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
}
}
void UrlInfo::SetNZBFilename(const char* szNZBFilename)
{
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
m_szNZBFilename = strdup(szNZBFilename);
}
void UrlInfo::SetCategory(const char* szCategory)
{
if (m_szCategory)
{
free(m_szCategory);
}
m_szCategory = strdup(szCategory);
}
void UrlInfo::GetName(char* szBuffer, int iSize)
{
MakeNiceName(m_szURL, m_szNZBFilename, szBuffer, iSize);
}
void UrlInfo::MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize)
{
URL url(szURL);
if (strlen(szNZBFilename) > 0)
{
char szNZBNicename[1024];
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNicename, sizeof(szNZBNicename), true);
snprintf(szBuffer, iSize, "%s @ %s", szNZBNicename, url.GetHost());
}
else
{
snprintf(szBuffer, iSize, "%s%s", url.GetHost(), url.GetResource());
}
szBuffer[iSize-1] = '\0';
}
HistoryInfo::HistoryInfo(NZBInfo* pNZBInfo)
{
m_eKind = hkNZBInfo;
m_pInfo = pNZBInfo;
pNZBInfo->AddReference();
m_tTime = 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
HistoryInfo::HistoryInfo(UrlInfo* pUrlInfo)
{
m_eKind = hkUrlInfo;
m_pInfo = pUrlInfo;
m_tTime = 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
HistoryInfo::~HistoryInfo()
{
if (m_eKind == hkNZBInfo && m_pInfo)
{
((NZBInfo*)m_pInfo)->Release();
}
else if (m_eKind == hkUrlInfo && m_pInfo)
{
delete (UrlInfo*)m_pInfo;
}
}
void HistoryInfo::SetID(int s)
{
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
}
}
void HistoryInfo::GetName(char* szBuffer, int iSize)
{
if (m_eKind == hkNZBInfo)
{
strncpy(szBuffer, GetNZBInfo()->GetName(), iSize);
szBuffer[iSize-1] = '\0';
}
else if (m_eKind == hkUrlInfo)
{
GetUrlInfo()->GetName(szBuffer, iSize);
}
else
{
strncpy(szBuffer, "<unknown>", iSize);
}
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -29,9 +29,14 @@
#include <vector>
#include <deque>
#include <time.h>
#include "Log.h"
#include "Thread.h"
class NZBInfo;
class DownloadQueue;
class ArticleInfo
{
public:
@@ -73,20 +78,24 @@ public:
private:
int m_iID;
NZBInfo* m_pNZBInfo;
Articles m_Articles;
Groups m_Groups;
char* m_szNZBFilename;
char* m_szSubject;
char* m_szFilename;
char* m_szDestDir;
long long m_lSize;
long long m_lRemainingSize;
time_t m_tTime;
bool m_bPaused;
bool m_bDeleted;
bool m_bFilenameConfirmed;
int m_iCompleted;
bool m_bOutputInitialized;
char* m_szOutputFilename;
Mutex m_mutexOutputFile;
int m_iPriority;
bool m_bExtraPriority;
int m_iActiveDownloads;
static int m_iIDGen;
@@ -95,12 +104,10 @@ public:
~FileInfo();
int GetID() { return m_iID; }
void SetID(int s);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
Groups* GetGroups() { return &m_Groups; }
const char* GetNZBFilename() { return m_szNZBFilename; }
void SetNZBFilename(const char* szNZBFilename);
void GetNiceNZBName(char* szBuffer, int iSize);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
const char* GetSubject() { return m_szSubject; }
void SetSubject(const char* szSubject);
const char* GetFilename() { return m_szFilename; }
@@ -112,22 +119,487 @@ public:
long long GetSize() { return m_lSize; }
long long GetRemainingSize() { return m_lRemainingSize; }
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
bool GetPaused() { return m_bPaused; }
void SetPaused(bool Paused) { m_bPaused = Paused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
const char* GetDestDir() { return m_szDestDir; }
void SetDestDir(const char* szDestDir);
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int s) { m_iCompleted = s; }
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* szOutputFilename);
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
bool GetExtraPriority() { return m_bExtraPriority; }
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads) { m_iActiveDownloads = iActiveDownloads; }
};
typedef std::deque<FileInfo*> DownloadQueue;
typedef std::deque<FileInfo*> FileQueue;
class GroupInfo
{
private:
NZBInfo* m_pNZBInfo;
int m_iFirstID;
int m_iLastID;
int m_iRemainingFileCount;
int m_iPausedFileCount;
long long m_lRemainingSize;
long long m_lPausedSize;
int m_iRemainingParCount;
time_t m_tMinTime;
time_t m_tMaxTime;
int m_iMinPriority;
int m_iMaxPriority;
int m_iActiveDownloads;
friend class DownloadQueue;
public:
GroupInfo();
~GroupInfo();
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
int GetFirstID() { return m_iFirstID; }
int GetLastID() { return m_iLastID; }
long long GetRemainingSize() { return m_lRemainingSize; }
long long GetPausedSize() { return m_lPausedSize; }
int GetRemainingFileCount() { return m_iRemainingFileCount; }
int GetPausedFileCount() { return m_iPausedFileCount; }
int GetRemainingParCount() { return m_iRemainingParCount; }
time_t GetMinTime() { return m_tMinTime; }
time_t GetMaxTime() { return m_tMaxTime; }
int GetMinPriority() { return m_iMinPriority; }
int GetMaxPriority() { return m_iMaxPriority; }
int GetActiveDownloads() { return m_iActiveDownloads; }
};
typedef std::deque<GroupInfo*> GroupQueue;
class NZBParameter
{
private:
char* m_szName;
char* m_szValue;
void SetValue(const char* szValue);
friend class NZBParameterList;
public:
NZBParameter(const char* szName);
~NZBParameter();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
typedef std::deque<NZBParameter*> NZBParameterListBase;
class NZBParameterList : public NZBParameterListBase
{
public:
void SetParameter(const char* szName, const char* szValue);
};
class NZBInfoList;
class NZBInfo
{
public:
enum ERenameStatus
{
rsNone,
rsSkipped,
rsFailure,
rsSuccess
};
enum EParStatus
{
psNone,
psSkipped,
psFailure,
psSuccess,
psRepairPossible
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum EScriptStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
};
enum EMoveStatus
{
msNone,
msFailure,
msSuccess
};
typedef std::vector<char*> Files;
typedef std::deque<Message*> Messages;
private:
int m_iID;
int m_iRefCount;
char* m_szFilename;
char* m_szName;
char* m_szDestDir;
char* m_szCategory;
int m_iFileCount;
int m_iParkedFileCount;
long long m_lSize;
Files m_completedFiles;
bool m_bPostProcess;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
EScriptStatus m_eScriptStatus;
EMoveStatus m_eMoveStatus;
char* m_szQueuedFilename;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bCleanupDisk;
bool m_bUnpackCleanedUpDisk;
NZBInfoList* m_Owner;
NZBParameterList m_ppParameters;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
static int m_iIDGen;
friend class NZBInfoList;
public:
NZBInfo();
~NZBInfo();
void AddReference();
void Release();
int GetID() { return m_iID; }
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
const char* GetName() { return m_szName; } // needs locking (for shared objects)
void SetName(const char* szName); // needs locking (for shared objects)
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
int GetFileCount() { return m_iFileCount; }
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
int GetParkedFileCount() { return m_iParkedFileCount; }
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
void BuildDestDirName();
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
void ClearCompletedFiles();
bool GetPostProcess() { return m_bPostProcess; }
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetParCleanup() { return m_bParCleanup; }
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
bool GetCleanupDisk() { return m_bCleanupDisk; }
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
bool GetUnpackCleanedUpDisk() { return m_bUnpackCleanedUpDisk; }
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
};
typedef std::deque<NZBInfo*> NZBInfoListBase;
class NZBInfoList : public NZBInfoListBase
{
public:
void Add(NZBInfo* pNZBInfo);
void Remove(NZBInfo* pNZBInfo);
void ReleaseAll();
};
class PostInfo
{
public:
enum EStage
{
ptQueued,
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptRenaming,
ptUnpacking,
ptMoving,
ptExecutingScript,
ptFinished
};
enum ERenameStatus
{
rsNone,
rsSkipped,
rsFailure,
rsSuccess
};
enum EParStatus
{
psNone,
psSkipped,
psFailure,
psSuccess,
psRepairPossible
};
enum ERequestParCheck
{
rpNone,
rpCurrent,
rpAll
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum EScriptStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
};
typedef std::deque<Message*> Messages;
private:
int m_iID;
NZBInfo* m_pNZBInfo;
char* m_szParFilename;
char* m_szInfoName;
bool m_bWorking;
bool m_bDeleted;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
EScriptStatus m_eScriptStatus;
ERequestParCheck m_eRequestParCheck;
bool m_bRequestParRename;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pPostThread;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
static int m_iIDGen;
public:
PostInfo();
~PostInfo();
int GetID() { return m_iID; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
EStage GetStage() { return m_eStage; }
void SetStage(EStage eStage) { m_eStage = eStage; }
void SetProgressLabel(const char* szProgressLabel);
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
int GetStageProgress() { return m_iStageProgress; }
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
time_t GetStartTime() { return m_tStartTime; }
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
time_t GetStageTime() { return m_tStageTime; }
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
bool GetWorking() { return m_bWorking; }
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
bool GetRequestParRename() { return m_bRequestParRename; }
void SetRequestParRename(bool bRequestParRename) { m_bRequestParRename = bRequestParRename; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
Messages* LockMessages();
void UnlockMessages();
};
typedef std::deque<PostInfo*> PostQueue;
typedef std::vector<int> IDList;
typedef std::vector<char*> NameList;
class UrlInfo
{
public:
enum EStatus
{
aiUndefined,
aiRunning,
aiFinished,
aiFailed,
aiRetry
};
private:
int m_iID;
char* m_szURL;
char* m_szNZBFilename;
char* m_szCategory;
int m_iPriority;
bool m_bAddTop;
bool m_bAddPaused;
EStatus m_eStatus;
static int m_iIDGen;
public:
UrlInfo();
~UrlInfo();
int GetID() { return m_iID; }
void SetID(int s);
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
void SetURL(const char* szURL); // needs locking (for shared objects)
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
void SetNZBFilename(const char* szNZBFilename); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
bool GetAddTop() { return m_bAddTop; }
void SetAddTop(bool bAddTop) { m_bAddTop = bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
void SetAddPaused(bool bAddPaused) { m_bAddPaused = bAddPaused; }
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
static void MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize);
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
};
typedef std::deque<UrlInfo*> UrlQueue;
class HistoryInfo
{
public:
enum EKind
{
hkUnknown,
hkNZBInfo,
hkUrlInfo
};
private:
int m_iID;
EKind m_eKind;
void* m_pInfo;
time_t m_tTime;
static int m_iIDGen;
public:
HistoryInfo(NZBInfo* pNZBInfo);
HistoryInfo(UrlInfo* pUrlInfo);
~HistoryInfo();
int GetID() { return m_iID; }
void SetID(int s);
EKind GetKind() { return m_eKind; }
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }
void DiscardUrlInfo() { m_pInfo = NULL; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
};
typedef std::deque<HistoryInfo*> HistoryList;
class DownloadQueue
{
protected:
NZBInfoList m_NZBInfoList;
FileQueue m_FileQueue;
PostQueue m_PostQueue;
HistoryList m_HistoryList;
FileQueue m_ParkedFiles;
UrlQueue m_UrlQueue;
public:
NZBInfoList* GetNZBInfoList() { return &m_NZBInfoList; }
FileQueue* GetFileQueue() { return &m_FileQueue; }
PostQueue* GetPostQueue() { return &m_PostQueue; }
HistoryList* GetHistoryList() { return &m_HistoryList; }
FileQueue* GetParkedFiles() { return &m_ParkedFiles; }
UrlQueue* GetUrlQueue() { return &m_UrlQueue; }
void BuildGroups(GroupQueue* pGroupQueue);
};
class DownloadQueueHolder
{
public:
virtual ~DownloadQueueHolder() {};
virtual DownloadQueue* LockQueue() = 0;
virtual void UnlockQueue() = 0;
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -34,6 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
@@ -47,12 +49,10 @@
#include "Connection.h"
#include "MessageBase.h"
#include "QueueCoordinator.h"
#include "PrePostProcessor.h"
#include "RemoteClient.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Options* g_pOptions;
Frontend::Frontend()
@@ -63,18 +63,17 @@ Frontend::Frontend()
m_iNeededLogEntries = 0;
m_bSummary = false;
m_bFileList = false;
m_fCurrentDownloadSpeed = 0;
m_iCurrentDownloadSpeed = 0;
m_lRemainingSize = 0;
m_bPause = false;
m_fDownloadLimit = 0;
m_bPauseDownload = false;
m_bPauseDownload2 = false;
m_iDownloadLimit = 0;
m_iThreadCount = 0;
m_iParJobCount = 0;
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
m_iDnTimeSec = 0;
m_iAllBytes = 0;
m_bStandBy = 0;
m_RemoteMessages.clear();
m_RemoteQueue.clear();
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
}
@@ -88,7 +87,7 @@ bool Frontend::PrepareData()
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
Stop();
return false;
}
@@ -97,14 +96,15 @@ bool Frontend::PrepareData()
{
if (m_bSummary)
{
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_iCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
m_bPause = g_pOptions->GetPause();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_bPauseDownload = g_pOptions->GetPauseDownload();
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
m_iDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
m_iParJobCount = pParQueue->size();
g_pPrePostProcessor->UnlockParQueue();
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
m_iPostJobCount = pPostQueue->size();
g_pQueueCoordinator->UnlockQueue();
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
}
}
@@ -121,11 +121,11 @@ void Frontend::FreeData()
}
m_RemoteMessages.clear();
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
for (FileQueue::iterator it = m_RemoteQueue.GetFileQueue()->begin(); it != m_RemoteQueue.GetFileQueue()->end(); it++)
{
delete *it;
}
m_RemoteQueue.clear();
m_RemoteQueue.GetFileQueue()->clear();
}
}
@@ -149,7 +149,7 @@ void Frontend::UnlockMessages()
}
}
DownloadQueue * Frontend::LockQueue()
DownloadQueue* Frontend::LockQueue()
{
if (IsRemoteMode())
{
@@ -174,27 +174,35 @@ bool Frontend::IsRemoteMode()
return g_pOptions->GetRemoteClientMode();
}
void Frontend::ServerPauseUnpause(bool bPause)
void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
{
if (IsRemoteMode())
{
RequestPauseUnpause(bPause);
RequestPauseUnpause(bPause, bSecondRegister);
}
else
{
g_pOptions->SetPause(bPause);
g_pOptions->SetResumeTime(0);
if (bSecondRegister)
{
g_pOptions->SetPauseDownload2(bPause);
}
else
{
g_pOptions->SetPauseDownload(bPause);
}
}
}
void Frontend::ServerSetDownloadRate(float fRate)
void Frontend::ServerSetDownloadRate(int iRate)
{
if (IsRemoteMode())
{
RequestSetDownloadRate(fRate);
RequestSetDownloadRate(iRate);
}
else
{
g_pOptions->SetDownloadRate(fRate);
g_pOptions->SetDownloadRate(iRate);
}
}
@@ -214,11 +222,11 @@ bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, in
{
if (IsRemoteMode())
{
return RequestEditQueue(eAction, iOffset, iID);
return RequestEditQueue((eRemoteEditAction)eAction, iOffset, iID);
}
else
{
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset, NULL);
}
return false;
}
@@ -228,16 +236,15 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
bool Frontend::RequestMessages()
{
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
bool OK = connection.Connect() >= 0;
bool OK = connection.Connect();
if (!OK)
{
return false;
@@ -255,15 +262,15 @@ bool Frontend::RequestMessages()
LogRequest.m_iIDFrom = 0;
}
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
{
return false;
}
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
bool bRead = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (!bRead ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
@@ -274,7 +281,7 @@ bool Frontend::RequestMessages()
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
if (!connection.Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -306,10 +313,9 @@ bool Frontend::RequestMessages()
bool Frontend::RequestFileList()
{
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
bool OK = connection.Connect() >= 0;
bool OK = connection.Connect();
if (!OK)
{
return false;
@@ -320,15 +326,15 @@ bool Frontend::RequestFileList()
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
{
return false;
}
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
bool bRead = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (!bRead ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
@@ -339,7 +345,7 @@ bool Frontend::RequestFileList()
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
if (!connection.Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -350,47 +356,26 @@ bool Frontend::RequestFileList()
if (m_bSummary)
{
m_bPause = ntohl(ListResponse.m_bServerPaused);
m_lRemainingSize = JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0;
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0;
m_bPauseDownload = ntohl(ListResponse.m_bDownloadPaused);
m_bPauseDownload2 = ntohl(ListResponse.m_bDownload2Paused);
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
m_iCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate);
m_iDownloadLimit = ntohl(ListResponse.m_iDownloadLimit);
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
m_iParJobCount = ntohl(ListResponse.m_iParJobCount);
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
m_iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
m_bStandBy = ntohl(ListResponse.m_bDownloadStandBy);
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
}
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
{
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
pFileInfo->SetSize(JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
pFileInfo->SetRemainingSize(JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
pFileInfo->SetNZBFilename(szNZBFilename);
pFileInfo->SetSubject(szSubject);
pFileInfo->SetFilename(szFileName);
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
pFileInfo->SetDestDir(szDestDir);
m_RemoteQueue.push_back(pFileInfo);
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
}
RemoteClient client;
client.SetVerbose(false);
client.BuildFileList(&ListResponse, pBuf, &m_RemoteQueue);
}
if (pBuf)
{
free(pBuf);
@@ -399,18 +384,18 @@ bool Frontend::RequestFileList()
return true;
}
bool Frontend::RequestPauseUnpause(bool bPause)
bool Frontend::RequestPauseUnpause(bool bPause, bool bSecondRegister)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerPauseUnpause(bPause);
return client.RequestServerPauseUnpause(bPause, bSecondRegister ? eRemotePauseUnpauseActionDownload2 : eRemotePauseUnpauseActionDownload);
}
bool Frontend::RequestSetDownloadRate(float fRate)
bool Frontend::RequestSetDownloadRate(int iRate)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerSetDownloadRate(fRate);
return client.RequestServerSetDownloadRate(iRate);
}
bool Frontend::RequestDumpDebug()
@@ -420,9 +405,9 @@ bool Frontend::RequestDumpDebug()
return client.RequestServerDumpDebug();
}
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID, false);
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -50,12 +50,13 @@ protected:
int m_iUpdateInterval;
// summary
float m_fCurrentDownloadSpeed;
int m_iCurrentDownloadSpeed;
long long m_lRemainingSize;
bool m_bPause;
float m_fDownloadLimit;
bool m_bPauseDownload;
bool m_bPauseDownload2;
int m_iDownloadLimit;
int m_iThreadCount;
int m_iParJobCount;
int m_iPostJobCount;
int m_iUpTimeSec;
int m_iDnTimeSec;
long long m_iAllBytes;
@@ -69,14 +70,14 @@ protected:
void UnlockQueue();
bool IsRemoteMode();
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
void ServerPauseUnpause(bool bPause);
bool RequestPauseUnpause(bool bPause);
void ServerSetDownloadRate(float fRate);
bool RequestSetDownloadRate(float fRate);
void ServerPauseUnpause(bool bPause, bool bSecondRegister);
bool RequestPauseUnpause(bool bPause, bool bSecondRegister);
void ServerSetDownloadRate(int iRate);
bool RequestSetDownloadRate(int iRate);
void ServerDumpDebug();
bool RequestDumpDebug();
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
bool RequestEditQueue(int iAction, int iOffset, int iID);
bool RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID);
public:
Frontend();

172
Log.cpp
View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -30,12 +30,15 @@
#ifdef WIN32
#include "win32.h"
#else
#include <pthread.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <cstdio>
#include "nzbget.h"
#include "Options.h"
@@ -50,18 +53,13 @@ Log::Log()
m_iIDGen = 0;
m_szLogFilename = NULL;
#ifdef DEBUG
struct stat buffer;
m_bExtraDebug = !stat("extradebug", &buffer);
m_bExtraDebug = Util::FileExists("extradebug");
#endif
}
Log::~Log()
{
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
Clear();
if (m_szLogFilename)
{
free(m_szLogFilename);
@@ -70,18 +68,8 @@ Log::~Log()
void Log::Filelog(const char* msg, ...)
{
if (
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
#ifdef DEBUG
|| (m_szLogFilename && m_bExtraDebug)
#endif
)
if (m_szLogFilename)
{
if (!m_szLogFilename)
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
}
char tmp2[1024];
va_list ap;
@@ -102,7 +90,7 @@ void Log::Filelog(const char* msg, ...)
szTime[50-1] = '\0';
szTime[strlen(szTime) - 1] = '\0'; // trim LF
FILE* file = fopen(m_szLogFilename, "a+");
FILE* file = fopen(m_szLogFilename, "ab+");
if (file)
{
#ifdef WIN32
@@ -111,9 +99,9 @@ void Log::Filelog(const char* msg, ...)
unsigned long iThreadId = (unsigned long)pthread_self();
#endif
#ifdef DEBUG
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
fprintf(file, "%s\t%lu\t%s%s", szTime, iThreadId, tmp2, LINE_ENDING);
#else
fprintf(file, "%s\t%s\n", szTime, tmp2);
fprintf(file, "%s\t%s%s", szTime, tmp2, LINE_ENDING);
#endif
fclose(file);
}
@@ -124,6 +112,7 @@ void Log::Filelog(const char* msg, ...)
}
}
#ifdef DEBUG
#undef debug
#ifdef HAVE_VARIADIC_MACROS
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
@@ -131,7 +120,6 @@ void debug(const char* szFilename, const char* szFuncname, int iLineNr, const ch
void debug(const char* msg, ...)
#endif
{
#ifdef DEBUG
char tmp1[1024];
va_list ap;
@@ -144,11 +132,11 @@ void debug(const char* msg, ...)
#ifdef HAVE_VARIADIC_MACROS
if (szFuncname)
{
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, BaseFileName(szFilename), iLineNr, szFuncname);
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, Util::BaseFileName(szFilename), iLineNr, szFuncname);
}
else
{
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, BaseFileName(szFilename), iLineNr);
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, Util::BaseFileName(szFilename), iLineNr);
}
#else
snprintf(tmp2, 1024, "%s", tmp1);
@@ -157,18 +145,12 @@ void debug(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
if (!g_pOptions)
if (!g_pOptions && g_pLog->m_bExtraDebug)
{
if (g_pLog->m_bExtraDebug)
{
printf("%s\n", tmp2);
g_pLog->Filelog("DEBUG\t%s", tmp2);
}
g_pLog->m_mutexLog.Unlock();
return;
printf("%s\n", tmp2);
}
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDebugTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("DEBUG\t%s", tmp2);
@@ -179,8 +161,8 @@ void debug(const char* msg, ...)
}
g_pLog->m_mutexLog.Unlock();
#endif
}
#endif
void error(const char* msg, ...)
{
@@ -194,7 +176,7 @@ void error(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetErrorTarget() : Options::mtBoth;
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("ERROR\t%s", tmp2);
@@ -219,7 +201,7 @@ void warn(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetWarningTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("WARNING\t%s", tmp2);
@@ -244,7 +226,7 @@ void info(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetInfoTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("INFO\t%s", tmp2);
@@ -257,6 +239,31 @@ void info(const char* msg, ...)
g_pLog->m_mutexLog.Unlock();
}
void detail(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDetailTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("DETAIL\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkDetail, tmp2);
}
g_pLog->m_mutexLog.Unlock();
}
void abort(const char* msg, ...)
{
char tmp2[1024];
@@ -304,16 +311,30 @@ Message::~ Message()
}
}
void Log::Clear()
{
m_mutexLog.Lock();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
m_mutexLog.Unlock();
}
void Log::AppendMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
if (g_pOptions)
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
}
}
@@ -332,3 +353,64 @@ void Log::ResetLog()
{
remove(g_pOptions->GetLogFile());
}
/*
* During intializing stage (when options were not read yet) all messages
* are saved in screen log, even if they shouldn't (according to options).
* Method "InitOptions()" check all messages added to screen log during
* intializing stage and does three things:
* 1) save the messages to log-file (if they should according to options);
* 2) delete messages from screen log (if they should not be saved in screen log).
* 3) renumerate IDs
*/
void Log::InitOptions()
{
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
}
m_iIDGen = 0;
for (unsigned int i = 0; i < m_Messages.size(); )
{
Message* pMessage = m_Messages.at(i);
Options::EMessageTarget eTarget = Options::mtNone;
switch (pMessage->GetKind())
{
case Message::mkDebug:
eTarget = g_pOptions->GetDebugTarget();
break;
case Message::mkDetail:
eTarget = g_pOptions->GetDetailTarget();
break;
case Message::mkInfo:
eTarget = g_pOptions->GetInfoTarget();
break;
case Message::mkWarning:
eTarget = g_pOptions->GetWarningTarget();
break;
case Message::mkError:
eTarget = g_pOptions->GetErrorTarget();
break;
}
if (eTarget == Options::mtLog || eTarget == Options::mtBoth)
{
Filelog("%s\t%s", szMessageType[pMessage->GetKind()], pMessage->GetText());
}
if (eTarget == Options::mtLog || eTarget == Options::mtNone)
{
delete pMessage;
m_Messages.erase(m_Messages.begin() + i);
}
else
{
pMessage->m_iID = ++m_iIDGen;
i++;
}
}
}

42
Log.h
View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -35,24 +35,27 @@
void error(const char* msg, ...);
void warn(const char* msg, ...);
void info(const char* msg, ...);
void detail(const char* msg, ...);
void abort(const char* msg, ...);
#ifdef DEBUG
#ifdef HAVE_VARIADIC_MACROS
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
#else
void debug(const char* msg, ...);
#endif
#endif
class Message
{
public:
enum EKind
{
mkInfo,
mkInfo,
mkWarning,
mkError,
mkDebug
mkDebug,
mkDetail
};
private:
@@ -61,9 +64,11 @@ private:
time_t m_tTime;
char* m_szText;
friend class Log;
public:
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
~Message();
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
~Message();
unsigned int GetID() { return m_iID; }
EKind GetKind() { return m_eKind; }
time_t GetTime() { return m_tTime; }
@@ -91,26 +96,31 @@ private:
friend void warn(const char* msg, ...);
friend void info(const char* msg, ...);
friend void abort(const char* msg, ...);
friend void detail(const char* msg, ...);
#ifdef DEBUG
#ifdef HAVE_VARIADIC_MACROS
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
#else
friend void debug(const char* msg, ...);
#endif
#endif
public:
Log();
~Log();
Log();
~Log();
Messages* LockMessages();
void UnlockMessages();
void Clear();
void ResetLog();
void InitOptions();
};
#ifdef DEBUG
#ifdef HAVE_VARIADIC_MACROS
#ifdef DEBUG
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
#else
#define debug(...) do { } while(0)
#endif
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
#endif
#else
#define debug(...) do { } while(0)
#endif
extern Log* g_pLog;

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -126,6 +126,9 @@ void LoggableFrontend::PrintMessage(Message * pMessage)
case Message::mkInfo:
printf("[INFO] %s\n", msg);
break;
case Message::mkDetail:
printf("[DETAIL] %s\n", msg);
break;
}
#ifdef WIN32
free(msg);

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$

View File

@@ -1,18 +1,170 @@
bin_PROGRAMS = nzbget
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h
#
# This file if part of nzbget
#
# Copyright (C) 2008-2013 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
EXTRA_DIST = nzbget.conf.example \
win32.h NTService.cpp NTService.h \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
bin_PROGRAMS = nzbget
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf nzbget-postprocess.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
webui/history.js webui/messages.js webui/status.js webui/style.css webui/upload.js \
webui/util.js webui/config.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
# Install
sbin_SCRIPTS = nzbgetd
bin_SCRIPTS = nzbget-postprocess.sh
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuiconfdir = $(datadir)/nzbget/webui
dist_webuiconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
# Note about "sed":
# We need to make some changes in installed files.
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
# 1) copy the original file to original.temp (delete existing original.temp, if any);
# 2) sed < original.temp > original
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration files
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
# (only if they do not exist there to prevent override by update)
install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
fi
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
# File is recreated only if revision number was changed.
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
# which was possibly created early.
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
# we create new file "svn_version.c" with empty revision number.
svn_version.cpp: FORCE
@ if test -d ./.svn ; then \
V="$(shell svnversion -n .)"; \
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
if test "/* $$V */" != "$$H" ; then \
( \
echo "/* $$V */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"$$V\";" ;\
echo " return SVN_Version;" ;\
echo "}" ;\
) > svn_version.cpp ; \
fi \
elif test -f ./svn_version.cpp ; then \
test "ok, reuse existing file"; \
else \
( \
echo "/* */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"\";" ;\
echo " return SVN_Version;" ;\
echo "}" ;\
) > svn_version.cpp ; \
fi
FORCE:
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
clean-bak: rm *~
# Fix premissions
dist-hook:
chmod -x $(distdir)/*.cpp $(distdir)/*.h
find $(distdir)/webui -type f -print -exec chmod -x {} \;

View File

@@ -1,8 +1,8 @@
# Makefile.in generated by automake 1.10 from Makefile.am.
# Makefile.in generated by automake 1.9.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# 2003, 2004, 2005 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -14,11 +14,38 @@
@SET_MAKE@
#
# This file if part of nzbget
#
# Copyright (C) 2008-2013 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = .
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
INSTALL = @INSTALL@
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
@@ -32,38 +59,52 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
bin_PROGRAMS = nzbget$(EXEEXT)
subdir = .
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
$(dist_exampleconf_DATA) $(dist_webuiconf_DATA) \
$(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
config.guess config.sub depcomp install-sh missing
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
configure.lineno configure.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" \
"$(DESTDIR)$(webuidir)"
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) \
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
RemoteServer.$(OBJEXT) ServerPool.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) nzbget.$(OBJEXT)
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
Options.$(OBJEXT) ParChecker.$(OBJEXT) ParRenamer.$(OBJEXT) \
ParCoordinator.$(OBJEXT) PrePostProcessor.$(OBJEXT) \
QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \
RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \
Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) Unpack.$(OBJEXT) \
nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
DEFAULT_INCLUDES = -I.@am__isrc@
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
@@ -77,6 +118,18 @@ CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(nzbget_SOURCES)
DIST_SOURCES = $(nzbget_SOURCES)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
dist_docDATA_INSTALL = $(INSTALL_DATA)
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
dist_webuiconfDATA_INSTALL = $(INSTALL_DATA)
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
$(dist_webuiconf_DATA) $(nobase_dist_webui_DATA)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -89,20 +142,14 @@ am__remove_distdir = \
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
distuninstallcheck_listfiles = find . -type f -print
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
ADDSRCS = @ADDSRCS@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CP = @CP@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
@@ -116,9 +163,7 @@ ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FALSE = @FALSE@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
@@ -126,13 +171,9 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LN = @LN@
LTLIBOBJS = @LTLIBOBJS@
MAKE = @MAKE@
MAKEINFO = @MAKEINFO@
MKDIR = @MKDIR@
MKDIR_P = @MKDIR_P@
MV = @MV@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -141,20 +182,15 @@ PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
RM = @RM@
PKG_CONFIG = @PKG_CONFIG@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
TAR = @TAR@
TRUE = @TRUE@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
@@ -166,7 +202,6 @@ build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
@@ -183,37 +218,88 @@ infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
libsigc_CFLAGS = @libsigc_CFLAGS@
libsigc_LIBS = @libsigc_LIBS@
libxml2_CFLAGS = @libxml2_CFLAGS@
libxml2_LIBS = @libxml2_LIBS@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
openssl_CFLAGS = @openssl_CFLAGS@
openssl_LIBS = @openssl_LIBS@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target = @target@
target_alias = @target_alias@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h
target_cpu = @target_cpu@
target_os = @target_os@
target_vendor = @target_vendor@
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = nzbget.conf.example \
win32.h NTService.cpp NTService.h \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf nzbget-postprocess.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
webui/history.js webui/messages.js webui/status.js webui/style.css webui/upload.js \
webui/util.js webui/config.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
# Install
sbin_SCRIPTS = nzbgetd
bin_SCRIPTS = nzbget-postprocess.sh
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuiconfdir = $(datadir)/nzbget/webui
dist_webuiconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -257,7 +343,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
config.h: stamp-h1
@if test ! -f $@; then \
rm -f stamp-h1; \
$(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
$(MAKE) stamp-h1; \
else :; fi
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
@@ -272,7 +358,7 @@ distclean-hdr:
-rm -f config.h stamp-h1
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; for p in $$list; do \
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
if test -f $$p \
@@ -295,7 +381,45 @@ clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
@rm -f nzbget$(EXEEXT)
$(CXXLINK) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
install-binSCRIPTS: $(bin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
@list='$(bin_SCRIPTS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
else :; fi; \
done
uninstall-binSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(bin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
rm -f "$(DESTDIR)$(bindir)/$$f"; \
done
install-sbinSCRIPTS: $(sbin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
$(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \
else :; fi; \
done
uninstall-sbinSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
rm -f "$(DESTDIR)$(sbindir)/$$f"; \
done
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -304,6 +428,7 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ArticleDownloader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BinRpc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ColoredFrontend.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@
@@ -315,34 +440,116 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NNTPConnection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NZBFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetAddress.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NewsServer.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)/ParChecker.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PrePostProcessor.Po@am__quote@
@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)/RemoteClient.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.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@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScriptController.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Unpack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
@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)/nzbget.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svn_version.Po@am__quote@
.cpp.o:
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
install-dist_docDATA: $(dist_doc_DATA)
@$(NORMAL_INSTALL)
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
@list='$(dist_doc_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \
$(dist_docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \
done
uninstall-dist_docDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_doc_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \
rm -f "$(DESTDIR)$(docdir)/$$f"; \
done
install-dist_exampleconfDATA: $(dist_exampleconf_DATA)
@$(NORMAL_INSTALL)
test -z "$(exampleconfdir)" || $(mkdir_p) "$(DESTDIR)$(exampleconfdir)"
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_exampleconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(exampleconfdir)/$$f'"; \
$(dist_exampleconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(exampleconfdir)/$$f"; \
done
uninstall-dist_exampleconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(exampleconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(exampleconfdir)/$$f"; \
done
install-dist_webuiconfDATA: $(dist_webuiconf_DATA)
@$(NORMAL_INSTALL)
test -z "$(webuiconfdir)" || $(mkdir_p) "$(DESTDIR)$(webuiconfdir)"
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_webuiconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuiconfdir)/$$f'"; \
$(dist_webuiconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuiconfdir)/$$f"; \
done
uninstall-dist_webuiconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(webuiconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(webuiconfdir)/$$f"; \
done
install-nobase_dist_webuiDATA: $(nobase_dist_webui_DATA)
@$(NORMAL_INSTALL)
test -z "$(webuidir)" || $(mkdir_p) "$(DESTDIR)$(webuidir)"
@$(am__vpath_adj_setup) \
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
$(am__vpath_adj) \
echo " $(nobase_dist_webuiDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuidir)/$$f'"; \
$(nobase_dist_webuiDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuidir)/$$f"; \
done
uninstall-nobase_dist_webuiDATA:
@$(NORMAL_UNINSTALL)
@$(am__vpath_adj_setup) \
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
$(am__vpath_adj) \
echo " rm -f '$(DESTDIR)$(webuidir)/$$f'"; \
rm -f "$(DESTDIR)$(webuidir)/$$f"; \
done
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
@@ -394,22 +601,24 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
test -d $(distdir) || mkdir $(distdir)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
mkdir $(distdir)
$(mkdir_p) $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
case $$file in \
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
esac; \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
dir="/$$dir"; \
$(mkdir_p) "$(distdir)$$dir"; \
else \
dir=''; \
fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
@@ -420,10 +629,13 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$(top_distdir)" distdir="$(distdir)" \
dist-hook
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r $(distdir)
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
@@ -498,7 +710,7 @@ distcheck: dist
$(am__remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
distuninstallcheck:
@cd $(distuninstallcheck_dir) \
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
@@ -519,10 +731,10 @@ distcleancheck: distclean
exit 1; } >&2
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) config.h
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" "$(DESTDIR)$(webuidir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
@@ -569,22 +781,20 @@ info: info-am
info-am:
install-data-am:
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
install-dist_webuiconfDATA install-nobase_dist_webuiDATA
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-dvi: install-dvi-am
install-exec-am: install-binPROGRAMS
install-html: install-html-am
install-exec-am: install-binPROGRAMS install-binSCRIPTS \
install-sbinSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-info: install-info-am
install-man:
install-pdf: install-pdf-am
install-ps: install-ps-am
installcheck-am:
maintainer-clean: maintainer-clean-am
@@ -606,27 +816,119 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS
.MAKE: install-am install-strip
uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
uninstall-dist_webuiconfDATA uninstall-info-am \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
dist-gzip dist-shar dist-tarZ dist-zip distcheck distclean \
distclean-compile distclean-generic distclean-hdr \
dist-gzip dist-hook dist-shar dist-tarZ dist-zip distcheck \
distclean distclean-compile distclean-generic distclean-hdr \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS
install-binPROGRAMS install-binSCRIPTS install-data \
install-data-am install-data-hook install-dist_docDATA \
install-dist_exampleconfDATA install-dist_webuiconfDATA \
install-exec install-exec-am install-exec-hook install-info \
install-info-am install-man install-nobase_dist_webuiDATA \
install-sbinSCRIPTS install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
ps ps-am tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-binSCRIPTS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-dist_webuiconfDATA \
uninstall-info-am uninstall-nobase_dist_webuiDATA \
uninstall-sbinSCRIPTS
# Note about "sed":
# We need to make some changes in installed files.
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
# 1) copy the original file to original.temp (delete existing original.temp, if any);
# 2) sed < original.temp > original
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration files
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
# (only if they do not exist there to prevent override by update)
install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
fi
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
# File is recreated only if revision number was changed.
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
# which was possibly created early.
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
# we create new file "svn_version.c" with empty revision number.
svn_version.cpp: FORCE
@ if test -d ./.svn ; then \
V="$(shell svnversion -n .)"; \
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
if test "/* $$V */" != "$$H" ; then \
( \
echo "/* $$V */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"$$V\";" ;\
echo " return SVN_Version;" ;\
echo "}" ;\
) > svn_version.cpp ; \
fi \
elif test -f ./svn_version.cpp ; then \
test "ok, reuse existing file"; \
else \
( \
echo "/* */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"\";" ;\
echo " return SVN_Version;" ;\
echo "}" ;\
) > svn_version.cpp ; \
fi
FORCE:
clean-bak: rm *~
# Fix premissions
dist-hook:
chmod -x $(distdir)/*.cpp $(distdir)/*.h
find $(distdir)/webui -type f -print -exec chmod -x {} \;
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,7 +27,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6212; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -35,17 +35,16 @@ static const int NZBREQUESTPASSWORDSIZE = 32;
* NZBGet communication protocol uses only two basic data types: integer and char.
* Integer values are passed using network byte order (Big-Endian).
* Use function "htonl" and "ntohl" to convert integers to/from machine
' (host) byte order.
* All char-strings must end with NULL-char.
* (host) byte order.
* All char-strings ends with NULL-char.
*
* NOTE:
* NZBGet communication protocol is intended for usage only by NZBGet itself.
* The communication works only if server and client has the same version.
* The compatibility with previous program versions is not provided.
* Third-party programs should use JSON-RPC or XML-RPC to communicate with NZBGet.
*/
// The pack-directive prevents aligning of structs.
// This makes them more portable and allows to use together servers and clients
// compiled on different cpu architectures
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
// Possible values for field "m_iType" of struct "SNZBRequestBase":
enum eRemoteRequest
{
@@ -57,7 +56,14 @@ enum eRemoteRequest
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestVersion
eRemoteRequestReload,
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog,
eRemoteRequestScan,
eRemoteRequestHistory,
eRemoteRequestDownloadUrl,
eRemoteRequestUrlQueue
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
@@ -65,22 +71,53 @@ enum eRemoteRequest
// Group is a list of files, added to queue from one NZB-File.
enum eRemoteEditAction
{
eRemoteEditActionFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eRemoteEditActionFileMoveTop, // move to top of queue
eRemoteEditActionFileMoveBottom, // move to bottom of queue
eRemoteEditActionFilePause, // pause
eRemoteEditActionFileResume, // resume (unpause)
eRemoteEditActionFileDelete, // delete
eRemoteEditActionFileMoveOffset = 1, // move files to m_iOffset relative to the current position in download-queue
eRemoteEditActionFileMoveTop, // move files to the top of download-queue
eRemoteEditActionFileMoveBottom, // move files to the bottom of download-queue
eRemoteEditActionFilePause, // pause files
eRemoteEditActionFileResume, // resume (unpause) files
eRemoteEditActionFileDelete, // delete files
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eRemoteEditActionGroupMoveTop, // move to top of queue
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
eRemoteEditActionGroupPause, // pause
eRemoteEditActionGroupResume, // resume (unpause)
eRemoteEditActionGroupDelete, // delete
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
eRemoteEditActionGroupPauseExtraPars // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionFileSetPriority, // set priority for files
eRemoteEditActionFileReorder, // (not supported)
eRemoteEditActionGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
eRemoteEditActionGroupMoveTop, // move group to the top of download-queue
eRemoteEditActionGroupMoveBottom, // move group to the bottom of download-queue
eRemoteEditActionGroupPause, // pause group
eRemoteEditActionGroupResume, // resume (unpause) group
eRemoteEditActionGroupDelete, // delete group
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files) in group
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars in group, except main par-file (does not affect other files)
eRemoteEditActionGroupSetPriority, // set priority for groups
eRemoteEditActionGroupSetCategory, // set or change category for a group
eRemoteEditActionGroupMerge, // merge group
eRemoteEditActionGroupSetParameter, // set post-process parameter for group
eRemoteEditActionGroupSetName, // set group name (rename group)
eRemoteEditActionPostMoveOffset = 51, // move post-job to m_iOffset relative to the current position in post-queue
eRemoteEditActionPostMoveTop, // move post-job to the top of post-queue
eRemoteEditActionPostMoveBottom, // move post-job to the bottom of post-queue
eRemoteEditActionPostDelete, // delete post-job
eRemoteEditActionHistoryDelete, // delete history-item
eRemoteEditActionHistoryReturn, // move history-item back to download queue
eRemoteEditActionHistoryProcess // move history-item back to download queue and start postprocessing
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
enum eRemotePauseUnpauseAction
{
eRemotePauseUnpauseActionDownload = 1, // pause/unpause download queue
eRemotePauseUnpauseActionDownload2, // pause/unpause download queue (second pause-register)
eRemotePauseUnpauseActionPostProcess, // pause/unpause post-processor queue
eRemotePauseUnpauseActionScan // pause/unpause scan of incoming nzb-directory
};
// Possible values for field "m_iMatchMode" of struct "SNZBEditQueueRequest":
enum eRemoteMatchMode
{
eRemoteMatchModeID = 1, // ID
eRemoteMatchModeName, // Name
eRemoteMatchModeRegEx, // RegEx
};
// The basic SNZBRequestBase struct, used in all requests
@@ -104,7 +141,10 @@ struct SNZBDownloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
@@ -124,6 +164,9 @@ struct SNZBListRequest
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList; // 1 - return file list
int32_t m_bServerState; // 1 - return server state
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode (only values eRemoteMatchModeID (no filter) and eRemoteMatchModeRegEx are allowed)
int32_t m_bMatchGroup; // 0 - match files; 1 - match nzbs (when m_iMatchMode == eRemoteMatchModeRegEx)
char m_szPattern[NZBREQUESTFILENAMESIZE]; // RegEx Pattern (when m_iMatchMode == eRemoteMatchModeRegEx)
};
// A list response
@@ -135,37 +178,73 @@ struct SNZBListResponse
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
int32_t m_bServerPaused; // 1 - server is currently in paused-state
int32_t m_bDownloadPaused; // 1 - download queue is currently in paused-state
int32_t m_bDownload2Paused; // 1 - download queue is currently in paused-state (second pause-register)
int32_t m_bDownloadStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (download queue paused or all download jobs completed)
int32_t m_bPostPaused; // 1 - post-processor queue is currently in paused-state
int32_t m_bScanPaused; // 1 - scaning of incoming directory is currently in paused-state
int32_t m_iThreadCount; // Number of threads running
int32_t m_iParJobCount; // Number of ParJobs in Par-Checker queue (including current file)
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
int32_t m_iUpTimeSec; // Server up time in seconds
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
int32_t m_iNrTrailingEntries; // Number of List-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
int32_t m_bRegExValid; // 0 - error in RegEx-pattern, 1 - RegEx-pattern is valid (only when Request has eRemoteMatchModeRegEx)
int32_t m_iNrTrailingNZBEntries; // Number of List-NZB-entries, following to this structure
int32_t m_iNrTrailingPPPEntries; // Number of List-PPP-entries, following to this structure
int32_t m_iNrTrailingFileEntries; // Number of List-File-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
// SNZBListResponseEntry m_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
// SNZBListResponseEntry m_PPPEntries[m_iNrTrailingPPPEntries] // variable sized
// SNZBListResponseEntry m_FileEntries[m_iNrTrailingFileEntries] // variable sized
};
// A list response entry
struct SNZBListResponseEntry
// A list response nzb entry
struct SNZBListResponseNZBEntry
{
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
int32_t m_bMatch; // 1 - group matches the pattern (only when Request has eRemoteMatchModeRegEx)
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szName[m_iNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szCategory[m_iCategoryLen]; // variable sized
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
};
// A list response pp-parameter entry
struct SNZBListResponsePPPEntry
{
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
int32_t m_iValueLen; // Length of Value-string (m_szValue), following to this record
//char m_szName[m_iNameLen]; // variable sized
//char m_szValue[m_iValueLen]; // variable sized
};
// A list response file entry
struct SNZBListResponseFileEntry
{
int32_t m_iID; // Entry-ID
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_bPaused; // 1 - file is paused
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
int32_t m_iPriority; // Download priority
int32_t m_iActiveDownloads; // Number of active downloads for this file
int32_t m_bMatch; // 1 - file matches the pattern (only when Request has eRemoteMatchModeRegEx)
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szSubject[m_iSubjectLen]; // variable sized
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
};
// A log request
@@ -201,6 +280,7 @@ struct SNZBPauseUnpauseRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
int32_t m_iAction; // Action to be executed, see enum eRemotePauseUnpauseAction
};
// A Pause/Unpause response
@@ -228,17 +308,23 @@ struct SNZBSetDownloadRateResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// An edit queue request
// edit queue request
struct SNZBEditQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace
int32_t m_iAction; // Action to be executed, see enum eRemoteEditAction
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode
int32_t m_iNrTrailingIDEntries; // Number of ID-entries, following to this structure
int32_t m_iNrTrailingNameEntries; // Number of Name-entries, following to this structure
int32_t m_iTrailingNameEntriesLen; // Length of all Name-entries, following to this structure
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
//char m_szText[m_iTextLen]; // variable sized
//int32_t m_iIDs[m_iNrTrailingIDEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
//char* m_szNames[m_iNrTrailingNameEntries]; // variable sized array of strings. For File-Actions - name of file incl. nzb-name as path, for Group-Actions - name of group
};
// An edit queue response
@@ -280,6 +366,21 @@ struct SNZBShutdownResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Reload server request
struct SNZBReloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Reload server response
struct SNZBReloadResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Server version request
struct SNZBVersionRequest
{
@@ -295,8 +396,157 @@ struct SNZBVersionResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
// PostQueue request
struct SNZBPostQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// A PostQueue response
struct SNZBPostQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBPostQueueResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of PostQueue-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all PostQueue-entries, following to this structure
// SNZBPostQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A PostQueue response entry
struct SNZBPostQueueResponseEntry
{
int32_t m_iID; // ID of Post-entry
int32_t m_iStage; // See PrePostProcessor::EPostJobStage
int32_t m_iStageProgress; // Progress of current stage, value in range 0..1000
int32_t m_iFileProgress; // Progress of current file, value in range 0..1000
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szParFilename[m_iParFilename]; // variable sized
//char m_szInfoName[m_iInfoNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
};
// Write log request
struct SNZBWriteLogRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Write log response
struct SNZBWriteLogResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Scan nzb directory request
struct SNZBScanRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bSyncMode; // 0 - asynchronous Scan (the command returns immediately), 1 - synchronous Scan (the command returns when the scan is completed)
};
// Scan nzb directory response
struct SNZBScanResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// A history request
struct SNZBHistoryRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// history response
struct SNZBHistoryResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBHistoryResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of History-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all History-entries, following to this structure
// SNZBHistoryResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// history entry
struct SNZBHistoryResponseEntry
{
int32_t m_iID; // History-ID
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
int32_t m_tTime; // When the item was added to history. time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iNicenameLen; // Length of Nicename-string (m_szNicename), following to this record
// for Collection items (m_iKind = 1)
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
int32_t m_iFileCount; // Initial number of files included in NZB-file
int32_t m_iParStatus; // See NZBInfo::EParStatus
int32_t m_iScriptStatus; // See NZBInfo::EScriptStatus
// for URL items (m_iKind = 2)
int32_t m_iUrlStatus; // See UrlInfo::EStatus
// trailing data
//char m_szNicename[m_iNicenameLen]; // variable sized
};
// download url request
struct SNZBDownloadUrlRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szURL[NZBREQUESTFILENAMESIZE]; // url to nzb-file
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. Can be empty, then the filename is read from URL download response
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
int32_t m_bAddFirst; // 1 - add url to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
};
// download url response
struct SNZBDownloadUrlResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// UrlQueue request
struct SNZBUrlQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// UrlQueue response
struct SNZBUrlQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBUrlQueueResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of UrlQueue-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all UrlQueue-entries, following to this structure
// SNZBUrlQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// UrlQueue response entry
struct SNZBUrlQueueResponseEntry
{
int32_t m_iID; // ID of Url-entry
int32_t m_iURLLen; // Length of URL-string (m_szURL), following to this record
int32_t m_iNZBFilenameLen; // Length of NZBFilename-string (m_szNZBFilename), following to this record
//char m_szURL[m_iURLLen]; // variable sized
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -50,6 +50,7 @@
#include "nzbget.h"
#include "NCursesFrontend.h"
#include "Options.h"
#include "Util.h"
#ifdef HAVE_CURSES_H
// curses.h header must be included last to avoid problems on Solaris
@@ -59,6 +60,18 @@
#undef erase
#endif
#ifndef WIN32
// curses.h on Solaris declares "clear()" via DEFINE. That causes problems, because
// it also affects calls to deque's method "clear()", producing compiler errors.
// We use function "curses_clear()" to call macro "clear" of curses, then
// undefine macro "clear".
void curses_clear()
{
clear();
}
#undef clear
#endif
extern Options* g_pOptions;
extern void ExitProc();
@@ -67,11 +80,13 @@ static const int NCURSES_COLORPAIR_INFO = 2;
static const int NCURSES_COLORPAIR_WARNING = 3;
static const int NCURSES_COLORPAIR_ERROR = 4;
static const int NCURSES_COLORPAIR_DEBUG = 5;
static const int NCURSES_COLORPAIR_STATUS = 6;
static const int NCURSES_COLORPAIR_KEYBAR = 7;
static const int NCURSES_COLORPAIR_INFOLINE = 8;
static const int NCURSES_COLORPAIR_TEXTHIGHL = 9;
static const int NCURSES_COLORPAIR_CURSOR = 10;
static const int NCURSES_COLORPAIR_DETAIL = 6;
static const int NCURSES_COLORPAIR_STATUS = 7;
static const int NCURSES_COLORPAIR_KEYBAR = 8;
static const int NCURSES_COLORPAIR_INFOLINE = 9;
static const int NCURSES_COLORPAIR_TEXTHIGHL = 10;
static const int NCURSES_COLORPAIR_CURSOR = 11;
static const int NCURSES_COLORPAIR_HINT = 12;
static const int MAX_SCREEN_WIDTH = 512;
@@ -101,25 +116,6 @@ static const int READKEY_EMPTY = ERR;
#endif
NCursesFrontend::GroupInfo::GroupInfo(int iID, const char* szNZBFilename)
{
m_iID = iID;
m_szNZBFilename = strdup(szNZBFilename);
m_iFileCount = 0;
m_lSize = 0;
m_lRemainingSize = 0;
m_lPausedSize = 0;
m_iParCount = 0;
}
NCursesFrontend::GroupInfo::~GroupInfo()
{
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
}
NCursesFrontend::NCursesFrontend()
{
m_iScreenHeight = 0;
@@ -142,10 +138,10 @@ NCursesFrontend::NCursesFrontend()
m_bGroupFiles = g_pOptions->GetCursesGroup();
m_QueueWindowPercentage = 0.5f;
m_iDataUpdatePos = 0;
m_bUpdateNextTime = false;
m_iLastEditEntry = -1;
m_bLastPausePars = false;
m_groupQueue.clear();
m_szHint = NULL;
// Setup curses
#ifdef WIN32
@@ -193,11 +189,13 @@ NCursesFrontend::NCursesFrontend()
init_pair(NCURSES_COLORPAIR_WARNING, COLOR_MAGENTA, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_ERROR, COLOR_RED, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_DEBUG, COLOR_WHITE, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_DETAIL, COLOR_GREEN, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_STATUS, COLOR_BLUE, COLOR_WHITE);
init_pair(NCURSES_COLORPAIR_KEYBAR, COLOR_WHITE, COLOR_BLUE);
init_pair(NCURSES_COLORPAIR_INFOLINE, COLOR_WHITE, COLOR_BLUE);
init_pair(NCURSES_COLORPAIR_TEXTHIGHL, COLOR_BLACK, COLOR_CYAN);
init_pair(NCURSES_COLORPAIR_CURSOR, COLOR_BLACK, COLOR_YELLOW);
init_pair(NCURSES_COLORPAIR_HINT, COLOR_WHITE, COLOR_RED);
}
}
@@ -226,32 +224,52 @@ NCursesFrontend::~NCursesFrontend()
endwin();
#endif
printf("\n");
SetHint(NULL);
}
void NCursesFrontend::Run()
{
debug("Entering NCursesFrontend-loop");
int iScreenUpdateInterval = 25;
int iScreenUpdatePos = 0;
m_iDataUpdatePos = 0;
while (!IsStopped())
{
// The data (queue and log) is updated each m_iUpdateInterval msec,
// but the window is updated more often for better reaction on user's input
if (iScreenUpdatePos <= 0)
bool updateNow = false;
int iKey = ReadConsoleKey();
if (iKey != READKEY_EMPTY)
{
iScreenUpdatePos = iScreenUpdateInterval;
Update();
if (m_iDataUpdatePos <= 0)
{
m_iDataUpdatePos = m_iUpdateInterval;
}
// Update now and next if a key is pressed.
updateNow = true;
m_bUpdateNextTime = true;
}
else if (m_bUpdateNextTime)
{
// Update due to key being pressed during previous call.
updateNow = true;
m_bUpdateNextTime = false;
}
else if (m_iDataUpdatePos <= 0)
{
updateNow = true;
m_bUpdateNextTime = false;
}
if (updateNow)
{
Update(iKey);
}
if (m_iDataUpdatePos <= 0)
{
m_iDataUpdatePos = m_iUpdateInterval;
}
usleep(10 * 1000);
iScreenUpdatePos -= 10;
m_iDataUpdatePos -= 10;
}
@@ -264,9 +282,10 @@ void NCursesFrontend::Run()
void NCursesFrontend::NeedUpdateData()
{
m_iDataUpdatePos = 10;
m_bUpdateNextTime = true;
}
void NCursesFrontend::Update()
void NCursesFrontend::Update(int iKey)
{
// Figure out how big the screen is
CalcWindowSizes();
@@ -281,6 +300,9 @@ void NCursesFrontend::Update()
return;
}
PrepareGroupQueue();
// recalculate frame sizes
CalcWindowSizes();
}
if (m_eInputMode == eEditQueue)
@@ -294,7 +316,7 @@ void NCursesFrontend::Update()
}
//------------------------------------------
// Print Current NZBQueue
// Print Current NZBInfoList
//------------------------------------------
if (m_iQueueWinHeight > 0)
{
@@ -313,8 +335,7 @@ void NCursesFrontend::Update()
PrintKeyInputBar();
// Update the input
UpdateInput();
UpdateInput(iKey);
RefreshScreen();
}
@@ -335,12 +356,12 @@ void NCursesFrontend::CalcWindowSizes()
{
#ifdef WIN32
m_iScreenBufferSize = iNrRows * iNrColumns * sizeof(CHAR_INFO);
m_pScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
m_pScreenBuffer = (CHAR_INFO*)realloc(m_pScreenBuffer, m_iScreenBufferSize);
memset(m_pScreenBuffer, 0, m_iScreenBufferSize);
m_pOldScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
m_pOldScreenBuffer = (CHAR_INFO*)realloc(m_pOldScreenBuffer, m_iScreenBufferSize);
memset(m_pOldScreenBuffer, 0, m_iScreenBufferSize);
#else
clear();
curses_clear();
#endif
m_iScreenHeight = iNrRows;
m_iScreenWidth = iNrColumns;
@@ -378,7 +399,7 @@ int NCursesFrontend::CalcQueueSize()
else
{
DownloadQueue* pDownloadQueue = LockQueue();
int iQueueSize = pDownloadQueue->size();
int iQueueSize = pDownloadQueue->GetFileQueue()->size();
UnlockQueue();
return iQueueSize;
}
@@ -517,9 +538,9 @@ void NCursesFrontend::PrintMessages()
int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
{
char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG "};
const char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG ", "DETAIL "};
const int iMessageTypeColor[] = { NCURSES_COLORPAIR_INFO, NCURSES_COLORPAIR_WARNING,
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG };
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG, NCURSES_COLORPAIR_DETAIL };
char* szText = (char*)Msg->GetText();
@@ -546,10 +567,10 @@ int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
szText = strdup(szText);
}
// replace CR and LF characters with spaces
// replace some special characters with spaces
for (char* p = szText; *p; p++)
{
if (*p == '\n' || *p == '\r')
if (*p == '\n' || *p == '\r' || *p == '\b')
{
*p = ' ';
}
@@ -592,41 +613,45 @@ void NCursesFrontend::PrintStatus()
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
int h = remain_sec / 3600;
int m = (remain_sec % 3600) / 60;
int s = remain_sec % 60;
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
}
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
}
else
{
szDownloadLimit[0] = 0;
}
char szParStatus[128];
if (m_iParJobCount > 0)
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szParStatus, ", %i par%s", m_iParJobCount, m_iParJobCount > 1 ? "s" : "");
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
szParStatus[0] = 0;
szPostStatus[0] = 0;
}
float fAverageSpeed = m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec / 1024 : 0;
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s, Avg. %.0f KB/s",
m_iThreadCount, fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0), timeString,
szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, fAverageSpeed);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s, Avg. %.*f KB/s",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
m_bPauseDownload || m_bPauseDownload2 ?
(m_bPauseDownload && m_bPauseDownload2 ? " (+2)" : m_bPauseDownload2 ? " (2)" : "") : "",
szDownloadLimit, (fAverageSpeed >= 10 ? 0 : 1), fAverageSpeed);
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
}
@@ -636,6 +661,20 @@ void NCursesFrontend::PrintKeyInputBar()
int iQueueSize = CalcQueueSize();
int iInputBarRow = m_iScreenHeight - 1;
if (m_szHint)
{
time_t tTime = time(NULL);
if (tTime - m_tStartHint < 5)
{
PlotLine(m_szHint, iInputBarRow, 0, NCURSES_COLORPAIR_HINT);
return;
}
else
{
SetHint(NULL);
}
}
switch (m_eInputMode)
{
case eNormal:
@@ -650,7 +689,7 @@ void NCursesFrontend::PrintKeyInputBar()
break;
case eEditQueue:
{
char* szStatus = NULL;
const char* szStatus = NULL;
if (m_iSelectedQueueEntry > 0 && iQueueSize > 1 && m_iSelectedQueueEntry == iQueueSize - 1)
{
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/(T)op";
@@ -682,6 +721,20 @@ void NCursesFrontend::PrintKeyInputBar()
}
}
void NCursesFrontend::SetHint(const char* szHint)
{
if (m_szHint)
{
free(m_szHint);
m_szHint = NULL;
}
if (szHint)
{
m_szHint = strdup(szHint);
m_tStartHint = time(NULL);
}
}
void NCursesFrontend::PrintQueue()
{
if (m_bGroupFiles)
@@ -699,7 +752,7 @@ void NCursesFrontend::PrintFileQueue()
int iLineNr = m_iQueueWinTop;
DownloadQueue* pDownloadQueue = LockQueue();
if (pDownloadQueue->empty())
if (pDownloadQueue->GetFileQueue()->empty())
{
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), "%s Files for downloading", m_bUseColor ? "" : "*** ");
@@ -714,7 +767,7 @@ void NCursesFrontend::PrintFileQueue()
long long lPaused = 0;
int iPausedFiles = 0;
int i = 0;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++, i++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++, i++)
{
FileInfo* pFileInfo = *it;
@@ -732,14 +785,15 @@ void NCursesFrontend::PrintFileQueue()
}
char szRemaining[20];
FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
char szUnpaused[20];
FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
m_bUseColor ? "" : "*** ", pDownloadQueue->size(), pDownloadQueue->size() - iPausedFiles, szRemaining, szUnpaused);
m_bUseColor ? "" : "*** ", pDownloadQueue->GetFileQueue()->size(),
pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
}
@@ -765,17 +819,30 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
color = NCURSES_COLORPAIR_TEXT;
}
const char* szDownloading = "";
if (pFileInfo->GetActiveDownloads() > 0)
{
szDownloading = " *";
}
char szPriority[100];
szPriority[0] = '\0';
if (pFileInfo->GetPriority() != 0)
{
sprintf(szPriority, " [%+i]", pFileInfo->GetPriority());
}
char szCompleted[20];
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
{
sprintf(szCompleted, ", %i%%", (int)(100 - pFileInfo->GetRemainingSize() * 100.0 / pFileInfo->GetSize()));
sprintf(szCompleted, ", %i%%", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())));
}
char szNZBNiceName[1024];
if (m_bShowNZBname)
{
pFileInfo->GetNiceNZBName(szNZBNiceName, 1023);
strncpy(szNZBNiceName, pFileInfo->GetNZBInfo()->GetName(), 1023);
int len = strlen(szNZBNiceName);
szNZBNiceName[len] = PATH_SEPARATOR;
szNZBNiceName[len + 1] = '\0';
@@ -786,33 +853,15 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
}
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(), Brace2, szNZBNiceName, pFileInfo->GetFilename(), pFileInfo->GetSize() / 1024.0 / 1024.0, szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(szBuffer, iRow, 0, color);
}
void NCursesFrontend::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
{
if (lFileSize > 1024 * 1024 * 1024)
{
snprintf(szBuffer, iBufLen, "%.2f GB", (float)lFileSize / 1024 / 1024 / 1024);
}
else if (lFileSize > 1024 * 1024)
{
snprintf(szBuffer, iBufLen, "%.2f MB", (float)lFileSize / 1024 / 1024);
}
else if (lFileSize > 1024)
{
snprintf(szBuffer, iBufLen, "%.2f KB", (float)lFileSize / 1024);
}
else
{
snprintf(szBuffer, iBufLen, "%i", (int)lFileSize);
}
szBuffer[iBufLen - 1] = '\0';
}
void NCursesFrontend::PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime)
{
char szBuffer[MAX_SCREEN_WIDTH];
@@ -877,6 +926,7 @@ void NCursesFrontend::PrintGroupQueue()
{
int iLineNr = m_iQueueWinTop;
LockQueue();
GroupQueue* pGroupQueue = &m_groupQueue;
if (pGroupQueue->empty())
{
@@ -889,8 +939,9 @@ void NCursesFrontend::PrintGroupQueue()
else
{
iLineNr++;
long long lRemaining = 0;
long long lPaused = 0;
ResetColWidths();
int iCalcLineNr = iLineNr;
int i = 0;
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
{
@@ -898,7 +949,20 @@ void NCursesFrontend::PrintGroupQueue()
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
{
PrintGroupname(pGroupInfo, iLineNr++, i == m_iSelectedQueueEntry);
PrintGroupname(pGroupInfo, iCalcLineNr++, false, true);
}
}
long long lRemaining = 0;
long long lPaused = 0;
i = 0;
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
{
GroupInfo* pGroupInfo = *it;
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
{
PrintGroupname(pGroupInfo, iLineNr++, i == m_iSelectedQueueEntry, false);
}
lRemaining += pGroupInfo->GetRemainingSize();
@@ -906,10 +970,10 @@ void NCursesFrontend::PrintGroupQueue()
}
char szRemaining[20];
FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
char szUnpaused[20];
FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
@@ -917,50 +981,140 @@ void NCursesFrontend::PrintGroupQueue()
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, false);
}
UnlockQueue();
}
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected)
void NCursesFrontend::ResetColWidths()
{
int color = 0;
const char* Brace1 = "[";
const char* Brace2 = "]";
m_iColWidthFiles = 0;
m_iColWidthTotal = 0;
m_iColWidthLeft = 0;
}
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth)
{
int color = NCURSES_COLORPAIR_TEXT;
char chBrace1 = '[';
char chBrace2 = ']';
if (m_eInputMode == eEditQueue && bSelected)
{
color = NCURSES_COLORPAIR_TEXTHIGHL;
if (!m_bUseColor)
{
Brace1 = "<";
Brace2 = ">";
chBrace1 = '<';
chBrace2 = '>';
}
}
else
const char* szDownloading = "";
if (pGroupInfo->GetActiveDownloads() > 0)
{
color = NCURSES_COLORPAIR_TEXT;
szDownloading = " *";
}
long long lUnpausedRemainingSize = pGroupInfo->GetRemainingSize() - pGroupInfo->GetPausedSize();
char szRemaining[20];
FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
char szPaused[20];
szPaused[0] = '\0';
if (pGroupInfo->GetPausedSize() > 0)
char szPriority[100];
szPriority[0] = '\0';
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
{
char szPausedSize[20];
FormatFileSize(szPausedSize, sizeof(szPausedSize), pGroupInfo->GetPausedSize());
sprintf(szPaused, " + %s paused", szPausedSize);
if (pGroupInfo->GetMinPriority() == pGroupInfo->GetMaxPriority())
{
sprintf(szPriority, " [%+i]", pGroupInfo->GetMinPriority());
}
else
{
sprintf(szPriority, " [%+i..%+i]", pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority());
}
}
char szNZBNiceName[1024];
FileInfo::MakeNiceNZBName(pGroupInfo->GetNZBFilename(), szNZBNiceName, 1023);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s (%i file%s, %s%s)", Brace1, pGroupInfo->GetID(), Brace2, szNZBNiceName,
pGroupInfo->m_iFileCount, pGroupInfo->m_iFileCount > 1 ? "s" : "", szRemaining, szPaused);
// Format:
// [id - id] Name Left-Files/Paused Total Left Time
// [1-2] Nzb-name 999/999 999.99 MB 999.99 MB 00:00:00
int iNameLen = 0;
if (bCalcColWidth)
{
iNameLen = m_iScreenWidth - 1 - 9 - 11 - 11 - 9;
}
else
{
iNameLen = m_iScreenWidth - 1 - m_iColWidthFiles - 2 - m_iColWidthTotal - 2 - m_iColWidthLeft - 2 - 9;
}
bool bPrintFormatted = iNameLen > 20;
if (bPrintFormatted)
{
char szFiles[20];
snprintf(szFiles, 20, "%i/%i", pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetPausedFileCount());
szFiles[20-1] = '\0';
char szTotal[20];
Util::FormatFileSize(szTotal, sizeof(szTotal), pGroupInfo->GetNZBInfo()->GetSize());
char szNameWithIds[1024];
snprintf(szNameWithIds, 1024, "%c%i-%i%c%s%s %s", chBrace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), chBrace2,
szPriority, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
szNameWithIds[iNameLen] = '\0';
char szTime[100];
szTime[0] = '\0';
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (pGroupInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
{
snprintf(szTime, 100, "[paused]");
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pGroupInfo->GetRemainingSize());
}
else if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(lUnpausedRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
if (h < 100)
{
snprintf(szTime, 100, "%.2d:%.2d:%.2d", h, m, s);
}
else
{
snprintf(szTime, 100, "99:99:99");
}
}
if (bCalcColWidth)
{
int iColWidthFiles = strlen(szFiles);
m_iColWidthFiles = iColWidthFiles > m_iColWidthFiles ? iColWidthFiles : m_iColWidthFiles;
int iColWidthTotal = strlen(szTotal);
m_iColWidthTotal = iColWidthTotal > m_iColWidthTotal ? iColWidthTotal : m_iColWidthTotal;
int iColWidthLeft = strlen(szRemaining);
m_iColWidthLeft = iColWidthLeft > m_iColWidthLeft ? iColWidthLeft : m_iColWidthLeft;
}
else
{
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%-*s %*s %*s %*s %8s", iNameLen, szNameWithIds, m_iColWidthFiles, szFiles, m_iColWidthTotal, szTotal, m_iColWidthLeft, szRemaining, szTime);
}
}
else
{
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%c%i-%i%c%s %s", chBrace1, pGroupInfo->GetFirstID(),
pGroupInfo->GetLastID(), chBrace2, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
}
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(szBuffer, iRow, 0, color);
if (!bCalcColWidth)
{
PlotLine(szBuffer, iRow, 0, color);
}
}
void NCursesFrontend::PrepareGroupQueue()
@@ -968,41 +1122,7 @@ void NCursesFrontend::PrepareGroupQueue()
m_groupQueue.clear();
DownloadQueue* pDownloadQueue = LockQueue();
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo* pGroupInfo = NULL;
for (GroupQueue::iterator itg = m_groupQueue.begin(); itg != m_groupQueue.end(); itg++)
{
GroupInfo* pGroupInfo1 = *itg;
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupInfo = new GroupInfo(pFileInfo->GetID(), pFileInfo->GetNZBFilename());
m_groupQueue.push_back(pGroupInfo);
}
pGroupInfo->m_iFileCount++;
pGroupInfo->m_lSize += pFileInfo->GetSize();
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
if (pFileInfo->GetPaused())
{
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
}
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (strstr(szLoFileName, ".par2"))
{
pGroupInfo->m_iParCount++;
}
}
pDownloadQueue->BuildGroups(&m_groupQueue);
UnlockQueue();
}
@@ -1024,14 +1144,14 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)m_groupQueue.size())
{
GroupInfo* pGroupInfo = m_groupQueue[m_iSelectedQueueEntry];
ID = pGroupInfo->GetID();
ID = pGroupInfo->GetLastID();
if (eAction == QueueEditor::eaFilePause)
{
if (pGroupInfo->GetRemainingSize() == pGroupInfo->GetPausedSize())
{
eAction = QueueEditor::eaFileResume;
}
else if (pGroupInfo->GetPausedSize() == 0 && (pGroupInfo->m_iParCount > 0) &&
else if (pGroupInfo->GetPausedSize() == 0 && (pGroupInfo->GetRemainingParCount() > 0) &&
!(m_bLastPausePars && m_iLastEditEntry == m_iSelectedQueueEntry))
{
eAction = QueueEditor::eaFilePauseExtraPars;
@@ -1061,9 +1181,9 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
else
{
DownloadQueue* pDownloadQueue = LockQueue();
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)pDownloadQueue->size())
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)pDownloadQueue->GetFileQueue()->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[m_iSelectedQueueEntry];
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(m_iSelectedQueueEntry);
ID = pFileInfo->GetID();
if (eAction == QueueEditor::eaFilePause)
{
@@ -1075,6 +1195,8 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
m_iLastEditEntry = m_iSelectedQueueEntry;
NeedUpdateData();
if (ID != 0)
{
return ServerEditQueue(eAction, iOffset, ID);
@@ -1124,10 +1246,15 @@ void NCursesFrontend::SetCurrentQueueEntry(int iEntry)
m_iSelectedQueueEntry = iEntry;
}
void NCursesFrontend::UpdateInput()
/*
* Process keystrokes starting with the initialKey, which must not be
* READKEY_EMPTY but has alread been set via ReadConsoleKey.
*/
void NCursesFrontend::UpdateInput(int initialKey)
{
int iKey;
while ((iKey = ReadConsoleKey()) != READKEY_EMPTY)
int iKey = initialKey;
while (iKey != READKEY_EMPTY)
{
int iQueueSize = CalcQueueSize();
@@ -1179,9 +1306,9 @@ void NCursesFrontend::UpdateInput()
// Key 'p' for pause
if (!IsRemoteMode())
{
info(m_bPause ? "Unpausing download" : "Pausing download");
info(m_bPauseDownload || m_bPauseDownload2 ? "Unpausing download" : "Pausing download");
}
ServerPauseUnpause(!m_bPause);
ServerPauseUnpause(!(m_bPauseDownload || m_bPauseDownload2), m_bPauseDownload2 && !m_bPauseDownload);
break;
case '\'':
ServerDumpDebug();
@@ -1273,6 +1400,9 @@ void NCursesFrontend::UpdateInput()
EditQueue(QueueEditor::eaFilePause, 0);
break;
case 'd':
SetHint(" Use Uppercase \"D\" for delete");
break;
case 'D':
// Delete entry
if (EditQueue(QueueEditor::eaFileDelete, 0))
{
@@ -1318,7 +1448,7 @@ void NCursesFrontend::UpdateInput()
// Enter
else if (iKey == 10 || iKey == 13)
{
ServerSetDownloadRate((float)m_iInputValue);
ServerSetDownloadRate(m_iInputValue * 1024);
m_eInputMode = eNormal;
return;
}
@@ -1337,6 +1467,8 @@ void NCursesFrontend::UpdateInput()
m_iInputNumberIndex--;
}
}
iKey = ReadConsoleKey();
}
}
@@ -1357,7 +1489,13 @@ int NCursesFrontend::ReadConsoleKey()
InputRecord.EventType == KEY_EVENT &&
InputRecord.Event.KeyEvent.bKeyDown)
{
return tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
char c = tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
if (bool(InputRecord.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) ^
bool(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED))
{
c = toupper(c);
}
return c;
}
}
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -30,6 +30,7 @@
#ifndef DISABLE_CURSES
#include <vector>
#include <time.h>
#include "Frontend.h"
#include "Log.h"
@@ -46,33 +47,9 @@ private:
eDownloadRate
};
class GroupInfo
{
private:
int m_iID;
char* m_szNZBFilename;
public:
int m_iFileCount;
long long m_lSize;
long long m_lRemainingSize;
long long m_lPausedSize;
int m_iParCount;
public:
GroupInfo(int iID, const char* szNZBFilename);
~GroupInfo();
int GetID() { return m_iID; }
const char* GetNZBFilename() { return m_szNZBFilename; }
long long GetSize() { return m_lSize; }
long long GetRemainingSize() { return m_lRemainingSize; }
long long GetPausedSize() { return m_lPausedSize; }
};
typedef std::deque<GroupInfo*> GroupQueue;
bool m_bUseColor;
int m_iDataUpdatePos;
bool m_bUpdateNextTime;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
@@ -86,8 +63,13 @@ private:
bool m_bLastPausePars;
int m_iQueueScrollOffset;
GroupQueue m_groupQueue;
char* m_szHint;
time_t m_tStartHint;
int m_iColWidthFiles;
int m_iColWidthTotal;
int m_iColWidthLeft;
// Inputting numbres
// Inputting numbers
int m_iInputNumberIndex;
int m_iInputValue;
@@ -116,23 +98,24 @@ private:
void PrintFileQueue();
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
void PrintGroupQueue();
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
void ResetColWidths();
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth);
void PrepareGroupQueue();
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
void ClearGroupQueue();
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
void PrintKeyInputBar();
void PrintStatus();
void UpdateInput();
void Update();
void UpdateInput(int initialKey);
void Update(int iKey);
void SetCurrentQueueEntry(int iEntry);
void CalcWindowSizes();
void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
void RefreshScreen();
int ReadConsoleKey();
int CalcQueueSize();
void NeedUpdateData();
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
void SetHint(const char* szHint);
protected:
virtual void Run();

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
@@ -43,11 +44,13 @@
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer->GetHost(), pNewsServer->GetPort(), pNewsServer->GetTLS())
{
m_pNewsServer = pNewsServer;
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
SetCipher(pNewsServer->GetCipher());
}
NNTPConnection::~NNTPConnection()
@@ -60,7 +63,7 @@ NNTPConnection::~NNTPConnection()
free(m_szLineBuf);
}
char* NNTPConnection::Request(char* req)
const char* NNTPConnection::Request(const char* req)
{
if (!req)
{
@@ -80,7 +83,7 @@ char* NNTPConnection::Request(char* req)
if (!strncmp(answer, "480", 3))
{
debug("%s requested authorization", m_pNetAddress->GetHost());
debug("%s requested authorization", GetHost());
//authentication required!
if (!Authenticate())
@@ -100,10 +103,10 @@ char* NNTPConnection::Request(char* req)
bool NNTPConnection::Authenticate()
{
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
!((NewsServer*)m_pNetAddress)->GetPassword())
if (!(m_pNewsServer)->GetUser() ||
!(m_pNewsServer)->GetPassword())
{
return false;
return true;
}
return AuthInfoUser();
@@ -117,7 +120,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", m_pNewsServer->GetUser());
tmp[1024-1] = '\0';
WriteLine(tmp);
@@ -125,13 +128,13 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
ReportError("Authorization for %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
if (!strncmp(answer, "281", 3))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
debug("Authorization for %s successful", GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
@@ -147,7 +150,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
if (GetStatus() != csCancelled)
{
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
@@ -160,7 +163,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", m_pNewsServer->GetPassword());
tmp[1024-1] = '\0';
WriteLine(tmp);
@@ -168,12 +171,12 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
ReportError("Authorization for %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
else if (!strncmp(answer, "2", 1))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
debug("Authorization for %s successful", GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
@@ -185,63 +188,53 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
if (GetStatus() != csCancelled)
{
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
bool NNTPConnection::JoinGroup(char* grp)
const char* NNTPConnection::JoinGroup(const char* grp)
{
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
{
// already in group
return true;
strcpy(m_szLineBuf, "211 ");
return m_szLineBuf;
}
char tmp[1024];
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
tmp[1024-1] = '\0';
char* answer = Request(tmp);
const char* answer = Request(tmp);
if (m_bAuthError)
{
return false;
return answer;
}
if ((answer) && (!strncmp(answer, "2", 1)))
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
debug("Changed group to %s on %s", grp, GetHost());
if (m_szActiveGroup)
{
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
return true;
}
if (GetStatus() != csCancelled)
else
{
if (!answer)
{
warn("Error changing group on %s: Connection closed by remote host.",
GetServer()->GetHost());
}
else
{
warn("Error changing group on %s to %s: Answer was \"%s\".",
GetServer()->GetHost(), grp, answer);
}
debug("Error changing group on %s to %s: %s.", GetHost(), grp, answer);
}
return false;
return answer;
}
int NNTPConnection::DoConnect()
bool NNTPConnection::DoConnect()
{
debug("Opening connection to %s", GetServer()->GetHost());
int res = Connection::DoConnect();
if (res < 0)
debug("Opening connection to %s", GetHost());
bool res = Connection::DoConnect();
if (!res)
{
return res;
}
@@ -250,22 +243,22 @@ int NNTPConnection::DoConnect()
if (!answer)
{
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
return -1;
ReportError("Connection to %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
if (strncmp(answer, "2", 1))
{
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
return -1;
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
return false;
}
debug("Connection to %s established", GetServer()->GetHost());
return 0;
debug("Connection to %s established", GetHost());
return true;
}
int NNTPConnection::DoDisconnect()
bool NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
@@ -278,3 +271,12 @@ int NNTPConnection::DoDisconnect()
}
return Connection::DoDisconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -33,26 +33,27 @@
class NNTPConnection : public Connection
{
private:
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
virtual int DoConnect();
virtual int DoDisconnect();
virtual bool DoConnect();
virtual bool DoDisconnect();
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
public:
NNTPConnection(NewsServer* server);
~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
char* Request(char* req);
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
bool JoinGroup(char* grp);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -133,8 +133,12 @@ void InstallService(int argc, char *argv[])
return;
}
char szExeName[1024];
GetModuleFileName(NULL, szExeName, 1024);
szExeName[1024-1] = '\0';
char szCmdLine[1024];
snprintf(szCmdLine, 1024, "%s -D", argv[0]);
snprintf(szCmdLine, 1024, "%s -D", szExeName);
szCmdLine[1024-1] = '\0';
SC_HANDLE hService = CreateService(scm, strServiceName,

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -36,11 +36,13 @@
#include <list>
#ifdef WIN32
#include <comutil.h>
#import "MSXML.dll" named_guids
#import <msxml.tlb> named_guids
using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlerror.h>
#include <libxml/entities.h>
#endif
#include "nzbget.h"
@@ -54,11 +56,23 @@ using namespace MSXML;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
NZBFile::NZBFile(const char* szFileName)
NZBFile::NZBFile(const char* szFileName, const char* szCategory)
{
debug("Creating NZBFile");
m_szFileName = strdup(szFileName);
m_pNZBInfo = new NZBInfo();
m_pNZBInfo->AddReference();
m_pNZBInfo->SetFilename(szFileName);
m_pNZBInfo->SetCategory(szCategory);
m_pNZBInfo->BuildDestDirName();
#ifndef WIN32
m_pFileInfo = NULL;
m_pArticle = NULL;
m_szTagContent = NULL;
m_iTagContentLen = 0;
#endif
m_FileInfos.clear();
}
@@ -78,6 +92,23 @@ NZBFile::~NZBFile()
delete *it;
}
m_FileInfos.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
#ifndef WIN32
if (m_pFileInfo)
{
delete m_pFileInfo;
}
if (m_szTagContent)
{
free(m_szTagContent);
}
#endif
}
void NZBFile::LogDebugInfo()
@@ -90,14 +121,14 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
return Create(szFileName, szBuffer, iSize, true);
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, NULL, 0, false);
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
@@ -130,15 +161,10 @@ void NZBFile::AddFileInfo(FileInfo* pFileInfo)
if (!pArticles->empty())
{
ParseSubject(pFileInfo);
BuildDestDirName(pFileInfo);
m_FileInfos.push_back(pFileInfo);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
}
else
{
@@ -146,8 +172,34 @@ void NZBFile::AddFileInfo(FileInfo* pFileInfo)
}
}
void NZBFile::ParseSubject(FileInfo* pFileInfo)
void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
{
if (TryQuotes)
{
// try to use the filename in quatation marks
char* p = (char*)pFileInfo->GetSubject();
char* start = strchr(p, '\"');
if (start)
{
start++;
char* end = strchr(start + 1, '\"');
if (end)
{
int len = (int)(end - start);
char* point = strchr(start + 1, '.');
if (point && point < end)
{
char* filename = (char*)malloc(len + 1);
strncpy(filename, start, len);
filename[len] = '\0';
pFileInfo->SetFilename(filename);
free(filename);
return;
}
}
}
}
// tokenize subject, considering spaces as separators and quotation
// marks as non separatable token delimiters.
// then take the last token containing dot (".") as a filename
@@ -167,7 +219,7 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo)
if (sep)
{
// end of token
int len = p - start;
int len = (int)(p - start);
if (len > 0)
{
char* token = (char*)malloc(len + 1);
@@ -230,39 +282,14 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo)
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
pFileInfo->MakeValidFilename();
}
void NZBFile::BuildDestDirName(FileInfo* pFileInfo)
bool NZBFile::HasDuplicateFilenames()
{
char szBuffer[1024];
if (g_pOptions->GetAppendNZBDir())
{
char szNiceNZBName[1024];
pFileInfo->GetNiceNZBName(szNiceNZBName, 1024);
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
szBuffer[1024-1] = '\0';
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
pFileInfo->SetDestDir(szBuffer);
}
/**
* Check if the parsing of subject was correct
*/
void NZBFile::CheckFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo1 = *it;
int iDupe = 0;
int iDupe = 1;
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
@@ -281,25 +308,56 @@ void NZBFile::CheckFilenames()
// an often case by posting-errors to repost bad files
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
{
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
pFileInfo2->MakeValidFilename();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->LoadArticles(pFileInfo2);
g_pDiskState->SaveFile(pFileInfo2);
pFileInfo2->ClearArticles();
}
}
return true;
}
}
return false;
}
/**
* Generate filenames from subjects and check if the parsing of subject was correct
*/
void NZBFile::ProcessFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
ParseSubject(pFileInfo, true);
}
if (HasDuplicateFilenames())
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
ParseSubject(pFileInfo, false);
}
}
if (HasDuplicateFilenames())
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
}
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->MakeValidFilename();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
}
#ifdef WIN32
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
CoInitialize(NULL);
@@ -339,10 +397,10 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName);
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
pFile->ProcessFilenames();
}
else
{
@@ -388,9 +446,15 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
if (!attribute) return false;
_bstr_t subject(attribute->Gettext());
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetNZBFilename(m_szFileName);
pFileInfo->SetSubject(subject);
attribute = node->Getattributes()->getNamedItem("date");
if (attribute)
{
_bstr_t date(attribute->Gettext());
pFileInfo->SetTime(atoi(date));
}
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
for (int g = 0; g < groupList->Getlength(); g++)
{
@@ -437,146 +501,237 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
#else
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
xmlTextReaderPtr doc;
NZBFile* pFile = new NZBFile(szFileName, szCategory);
xmlSAXHandler SAX_handler = {0};
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
int ret = 0;
pFile->m_bIgnoreNextError = false;
if (bFromBuffer)
{
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
ret = xmlSAXUserParseMemory(&SAX_handler, pFile, szBuffer, iSize);
}
else
{
doc = xmlReaderForFile(szFileName, NULL, 0);
ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
}
if (!doc)
{
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName);
if (pFile->ParseNZB(doc))
if (ret == 0)
{
pFile->CheckFilenames();
pFile->ProcessFilenames();
}
else
{
error("Failed to parse nzb-file");
delete pFile;
pFile = NULL;
}
xmlFreeTextReader(doc);
return pFile;
return pFile;
}
bool NZBFile::ParseNZB(void* nzb)
void NZBFile::Parse_StartElement(const char *name, const char **atts)
{
FileInfo* pFileInfo = NULL;
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
// walk through whole doc and search for segments-tags
int ret = xmlTextReaderRead(node);
while (ret == 1)
{
if (node)
{
xmlChar *name, *value;
if (m_szTagContent)
{
free(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
if (!strcmp("file", name))
{
m_pFileInfo = new FileInfo();
m_pFileInfo->SetFilename(m_szFileName);
name = xmlTextReaderName(node);
if (name == NULL)
{
name = xmlStrdup(BAD_CAST "--");
}
value = xmlTextReaderValue(node);
for (int i = 0; atts[i]; i += 2)
{
const char* attrname = atts[i];
const char* attrvalue = atts[i + 1];
if (!strcmp("subject", attrname))
{
m_pFileInfo->SetSubject(attrvalue);
}
if (!strcmp("date", attrname))
{
m_pFileInfo->SetTime(atoi(attrvalue));
}
}
}
else if (!strcmp("segment", name))
{
if (!m_pFileInfo)
{
// error: bad nzb-file
return;
}
long long lsize = -1;
int partNumber = -1;
if (xmlTextReaderNodeType(node) == 1)
{
if (!strcmp("file", (char*)name))
{
pFileInfo = new FileInfo();
pFileInfo->SetNZBFilename(m_szFileName);
for (int i = 0; atts[i]; i += 2)
{
const char* attrname = atts[i];
const char* attrvalue = atts[i + 1];
if (!strcmp("bytes", attrname))
{
lsize = atol(attrvalue);
}
if (!strcmp("number", attrname))
{
partNumber = atol(attrvalue);
}
}
if (lsize > 0)
{
m_pFileInfo->SetSize(m_pFileInfo->GetSize() + lsize);
}
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
if (!strcmp("subject",(char*)name))
{
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetSubject((char*)value);
}
}
}
else if (!strcmp("segment",(char*)name))
{
long long lsize = -1;
int partNumber = -1;
if (partNumber > 0)
{
// new segment, add it!
m_pArticle = new ArticleInfo();
m_pArticle->SetPartNumber(partNumber);
m_pArticle->SetSize(lsize);
AddArticle(m_pFileInfo, m_pArticle);
}
}
}
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
xmlFree(value);
value = xmlTextReaderValue(node);
if (!strcmp("bytes",(char*)name))
{
lsize = atol((char*)value);
}
if (!strcmp("number",(char*)name))
{
partNumber = atol((char*)value);
}
}
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
/* Get the #text part */
ret = xmlTextReaderRead(node);
void NZBFile::Parse_EndElement(const char *name)
{
if (!strcmp("file", name))
{
// Close the file element, add the new file to file-list
AddFileInfo(m_pFileInfo);
m_pFileInfo = NULL;
m_pArticle = NULL;
}
else if (!strcmp("group", name))
{
if (!m_pFileInfo)
{
// error: bad nzb-file
return;
}
m_pFileInfo->GetGroups()->push_back(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
else if (!strcmp("segment", name))
{
if (!m_pFileInfo || !m_pArticle)
{
// error: bad nzb-file
return;
}
if (partNumber > 0)
{
// new segment, add it!
xmlFree(value);
value = xmlTextReaderValue(node);
char tmp[2048];
snprintf(tmp, 2048, "<%s>", (char*)value);
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(tmp);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
}
else if (!strcmp("group",(char*)name))
{
ret = xmlTextReaderRead(node);
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->GetGroups()->push_back(strdup((char*)value));
}
}
// Get the #text part
char ID[2048];
snprintf(ID, 2048, "<%s>", m_szTagContent);
m_pArticle->SetMessageID(ID);
m_pArticle = NULL;
}
}
if (xmlTextReaderNodeType(node) == 15)
{
/* Close the file element, add the new file to file-list */
if (!strcmp("file",(char*)name))
{
AddFileInfo(pFileInfo);
}
}
void NZBFile::Parse_Content(const char *buf, int len)
{
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
m_iTagContentLen += len;
m_szTagContent[m_iTagContentLen] = '\0';
}
xmlFree(name);
xmlFree(value);
}
ret = xmlTextReaderRead(node);
}
if (ret != 0)
{
error("Failed to parse nzb-file\n");
return false;
}
return true;
void NZBFile::SAX_StartElement(NZBFile* pFile, const char *name, const char **atts)
{
pFile->Parse_StartElement(name, atts);
}
void NZBFile::SAX_EndElement(NZBFile* pFile, const char *name)
{
pFile->Parse_EndElement(name);
}
void NZBFile::SAX_characters(NZBFile* pFile, const char * xmlstr, int len)
{
char* str = (char*)xmlstr;
// trim starting blanks
int off = 0;
for (int i = 0; i < len; i++)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
off++;
}
else
{
break;
}
}
int newlen = len - off;
// trim ending blanks
for (int i = len - 1; i >= off; i--)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
newlen--;
}
else
{
break;
}
}
if (newlen > 0)
{
// interpret tag content
pFile->Parse_Content(str + off, newlen);
}
}
void* NZBFile::SAX_getEntity(NZBFile* pFile, const char * name)
{
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
if (!e)
{
warn("entity not found");
pFile->m_bIgnoreNextError = true;
}
return e;
}
void NZBFile::SAX_error(NZBFile* pFile, const char *msg, ...)
{
if (pFile->m_bIgnoreNextError)
{
pFile->m_bIgnoreNextError = false;
return;
}
va_list argp;
va_start(argp, msg);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing nzb-file: %s", szErrMsg);
}
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -38,28 +38,43 @@ public:
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
NZBFile(const char* szFileName);
NZBFile(const char* szFileName, const char* szCategory);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo);
void BuildDestDirName(FileInfo* pFileInfo);
void CheckFilenames();
void ParseSubject(FileInfo* pFileInfo, bool TryQuotes);
void ProcessFilenames();
bool HasDuplicateFilenames();
#ifdef WIN32
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
bool ParseNZB(void* nzb);
FileInfo* m_pFileInfo;
ArticleInfo* m_pArticle;
char* m_szTagContent;
int m_iTagContentLen;
bool m_bIgnoreNextError;
static void SAX_StartElement(NZBFile* pFile, const char *name, const char **atts);
static void SAX_EndElement(NZBFile* pFile, const char *name);
static void SAX_characters(NZBFile* pFile, const char * xmlstr, int len);
static void* SAX_getEntity(NZBFile* pFile, const char * name);
static void SAX_error(NZBFile* pFile, const char *msg, ...);
void Parse_StartElement(const char *name, const char **atts);
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
#endif
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
static NZBFile* Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer);
public:
virtual ~NZBFile();
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName);
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void DetachFileInfos();
void LogDebugInfo();

View File

@@ -1,54 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NetAddress.h"
NetAddress::NetAddress(const char* szHost, int iPort)
{
m_szHost = NULL;
m_iPort = iPort;
if (szHost)
m_szHost = strdup(szHost);
}
NetAddress::~NetAddress()
{
if (m_szHost)
free(m_szHost);
m_szHost = NULL;
}

View File

@@ -1,44 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <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, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef NETADDRESS_H
#define NETADDRESS_H
class NetAddress
{
private:
char* m_szHost;
int m_iPort;
public:
NetAddress(const char* szHost, int iPort);
virtual ~NetAddress();
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -37,29 +37,42 @@
#include "nzbget.h"
#include "NewsServer.h"
#include "Log.h"
NewsServer::NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level) : NetAddress(host, port)
NewsServer::NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
{
m_iID = iID;
m_szHost = NULL;
m_iPort = iPort;
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = level;
m_iMaxConnections = maxConnections;
if (pass)
{
m_szPassword = strdup(pass);
}
if (user)
{
m_szUser = strdup(user);
}
m_iLevel = iLevel;
m_iGroup = iGroup;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
m_szHost = szHost ? strdup(szHost) : NULL;
m_szUser = szUser ? strdup(szUser) : NULL;
m_szPassword = szPass ? strdup(szPass) : NULL;
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
NewsServer::~NewsServer()
{
free(m_szUser);
m_szUser = NULL;
free(m_szPassword);
m_szPassword = NULL;
if (m_szHost)
{
free(m_szHost);
}
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
if (m_szCipher)
{
free(m_szCipher);
}
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,23 +27,37 @@
#ifndef NEWSSERVER_H
#define NEWSSERVER_H
#include "NetAddress.h"
class NewsServer : public NetAddress
class NewsServer
{
private:
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
int m_iID;
int m_iGroup;
char* m_szHost;
int m_iPort;
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
bool m_bJoinGroup;
bool m_bTLS;
char* m_szCipher;
public:
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
virtual ~NewsServer();
NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
~NewsServer();
int GetID() { return m_iID; }
int GetGroup() { return m_iGroup; }
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
void SetLevel(int iLevel) { m_iLevel = iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
};
#endif

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$

View File

File diff suppressed because it is too large Load Diff

342
Options.h
View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -28,97 +28,206 @@
#define OPTIONS_H
#include <vector>
#include <time.h>
#include "Thread.h"
class Options
{
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestList,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestVersion
opClientNoOperation,
opClientRequestDownload,
opClientRequestListFiles,
opClientRequestListGroups,
opClientRequestListStatus,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestReload,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScanSync,
opClientRequestScanAsync,
opClientRequestDownloadPause,
opClientRequestDownloadUnpause,
opClientRequestDownload2Pause,
opClientRequestDownload2Unpause,
opClientRequestPostPause,
opClientRequestPostUnpause,
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory,
opClientRequestDownloadUrl,
opClientRequestUrlQueue
};
enum EMessageTarget
{
mtNone,
mtScreen,
mtLog,
mtBoth
};
enum EDecoder
{
dcNone,
dcUulib,
dcYenc
mtNone,
mtScreen,
mtLog,
mtBoth
};
enum EOutputMode
{
omLoggable,
omColored,
omNCurses
omLoggable,
omColored,
omNCurses
};
enum ELoadPars
{
plNone,
plOne,
plAll
lpNone,
lpOne,
lpAll
};
enum EParScan
{
psLimited,
psFull,
psAuto
};
enum EScriptLogKind
{
slNone,
slDetail,
slInfo,
slWarning,
slError,
slDebug
};
enum EMatchMode
{
mmID = 1,
mmName,
mmRegEx
};
enum EDomain
{
dmServer = 1,
dmPostProcess
};
class OptEntry
{
private:
char* m_szName;
char* m_szValue;
char* m_szDefValue;
int m_iLineNo;
void SetName(const char* szName);
void SetValue(const char* szValue);
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
friend class Options;
public:
OptEntry();
OptEntry(const char* szName, const char* szValue);
~OptEntry();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
const char* GetDefValue() { return m_szDefValue; }
int GetLineNo() { return m_iLineNo; }
};
typedef std::vector<OptEntry*> OptEntriesBase;
class OptEntries: public OptEntriesBase
{
public:
~OptEntries();
OptEntry* FindOption(const char* szName);
};
typedef std::vector<char*> NameList;
class Category
{
private:
char* m_szName;
char* m_szDestDir;
public:
Category(const char* szName, const char* szDestDir);
~Category();
const char* GetName() { return m_szName; }
const char* GetDestDir() { return m_szDestDir; }
};
typedef std::vector<Category*> CategoriesBase;
class Categories: public CategoriesBase
{
public:
~Categories();
Category* FindCategory(const char* szName);
};
private:
struct OptEntry
{
char* name;
char* value;
};
std::vector< struct OptEntry > optEntries;
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
Categories m_Categories;
// Options
bool m_bConfigErrors;
int m_iConfigLine;
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szInterDir;
char* m_szTempDir;
char* m_szQueueDir;
char* m_szNzbDir;
char* m_szWebDir;
EMessageTarget m_eInfoTarget;
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
EMessageTarget m_eDebugTarget;
EDecoder m_eDecoder;
EMessageTarget m_eDetailTarget;
bool m_bDecode;
bool m_bCreateBrokenLog;
bool m_bResetLog;
int m_iConnectionTimeout;
int m_iTerminateTimeout;
bool m_bAppendNZBDir;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
bool m_bRenameBroken;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bDupeCheck;
char* m_szServerIP;
char* m_szServerPassword;
int m_szServerPort;
char* m_szControlIP;
char* m_szControlPassword;
int m_iControlPort;
bool m_bSecureControl;
int m_iSecurePort;
char* m_szSecureCert;
char* m_szSecureKey;
char* m_szLockFile;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadUrlQueue;
bool m_bReloadPostQueue;
int m_iUrlConnections;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
ELoadPars m_eLoadPars;
bool m_bParCheck;
bool m_bParRepair;
EParScan m_eParScan;
char* m_szPostProcess;
char* m_szPostConfigFilename;
char* m_szNZBProcess;
char* m_szNZBAddedProcess;
bool m_bStrictParName;
bool m_bNoConfig;
int m_iUMask;
@@ -127,12 +236,30 @@ private:
bool m_bCursesTime;
bool m_bCursesGroup;
bool m_bCrcCheck;
bool m_bRetryOnCrcError;
int m_iThreadLimit;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EScriptLogKind m_eProcessLogKind;
bool m_bAllowReProcess;
bool m_bTLS;
bool m_bDumpCore;
bool m_bParPauseQueue;
bool m_bPostPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
int m_iParTimeLimit;
int m_iKeepHistory;
bool m_bAccurateRate;
bool m_bUnpack;
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
// Parsed command-line parameters
bool m_bServerMode;
@@ -142,75 +269,122 @@ private:
int m_iEditQueueOffset;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
NameList m_EditQueueNameList;
EMatchMode m_EMatchMode;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szAddCategory;
int m_iAddPriority;
bool m_bAddPaused;
char* m_szAddNZBFilename;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
// Current state
bool m_bPause;
float m_fDownloadRate;
bool m_bPauseDownload;
bool m_bPauseDownload2;
bool m_bPausePostProcess;
bool m_bPauseScan;
int m_iDownloadRate;
EClientOperation m_eClientOperation;
time_t m_tResumeTime;
void InitDefault();
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitPostConfig();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitCategories();
void InitScheduler();
void CheckOptions();
void PrintUsage(char* com);
void Dump();
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
int ParseIntValue(const char* OptName, int iBase);
float ParseFloatValue(const char* OptName);
OptEntry* FindOption(const char* optname);
const char* GetOption(const char* optname);
void DelOption(const char* optname);
void SetOption(const char* optname, const char* value);
bool SetOptionString(const char* option);
bool ValidateOptionName(const char* optname);
void LoadConfig(const char* configfile);
void CheckDir(char** dir, const char* szOptionName);
void LoadConfigFile();
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
void ParseFileIDList(int argc, char* argv[], int optind);
void ParseFileNameList(int argc, char* argv[], int optind);
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
void ConfigError(const char* msg, ...);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *szOptionName);
void ConvertOldOptionName(char *szOption, int iBufLen);
public:
Options(int argc, char* argv[]);
~Options();
bool LoadConfig(EDomain eDomain, OptEntries* pOptEntries);
bool SaveConfig(EDomain eDomain, OptEntries* pOptEntries);
// Options
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetConfigFilename() { return m_szConfigFilename; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetInterDir() { return m_szInterDir; }
const char* GetTempDir() { return m_szTempDir; }
const char* GetQueueDir() { return m_szQueueDir; }
const char* GetNzbDir() { return m_szNzbDir; }
const char* GetWebDir() { return m_szWebDir; }
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
bool GetResetLog() const { return m_bResetLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
EDecoder GetDecoder() { return m_eDecoder; };
bool GetDecode() { return m_bDecode; };
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
bool GetContinuePartial() { return m_bContinuePartial; }
bool GetRenameBroken() { return m_bRenameBroken; }
int GetRetries() { return m_iRetries; }
int GetRetryInterval() { return m_iRetryInterval; }
bool GetSaveQueue() { return m_bSaveQueue; }
bool GetDupeCheck() { return m_bDupeCheck; }
char* GetServerIP() { return m_szServerIP; }
char* GetServerPassword() { return m_szServerPassword; }
int GetServerPort() { return m_szServerPort; }
char* GetLockFile() { return m_szLockFile; }
char* GetDaemonUserName() { return m_szDaemonUserName; }
const char* GetControlIP() { return m_szControlIP; }
const char* GetControlPassword() { return m_szControlPassword; }
int GetControlPort() { return m_iControlPort; }
bool GetSecureControl() { return m_bSecureControl; }
int GetSecurePort() { return m_iSecurePort; }
const char* GetSecureCert() { return m_szSecureCert; }
const char* GetSecureKey() { return m_szSecureKey; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUserName() { return m_szDaemonUserName; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
int GetUrlConnections() { return m_iUrlConnections; }
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
char* GetLogFile() { return m_szLogFile; }
const char* GetLogFile() { return m_szLogFile; }
ELoadPars GetLoadPars() { return m_eLoadPars; }
bool GetParCheck() { return m_bParCheck; }
bool GetParRepair() { return m_bParRepair; }
EParScan GetParScan() { return m_eParScan; }
const char* GetPostProcess() { return m_szPostProcess; }
const char* GetPostConfigFilename() { return m_szPostConfigFilename; }
const char* GetNZBProcess() { return m_szNZBProcess; }
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
bool GetStrictParName() { return m_bStrictParName; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
@@ -218,32 +392,70 @@ public:
bool GetCursesTime() { return m_bCursesTime; }
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
int GetThreadLimit() { return m_iThreadLimit; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
int GetDiskSpace() { return m_iDiskSpace; }
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
bool GetTLS() { return m_bTLS; }
bool GetDumpCore() { return m_bDumpCore; }
bool GetParPauseQueue() { return m_bParPauseQueue; }
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
int GetParTimeLimit() { return m_iParTimeLimit; }
int GetKeepHistory() { return m_iKeepHistory; }
bool GetAccurateRate() { return m_bAccurateRate; }
bool GetUnpack() { return m_bUnpack; }
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
Category* FindCategory(const char* szName) { return m_Categories.FindCategory(szName); }
// Parsed command-line parameters
bool GetServerMode() { return m_bServerMode; }
bool GetDaemonMode() { return m_bDaemonMode; }
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
int GetEditQueueAction() { return m_iEditQueueAction; }
int GetEditQueueOffset() { return m_iEditQueueOffset; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
EMatchMode GetMatchMode() { return m_EMatchMode; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetAddCategory() { return m_szAddCategory; }
bool GetAddPaused() { return m_bAddPaused; }
const char* GetLastArg() { return m_szLastArg; }
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
float GetSetRate() { return m_fSetRate; }
int GetSetRate() { return m_iSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
// Current state
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
bool GetPause() const { return m_bPause; }
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
float GetDownloadRate() const { return m_fDownloadRate; }
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }
bool GetPauseDownload() const { return m_bPauseDownload; }
void SetPauseDownload2(bool bPauseDownload2) { m_bPauseDownload2 = bPauseDownload2; }
bool GetPauseDownload2() const { return m_bPauseDownload2; }
void SetPausePostProcess(bool bPausePostProcess) { m_bPausePostProcess = bPausePostProcess; }
bool GetPausePostProcess() const { return m_bPausePostProcess; }
void SetPauseScan(bool bPauseScan) { m_bPauseScan = bPauseScan; }
bool GetPauseScan() const { return m_bPauseScan; }
void SetDownloadRate(int iRate) { m_iDownloadRate = iRate; }
int GetDownloadRate() const { return m_iDownloadRate; }
void SetResumeTime(time_t tResumeTime) { m_tResumeTime = tResumeTime; }
time_t GetResumeTime() const { return m_tResumeTime; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -36,10 +36,12 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#else
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#endif
@@ -47,11 +49,9 @@
#include "nzbget.h"
#include "ParChecker.h"
#include "Log.h"
#include "QueueCoordinator.h"
#include "Options.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
const char* Par2CmdLineErrStr[] = { "OK",
@@ -64,21 +64,108 @@ const char* Par2CmdLineErrStr[] = { "OK",
"internal error occurred",
"out of memory" };
class Repairer : public Par2Repairer
{
private:
CommandLine commandLine;
public:
Result PreProcess(const char *szParFilename);
Result Process(bool dorepair);
friend class ParChecker;
};
Result Repairer::PreProcess(const char *szParFilename)
{
#ifdef HAVE_PAR2_BUGFIXES_V2
// Ensure linking against the patched version of libpar2
BugfixesPatchVersion2();
#endif
if (g_pOptions->GetParScan() == Options::psFull)
{
char szWildcardParam[1024];
strncpy(szWildcardParam, szParFilename, 1024);
szWildcardParam[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szWildcardParam);
if (szBasename != szWildcardParam && strlen(szBasename) > 0)
{
szBasename[0] = '*';
szBasename[1] = '\0';
}
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename, szWildcardParam };
if (!commandLine.Parse(6, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
else
{
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
return Par2Repairer::PreProcess(commandLine);
}
Result Repairer::Process(bool dorepair)
{
return Par2Repairer::Process(commandLine, dorepair);
}
class MissingFilesComparator
{
private:
const char* m_szBaseParFilename;
public:
MissingFilesComparator(const char* szBaseParFilename) : m_szBaseParFilename(szBaseParFilename) {}
bool operator()(CommandLine::ExtraFile* pFirst, CommandLine::ExtraFile* pSecond) const;
};
/*
* Files with the same name as in par-file (and a differnt extension) are
* placed at the top of the list to be scanned first.
*/
bool MissingFilesComparator::operator()(CommandLine::ExtraFile* pFile1, CommandLine::ExtraFile* pFile2) const
{
char name1[1024];
strncpy(name1, Util::BaseFileName(pFile1->FileName().c_str()), 1024);
name1[1024-1] = '\0';
if (char* ext = strrchr(name1, '.')) *ext = '\0'; // trim extension
char name2[1024];
strncpy(name2, Util::BaseFileName(pFile2->FileName().c_str()), 1024);
name2[1024-1] = '\0';
if (char* ext = strrchr(name2, '.')) *ext = '\0'; // trim extension
return strcmp(name1, m_szBaseParFilename) == 0 && strcmp(name1, name2) != 0;
}
ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psUndefined;
m_szParFilename = NULL;
m_szNZBFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
m_QueuedParFiles.clear();
m_szProgressLabel = (char*)malloc(1024);
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
m_eStage = ptLoadingPars;
}
ParChecker::~ParChecker()
@@ -89,10 +176,6 @@ ParChecker::~ParChecker()
{
free(m_szParFilename);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
@@ -101,12 +184,24 @@ ParChecker::~ParChecker()
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
Cleanup();
}
void ParChecker::Cleanup()
{
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end() ;it++)
{
free(*it);
}
m_ProcessedFiles.clear();
}
void ParChecker::SetParFilename(const char * szParFilename)
@@ -127,15 +222,6 @@ void ParChecker::SetInfoName(const char * szInfoName)
m_szInfoName = strdup(szInfoName);
}
void ParChecker::SetNZBFilename(const char * szNZBFilename)
{
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
m_szNZBFilename = strdup(szNZBFilename);
}
void ParChecker::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
@@ -144,31 +230,52 @@ void ParChecker::SetStatus(EStatus eStatus)
void ParChecker::Run()
{
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
CommandLine commandLine;
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
SetStatus(psFailed);
return;
}
Cleanup();
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
Result res;
Repairer* repairer = new Repairer();
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
res = repairer->PreProcess(commandLine);
Result res;
Repairer* pRepairer = new Repairer();
m_pRepairer = pRepairer;
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
UpdateProgress();
res = pRepairer->PreProcess(m_szParFilename);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
error("Could not verify %s: ", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
if (res == eInvalidCommandLineArguments)
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
}
else
{
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
}
SetStatus(psFailed);
delete repairer;
delete pRepairer;
Cleanup();
return;
}
@@ -180,15 +287,31 @@ void ParChecker::Run()
}
m_szErrMsg = NULL;
m_bRepairNotNeeded = false;
m_bRepairing = false;
res = repairer->Process(commandLine, false);
m_eStage = ptVerifyingSources;
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && pRepairer->missingfilecount > 0 && g_pOptions->GetParScan() == Options::psAuto && AddMissingFiles())
{
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
@@ -198,9 +321,17 @@ void ParChecker::Run()
{
int iBlockFound = 0;
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_bQueuedParFilesChanged = false;
m_mutexQueuedParFiles.Unlock();
if (!requested && !hasMorePars)
@@ -213,7 +344,15 @@ void ParChecker::Run()
if (!hasMorePars)
{
m_semNeedMoreFiles.Wait();
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool bQueuedParFilesChanged = false;
while (!bQueuedParFilesChanged && !IsStopped())
{
m_mutexQueuedParFiles.Lock();
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
m_mutexQueuedParFiles.Unlock();
usleep(100 * 1000);
}
}
}
@@ -222,18 +361,20 @@ void ParChecker::Run()
break;
}
LoadMorePars(repairer);
repairer->UpdateVerificationResults();
m_bRepairing = false;
res = repairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
bMoreFilesLoaded = LoadMorePars();
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
SetStatus(psFailed);
delete repairer;
delete pRepairer;
Cleanup();
return;
}
@@ -247,8 +388,17 @@ void ParChecker::Run()
if (g_pOptions->GetParRepair())
{
info("Repairing %s", m_szInfoName);
m_bRepairing = true;
res = repairer->Process(commandLine, true);
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iProcessedFiles = 0;
m_eStage = ptRepairing;
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
UpdateProgress();
res = pRepairer->Process(true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
@@ -262,7 +412,13 @@ void ParChecker::Run()
}
}
if (res == eSuccess)
if (m_bCancelled)
{
warn("Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
SetStatus(psFailed);
}
else if (res == eSuccess)
{
SetStatus(psFinished);
}
@@ -276,255 +432,319 @@ void ParChecker::Run()
SetStatus(psFailed);
}
delete repairer;
delete pRepairer;
Cleanup();
}
bool ParChecker::ParseParFilename(const char * szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
bool ParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
Blocks blocks;
blocks.clear();
int iBlockFound = 0;
FindPars(pDownloadQueue, &blocks, true, &iBlockFound);
if (iBlockFound == 0 && !g_pOptions->GetStrictParName())
{
FindPars(pDownloadQueue, &blocks, false, &iBlockFound);
}
if (iBlockFound >= iBlockNeeded)
{
char szNZBNiceName[1024];
FileInfo::MakeNiceNZBName(m_szNZBFilename, szNZBNiceName, 1024);
// 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 (iBlockNeeded > 0)
{
BlockInfo* pBestBlockInfo = NULL;
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
{
pBestBlockInfo = pBlockInfo;
}
}
if (pBestBlockInfo)
{
if (pBestBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
}
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
}
else
{
break;
}
}
// 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 (iBlockNeeded > 0)
{
BlockInfo* pBlockInfo = blocks.front();
if (pBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
}
iBlockNeeded -= pBlockInfo->m_iBlockCount;
}
}
g_pQueueCoordinator->UnlockQueue();
if (pBlockFound)
{
*pBlockFound = iBlockFound;
}
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
delete *it;
}
blocks.clear();
return iBlockNeeded <= 0;
}
void ParChecker::FindPars(DownloadQueue * pDownloadQueue, Blocks * pBlocks, bool bStrictParName, int* pBlockFound)
{
*pBlockFound = 0;
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* szBaseParFilename = BaseFileName(m_szParFilename);
char szMainBaseFilename[1024];
int iMainBaseLen = 0;
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
{
// should not happen
error("Internal error: could not parse filename %s", szBaseParFilename);
return;
}
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
szMainBaseFilename[maxlen] = '\0';
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (!strcmp(pFileInfo->GetNZBFilename(), m_szNZBFilename) &&
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
if (bStrictParName)
{
// the pFileInfo->GetFilename() may be not confirmed and may contain
// additional texts if Subject could not be parsed correctly
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
char szCandidateFileName[1024];
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
continue;
}
}
}
// if it is a par2-file with blocks and it was from the same NZB-request
// and it belongs to the same file collection (same base name),
// then OK, we can use it
BlockInfo* pBlockInfo = new BlockInfo();
pBlockInfo->m_pFileInfo = pFileInfo;
pBlockInfo->m_iBlockCount = iBlocks;
pBlocks->push_back(pBlockInfo);
*pBlockFound += iBlocks;
}
}
}
void ParChecker::LoadMorePars(void* repairer)
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
QueuedParFiles moreFiles;
FileList moreFiles;
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
for (FileList::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
{
char* szParFilename = *it;
bool loadedOK = ((Repairer*)repairer)->LoadPacketsFromFile(szParFilename);
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
return !moreFiles.empty();
}
void ParChecker::AddParFile(const char * szParFilename)
{
m_mutexQueuedParFiles.Lock();
m_QueuedParFiles.push_back(strdup(szParFilename));
m_semNeedMoreFiles.Post();
m_bQueuedParFilesChanged = true;
m_mutexQueuedParFiles.Unlock();
}
void ParChecker::QueueChanged()
{
m_mutexQueuedParFiles.Lock();
m_semNeedMoreFiles.Post();
m_bQueuedParFilesChanged = true;
m_mutexQueuedParFiles.Unlock();
}
bool ParChecker::CheckSplittedFragments()
{
bool bFragmentsAdded = false;
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
}
return bFragmentsAdded;
}
bool ParChecker::AddSplittedFragments(const char* szFilename)
{
char szDirectory[1024];
strncpy(szDirectory, szFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
int iBaseLen = strlen(szBasename);
std::list<CommandLine::ExtraFile> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (!strncasecmp(filename, szBasename, iBaseLen))
{
const char* p = filename + iBaseLen;
if (*p == '.')
{
for (p++; *p && strchr("0123456789", *p); p++) ;
if (!*p)
{
debug("Found splitted fragment %s", filename);
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
extrafiles.push_back(extrafile);
}
}
}
}
bool bFragmentsAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
m_bVerifyingExtraFiles = false;
}
return bFragmentsAdded;
}
bool ParChecker::AddMissingFiles()
{
info("Performing extra par-scan for %s", m_szInfoName);
char szDirectory[1024];
strncpy(szDirectory, m_szParFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
std::list<CommandLine::ExtraFile*> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && strcmp(filename, "_brokenlog.txt"))
{
bool bAlreadyScanned = false;
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end(); it++)
{
const char* szProcessedFilename = *it;
if (!strcasecmp(Util::BaseFileName(szProcessedFilename), filename))
{
bAlreadyScanned = true;
break;
}
}
if (!bAlreadyScanned)
{
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
extrafiles.push_back(new CommandLine::ExtraFile(fullfilename, Util::FileSize(fullfilename)));
}
}
}
// Sort the list
char* szBaseParFilename = strdup(Util::BaseFileName(m_szParFilename));
if (char* ext = strrchr(szBaseParFilename, '.')) *ext = '\0'; // trim extension
extrafiles.sort(MissingFilesComparator(szBaseParFilename));
free(szBaseParFilename);
// Scan files
bool bFilesAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
std::list<CommandLine::ExtraFile> extrafiles1;
// adding files one by one until all missing files are found
while (!IsStopped() && !m_bCancelled && extrafiles.size() > 0 && ((Repairer*)m_pRepairer)->missingfilecount > 0)
{
CommandLine::ExtraFile* pExtraFile = extrafiles.front();
extrafiles.pop_front();
extrafiles1.clear();
extrafiles1.push_back(*pExtraFile);
bFilesAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles1) || bFilesAdded;
((Repairer*)m_pRepairer)->UpdateVerificationResults();
delete pExtraFile;
}
m_bVerifyingExtraFiles = false;
// free any remaining objects
for (std::list<CommandLine::ExtraFile*>::iterator it = extrafiles.begin(); it != extrafiles.end() ;it++)
{
delete *it;
}
}
return bFilesAdded;
}
void ParChecker::signal_filename(std::string str)
{
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
if (m_eStage == ptRepairing)
{
m_eStage = ptVerifyingRepaired;
}
info("%s %s", szStageMessage[m_eStage], str.c_str());
if (m_eStage == ptLoadingPars || m_eStage == ptVerifyingSources)
{
m_ProcessedFiles.push_back(strdup(str.c_str()));
}
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
void ParChecker::signal_progress(double progress)
{
m_iFileProgress = (int)progress;
if (m_eStage == ptRepairing)
{
// calculating repair-data for all files
m_iStageProgress = m_iFileProgress;
}
else
{
// processing individual files
int iTotalFiles = 0;
if (m_eStage == ptVerifyingRepaired)
{
// repairing individual files
iTotalFiles = m_iFilesToRepair;
}
else
{
// verifying individual files
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
}
if (iTotalFiles > 0)
{
if (m_iFileProgress < 1000)
{
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
}
else
{
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
}
}
else
{
m_iStageProgress = 0;
}
}
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
UpdateProgress();
}
void ParChecker::signal_done(std::string str, int available, int total)
{
m_iProcessedFiles++;
if (m_eStage == ptVerifyingSources)
{
if (available < total && !m_bVerifyingExtraFiles)
{
bool bFileExists = true;
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
!sourcefile->GetTargetExists())
{
bFileExists = false;
break;
}
}
if (bFileExists)
{
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
}
else
{
warn("File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
}
void ParChecker::Cancel()
{
#ifdef HAVE_PAR2_CANCEL
((Repairer*)m_pRepairer)->cancelled = true;
m_bCancelled = true;
#else
error("Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details).");
#endif
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -32,7 +32,6 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
class ParChecker : public Thread, public Subject
{
@@ -44,40 +43,66 @@ public:
psFailed,
psFinished
};
struct BlockInfo
enum EStage
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
};
typedef std::deque<char*> QueuedParFiles;
typedef std::deque<BlockInfo*> Blocks;
typedef std::deque<char*> FileList;
private:
char* m_szInfoName;
char* m_szNZBFilename;
char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
char* m_szErrMsg;
bool m_bRepairNotNeeded;
QueuedParFiles m_QueuedParFiles;
FileList m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
Semaphore m_semNeedMoreFiles;
bool m_bRepairing;
bool m_bQueuedParFilesChanged;
FileList m_ProcessedFiles;
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
bool m_bVerifyingExtraFiles;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
void LoadMorePars(void* repairer);
void Cleanup();
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
bool AddMissingFiles();
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
protected:
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParChecker();
virtual ~ParChecker();
virtual void Run();
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetNZBFilename() { return m_szNZBFilename; }
void SetNZBFilename(const char* szNZBFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
@@ -86,7 +111,8 @@ public:
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
void Cancel();
bool GetCancelled() { return m_bCancelled; }
};
#endif

768
ParCoordinator.cpp Normal file
View File

@@ -0,0 +1,768 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nzbget.h"
#include "ParCoordinator.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "QueueCoordinator.h"
#include "DiskState.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#ifndef DISABLE_PARCHECK
bool ParCoordinator::PostParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
return m_pOwner->RequestMorePars(m_pPostInfo->GetNZBInfo(), GetParFilename(), iBlockNeeded, pBlockFound);
}
void ParCoordinator::PostParChecker::UpdateProgress()
{
m_pOwner->UpdateParCheckProgress();
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_pOwner->UpdateParRenameProgress();
}
#endif
ParCoordinator::ParCoordinator()
{
debug("Creating ParCoordinator");
#ifndef DISABLE_PARCHECK
m_ParCheckerObserver.m_pOwner = this;
m_ParChecker.Attach(&m_ParCheckerObserver);
m_ParChecker.m_pOwner = this;
m_ParRenamerObserver.m_pOwner = this;
m_ParRenamer.Attach(&m_ParRenamerObserver);
m_ParRenamer.m_pOwner = this;
m_bStopped = false;
const char* szPostScript = g_pOptions->GetPostProcess();
m_bPostScript = szPostScript && strlen(szPostScript) > 0;
#endif
}
ParCoordinator::~ParCoordinator()
{
debug("Destroying ParCoordinator");
}
#ifndef DISABLE_PARCHECK
void ParCoordinator::Stop()
{
debug("Stopping ParCoordinator");
m_bStopped = true;
if (m_ParChecker.IsRunning())
{
m_ParChecker.Stop();
int iMSecWait = 5000;
while (m_ParChecker.IsRunning() && iMSecWait > 0)
{
usleep(50 * 1000);
iMSecWait -= 50;
}
if (m_ParChecker.IsRunning())
{
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
m_ParChecker.Kill();
}
}
}
#endif
void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("ParCoordinator: Pausing pars");
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
(g_pOptions->GetLoadPars() == Options::lpOne ||
(g_pOptions->GetLoadPars() == Options::lpNone && g_pOptions->GetParCheck()))
? QueueEditor::eaGroupPauseExtraPars : QueueEditor::eaGroupPauseAllPars,
0, NULL);
break;
}
}
}
bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
{
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
const char* filename2 = *it;
exists = SameParCollection(filename, filename2);
if (exists)
{
break;
}
}
if (!exists)
{
pFileList->push_back(strdup(filename));
}
}
}
return pFileList && !pFileList->empty();
}
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
{
int iBaseLen1 = 0, iBaseLen2 = 0;
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
iBaseLen1 == iBaseLen2 &&
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
}
bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
#ifndef DISABLE_PARCHECK
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
{
info("Checking pars for %s", pPostInfo->GetInfoName());
m_eCurrentJob = jkParCheck;
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetParFilename(pPostInfo->GetParFilename());
m_ParChecker.SetInfoName(pPostInfo->GetInfoName());
pPostInfo->SetWorking(true);
m_ParChecker.Start();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
{
info("Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
m_eCurrentJob = jkParRename;
m_ParRenamer.SetPostInfo(pPostInfo);
m_ParRenamer.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
m_ParRenamer.Start();
}
bool ParCoordinator::Cancel()
{
if (m_eCurrentJob == jkParCheck)
{
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName());
m_ParChecker.Cancel();
return true;
}
#else
warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName());
#endif
}
else if (m_eCurrentJob == 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* pFileInfo, bool bDeleted)
{
bool bSameCollection = m_ParChecker.IsRunning() &&
pFileInfo->GetNZBInfo() == m_ParChecker.GetPostInfo()->GetNZBInfo() &&
SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(m_ParChecker.GetParFilename()));
if (bSameCollection && !bDeleted)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
szFullFilename[1024-1] = '\0';
m_ParChecker.AddParFile(szFullFilename);
if (g_pOptions->GetParPauseQueue())
{
PauseDownload();
}
}
else
{
m_ParChecker.QueueChanged();
}
return bSameCollection;
}
void ParCoordinator::ParCheckerUpdate(Subject* Caller, void* Aspect)
{
if (m_ParChecker.GetStatus() == ParChecker::psFinished ||
m_ParChecker.GetStatus() == ParChecker::psFailed)
{
char szPath[1024];
strncpy(szPath, m_ParChecker.GetParFilename(), 1024);
szPath[1024-1] = '\0';
if (char* p = strrchr(szPath, PATH_SEPARATOR)) *p = '\0';
if (g_pOptions->GetCreateBrokenLog())
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szPath, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (!m_ParChecker.GetRepairNotNeeded() || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
{
if (m_ParChecker.GetCancelled())
{
fprintf(file, "Repair cancelled for %s\n", m_ParChecker.GetInfoName());
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_ParChecker.GetInfoName(), m_ParChecker.GetErrMsg() ? m_ParChecker.GetErrMsg() : "");
}
}
else if (m_ParChecker.GetRepairNotNeeded())
{
fprintf(file, "Repair not needed for %s\n", m_ParChecker.GetInfoName());
}
else
{
if (g_pOptions->GetParRepair())
{
fprintf(file, "Successfully repaired %s\n", m_ParChecker.GetInfoName());
}
else
{
fprintf(file, "Repair possible for %s\n", m_ParChecker.GetInfoName());
}
}
fclose(file);
}
else
{
error("Could not open file %s", szBrokenLogName);
}
}
}
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
pPostInfo->SetWorking(false);
if (pPostInfo->GetDeleted())
{
pPostInfo->SetStage(PostInfo::ptFinished);
}
else
{
pPostInfo->SetStage(PostInfo::ptQueued);
}
// Update ParStatus by NZBInfo (accumulate result)
if (m_ParChecker.GetStatus() == ParChecker::psFailed && !m_ParChecker.GetCancelled())
{
pPostInfo->SetParStatus(PostInfo::psFailure);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
else if (m_ParChecker.GetStatus() == ParChecker::psFinished &&
(g_pOptions->GetParRepair() || m_ParChecker.GetRepairNotNeeded()))
{
pPostInfo->SetParStatus(PostInfo::psSuccess);
if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
}
else
{
pPostInfo->SetParStatus(PostInfo::psRepairPossible);
if (pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
}
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
Blocks blocks;
blocks.clear();
int iBlockFound = 0;
int iCurBlockFound = 0;
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, true, &iCurBlockFound);
iBlockFound += iCurBlockFound;
if (iBlockFound < iBlockNeeded)
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound < iBlockNeeded && !g_pOptions->GetStrictParName())
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, false, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound >= iBlockNeeded)
{
// 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 (iBlockNeeded > 0)
{
BlockInfo* pBestBlockInfo = NULL;
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
{
pBestBlockInfo = pBlockInfo;
}
}
if (pBestBlockInfo)
{
if (pBestBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
pBestBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
blocks.remove(pBestBlockInfo);
delete pBestBlockInfo;
}
else
{
break;
}
}
// 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 (iBlockNeeded > 0)
{
BlockInfo* pBlockInfo = blocks.front();
if (pBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
pBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
iBlockNeeded -= pBlockInfo->m_iBlockCount;
}
}
g_pQueueCoordinator->UnlockQueue();
if (pBlockFound)
{
*pBlockFound = iBlockFound;
}
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
delete *it;
}
blocks.clear();
bool bOK = iBlockNeeded <= 0;
if (bOK && g_pOptions->GetParPauseQueue())
{
UnpauseDownload();
}
return bOK;
}
void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound)
{
*pBlockFound = 0;
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* szBaseParFilename = Util::BaseFileName(szParFilename);
char szMainBaseFilename[1024];
int iMainBaseLen = 0;
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
{
// should not happen
error("Internal error: could not parse filename %s", szBaseParFilename);
return;
}
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
szMainBaseFilename[maxlen] = '\0';
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (pFileInfo->GetNZBInfo() == pNZBInfo &&
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
bool bUseFile = true;
if (bExactParName)
{
bUseFile = SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
}
else if (bStrictParName)
{
// the pFileInfo->GetFilename() may be not confirmed and may contain
// additional texts if Subject could not be parsed correctly
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
char szCandidateFileName[1024];
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
bUseFile = strstr(szLoFileName, szCandidateFileName);
}
}
bool bAlreadyAdded = false;
// check if file is not in the list already
if (bUseFile)
{
for (Blocks::iterator it = pBlocks->begin(); it != pBlocks->end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_pFileInfo == pFileInfo)
{
bAlreadyAdded = true;
break;
}
}
}
// if it is a par2-file with blocks and it was from the same NZB-request
// and it belongs to the same file collection (same base name),
// then OK, we can use it
if (bUseFile && !bAlreadyAdded)
{
BlockInfo* pBlockInfo = new BlockInfo();
pBlockInfo->m_pFileInfo = pFileInfo;
pBlockInfo->m_iBlockCount = iBlocks;
pBlocks->push_back(pBlockInfo);
*pBlockFound += iBlocks;
}
}
}
}
void ParCoordinator::UpdateParCheckProgress()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = pDownloadQueue->GetPostQueue()->front();
if (m_ParChecker.GetFileProgress() == 0)
{
pPostInfo->SetProgressLabel(m_ParChecker.GetProgressLabel());
}
pPostInfo->SetFileProgress(m_ParChecker.GetFileProgress());
pPostInfo->SetStageProgress(m_ParChecker.GetStageProgress());
PostInfo::EStage StageKind[] = { PostInfo::ptLoadingPars, PostInfo::ptVerifyingSources, PostInfo::ptRepairing, PostInfo::ptVerifyingRepaired };
PostInfo::EStage eStage = StageKind[m_ParChecker.GetStage()];
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != eStage)
{
pPostInfo->SetStage(eStage);
pPostInfo->SetStageTime(tCurrent);
}
bool bParCancel = false;
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
if ((g_pOptions->GetParTimeLimit() > 0) &&
m_ParChecker.GetStage() == ParChecker::ptRepairing &&
((g_pOptions->GetParTimeLimit() > 5 && tCurrent - pPostInfo->GetStageTime() > 5 * 60) ||
(g_pOptions->GetParTimeLimit() <= 5 && tCurrent - pPostInfo->GetStageTime() > 1 * 60)))
{
// first five (or one) minutes elapsed, now can check the estimated time
int iEstimatedRepairTime = (int)((tCurrent - pPostInfo->GetStartTime()) * 1000 /
(pPostInfo->GetStageProgress() > 0 ? pPostInfo->GetStageProgress() : 1));
if (iEstimatedRepairTime > g_pOptions->GetParTimeLimit() * 60)
{
debug("Estimated repair time %i seconds", iEstimatedRepairTime);
warn("Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
bParCancel = true;
}
}
}
#endif
if (bParCancel)
{
m_ParChecker.Cancel();
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
{
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = pPostInfo->GetStageTime();
time_t tStartTime = pPostInfo->GetStartTime();
time_t tWaitTime = time(NULL);
// wait until Post-processor is unpaused
while (g_pOptions->GetPausePostProcess() && !m_bStopped)
{
usleep(100 * 1000);
// update time stamps
time_t tDelta = time(NULL) - tWaitTime;
if (tStageTime > 0)
{
pPostInfo->SetStageTime(tStageTime + tDelta);
}
if (tStartTime > 0)
{
pPostInfo->SetStartTime(tStartTime + tDelta);
}
}
}
}
void ParCoordinator::ParRenamerUpdate(Subject* Caller, void* Aspect)
{
if (m_ParRenamer.GetStatus() == ParRenamer::psFinished ||
m_ParRenamer.GetStatus() == ParRenamer::psFailed)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->SetWorking(false);
if (pPostInfo->GetDeleted())
{
pPostInfo->SetStage(PostInfo::ptFinished);
}
else
{
pPostInfo->SetStage(PostInfo::ptQueued);
}
// Update ParStatus by NZBInfo
if (m_ParRenamer.GetStatus() == ParRenamer::psFailed && !m_ParRenamer.GetCancelled())
{
pPostInfo->SetRenameStatus(PostInfo::rsFailure);
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsFailure);
}
else if (m_ParRenamer.GetStatus() == ParRenamer::psFinished)
{
pPostInfo->SetRenameStatus(PostInfo::rsSuccess);
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsSuccess);
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
}
void ParCoordinator::UpdateParRenameProgress()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = pDownloadQueue->GetPostQueue()->front();
pPostInfo->SetProgressLabel(m_ParRenamer.GetProgressLabel());
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != PostInfo::ptRenaming)
{
pPostInfo->SetStage(PostInfo::ptRenaming);
pPostInfo->SetStageTime(tCurrent);
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
#endif

143
ParCoordinator.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARCOORDINATOR_H
#define PARCOORDINATOR_H
#include <list>
#include <deque>
#include "Observer.h"
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#include "ParRenamer.h"
#endif
class ParCoordinator
{
private:
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
{
public:
ParCoordinator* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
class ParRenamerObserver: public Observer
{
public:
ParCoordinator* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->ParRenamerUpdate(Caller, Aspect); }
};
class PostParRenamer: public ParRenamer
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual void UpdateProgress();
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
enum EJobKind
{
jkParCheck,
jkParRename
};
private:
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
bool m_bStopped;
bool m_bPostScript;
PostParRenamer m_ParRenamer;
ParRenamerObserver m_ParRenamerObserver;
EJobKind m_eCurrentJob;
protected:
virtual bool PauseDownload() = 0;
virtual bool UnpauseDownload() = 0;
#endif
public:
typedef std::deque<char*> FileList;
public:
ParCoordinator();
virtual ~ParCoordinator();
static bool FindMainPars(const char* szPath, FileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK
void ParCheckerUpdate(Subject* Caller, void* Aspect);
void ParRenamerUpdate(Subject* Caller, void* Aspect);
void CheckPauseState(PostInfo* pPostInfo);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void StartParCheckJob(PostInfo* pPostInfo);
void StartParRenameJob(PostInfo* pPostInfo);
void Stop();
bool Cancel();
#endif
};
#endif

323
ParRenamer.cpp Normal file
View File

@@ -0,0 +1,323 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#ifndef DISABLE_PARCHECK
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#include <md5.h>
#else
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#include <libpar2/md5.h>
#endif
#include "nzbget.h"
#include "ParRenamer.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
class ParRenamerRepairer : public Par2Repairer
{
public:
friend class ParRenamer;
};
ParRenamer::FileHash::FileHash(const char* szFilename, const char* szHash)
{
m_szFilename = strdup(szFilename);
m_szHash = strdup(szHash);
}
ParRenamer::FileHash::~FileHash()
{
free(m_szFilename);
free(m_szHash);
}
ParRenamer::ParRenamer()
{
debug("Creating ParRenamer");
m_eStatus = psUnknown;
m_szDestDir = NULL;
m_szInfoName = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_iStageProgress = 0;
m_bCancelled = false;
}
ParRenamer::~ParRenamer()
{
debug("Destroying ParRenamer");
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
free(m_szProgressLabel);
Cleanup();
}
void ParRenamer::Cleanup()
{
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
delete *it;
}
m_fileHashList.clear();
}
void ParRenamer::SetDestDir(const char * szDestDir)
{
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void ParRenamer::SetInfoName(const char * szInfoName)
{
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
void ParRenamer::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParRenamer::Cancel()
{
m_bCancelled = true;
}
void ParRenamer::Run()
{
Cleanup();
m_bCancelled = false;
m_iRenamedCount = 0;
SetStatus(psUnknown);
snprintf(m_szProgressLabel, 1024, "Checking renamed files for %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = 0;
UpdateProgress();
LoadParFiles();
CheckFiles();
if (m_bCancelled)
{
warn("Renaming cancelled for %s", m_szInfoName);
SetStatus(psFailed);
}
else if (m_iRenamedCount > 0)
{
info("Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
SetStatus(psFinished);
}
else
{
info("Could not rename any files for %s", m_szInfoName);
SetStatus(psFailed);
}
Cleanup();
}
void ParRenamer::LoadParFiles()
{
ParCoordinator::FileList parFileList;
ParCoordinator::FindMainPars(m_szDestDir, &parFileList);
for (ParCoordinator::FileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
{
char* szParFilename = *it;
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
LoadParFile(szFullParFilename);
free(*it);
}
}
void ParRenamer::LoadParFile(const char* szParFilename)
{
ParRenamerRepairer* pRepairer = new ParRenamerRepairer();
if (!pRepairer->LoadPacketsFromFile(szParFilename))
{
warn("Could not load par2-file %s", szParFilename);
delete pRepairer;
return;
}
for (map<MD5Hash, Par2RepairerSourceFile*>::iterator it = pRepairer->sourcefilemap.begin(); it != pRepairer->sourcefilemap.end(); it++)
{
if (m_bCancelled)
{
break;
}
Par2RepairerSourceFile* sourceFile = (*it).second;
m_fileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
sourceFile->GetDescriptionPacket()->Hash16k().print().c_str()));
}
delete pRepairer;
}
void ParRenamer::CheckFiles()
{
int iFileCount = 0;
DirBrowser dir2(m_szDestDir);
while (const char* filename = dir2.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
iFileCount++;
}
}
int iCurFile = 0;
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = iCurFile * 1000 / iFileCount;
UpdateProgress();
iCurFile++;
CheckFile(szFullFilename);
}
}
}
void ParRenamer::CheckFile(const char* szFilename)
{
debug("Computing hash for %s", szFilename);
const int iBlockSize = 16*1024;
FILE* pFile = fopen(szFilename, "rb");
if (!pFile)
{
error("Could not open file %s", szFilename);
return;
}
// load first 16K of the file into buffer
void* pBuffer = malloc(iBlockSize);
int iReadBytes = fread(pBuffer, 1, iBlockSize, pFile);
int iError = ferror(pFile);
if (iReadBytes != iBlockSize && iError)
{
error("Could not read file %s", szFilename);
return;
}
fclose(pFile);
MD5Hash hash16k;
MD5Context context;
context.Update(pBuffer, iReadBytes);
context.Final(hash16k);
free(pBuffer);
debug("file: %s; hash16k: %s", Util::BaseFileName(szFilename), hash16k.print().c_str());
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
FileHash* pFileHash = *it;
if (!strcmp(pFileHash->GetHash(), hash16k.print().c_str()))
{
debug("Found correct filename: %s", pFileHash->GetFilename());
char szDstFilename[1024];
snprintf(szDstFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
szDstFilename[1024-1] = '\0';
if (!Util::FileExists(szDstFilename))
{
info("Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
if (Util::MoveFile(szFilename, szDstFilename))
{
m_iRenamedCount++;
}
else
{
error("Could not rename %s to %s", szFilename, szDstFilename);
}
}
break;
}
}
}
#endif

97
ParRenamer.h Normal file
View File

@@ -0,0 +1,97 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARRENAMER_H
#define PARRENAMER_H
#ifndef DISABLE_PARCHECK
#include <deque>
#include "Thread.h"
#include "Observer.h"
class ParRenamer : public Thread, public Subject
{
public:
enum EStatus
{
psUnknown,
psFailed,
psFinished
};
class FileHash
{
private:
char* m_szFilename;
char* m_szHash;
public:
FileHash(const char* szFilename, const char* szHash);
~FileHash();
const char* GetFilename() { return m_szFilename; }
const char* GetHash() { return m_szHash; }
};
typedef std::deque<FileHash*> FileHashList;
private:
char* m_szInfoName;
char* m_szDestDir;
EStatus m_eStatus;
char* m_szProgressLabel;
int m_iStageProgress;
bool m_bCancelled;
FileHashList m_fileHashList;
int m_iRenamedCount;
void Cleanup();
void LoadParFiles();
void LoadParFile(const char* szParFilename);
void CheckFiles();
void CheckFile(const char* szFilename);
protected:
virtual void UpdateProgress() {}
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParRenamer();
virtual ~ParRenamer();
virtual void Run();
void SetDestDir(const char* szDestDir);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
void Cancel();
bool GetCancelled() { return m_bCancelled; }
};
#endif
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -31,75 +31,82 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#endif
#include "Scanner.h"
#include "ParCoordinator.h"
class PrePostProcessor : public Thread
{
public:
class ParJob
enum EEditAction
{
private:
char* m_szNZBFilename;
char* m_szParFilename;
char* m_szInfoName;
public:
ParJob(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
~ParJob();
const char* GetNZBFilename() { return m_szNZBFilename; }
const char* GetParFilename() { return m_szParFilename; }
const char* GetInfoName() { return m_szInfoName; }
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
eaPostMoveTop,
eaPostMoveBottom,
eaPostDelete,
eaHistoryDelete,
eaHistoryReturn,
eaHistoryProcess
};
typedef std::deque<ParJob*> ParQueue;
private:
typedef std::deque<char*> FileList;
class QueueCoordinatorObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
PrePostProcessor* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->QueueCoordinatorUpdate(Caller, Aspect); }
};
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
class PostParCoordinator: public ParCoordinator
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
private:
PrePostProcessor* m_pOwner;
protected:
virtual bool PauseDownload() { return m_pOwner->PauseDownload(); }
virtual bool UnpauseDownload() { return m_pOwner->UnpauseDownload(); }
friend class PrePostProcessor;
};
#endif
private:
PostParCoordinator m_ParCoordinator;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
Scanner m_Scanner;
const char* m_szPauseReason;
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
void CheckIncomingNZBs();
bool WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, bool bIgnorePaused);
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, int iParStatus);
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartProcessJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
void ApplySchedulerState();
void CheckScheduledResume();
void UpdatePauseState(bool bNeedPause, const char* szReason);
bool PauseDownload();
bool UnpauseDownload();
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bUnpackOrScript, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
bool PostQueueDelete(IDList* pIDList);
bool HistoryDelete(IDList* pIDList);
bool HistoryReturn(IDList* pIDList, bool bReprocess);
void Cleanup();
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void CheckHistory();
void DeletePostThread(PostInfo* pPostInfo);
Mutex m_mutexParChecker;
ParQueue m_ParQueue;
#ifndef DISABLE_PARCHECK
ParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
void ParCheckerUpdate(Subject* Caller, void* Aspect);
void CheckParQueue();
void CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
bool FindMainPars(const char* szPath, FileList* pFileList);
#endif
public:
PrePostProcessor();
virtual ~PrePostProcessor();
@@ -107,8 +114,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
ParQueue* LockParQueue();
void UnlockParQueue();
void ScanNZBDir(bool bSyncMode);
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
@@ -59,14 +60,7 @@ QueueCoordinator::QueueCoordinator()
debug("Creating QueueCoordinator");
m_bHasMoreJobs = true;
m_DownloadQueue.clear();
m_ActiveDownloads.clear();
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
{
m_iSpeedBytes[i] = 0;
}
m_iSpeedBytesIndex = 0;
ResetSpeedStat();
m_iAllBytes = 0;
m_tStartServer = 0;
@@ -83,11 +77,11 @@ QueueCoordinator::~QueueCoordinator()
// Cleanup
debug("Deleting DownloadQueue");
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
delete *it;
}
m_DownloadQueue.clear();
m_DownloadQueue.GetFileQueue()->clear();
debug("Deleting ArticleDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
@@ -107,15 +101,15 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Lock();
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->Exists())
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
{
if (g_pOptions->GetReloadQueue())
{
g_pDiskState->Load(&m_DownloadQueue);
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
}
else
{
g_pDiskState->Discard();
g_pDiskState->DiscardDownloadQueue();
}
}
@@ -124,15 +118,16 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Unlock();
m_tStartServer = time(NULL);
m_tLastCheck = m_tStartServer;
bool bWasStandBy = true;
bool bArticeDownloadsRunning = false;
int iResetCounter = 0;
while (!IsStopped())
{
if (!g_pOptions->GetPause())
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, NULL, NULL);
if (pConnection)
{
// start download for next article
@@ -173,15 +168,19 @@ void QueueCoordinator::Run()
int iSleepInterval = bStandBy ? 100 : 5;
usleep(iSleepInterval * 1000);
AddSpeedReading(0);
if (!bStandBy)
{
AddSpeedReading(0);
}
iResetCounter+= iSleepInterval;
iResetCounter += iSleepInterval;
if (iResetCounter >= 1000)
{
// this code should not be called too often, once per second is OK
g_pServerPool->CloseUnusedConnections();
ResetHangingDownloads();
iResetCounter = 0;
AdjustStartTime();
}
}
@@ -207,13 +206,15 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
m_mutexDownloadQueue.Lock();
DownloadQueue tmpDownloadQueue;
tmpDownloadQueue.clear();
DownloadQueue DupeList;
FileQueue tmpFileQueue;
tmpFileQueue.clear();
FileQueue DupeList;
DupeList.clear();
int index1 = 0;
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
index1++;
FileInfo* pFileInfo = *it;
if (g_pOptions->GetDupeCheck())
@@ -224,11 +225,15 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
dupe = true;
}
int index2 = 0;
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
{
index2++;
FileInfo* pFileInfo2 = *it2;
if (!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
(pFileInfo->GetSize() < pFileInfo2->GetSize()))
if (pFileInfo != pFileInfo2 &&
!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
(pFileInfo->GetSize() < pFileInfo2->GetSize() ||
(pFileInfo->GetSize() == pFileInfo2->GetSize() && index2 < index1)))
{
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
dupe = true;
@@ -244,116 +249,147 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
if (bAddFirst)
{
tmpDownloadQueue.push_front(pFileInfo);
tmpFileQueue.push_front(pFileInfo);
}
else
{
tmpDownloadQueue.push_back(pFileInfo);
tmpFileQueue.push_back(pFileInfo);
}
}
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
for (FileQueue::iterator it = tmpFileQueue.begin(); it != tmpFileQueue.end(); it++)
{
if (bAddFirst)
{
m_DownloadQueue.push_front(*it);
m_DownloadQueue.GetFileQueue()->push_front(*it);
}
else
{
m_DownloadQueue.push_back(*it);
m_DownloadQueue.GetFileQueue()->push_back(*it);
}
}
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
for (FileQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
{
FileInfo* pFileInfo = *it;
g_pDiskState->DiscardFile(NULL, pFileInfo);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(pFileInfo);
}
delete pFileInfo;
}
m_DownloadQueue.GetNZBInfoList()->Add(pNZBFile->GetNZBInfo());
pNZBFile->DetachFileInfos();
Aspect aspect = { eaNZBFileAdded, NULL, &m_DownloadQueue, pNZBFile->GetFileName() };
Aspect aspect = { eaNZBFileAdded, &m_DownloadQueue, pNZBFile->GetNZBInfo(), NULL };
Notify(&aspect);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->Save(&m_DownloadQueue);
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
}
bool QueueCoordinator::AddFileToQueue(const char* szFileName)
{
// Parse the buffer and make it into a NZBFile
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFileName);
// Did file parse correctly?
if (!pNZBFile)
{
return false;
}
// Add NZBFile to Queue
AddNZBFileToQueue(pNZBFile, false);
delete pNZBFile;
return true;
}
/*
* NOTE: see note to "AddSpeedReading"
*/
float QueueCoordinator::CalcCurrentDownloadSpeed()
int QueueCoordinator::CalcCurrentDownloadSpeed()
{
int iTotal = 0;
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
if (m_bStandBy)
{
iTotal += m_iSpeedBytes[i];
return 0;
}
float fSpeed = iTotal / 1024.0 / SPEEDMETER_SECONDS;
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
if (iTimeDiff == 0)
{
return 0;
}
return fSpeed;
return m_iSpeedTotalBytes / iTimeDiff;
}
/*
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
* but this would results in a big performance loss (the function
* "AddSpeedReading" is called extremly often), so we better agree with calculation
* errors possible because of simultaneuos access from several threads.
* The used algorithm is able to recover after few seconds.
* In any case the calculation errors can not result in fatal system
* errors (segmentation faults).
*/
void QueueCoordinator::AddSpeedReading(int iBytes)
{
int iIndex = time(NULL);
time_t tCurTime = time(NULL);
int iNowSlot = (int)tCurTime / SPEEDMETER_SLOTSIZE;
if (iIndex - m_iSpeedBytesIndex > SPEEDMETER_SECONDS)
if (g_pOptions->GetAccurateRate())
{
m_iSpeedBytesIndex = iIndex - SPEEDMETER_SECONDS - 1;
#ifdef HAVE_SPINLOCK
m_spinlockSpeed.Lock();
#else
m_mutexSpeed.Lock();
#endif
}
for (int i = m_iSpeedBytesIndex + 1; i < iIndex; i++)
while (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
{
m_iSpeedBytes[i % SPEEDMETER_SECONDS] = 0;
//record bytes in next slot
m_iSpeedBytesIndex++;
if (m_iSpeedBytesIndex >= SPEEDMETER_SLOTS)
{
m_iSpeedBytesIndex = 0;
}
//Adjust counters with outgoing information.
m_iSpeedTotalBytes -= m_iSpeedBytes[m_iSpeedBytesIndex];
//Note we should really use the start time of the next slot
//but its easier to just use the outgoing slot time. This
//will result in a small error.
m_iSpeedStartTime = m_iSpeedTime[m_iSpeedBytesIndex];
//Now reset.
m_iSpeedBytes[m_iSpeedBytesIndex] = 0;
m_iSpeedTime[m_iSpeedBytesIndex] = iNowSlot;
}
if (iIndex > m_iSpeedBytesIndex)
// Once per second recalculate summary field "m_iSpeedTotalBytes" to recover from possible synchronisation errors
if (tCurTime > m_tSpeedCorrection)
{
m_iSpeedBytesIndex = iIndex;
m_iSpeedBytes[iIndex % SPEEDMETER_SECONDS] = iBytes;
}
else
{
m_iSpeedBytes[m_iSpeedBytesIndex % SPEEDMETER_SECONDS] += iBytes;
int iSpeedTotalBytes = 0;
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
{
iSpeedTotalBytes += m_iSpeedBytes[i];
}
m_iSpeedTotalBytes = iSpeedTotalBytes;
m_tSpeedCorrection = tCurTime;
}
if (m_iSpeedTotalBytes == 0)
{
m_iSpeedStartTime = iNowSlot;
}
m_iSpeedBytes[m_iSpeedBytesIndex] += iBytes;
m_iSpeedTotalBytes += iBytes;
m_iAllBytes += iBytes;
if (g_pOptions->GetAccurateRate())
{
#ifdef HAVE_SPINLOCK
m_spinlockSpeed.Unlock();
#else
m_mutexSpeed.Unlock();
#endif
}
}
void QueueCoordinator::ResetSpeedStat()
{
time_t tCurTime = time(NULL);
m_iSpeedStartTime = (int)tCurTime / SPEEDMETER_SLOTSIZE;
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
{
m_iSpeedBytes[i] = 0;
m_iSpeedTime[i] = m_iSpeedStartTime;
}
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
m_tSpeedCorrection = tCurTime;
}
long long QueueCoordinator::CalcRemainingSize()
@@ -361,7 +397,7 @@ long long QueueCoordinator::CalcRemainingSize()
long long lRemainingSize = 0;
m_mutexDownloadQueue.Lock();
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
@@ -394,7 +430,7 @@ bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
}
if (!hasDownloads)
{
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
Aspect aspect = { eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
DeleteFileInfo(pFileInfo, false);
@@ -416,31 +452,90 @@ void QueueCoordinator::Stop()
debug("ArticleDownloads are notified");
}
/*
* Returns next article for download.
*/
bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo)
{
// find an unpaused file with the highest priority, then take the next article from the file.
// if the file doesn't have any articles left for download, we store that fact and search again,
// ignoring all files which were previously marked as not having any articles.
// special case: if the file has ExtraPriority-flag set, it has the highest priority and the
// Paused-flag is ignored.
//debug("QueueCoordinator::GetNextArticle()");
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
bool bOK = false;
// pCheckedFiles stores
bool* pCheckedFiles = NULL;
while (!bOK)
{
pFileInfo = *it;
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
//debug("QueueCoordinator::GetNextArticle() - in loop");
pFileInfo = NULL;
int iNum = 0;
int iFileNum = 0;
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
FileInfo* pFileInfo1 = *it;
if ((!pCheckedFiles || !pCheckedFiles[iNum]) &&
!pFileInfo1->GetPaused() && !pFileInfo1->GetDeleted() &&
(!pFileInfo ||
(pFileInfo1->GetExtraPriority() == pFileInfo->GetExtraPriority() &&
pFileInfo1->GetPriority() > pFileInfo->GetPriority()) ||
(pFileInfo1->GetExtraPriority() > pFileInfo->GetExtraPriority())))
{
g_pDiskState->LoadArticles(pFileInfo);
pFileInfo = pFileInfo1;
iFileNum = iNum;
}
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
iNum++;
}
if (!pFileInfo)
{
// there are no more files for download
break;
}
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->LoadArticles(pFileInfo);
}
// check if the file has any articles left for download
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
{
pArticleInfo = *at;
if (pArticleInfo->GetStatus() == 0)
{
pArticleInfo = *at;
if (pArticleInfo->GetStatus() == 0)
{
return true;
}
bOK = true;
break;
}
}
if (!bOK)
{
// the file doesn't have any articles left for download, we mark the file as such
if (!pCheckedFiles)
{
int iArrSize = sizeof(bool) * m_DownloadQueue.GetFileQueue()->size();
pCheckedFiles = (bool*)malloc(iArrSize);
memset(pCheckedFiles, false, iArrSize);
}
pCheckedFiles[iFileNum] = true;
}
}
return false;
if (pCheckedFiles)
{
free(pCheckedFiles);
}
return bOK;
}
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
@@ -456,6 +551,7 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() + 1);
m_ActiveDownloads.push_back(pArticleDownloader);
pArticleDownloader->Start();
@@ -474,17 +570,28 @@ void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloade
tmpname[1024-1] = '\0';
pArticleDownloader->SetTempFilename(tmpname);
char szNZBNiceName[1024];
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
snprintf(name, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
name[1024-1] = '\0';
pArticleDownloader->SetInfoName(name);
if (g_pOptions->GetDirectWrite())
{
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
pFileInfo->LockOutputFile();
if (pFileInfo->GetOutputFilename())
{
strncpy(name, pFileInfo->GetOutputFilename(), 1024);
name[1024-1] = '\0';
}
else
{
snprintf(name, 1024, "%s%c%i.out.tmp", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetID());
name[1024-1] = '\0';
pFileInfo->SetOutputFilename(name);
}
pFileInfo->UnlockOutputFile();
pArticleDownloader->SetOutputFilename(name);
}
}
@@ -506,7 +613,8 @@ void QueueCoordinator::Update(Subject* Caller, void* Aspect)
ArticleDownloader* pArticleDownloader = (ArticleDownloader*) Caller;
if ((pArticleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed))
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed) ||
(pArticleDownloader->GetStatus() == ArticleDownloader::adRetry))
{
ArticleCompleted(pArticleDownloader);
}
@@ -518,6 +626,8 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
bool bPaused = false;
bool fileCompleted = false;
m_mutexDownloadQueue.Lock();
@@ -529,10 +639,18 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
}
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adRetry)
{
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
bPaused = true;
}
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
if (!bPaused)
{
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
}
if (!pFileInfo->GetFilenameConfirmed() &&
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
@@ -586,15 +704,23 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
break;
}
}
pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() - 1);
if (deleteFileObj)
{
bool fileDeleted = pFileInfo->GetDeleted();
// delete File from Queue
pFileInfo->SetDeleted(true);
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
DeleteFileInfo(pFileInfo, fileCompleted);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
}
m_mutexDownloadQueue.Unlock();
@@ -602,49 +728,51 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
{
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
FileInfo* pa = *it;
if (pa == pFileInfo)
{
m_DownloadQueue.erase(it);
m_DownloadQueue.GetFileQueue()->erase(it);
break;
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(&m_DownloadQueue, pFileInfo);
g_pDiskState->DiscardFile(pFileInfo);
}
if (!bCompleted)
{
// deleting temporary files
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
{
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pa = *it;
if (pa->GetResultFilename())
{
remove(pa->GetResultFilename());
}
}
}
if (g_pOptions->GetDirectWrite())
{
char name[1024];
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
remove(name);
}
DiscardDiskFile(pFileInfo);
}
delete pFileInfo;
}
void QueueCoordinator::DiscardDiskFile(FileInfo* pFileInfo)
{
// deleting temporary files
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
{
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pa = *it;
if (pa->GetResultFilename())
{
remove(pa->GetResultFilename());
}
}
}
if (g_pOptions->GetDirectWrite() && pFileInfo->GetOutputFilename())
{
remove(pFileInfo->GetOutputFilename());
}
}
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
{
debug("Checking if the file is already queued");
@@ -656,10 +784,10 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
}
// checking in queue
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
FileInfo* pQueueEntry = *it;
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
pFileInfo != pQueueEntry)
{
@@ -673,9 +801,25 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
void QueueCoordinator::LogDebugInfo()
{
debug("--------------------------------------------");
debug("Dumping debug info to log");
debug("Dumping debug debug to log");
debug("--------------------------------------------");
debug(" SpeedMeter");
debug(" ----------");
float fSpeed = (float)(CalcCurrentDownloadSpeed() / 1024.0);
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
debug(" Speed: %f", fSpeed);
debug(" SpeedStartTime: %i", m_iSpeedStartTime);
debug(" SpeedTotalBytes: %i", m_iSpeedTotalBytes);
debug(" SpeedBytesIndex: %i", m_iSpeedBytesIndex);
debug(" AllBytes: %i", m_iAllBytes);
debug(" Time: %i", (int)time(NULL));
debug(" TimeDiff: %i", iTimeDiff);
for (int i=0; i < SPEEDMETER_SLOTS; i++)
{
debug(" Bytes[%i]: %i, Time[%i]: %i", i, m_iSpeedBytes[i], i, m_iSpeedTime[i]);
}
debug(" QueueCoordinator");
debug(" ----------------");
@@ -695,8 +839,7 @@ void QueueCoordinator::LogDebugInfo()
void QueueCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
if (g_pOptions->GetTerminateTimeout() == 0 && g_pOptions->GetConnectionTimeout() == 0)
{
return;
}
@@ -707,7 +850,15 @@ void QueueCoordinator::ResetHangingDownloads()
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
ArticleDownloader* pArticleDownloader = *it;
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetConnectionTimeout() + 1 &&
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
{
error("Cancelling hanging download %s", pArticleDownloader->GetInfoName());
pArticleDownloader->Stop();
}
if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetTerminateTimeout() &&
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
{
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
@@ -719,9 +870,10 @@ void QueueCoordinator::ResetHangingDownloads()
}
else
{
error("Could not terminate hanging download %s", BaseFileName(pArticleInfo->GetResultFilename()));
error("Could not terminate hanging download %s", Util::BaseFileName(pArticleInfo->GetResultFilename()));
}
m_ActiveDownloads.erase(it);
pArticleDownloader->GetFileInfo()->SetActiveDownloads(pArticleDownloader->GetFileInfo()->GetActiveDownloads() - 1);
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
delete pArticleDownloader;
it = m_ActiveDownloads.begin();
@@ -752,6 +904,7 @@ void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
m_tStartDownload += time(NULL) - m_tPausedFrom;
}
m_tPausedFrom = 0;
ResetSpeedStat();
}
m_mutexStat.Unlock();
}
@@ -761,7 +914,7 @@ void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAl
m_mutexStat.Lock();
if (m_tStartServer > 0)
{
*iUpTimeSec = time(NULL) - m_tStartServer;
*iUpTimeSec = (int)(time(NULL) - m_tStartServer);
}
else
{
@@ -770,12 +923,128 @@ void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAl
*bStandBy = m_bStandBy;
if (m_bStandBy)
{
*iDnTimeSec = m_tPausedFrom - m_tStartDownload;
*iDnTimeSec = (int)(m_tPausedFrom - m_tStartDownload);
}
else
{
*iDnTimeSec = time(NULL) - m_tStartDownload;
*iDnTimeSec = (int)(time(NULL) - m_tStartDownload);
}
*iAllBytes = m_iAllBytes;
m_mutexStat.Unlock();
}
/*
* Detects large step changes of system time and adjust statistics.
*/
void QueueCoordinator::AdjustStartTime()
{
time_t m_tCurTime = time(NULL);
time_t tDiff = m_tCurTime - m_tLastCheck;
if (tDiff > 60 || tDiff < 0)
{
m_tStartServer += tDiff + 1; // "1" because the method is called once per second
if (m_tStartDownload != 0)
{
m_tStartDownload += tDiff + 1;
}
}
m_tLastCheck = m_tCurTime;
}
bool QueueCoordinator::SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
{
if (pNZBInfo->GetPostProcess())
{
error("Could not change category for %s. File in post-process-stage", pNZBInfo->GetName());
return false;
}
char szOldDestDir[1024];
strncpy(szOldDestDir, pNZBInfo->GetDestDir(), 1024);
szOldDestDir[1024-1] = '\0';
pNZBInfo->SetCategory(szCategory);
pNZBInfo->BuildDestDirName();
bool bDirUnchanged = !strcmp(pNZBInfo->GetDestDir(), szOldDestDir);
bool bOK = bDirUnchanged || ArticleDownloader::MoveCompletedFiles(pNZBInfo, szOldDestDir);
return bOK;
}
bool QueueCoordinator::SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName)
{
if (pNZBInfo->GetPostProcess())
{
error("Could not rename %s. File in post-process-stage", pNZBInfo->GetName());
return false;
}
if (strlen(szName) == 0)
{
error("Could not rename %s. The new name cannot be empty", pNZBInfo->GetName());
return false;
}
char szOldDestDir[1024];
strncpy(szOldDestDir, pNZBInfo->GetDestDir(), 1024);
szOldDestDir[1024-1] = '\0';
char szNZBNicename[1024];
NZBInfo::MakeNiceNZBName(szName, szNZBNicename, sizeof(szNZBNicename), false);
pNZBInfo->SetName(szNZBNicename);
pNZBInfo->BuildDestDirName();
bool bDirUnchanged = !strcmp(pNZBInfo->GetDestDir(), szOldDestDir);
bool bOK = bDirUnchanged || ArticleDownloader::MoveCompletedFiles(pNZBInfo, szOldDestDir);
return bOK;
}
/*
* NOTE: DownloadQueue must be locked prior to call of this function
*/
bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo)
{
if (pDestNZBInfo->GetPostProcess() || pSrcNZBInfo->GetPostProcess())
{
error("Could not merge %s and %s. File in post-process-stage", pDestNZBInfo->GetName(), pSrcNZBInfo->GetName());
return false;
}
// set new dest directory, new category and move downloaded files to new dest directory
pSrcNZBInfo->SetFilename(pSrcNZBInfo->GetFilename());
SetQueueEntryNZBCategory(pSrcNZBInfo, pDestNZBInfo->GetCategory());
// reattach file items to new NZBInfo-object
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pSrcNZBInfo)
{
pFileInfo->SetNZBInfo(pDestNZBInfo);
}
}
pDestNZBInfo->SetFileCount(pDestNZBInfo->GetFileCount() + pSrcNZBInfo->GetFileCount());
pDestNZBInfo->SetSize(pDestNZBInfo->GetSize() + pSrcNZBInfo->GetSize());
// reattach completed file items to new NZBInfo-object
for (NZBInfo::Files::iterator it = pSrcNZBInfo->GetCompletedFiles()->begin(); it != pSrcNZBInfo->GetCompletedFiles()->end(); it++)
{
char* szFileName = *it;
pDestNZBInfo->GetCompletedFiles()->push_back(szFileName);
}
pSrcNZBInfo->GetCompletedFiles()->clear();
// concatenate QueuedFilenames using character '|' as separator
int iLen = strlen(pDestNZBInfo->GetQueuedFilename()) + strlen(pSrcNZBInfo->GetQueuedFilename()) + 1;
char* szQueuedFilename = (char*)malloc(iLen);
snprintf(szQueuedFilename, iLen, "%s|%s", pDestNZBInfo->GetQueuedFilename(), pSrcNZBInfo->GetQueuedFilename());
szQueuedFilename[iLen - 1] = '\0';
pDestNZBInfo->SetQueuedFilename(szQueuedFilename);
free(szQueuedFilename);
return true;
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -30,9 +30,6 @@
#include <deque>
#include <list>
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#include "Thread.h"
#include "NZBFile.h"
@@ -42,22 +39,22 @@
#include "QueueEditor.h"
#include "NNTPConnection.h"
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter, public DownloadQueueHolder
{
public:
typedef std::list<ArticleDownloader*> ActiveDownloads;
typedef enum EAspectAction
enum EAspectAction
{
eaNZBFileAdded,
eaFileCompleted,
eaFileDeleted
};
typedef struct Aspect
struct Aspect
{
EAspectAction eAction;
FileInfo* pFileInfo;
DownloadQueue* pDownloadQueue;
const char* szNZBFilename;
NZBInfo* pNZBInfo;
FileInfo* pFileInfo;
};
private:
@@ -68,14 +65,25 @@ private:
bool m_bHasMoreJobs;
// statistics
static const int SPEEDMETER_SECONDS = 5;
int m_iSpeedBytes[SPEEDMETER_SECONDS];
static const int SPEEDMETER_SLOTS = 30;
static const int SPEEDMETER_SLOTSIZE = 1; //Split elapsed time into this number of secs.
int m_iSpeedBytes[SPEEDMETER_SLOTS];
int m_iSpeedTotalBytes;
int m_iSpeedTime[SPEEDMETER_SLOTS];
int m_iSpeedStartTime;
time_t m_tSpeedCorrection;
#ifdef HAVE_SPINLOCK
SpinLock m_spinlockSpeed;
#else
Mutex m_mutexSpeed;
#endif
int m_iSpeedBytesIndex;
long long m_iAllBytes;
time_t m_tStartServer;
time_t m_tLastCheck;
time_t m_tStartDownload;
time_t m_tPausedFrom;
void EnterLeaveStandBy(bool bEnter);
bool m_bStandBy;
Mutex m_mutexStat;
@@ -86,6 +94,9 @@ private:
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
void AdjustStartTime();
public:
QueueCoordinator();
@@ -96,19 +107,23 @@ public:
// statistics
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
virtual int CalcCurrentDownloadSpeed();
virtual void AddSpeedReading(int iBytes);
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
// Editing the queue
DownloadQueue* LockQueue();
void UnlockQueue() ;
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
bool AddFileToQueue(const char* szFileName);
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
void DiscardDiskFile(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
void LogDebugInfo();
};

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -33,6 +33,10 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#include <set>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
@@ -71,7 +75,7 @@ QueueEditor::~QueueEditor()
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetID() == iID)
@@ -85,7 +89,7 @@ FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
{
int iEntry = 0;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (pFileInfo2 == pFileInfo)
@@ -107,7 +111,7 @@ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
}
/*
* Removes entry with index iEntry
* Removes entry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
@@ -117,7 +121,7 @@ void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
}
/*
* Moves entry identified with iID in the queue
* Moves entry in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
@@ -131,53 +135,83 @@ void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
{
iNewEntry = 0;
}
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
if ((unsigned int)iNewEntry > pDownloadQueue->GetFileQueue()->size() - 1)
{
iNewEntry = (int)pDownloadQueue->size() - 1;
iNewEntry = (int)pDownloadQueue->GetFileQueue()->size() - 1;
}
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->GetFileQueue()->size() - 1)
{
FileInfo* fi = (*pDownloadQueue)[iEntry];
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
FileInfo* fi = pDownloadQueue->GetFileQueue()->at(iEntry);
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iEntry);
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iNewEntry, fi);
}
}
}
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
/*
* Set priority for entry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority)
{
debug("Setting priority %s for file %s", szPriority, pFileInfo->GetFilename());
int iPriority = atoi(szPriority);
pFileInfo->SetPriority(iPriority);
}
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
return EditList(&cIDList, NULL, mmID, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder,
EEditAction eAction, int iOffset, const char* szText)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
bool bOK = true;
if (pNameList)
{
pIDList = new IDList();
bOK = BuildIDListFromNameList(pDownloadQueue, pIDList, pNameList, eMatchMode, eAction);
}
bOK = bOK && (InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText) || eMatchMode == mmRegEx);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->Save(pDownloadQueue);
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
if (pNameList)
{
delete pIDList;
}
return bOK;
}
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
bool QueueEditor::LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
return InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
if (eAction == eaGroupMoveOffset)
{
@@ -191,6 +225,14 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
{
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
}
else if (eAction == eaGroupMerge)
{
MergeGroups(pDownloadQueue, &cItemList);
}
else if (eAction == eaFileReorder)
{
ReorderFiles(pDownloadQueue, &cItemList);
}
else
{
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
@@ -216,9 +258,20 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
DeleteEntry(pItem->m_pFileInfo);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
// remove compiler warning "enumeration not handled in switch"
case eaFileSetPriority:
SetPriorityEntry(pItem->m_pFileInfo, szText);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetName:
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
@@ -229,7 +282,15 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
case eaGroupSetPriority:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
@@ -261,18 +322,18 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
if (iOffset < 0)
{
iStart = 0;
iEnd = pDownloadQueue->size();
iEnd = pDownloadQueue->GetFileQueue()->size();
iStep = 1;
}
else
{
iStart = pDownloadQueue->size() - 1;
iStart = pDownloadQueue->GetFileQueue()->size() - 1;
iEnd = -1;
iStep = -1;
}
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
{
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iIndex);
int iID = pFileInfo->GetID();
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
{
@@ -286,9 +347,9 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
{
iWorkOffset = -iIndex;
}
else if (iDestPos > int(pDownloadQueue->size()) - 1)
else if (iDestPos > int(pDownloadQueue->GetFileQueue()->size()) - 1)
{
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
iWorkOffset = int(pDownloadQueue->GetFileQueue()->size()) - 1 - iIndex;
}
}
else
@@ -314,7 +375,7 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
// check ID range
int iMaxID = 0;
int iMinID = MAX_ID;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
int ID = pFileInfo->GetID();
@@ -344,16 +405,88 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
}
}
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
bool QueueEditor::BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction)
{
#ifndef HAVE_REGEX_H
if (eMatchMode == mmRegEx)
{
return false;
}
#endif
std::set<int> uniqueIDs;
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
{
const char* szName = *it;
RegEx *pRegEx = NULL;
if (eMatchMode == mmRegEx)
{
pRegEx = new RegEx(szName);
if (!pRegEx->IsValid())
{
delete pRegEx;
return false;
}
}
bool bFound = false;
for (FileQueue::iterator it2 = pDownloadQueue->GetFileQueue()->begin(); it2 != pDownloadQueue->GetFileQueue()->end(); it2++)
{
FileInfo* pFileInfo = *it2;
if (eAction < eaGroupMoveOffset)
{
// file action
char szFilename[MAX_PATH];
snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename()));
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
(uniqueIDs.find(pFileInfo->GetID()) == uniqueIDs.end()))
{
uniqueIDs.insert(pFileInfo->GetID());
pIDList->push_back(pFileInfo->GetID());
bFound = true;
}
}
else
{
// group action
const char *szFilename = pFileInfo->GetNZBInfo()->GetName();
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
(uniqueIDs.find(pFileInfo->GetNZBInfo()->GetID()) == uniqueIDs.end()))
{
uniqueIDs.insert(pFileInfo->GetNZBInfo()->GetID());
pIDList->push_back(pFileInfo->GetID());
bFound = true;
}
}
}
if (pRegEx)
{
delete pRegEx;
}
if (!bFound && (eMatchMode == mmName))
{
return false;
}
}
return true;
}
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
// collecting files belonging to group
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
}
@@ -368,7 +501,7 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pGroupInfo = *it;
if (!strcmp(pGroupInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
break;
}
@@ -404,24 +537,32 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
}
iOffset = iFileOffset;
}
else if (eAction == eaGroupDelete)
{
pFileInfo->GetNZBInfo()->SetDeleted(true);
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom,
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, szText);
}
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
{
pGroupList->clear();
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
FileInfo* pGroupInfo = NULL;
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
{
FileInfo* pGroupInfo1 = *itg;
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
@@ -465,7 +606,7 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pFileInfo = *it;
if (!strcmp(pItem->m_pFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (!ItemExists(&cAffectedGroupList, pFileInfo))
{
@@ -507,24 +648,24 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
{
FileInfo* pFileInfo = *it;
AlignGroup(pDownloadQueue, pFileInfo);
AlignGroup(pDownloadQueue, pFileInfo->GetNZBInfo());
}
}
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
FileInfo* pLastFileInfo = NULL;
unsigned int iLastNum = 0;
unsigned int iNum = 0;
while (iNum < pDownloadQueue->size())
while (iNum < pDownloadQueue->GetFileQueue()->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
if (!strcmp(pFirstFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
if (pLastFileInfo && iNum - iLastNum > 1)
{
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iNum);
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iLastNum + 1, pFileInfo);
iLastNum++;
}
else
@@ -549,7 +690,7 @@ void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
{
EditItem* pItem = *it;
if (!pFirstFileInfo ||
!strcmp(pFirstFileInfo->GetNZBFilename(), pItem->m_pFileInfo->GetNZBFilename()))
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
{
GroupFileList.push_back(pItem->m_pFileInfo);
if (!pFirstFileInfo)
@@ -653,3 +794,134 @@ void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
}
}
}
void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
{
debug("QueueEditor: setting category '%s' for '%s'", szCategory, Util::BaseFileName(pNZBInfo->GetFilename()));
g_pQueueCoordinator->SetQueueEntryNZBCategory(pNZBInfo, szCategory);
}
void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName)
{
debug("QueueEditor: renaming '%s' to '%s'", Util::BaseFileName(pNZBInfo->GetFilename()), szName);
g_pQueueCoordinator->SetQueueEntryNZBName(pNZBInfo, szName);
}
/**
* Check if deletion of already downloaded files is possible (when nzb id deleted from queue).
* The deletion is most always possible, except the case if all remaining files in queue
* (belonging to this nzb-file) are PARS.
*/
bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (!strstr(szLoFileName, ".par2"))
{
// non-par file found
return true;
}
}
return false;
}
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return;
}
EditItem* pDestItem = pItemList->front();
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
{
EditItem* pItem = *it;
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
{
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo());
}
delete pItem;
}
// align group
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
delete pDestItem;
}
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return;
}
EditItem* pFirstItem = pItemList->front();
NZBInfo* pNZBInfo = pFirstItem->m_pFileInfo->GetNZBInfo();
unsigned int iInsertPos = 0;
// find first file of the group
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
break;
}
iInsertPos++;
}
// now can reorder
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
{
EditItem* pItem = *it;
FileInfo* pFileInfo = pItem->m_pFileInfo;
// move file item
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo1 = *it;
if (pFileInfo1 == pFileInfo)
{
pDownloadQueue->GetFileQueue()->erase(it);
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iInsertPos, pFileInfo);
iInsertPos++;
break;
}
}
delete pItem;
}
}
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
{
debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename()));
char* szStr = strdup(szParamString);
char* szValue = strchr(szStr, '=');
if (szValue)
{
*szValue = '\0';
szValue++;
pNZBInfo->SetParameter(szStr, szValue);
}
else
{
error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString);
}
free(szStr);
}

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -33,8 +33,6 @@
class QueueEditor
{
public:
typedef std::vector<int> IDList;
enum EEditAction
{
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
@@ -45,6 +43,8 @@ public:
eaFileDelete,
eaFilePauseAllPars,
eaFilePauseExtraPars,
eaFileSetPriority,
eaFileReorder,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
@@ -52,7 +52,19 @@ public:
eaGroupResume,
eaGroupDelete,
eaGroupPauseAllPars,
eaGroupPauseExtraPars
eaGroupPauseExtraPars,
eaGroupSetPriority,
eaGroupSetCategory,
eaGroupMerge,
eaGroupSetParameter,
eaGroupSetName
};
enum EMatchMode
{
mmID = 1,
mmName,
mmRegEx
};
private:
@@ -71,28 +83,37 @@ private:
private:
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
bool BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction);
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText);
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
void AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
void PausePars(FileList* pFileList, bool bExtraParsOnly);
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
void DeleteEntry(FileInfo* pFileInfo);
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
void SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority);
public:
QueueEditor();
~QueueEditor();
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
};
#endif

333
README
View File

@@ -2,6 +2,10 @@
NZBGet ReadMe
=====================================
This is a short documentation. For more information please
visit NZBGet home page at
http://nzbget.sourceforge.net
Contents
--------
1. About NZBGet
@@ -29,6 +33,9 @@ In server/client mode NZBGet runs as server in background.
Then you use client to send requests to server. The sample requests
are: download nzb-file, list files in queue, etc.
There is also a built-in web-interface. The server has RPC-support
and can be controlled from third party applications too.
Standalone-tool, server and client are all contained in only one
executable file "nzbget". The mode in which the program works
depends on command-line parameters passed to the program.
@@ -41,16 +48,12 @@ NZBGet is written in C++ and was initialy developed on Linux.
It was ported to Windows later and tested for compatibility with
several POSIX-OS'es.
The current version (0.3.1) should run at least on:
- Linux Debian 4.0 on x86;
- Linux BusyBox with uClibc on MIPSEL;
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
- Windows XP SP2 on x86.
The previous version (0.3.0) was also tested on:
- Linux Debian 3.1 on x86;
- Solaris 10 on x86;
- Linux Debian 3.1 on SPARC (QEmu).
It should run at least on:
- Linux Debian 5.0 on x86;
- Linux with uClibc on MIPSEL and ARM;
- OpenBSD 5.0 on x86;
- Mac OS X 10.7 Lion on x64;
- Windows XP SP3 on x86 and Windows 7 on x64.
Clients and servers running on different OS'es may communicate with
each other. For example, you can use NZBGet as client on Windows to
@@ -86,9 +89,14 @@ And the following libraries are optional:
- for par-check and -repair (enabled by default):
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for encrypted connections (TLS/SSL):
- GnuTLS (http://www.gnu.org/software/gnutls)
or
- OpenSSL (http://www.openssl.org)
- for support of encoding-formats other than yEnc (disabled by default):
- libuu (http://www.fpx.de/fp/Software/UUDeview)
- for gzip support in web-server and web-client (enabled by default):
- zlib (http://www.zlib.net)
All these libraries are included in modern Linux distributions and
should be available as installable packages. Please note that you also
@@ -96,103 +104,76 @@ need the developer packages for these libraries too, they package names
have often suffix "dev" or "devel". On other systems you may need to
download the libraries at the given URLs and compile them (see hints below).
=====================================
4. Installation on POSIX
=====================================
Well, the usual stuff:
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
- untar the nzbget-source via
tar -zxf nzbget-VERSION.tar.gz
- change into nzbget-directory via
cd nzbget-VERSION
- configure it via
./configure
(maybe you have to tell configure, where to find some libraries.
./configure --help is your friend! ;-)
also see "Configure-options" later.)
./configure --help is your friend!
also see "Configure-options" later)
- in a case you don't have root access or want to install the program
in your home directory use the configure parameter --prefix, e. g.:
./configure --prefix ~/usr
- compile it via
make
(you may get some warnings concerning 'mktemp', simply ignore them!)
- become root via
- to install system wide become root via:
su
- install it via
- install it via:
make install
- install configuration files into <prefix>/etc via:
make install-conf
(you can skip this step if you intend to store configuration
files in a non-standard location)
Configure-options
-----------------
You may run configure with additional arguments:
--enable-uulib - to make with uulib-library, in a case you want it
(see later section "Optional package: uulib"). This option is not
enabled by default.
--disable-curses - to make without curses-support. Use this option
if you can not use curses/ncurses.
--disable-parcheck - to make without parcheck-support. Use this option
if you can not use libpar2 or libsigc++.
--with-tlslib=(GnuTLS, OpenSSL) - to select which TLS/SSL library
should be used for encrypted server connections.
--disable-tls - to make without TLS/SSL support. Use this option if
you can not neither GnuTLS nor OpenSSL.
--disable-gzip - to make without gzip support. Use this option
if you can not use zlib.
--enable-debug - to build in debug-mode, if you want to see and log
debug-messages.
Optional package: uulib
-----------------------
uulib is not required to compile and run nzbget, because nzbget includes
internal decoder for yEnc-format. However, uulib supports many other formats,
you may possibly want to have support for. In this case you can build the
program with uulib-support enabled.
NOTE: enabling uulib does not disable internal decoder. The program built with
uulib-support can use both decoders (internal and uulib), depending on option
"decoder" in program's configuration file.
To build with uulib use option "--enable-uulib" while running configure:
./configure --enable-uulib
The uulib must be installed on your system. On most linux distributions
the package uulib-dev is available. So you only need to install this package
and run configure with parameter "--enable-uulib".
If you do not have this package you can compile uulib yourself:
- download source code of uudeview from
http://www.fpx.de/fp/Software/UUDeview;
- build uudeview as usually:
/.confugure
make
- start nzbget's configure-script with following parameters:
./configure --enable-uulib \
--with-uulib-includes=<path to uudeview>/uulib \
--with-uulib-libraries=<path to uudeview>/uulib
for example:
./configure --enable-uulib \
--with-uulib-includes=/home/user/uudeview-0.5.20/uulib \
--with-uulib-libraries=/home/user/uudeview-0.5.20/uulib
- now you can compile nzbget.
NOTE: after nzbget is compiled, the code of uulib-library is built into
nzbget's executable. You do not need to have uulib on target system
to run nzbget.
Optional package: par-check
---------------------------
NZBGet can check and repair downloaded files for you. For this purpose
it uses library par2 (libpar2), which needs sigc++ on its part.
To build with par-check use option "--enable-parcheck" while running
configure:
./configure --enable-parcheck
The libpar2 and libsigc++ (version 2 or later) must be installed on your
system. On most linux distributions these libraries are available as packages.
So you only need to install theme and run configure with parameter
"--enable-parcheck".
If you do not have these package you can compile them yourself. Please
refer to section "Optional package: uulib" for an example on how to
compile additional library. Following configure-parameters may be usefull:
If you do not have these packages you can compile them yourself.
Following configure-parameters may be usefull:
--with-libpar2-includes
--with-libpar2-libraries
@@ -201,12 +182,31 @@ compile additional library. Following configure-parameters may be usefull:
The library libsigc++ must be installed first, since libpar2 requires it.
If you use nzbget on a very slow computer like NAS-device, it may be good to
limit the time allowed for par-repair (option "ParTimeLimit" in nzbget
configuration file). This feature requires a patched version of libpar2.
To compile that version download the original source code of libpar2
(version 0.2) and apply patches "libpar2-0.2-bugfixes.patch" and
"libpar2-0.2-cancel.patch", provided with nzbget:
cd libpar2-0.2
cp ~/nzbget/libpar2-0.2-*.patch .
patch < libpar2-0.2-bugfixes.patch
patch < libpar2-0.2-cancel.patch
./configure
make
make install
If you are not able to use libpar2 or libsigc++ or do not want them you can
make nzbget without support for par-check using option "--disable-parcheck":
./configure --disable-parcheck
Optional package: curses
-------------------------
For curses-outputmode you need ncurses or curses on your system.
If you do not have one of them you can download and compile ncurses yourself.
Please refer to section "Optional package: uulib" for an example on how to
compile additional library. Following configure-parameters may be usefull:
Following configure-parameters may be usefull:
--with-libcurses-includes
--with-libcurses-libraries
@@ -216,6 +216,29 @@ make the program without support for curses using option "--disable-curses":
./configure --disable-curses
Optional package: TLS
-------------------------
To enable encrypted server connections (TLS/SSL) you need to build the program
with TLS/SSL support. NZBGet can use two libraries: GnuTLS or OpenSSL.
Configure-script checks which library is installed and use it. If both are
avialable it gives the precedence to GnuTLS. You may override that with
the option --with-tlslib=(GnuTLS, OpenSSL). For example to build whith OpenSSL:
./configure --with-tlslib=OpenSSL
Following configure-parameters may be usefull:
--with-libtls-includes
--with-libtls-libraries
--with-openssl-includes
--with-openssl-libraries
If none of these libraries is available you can make the program without
TLS/SSL support using option "--disable-tls":
./configure --disable-tls
=====================================
5. Compiling on Windows
=====================================
@@ -229,31 +252,49 @@ libraries:
- libsigc++ (http://libsigc.sourceforge.net)
- libpar2 (http://parchive.sourceforge.net)
Download these libaries, then use patch-files provided with NZBGet to create
preconfigured project files and solutions for each library.
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
to use patch-files, if you do not familiar with this technique.
After libsigc++ and libpar2 are compiled in static libraries (.lib)
and include- and libraries-paths are configured in MS Visual C++ 2005 you
should be able to compile NZBGet.
To compile the program with TLS/SSL support you also need the library:
- GnuTLS (http://www.gnu.org/software/gnutls)
Download a precompiled version of GnuTLS from http://josefsson.org/gnutls4win
and create lib-file as described there in section "Using the GnuTLS DLL from
your Visual Studio program".
After libsigc++ and libpar2 are compiled in static libraries (.lib), the
library for GnuTLS is created and include- and libraries-paths are configured
in MS Visual C++ 2005 you should be able to compile NZBGet.
=====================================
6. Configuration
=====================================
NZBGet needs a configuration-file to work properly.
NZBGet needs a configuration file.
You need to set at least the option "MAINDIR" and one newsserver in
configuration file. Have a look at the example in nzbget.conf.example,
it has comments on how to use each option.
An example configuration file is provided in "nzbget.conf", which
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
system configuration and configure options - typically "/usr/local",
"/usr" or "/opt"). The installer adjusts the file according to your
system paths. If you have performed the installation step
"make install-conf" this file is already copied to "<prefix>/etc" and
NZBGet finds it automatically. If you install the program manually
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
to one of the locations listed below.
Open the file in a text editor and modify it accodring to your needs.
You need to set at least the option "MAINDIR" and one news server in
configuration file. The file has comments on how to use each option.
The program looks for configuration file in following standard
locations (in this order):
On POSIX systems:
~/.nzbget
/etc/nzbget.conf
/usr/etc/nzbget.conf
@@ -277,6 +318,12 @@ options via command-line.
NZBGet can be used in either standalone mode which downloads a single file
or as a server which is able to queue up numerous download requests.
TIP for Windows users: NZBGet is controlled via various command line
parameters. For easier using there is a simple shell script included
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
be running a command shell with PATH adjusted to find NZBGet executable.
Then you can type all commands without full path to nzbget.exe.
Standalone mode:
----------------
@@ -304,15 +351,20 @@ To stop server use:
nzbget -Q
Depending on which frontend has been selected in the nzbget.conf file
(option "outputmode") the server should display a message that
it is ready to receive download requests (this applies only to console
mode, not to daemon mode).
TIP for POSIX users: with included script "nzbgetd" you can use standard
commands to control daemon:
nzbgetd start
nzbgetd stop
etc.
When NZBGet is started in console server mode it displays a message that
it is ready to receive download requests. In daemon mode it doesn't print any
messages to console since it runs in background.
When the server is running it is possible to queue up downloads. This can be
done either in terminal with "nzbget -A <nzb-file>" or by uploading
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default, the
directory must exist on server's start; otherwise it will not be monitored).
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default).
To check the status of server start client and connect it to server:
@@ -370,52 +422,121 @@ Running client & server on seperate machines:
Since nzbget communicates via TCP/IP it's possible to have a server running on
one computer and adding downloads via a client on another computer.
Do this by setting the "serverip" option in the nzbget.conf file to point to the
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
IP of the server (default is localhost which means client and server runs on
same computer)
Security warning
----------------
NZBGet communicates via unsecured socket connections. This makes it vulnerable.
Although server checks the password passed by client, this password is still
transmitted in unsecured way. For this reason it is highly recommended
to configure your Firewall to not expose the port used by NZBGet to WAN.
NZBGet client communicates with NZBGet server via unsecured socket connections.
This makes it vulnerable. Although server checks the password passed by client,
this password is still transmitted in unsecured way. For this reason it is
highly recommended to configure your firewall to not expose the port used by
NZBGet (option <ControlPort>) to WAN.
If you need to control server from WAN it is better to connect to server's
terminal via SSH (POSIX) or remote desktop (Windows) and then run
nzbget-client-commands in this terminal.
If you need to control server from WAN it is better to use web-interface via HTTPS
or (if you prefer remote commands) connect to server's terminal via SSH (POSIX)
or remote desktop (Windows) and then run nzbget-client-commands in this terminal.
Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-processing
script, defined in configuration file. See example configuration file for
the description of parameters passed to the script (option "PostProcess").
An example script for unraring of downloaded files is provided in file
"nzbget-postprocess.sh" installed into "<prefix>/bin". The script requires
configuration file "nzbget-postprocess.conf". If you have installed the
program with "make install" this file is copied to "<prefix>/etc",
where the post-processing script finds it automatically. If you install
the program manually from a binary archive you have to copy the file
from "<prefix>/share/nzbget" to the directory where you have put the
nzbget configuration file ("nzbget.conf").
Set the option "PostProcess" in "nzbget.conf" to point to the post-
processing script.
Additional usage instructions are included in "nzbget-postprocess.sh",
please open the file in a text editor to read them.
NOTE: The post-processing script "nzbget-postprocess.sh" is for
POSIX systems and will not work on Windows.
Web-interface
-------------
NZBGet has a built-in web-server providing the access to the program
functions via web-interface.
To activate web-interface set the option "WebDir" to the path with
web-interface files. If you install using "make install-conf" as
described above the option is set automatically. If you install using
binary files you should check if the option is set correctly.
To access web-interface from your web-browser use the server address
and port defined in NZBGet configuration file in options "ControlIP" and
"ControlPort". For example:
http://localhost:6789/
For login credentials type username "nzbget" (predefined and not changeable)
and the password from the option "ControlPassword" (default is tegbzn6789).
In a case your browser forget credentials, to prevent typing them each
time, there is a workaround - use URL in the form:
http://localhost:6789/nzbget:password/
Please note, that in this case the password is saved in a bookmark or in
browser history in plain text and is easy to find by persons having
access to your computer.
=====================================
8. Authors
=====================================
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
Up to version 0.2.3 it was been developed and maintained by Bo Cordes Petersen
(placebodk@users.sourceforge.net). Beginning at version 0.3.0 the program is
being developed by Andrei Prygounkov (hugbug@users.sourceforge.net).
NZBGet is developed and maintained by Andrey Prygunkov
(hugbug@users.sourceforge.net).
The original project was initially created by Sven Henkel
(sidddy@users.sourceforge.net) in 2004 and later developed by
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
Since then the program has been completely rewritten.
=====================================
9. Copyright
=====================================
NZBGet is distributed under GNU General Pubic License Version 2.
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.
The complete content of license is provided in file COPYING.
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
Binary distribution for Windows contains code from the following libraries:
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- GnuTLS (http://www.gnu.org/software/gnutls)
- zlib (http://www.zlib.net)
- regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
libpar2 is distributed under GPL and libsigc++ under LGPL.
libpar2 is distributed under GPL; libsigc++, GnuTLS and regex - under LGPL;
zlib - under zlib license.
=====================================
10. Contact
=====================================
If you encounter any problem, feel free to use tracker/forums on
If you encounter any problem, feel free to use the forum
sourceforge.net/projects/nzbget
nzbget.sourceforge.net/forum
or contact me at

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -30,33 +30,54 @@
#include "Options.h"
#include "MessageBase.h"
#include "Connection.h"
#include "DownloadInfo.h"
class RemoteClient
{
private:
class MatchedNZBInfo: public NZBInfo
{
public:
bool m_bMatch;
};
class MatchedFileInfo: public FileInfo
{
public:
bool m_bMatch;
};
Connection* m_pConnection;
NetAddress* m_pNetAddress;
bool m_bVerbose;
bool InitConnection();
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
bool ReceiveBoolResponse();
void printf(char* msg, ...);
void perror(char* msg);
void printf(const char* msg, ...);
void perror(const char* msg);
public:
RemoteClient();
~RemoteClient();
RemoteClient();
~RemoteClient();
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
bool RequestServerDownload(const char* szName, bool bAddFirst);
bool RequestServerList();
bool RequestServerPauseUnpause(bool bPause);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
bool RequestServerSetDownloadRate(int iRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);
bool RequestServerLog(int iLines);
bool RequestServerShutdown();
bool RequestServerReload();
bool RequestServerVersion();
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
bool RequestScan(bool bSyncMode);
bool RequestHistory();
bool RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestUrlQueue();
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -43,43 +43,22 @@
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "WebServer.h"
#include "Log.h"
#include "Options.h"
#include "QueueCoordinator.h"
#include "QueueEditor.h"
#include "PrePostProcessor.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern void ExitProc();
const char* g_szMessageRequestNames[] =
{ "N/A", "Download", "Pause/Unpause", "List",
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
};
const unsigned int g_iMessageRequestSizes[] =
{ 0,
sizeof(SNZBDownloadRequest),
sizeof(SNZBPauseUnpauseRequest),
sizeof(SNZBListRequest),
sizeof(SNZBSetDownloadRateRequest),
sizeof(SNZBDumpDebugRequest),
sizeof(SNZBEditQueueRequest),
sizeof(SNZBLogRequest),
sizeof(SNZBRequestBase)
};
//*****************************************************************
// RemoteServer
RemoteServer::RemoteServer()
RemoteServer::RemoteServer(bool bTLS)
{
debug("Creating RemoteServer");
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_bTLS = bTLS;
m_pConnection = NULL;
}
@@ -91,32 +70,50 @@ RemoteServer::~RemoteServer()
{
delete m_pConnection;
}
delete m_pNetAddress;
}
void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
#ifndef DISABLE_TLS
if (m_bTLS)
{
if (strlen(g_pOptions->GetSecureCert()) == 0 || !Util::FileExists(g_pOptions->GetSecureCert()))
{
error("Could not initialize TLS, secure certificate is not configured or the cert-file was not found. Check option <SecureCert>");
return;
}
if (strlen(g_pOptions->GetSecureKey()) == 0 || !Util::FileExists(g_pOptions->GetSecureKey()))
{
error("Could not initialize TLS, secure key is not configured or the key-file was not found. Check option <SecureKey>");
return;
}
}
#endif
while (!IsStopped())
{
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(m_pNetAddress);
m_pConnection = new Connection(g_pOptions->GetControlIP(),
m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(),
m_bTLS);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind() == 0;
}
// Accept connections and store the "new" socket value
SOCKET iSocket = INVALID_SOCKET;
// Accept connections and store the new Connection
Connection* pAcceptedConnection = NULL;
if (bBind)
{
iSocket = m_pConnection->Accept();
pAcceptedConnection = m_pConnection->Accept();
}
if (!bBind || iSocket == INVALID_SOCKET)
if (!bBind || pAcceptedConnection == NULL)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
@@ -131,9 +128,13 @@ void RemoteServer::Run()
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetSocket(iSocket);
commandThread->SetConnection(pAcceptedConnection);
#ifndef DISABLE_TLS
commandThread->SetTLS(m_bTLS);
#endif
commandThread->Start();
}
if (m_pConnection)
{
m_pConnection->Disconnect();
@@ -158,548 +159,80 @@ void RemoteServer::Stop()
//*****************************************************************
// RequestProcessor
RequestProcessor::~RequestProcessor()
{
m_pConnection->Disconnect();
delete m_pConnection;
}
void RequestProcessor::Run()
{
int iBytesReceived = 0;
bool bOK = false;
// Read the first package which needs to be a request
m_pConnection->SetSuppressErrors(true);
iBytesReceived = recv(m_iSocket, (char*) & m_MessageBase, sizeof(m_MessageBase), 0);
if (iBytesReceived < 0)
#ifndef DISABLE_TLS
if (m_bTLS && !m_pConnection->StartTLS(false, g_pOptions->GetSecureCert(), g_pOptions->GetSecureKey()))
{
debug("Could not establish secure connection to web-client: Start TLS failed");
return;
}
// Make sure this is a nzbget request from a client
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
{
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
{
warn("nzbget request received on port %i, but password invalid", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
// Info - connection received
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
char* ip = inet_ntoa(PeerName.sin_addr);
#else
char ip[20];
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
#endif
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], ip);
}
Dispatch();
// Close the socket
closesocket(m_iSocket);
}
void RequestProcessor::Dispatch()
{
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestShutdown &&
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
// Read the first 4 bytes to determine request type
int iSignature = 0;
if (!m_pConnection->Recv((char*)&iSignature, 4))
{
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
debug("Could not read request signature, request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
MessageCommand* command = NULL;
switch (ntohl(m_MessageBase.m_iType))
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
{
case eRemoteRequestDownload:
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetConnection(m_pConnection);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) ||
!strncmp((char*)&iSignature, "GET ", 4) ||
!strncmp((char*)&iSignature, "OPTI", 4))
{
// HTTP request received
char szBuffer[1024];
if (m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
command = new DownloadCommand();
break;
eHttpMethod = WebProcessor::hmPost;
szUrl++;
}
if (!strncmp((char*)&iSignature, "OPTI", 4) && strlen(szUrl) > 4)
{
eHttpMethod = WebProcessor::hmOptions;
szUrl += 4;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
case eRemoteRequestList:
{
command = new ListCommand();
break;
}
debug("url: %s", szUrl);
case eRemoteRequestLog:
{
command = new LogCommand();
break;
}
case eRemoteRequestPauseUnpause:
{
command = new PauseUnpauseCommand();
break;
}
case eRemoteRequestEditQueue:
{
command = new EditQueueCommand();
break;
}
case eRemoteRequestSetDownloadRate:
{
command = new SetDownloadRateCommand();
break;
}
case eRemoteRequestDumpDebug:
{
command = new DumpDebugCommand();
break;
}
case eRemoteRequestShutdown:
{
command = new ShutdownCommand();
break;
}
case eRemoteRequestVersion:
{
command = new VersionCommand();
break;
}
default:
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
break;
}
if (command)
{
command->SetSocket(m_iSocket);
command->SetMessageBase(&m_MessageBase);
command->Execute();
delete command;
}
}
//*****************************************************************
// Commands
void MessageCommand::SendBoolResponse(bool bSuccess, const char* szText)
{
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
SNZBDownloadResponse BoolResponse;
memset(&BoolResponse, 0, sizeof(BoolResponse));
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
BoolResponse.m_bSuccess = htonl(bSuccess);
int iTextLen = strlen(szText) + 1;
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
// Send the request answer
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
send(m_iSocket, (char*)szText, iTextLen, 0);
}
bool MessageCommand::ReceiveRequest(void* pBuffer, int iSize)
{
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
iSize -= sizeof(SNZBRequestBase);
if (iSize > 0)
{
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
if (iBytesReceived != iSize)
{
error("invalid request");
return false;
}
}
return true;
}
void PauseUnpauseCommand::Execute()
{
SNZBPauseUnpauseRequest PauseUnpauseRequest;
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
{
return;
}
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
}
void SetDownloadRateCommand::Execute()
{
SNZBSetDownloadRateRequest SetDownloadRequest;
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
{
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0);
SendBoolResponse(true, "Rate-Command completed successfully");
}
void DumpDebugCommand::Execute()
{
SNZBDumpDebugRequest DumpDebugRequest;
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
{
return;
}
g_pQueueCoordinator->LogDebugInfo();
SendBoolResponse(true, "Debug-Command completed successfully");
}
void ShutdownCommand::Execute()
{
SNZBShutdownRequest ShutdownRequest;
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
{
return;
}
SendBoolResponse(true, "Stopping server");
ExitProc();
}
void VersionCommand::Execute()
{
SNZBVersionRequest VersionRequest;
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
{
return;
}
SendBoolResponse(true, VERSION);
}
void DownloadCommand::Execute()
{
SNZBDownloadRequest DownloadRequest;
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
{
return;
}
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
if (NeedBytes == 0)
{
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
if (pNZBFile)
{
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
delete pNZBFile;
char tmp[1024];
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(false, tmp);
WebProcessor processor;
processor.SetConnection(m_pConnection);
processor.SetUrl(szUrl);
processor.SetHttpMethod(eHttpMethod);
processor.Execute();
bOK = true;
}
}
free(pRecvBuffer);
}
void ListCommand::Execute()
{
SNZBListRequest ListRequest;
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
if (!bOK)
{
return;
}
SNZBListResponse ListResponse;
memset(&ListResponse, 0, sizeof(ListResponse));
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseEntry));
char* buf = NULL;
int bufsize = 0;
if (ntohl(ListRequest.m_bFileList))
{
// Make a data structure and copy all the elements of the list into it
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
int NrEntries = pDownloadQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bufsize += strlen(pFileInfo->GetNZBFilename()) + 1;
bufsize += strlen(pFileInfo->GetSubject()) + 1;
bufsize += strlen(pFileInfo->GetFilename()) + 1;
bufsize += strlen(pFileInfo->GetDestDir()) + 1;
}
buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
unsigned int iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
pListAnswer->m_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBFilename()) + 1);
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetDestDir()) + 1);
bufptr += sizeof(SNZBListResponseEntry);
strcpy(bufptr, pFileInfo->GetNZBFilename());
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += ntohl(pListAnswer->m_iSubjectLen);
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pFileInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
}
g_pQueueCoordinator->UnlockQueue();
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
ListResponse.m_iTrailingDataLength = htonl(bufsize);
}
if (htonl(ListRequest.m_bServerState))
{
unsigned int iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
ListResponse.m_iParJobCount = htonl(pParQueue->size());
g_pPrePostProcessor->UnlockParQueue();
int iUpTimeSec, iDnTimeSec;
long long iAllBytes;
bool bStandBy;
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
ListResponse.m_bServerStandBy = htonl(bStandBy);
SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
}
// Send the request answer
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
if (buf)
{
free(buf);
}
}
void LogCommand::Execute()
{
SNZBLogRequest LogRequest;
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
{
return;
}
Log::Messages* pMessages = g_pLog->LockMessages();
int iNrEntries = ntohl(LogRequest.m_iLines);
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
int iStart = pMessages->size();
if (iNrEntries > 0)
{
if (iNrEntries > (int)pMessages->size())
{
iNrEntries = pMessages->size();
}
iStart = pMessages->size() - iNrEntries;
}
if (iIDFrom > 0 && !pMessages->empty())
{
iStart = iIDFrom - pMessages->front()->GetID();
if (iStart < 0)
{
iStart = 0;
}
iNrEntries = pMessages->size() - iStart;
if (iNrEntries < 0)
{
iNrEntries = 0;
}
}
// calculate required buffer size
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
bufsize += strlen(pMessage->GetText()) + 1;
}
char* buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
pLogAnswer->m_iID = htonl(pMessage->GetID());
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
pLogAnswer->m_tTime = htonl(pMessage->GetTime());
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
bufptr += sizeof(SNZBLogResponseEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += ntohl(pLogAnswer->m_iTextLen);
}
g_pLog->UnlockMessages();
SNZBLogResponse LogResponse;
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
LogResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}
void EditQueueCommand::Execute()
{
SNZBEditQueueRequest EditQueueRequest;
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
{
return;
}
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
int iAction = ntohl(EditQueueRequest.m_iAction);
int iOffset = ntohl(EditQueueRequest.m_iOffset);
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
if (iNrEntries * sizeof(int32_t) != iBufLength)
{
error("Invalid struct size");
return;
}
if (iNrEntries <= 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
return;
}
int32_t* pIDs = (int32_t*)malloc(iBufLength);
// Read from the socket until nothing remains
char* pBufPtr = (char*)pIDs;
int NeedBytes = iBufLength;
int iResult = 0;
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
QueueEditor::IDList cIDList;
cIDList.reserve(iNrEntries);
for (int i = 0; i < iNrEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
free(pIDs);
if (bOK)
{
SendBoolResponse(true, "Edit-Command completed successfully");
}
else
{
SendBoolResponse(false, "Edit-Command failed");
warn("Non-nzbget request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
}
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,21 +27,17 @@
#ifndef REMOTESERVER_H
#define REMOTESERVER_H
#include <list>
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
#include "MessageBase.h"
class RemoteServer : public Thread
{
private:
NetAddress* m_pNetAddress;
bool m_bTLS;
Connection* m_pConnection;
public:
RemoteServer();
RemoteServer(bool bTLS);
~RemoteServer();
virtual void Run();
virtual void Stop();
@@ -50,84 +46,14 @@ public:
class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
void Dispatch();
bool m_bTLS;
Connection* m_pConnection;
public:
~RequestProcessor();
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
class MessageCommand
{
protected:
SOCKET m_iSocket;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~MessageCommand() {};
virtual void Execute() = 0;
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; };
};
class DownloadCommand: public MessageCommand
{
public:
virtual void Execute();
};
class ListCommand: public MessageCommand
{
public:
virtual void Execute();
};
class LogCommand: public MessageCommand
{
public:
virtual void Execute();
};
class PauseUnpauseCommand: public MessageCommand
{
public:
virtual void Execute();
};
class EditQueueCommand: public MessageCommand
{
public:
virtual void Execute();
};
class SetDownloadRateCommand: public MessageCommand
{
public:
virtual void Execute();
};
class DumpDebugCommand: public MessageCommand
{
public:
virtual void Execute();
};
class ShutdownCommand: public MessageCommand
{
public:
virtual void Execute();
};
class VersionCommand: public MessageCommand
{
public:
virtual void Execute();
void SetTLS(bool bTLS) { m_bTLS = bTLS; }
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
};
#endif

387
Scanner.cpp Normal file
View File

@@ -0,0 +1,387 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "Scanner.h"
#include "Options.h"
#include "Log.h"
#include "QueueCoordinator.h"
#include "ScriptController.h"
#include "DiskState.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
Scanner::FileData::FileData(const char* szFilename)
{
m_szFilename = strdup(szFilename);
m_iSize = 0;
m_tLastChange = 0;
}
Scanner::FileData::~FileData()
{
free(m_szFilename);
}
Scanner::Scanner()
{
debug("Creating Scanner");
m_bRequestedNZBDirScan = false;
m_bScanning = false;
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
m_iPass = 0;
m_iStepMSec = 0;
const char* szNZBScript = g_pOptions->GetNZBProcess();
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
}
Scanner::~Scanner()
{
debug("Destroying Scanner");
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
{
delete *it;
}
m_FileList.clear();
}
void Scanner::Check()
{
if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan ||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
{
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
bool bCheckStat = !m_bRequestedNZBDirScan;
m_bRequestedNZBDirScan = false;
m_bScanning = true;
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
if (!bCheckStat && m_bNZBScript)
{
// if immediate scan requesten, we need second scan to process files extracted by NzbProcess-script
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
}
m_bScanning = false;
m_iNZBDirInterval = 0;
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
// is set for rare scans like once per hour) we make 4 scans:
// - one additional scan is neccessary to check sizes of detected files;
// - another scan is required to check files which were extracted by NzbProcess-script;
// - third scan is needed to check sizes of extracted files.
if (g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
{
int iMaxPass = m_bNZBScript ? 3 : 1;
if (m_iPass < iMaxPass)
{
// scheduling another scan of incoming directory in NzbDirFileAge seconds.
m_iNZBDirInterval = (g_pOptions->GetNzbDirInterval() - g_pOptions->GetNzbDirFileAge()) * 1000;
m_iPass++;
}
else
{
m_iPass = 0;
}
}
DropOldFiles();
}
m_iNZBDirInterval += m_iStepMSec;
}
/**
* Check if there are files in directory for incoming nzb-files
* and add them to download queue
*/
void Scanner::CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat)
{
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
struct stat buffer;
char fullfilename[1023 + 1]; // one char reserved for the trailing slash (if needed)
snprintf(fullfilename, 1023, "%s%s", szDirectory, filename);
fullfilename[1023 - 1] = '\0';
if (!stat(fullfilename, &buffer))
{
// check subfolders
if ((buffer.st_mode & S_IFDIR) != 0 && strcmp(filename, ".") && strcmp(filename, ".."))
{
fullfilename[strlen(fullfilename) + 1] = '\0';
fullfilename[strlen(fullfilename)] = PATH_SEPARATOR;
const char* szUseCategory = filename;
char szSubCategory[1024];
if (strlen(szCategory) > 0)
{
snprintf(szSubCategory, 1023, "%s%c%s", szCategory, PATH_SEPARATOR, filename);
szSubCategory[1024 - 1] = '\0';
szUseCategory = szSubCategory;
}
CheckIncomingNZBs(fullfilename, szUseCategory, bCheckStat);
}
else if ((buffer.st_mode & S_IFDIR) == 0 && CanProcessFile(fullfilename, bCheckStat))
{
ProcessIncomingFile(szDirectory, filename, fullfilename, szCategory);
}
}
}
}
/**
* Only files which were not changed during last g_pOptions->GetNzbDirFileAge() seconds
* can be processed. That prevents the processing of files, which are currently being
* copied into nzb-directory (eg. being downloaded in web-browser).
*/
bool Scanner::CanProcessFile(const char* szFullFilename, bool bCheckStat)
{
const char* szExtension = strrchr(szFullFilename, '.');
if (!szExtension ||
!strcasecmp(szExtension, ".queued") ||
!strcasecmp(szExtension, ".error") ||
!strcasecmp(szExtension, ".processed"))
{
return false;
}
if (!bCheckStat)
{
return true;
}
long long lSize = Util::FileSize(szFullFilename);
time_t tCurrent = time(NULL);
bool bCanProcess = false;
bool bInList = false;
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
{
FileData* pFileData = *it;
if (!strcmp(pFileData->GetFilename(), szFullFilename))
{
bInList = true;
if (pFileData->GetSize() == lSize &&
tCurrent - pFileData->GetLastChange() >= g_pOptions->GetNzbDirFileAge())
{
bCanProcess = true;
delete pFileData;
m_FileList.erase(it);
}
else
{
pFileData->SetSize(lSize);
if (pFileData->GetSize() != lSize)
{
pFileData->SetLastChange(tCurrent);
}
}
break;
}
}
if (!bInList)
{
FileData* pFileData = new FileData(szFullFilename);
pFileData->SetSize(lSize);
pFileData->SetLastChange(tCurrent);
m_FileList.push_back(pFileData);
}
return bCanProcess;
}
/**
* Remove old files from the list of monitored files.
* Normally these files are deleted from the list when they are processed.
* However if a file was detected by function "CanProcessFile" once but wasn't
* processed later (for example if the user deleted it), it will stay in the list,
* until we remove it here.
*/
void Scanner::DropOldFiles()
{
time_t tCurrent = time(NULL);
int i = 0;
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); )
{
FileData* pFileData = *it;
if ((tCurrent - pFileData->GetLastChange() >=
(g_pOptions->GetNzbDirInterval() + g_pOptions->GetNzbDirFileAge()) * 2) ||
// can occur if the system clock was adjusted
tCurrent < pFileData->GetLastChange())
{
debug("Removing file %s from scan file list", pFileData->GetFilename());
delete pFileData;
m_FileList.erase(it);
it = m_FileList.begin() + i;
}
else
{
it++;
i++;
}
}
}
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory)
{
const char* szExtension = strrchr(szBaseFilename, '.');
if (!szExtension)
{
return;
}
char* szNZBCategory = strdup(szCategory);
NZBParameterList* pParameterList = new NZBParameterList;
int iPriority = 0;
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
{
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
&szNZBCategory, &iPriority, pParameterList);
bExists = Util::FileExists(szFullFilename);
if (bExists && strcasecmp(szExtension, ".nzb"))
{
char bakname2[1024];
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
if (!bRenameOK)
{
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
}
}
}
if (!strcasecmp(szExtension, ".nzb_processed"))
{
char szRenamedName[1024];
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
if (bRenameOK)
{
AddFileToQueue(szRenamedName, szNZBCategory, iPriority, pParameterList);
}
else
{
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
AddFileToQueue(szFullFilename, szNZBCategory, iPriority, pParameterList);
}
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
delete *it;
}
pParameterList->clear();
delete pParameterList;
free(szNZBCategory);
}
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList)
{
const char* szBasename = Util::BaseFileName(szFilename);
info("Collection %s found", szBasename);
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
if (!pNZBFile)
{
error("Could not add collection %s to queue", szBasename);
}
char bakname2[1024];
bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
if (!bRenameOK)
{
error("Could not rename file %s to %s! Errcode: %i", szFilename, bakname2, errno);
}
if (pNZBFile && bRenameOK)
{
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
NZBParameter* pParameter = *it;
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
}
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
info("Collection %s added to queue", szBasename);
}
if (pNZBFile)
{
delete pNZBFile;
}
}
void Scanner::ScanNZBDir(bool bSyncMode)
{
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
// but it's not critical here.
m_bScanning = true;
m_bRequestedNZBDirScan = true;
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
{
usleep(100 * 1000);
}
}

77
Scanner.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SCANNER_H
#define SCANNER_H
#include <deque>
#include <time.h>
#include "DownloadInfo.h"
class Scanner
{
private:
class FileData
{
private:
char* m_szFilename;
long long m_iSize;
time_t m_tLastChange;
public:
FileData(const char* szFilename);
~FileData();
const char* GetFilename() { return m_szFilename; }
long long GetSize() { return m_iSize; }
void SetSize(long long lSize) { m_iSize = lSize; }
time_t GetLastChange() { return m_tLastChange; }
void SetLastChange(time_t tLastChange) { m_tLastChange = tLastChange; }
};
typedef std::deque<FileData*> FileList;
bool m_bRequestedNZBDirScan;
int m_iNZBDirInterval;
bool m_bNZBScript;
int m_iPass;
int m_iStepMSec;
FileList m_FileList;
bool m_bScanning;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
void AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
void DropOldFiles();
public:
Scanner();
~Scanner();
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
void ScanNZBDir(bool bSyncMode);
void Check();
};
#endif

246
Scheduler.cpp Normal file
View File

@@ -0,0 +1,246 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2009 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "Scheduler.h"
#include "ScriptController.h"
#include "Options.h"
#include "Log.h"
extern Options* g_pOptions;
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess)
{
m_iHours = iHours;
m_iMinutes = iMinutes;
m_iWeekDaysBits = iWeekDaysBits;
m_eCommand = eCommand;
m_iDownloadRate = iDownloadRate;
m_szProcess = NULL;
if (szProcess)
{
m_szProcess = strdup(szProcess);
}
m_tLastExecuted = 0;
}
Scheduler::Task::~Task()
{
if (m_szProcess)
{
free(m_szProcess);
}
}
Scheduler::Scheduler()
{
debug("Creating Scheduler");
m_tLastCheck = 0;
m_TaskList.clear();
}
Scheduler::~Scheduler()
{
debug("Destroying Scheduler");
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
delete *it;
}
}
void Scheduler::AddTask(Task* pTask)
{
m_mutexTaskList.Lock();
m_TaskList.push_back(pTask);
m_mutexTaskList.Unlock();
}
bool Scheduler::CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2)
{
return (pTask1->m_iHours < pTask2->m_iHours) ||
((pTask1->m_iHours == pTask2->m_iHours) && (pTask1->m_iMinutes < pTask2->m_iMinutes));
}
void Scheduler::FirstCheck()
{
m_mutexTaskList.Lock();
m_TaskList.sort(CompareTasks);
m_mutexTaskList.Unlock();
// check all tasks for the last week
time_t tCurrent = time(NULL);
m_tLastCheck = tCurrent - 60*60*24*7;
m_bDetectClockChanges = false;
m_bExecuteProcess = false;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
void Scheduler::IntervalCheck()
{
m_bDetectClockChanges = true;
m_bExecuteProcess = true;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
void Scheduler::CheckTasks()
{
m_mutexTaskList.Lock();
time_t tCurrent = time(NULL);
struct tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
struct tm tmLastCheck;
if (m_bDetectClockChanges)
{
// Detect large step changes of system time
time_t tDiff = tCurrent - m_tLastCheck;
if (tDiff > 60*90 || tDiff < -60*90)
{
debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)");
m_bExecuteProcess = false;
m_tLastCheck = tCurrent;
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
pTask->m_tLastExecuted = 0;
}
}
}
localtime_r(&m_tLastCheck, &tmLastCheck);
struct tm tmLoop;
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
tmLoop.tm_hour = tmCurrent.tm_hour;
tmLoop.tm_min = tmCurrent.tm_min;
tmLoop.tm_sec = tmCurrent.tm_sec;
time_t tLoop = mktime(&tmLoop);
while (tLoop <= tCurrent)
{
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
if (pTask->m_tLastExecuted != tLoop)
{
struct tm tmAppoint;
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
tmAppoint.tm_hour = pTask->m_iHours;
tmAppoint.tm_min = pTask->m_iMinutes;
tmAppoint.tm_sec = 0;
time_t tAppoint = mktime(&tmAppoint);
int iWeekDay = tmAppoint.tm_wday;
if (iWeekDay == 0)
{
iWeekDay = 7;
}
bool bWeekDayOK = pTask->m_iWeekDaysBits == 0 || (pTask->m_iWeekDaysBits & (1 << (iWeekDay - 1)));
bool bDoTask = bWeekDayOK && m_tLastCheck < tAppoint && tAppoint <= tCurrent;
//debug("TEMP: 1) m_tLastCheck=%i, tCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
if (bDoTask)
{
ExecuteTask(pTask);
pTask->m_tLastExecuted = tLoop;
}
}
}
tLoop += 60*60*24; // inc day
localtime_r(&tLoop, &tmLoop);
}
m_tLastCheck = tCurrent;
m_mutexTaskList.Unlock();
}
void Scheduler::ExecuteTask(Task* pTask)
{
if (pTask->m_eCommand == scDownloadRate)
{
debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate);
}
else
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
}
switch (pTask->m_eCommand)
{
case scDownloadRate:
m_iDownloadRate = pTask->m_iDownloadRate;
m_bDownloadRateChanged = true;
break;
case scPauseDownload:
case scUnpauseDownload:
m_bPauseDownload = pTask->m_eCommand == scPauseDownload;
m_bPauseDownloadChanged = true;
break;
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szProcess);
}
break;
case scPauseScan:
case scUnpauseScan:
m_bPauseScan = pTask->m_eCommand == scPauseScan;
m_bPauseScanChanged = true;
break;
}
}

98
Scheduler.h Normal file
View File

@@ -0,0 +1,98 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2009 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SCHEDULER_H
#define SCHEDULER_H
#include <list>
#include <time.h>
#include "Thread.h"
class Scheduler
{
public:
enum ECommand
{
scPauseDownload,
scUnpauseDownload,
scDownloadRate,
scProcess,
scPauseScan,
scUnpauseScan
};
class Task
{
private:
int m_iHours;
int m_iMinutes;
int m_iWeekDaysBits;
ECommand m_eCommand;
int m_iDownloadRate;
char* m_szProcess;
time_t m_tLastExecuted;
public:
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess);
~Task();
friend class Scheduler;
};
private:
typedef std::list<Task*> TaskList;
TaskList m_TaskList;
Mutex m_mutexTaskList;
time_t m_tLastCheck;
bool m_bDetectClockChanges;
bool m_bDownloadRateChanged;
bool m_bExecuteProcess;
int m_iDownloadRate;
bool m_bPauseDownloadChanged;
bool m_bPauseDownload;
bool m_bPauseScanChanged;
bool m_bPauseScan;
void ExecuteTask(Task* pTask);
void CheckTasks();
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
public:
Scheduler();
~Scheduler();
void AddTask(Task* pTask);
void FirstCheck();
void IntervalCheck();
bool GetDownloadRateChanged() { return m_bDownloadRateChanged; }
int GetDownloadRate() { return m_iDownloadRate; }
bool GetPauseDownloadChanged() { return m_bPauseDownloadChanged; }
bool GetPauseDownload() { return m_bPauseDownload; }
bool GetPauseScanChanged() { return m_bPauseScanChanged; }
bool GetPauseScan() { return m_bPauseScan; }
};
#endif

1047
ScriptController.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

148
ScriptController.h Normal file
View File

@@ -0,0 +1,148 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SCRIPTCONTROLLER_H
#define SCRIPTCONTROLLER_H
#include <list>
#include <fstream>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "Options.h"
class EnvironmentStrings
{
private:
typedef std::vector<char*> Strings;
Strings m_strings;
public:
EnvironmentStrings();
~EnvironmentStrings();
void InitFromCurrentProcess();
void Append(char* szString);
#ifdef WIN32
char* GetStrings();
#else
char** GetStrings();
#endif
};
class ScriptController
{
private:
const char* m_szScript;
const char* m_szWorkingDir;
const char** m_szArgs;
bool m_bFreeArgs;
const char* m_szInfoName;
const char* m_szDefaultKindPrefix;
EnvironmentStrings m_environmentStrings;
Options::EScriptLogKind m_eDefaultLogKind;
bool m_bTerminated;
#ifdef WIN32
HANDLE m_hProcess;
#else
pid_t m_hProcess;
#endif
void PrepareEnvOptions();
protected:
void ProcessOutput(char* szText);
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
bool GetTerminated() { return m_bTerminated; }
void PrepareEnvParameters(NZBInfo* pNZBInfo);
public:
ScriptController();
virtual ~ScriptController();
int Execute();
void Terminate();
void SetScript(const char* szScript) { m_szScript = szScript; }
const char* GetScript() { return m_szScript; }
void SetWorkingDir(const char* szWorkingDir) { m_szWorkingDir = szWorkingDir; }
void SetArgs(const char** szArgs, bool bFreeArgs) { m_szArgs = szArgs; m_bFreeArgs = bFreeArgs; }
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
const char* GetInfoName() { return m_szInfoName; }
void SetDefaultKindPrefix(const char* szDefaultKindPrefix) { m_szDefaultKindPrefix = szDefaultKindPrefix; }
void SetDefaultLogKind(Options::EScriptLogKind eDefaultLogKind) { m_eDefaultLogKind = eDefaultLogKind; }
void SetEnvVar(const char* szName, const char* szValue);
};
class PostScriptController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
{
private:
char** m_pCategory;
int* m_iPriority;
NZBParameterList* m_pParameterList;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
};
class NZBAddedScriptController : public Thread, public ScriptController
{
private:
char* m_szNZBName;
public:
virtual void Run();
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
};
class SchedulerScriptController : public Thread, public ScriptController
{
public:
virtual void Run();
static void StartScript(const char* szCommandLine);
};
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,24 +16,16 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
/*
*
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
* The queue of mutexes that was used did not work for every
* implementation of POSIX threads. Now a m_Semaphore is used.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -46,6 +38,7 @@
#include <unistd.h>
#include <errno.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ServerPool.h"
@@ -65,20 +58,13 @@ ServerPool::ServerPool()
m_iMaxLevel = 0;
m_iTimeout = 60;
m_Servers.clear();
m_Connections.clear();
m_Semaphores.clear();
}
ServerPool::~ ServerPool()
{
debug("Destroying ServerPool");
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
{
delete *it;
}
m_Semaphores.clear();
m_Levels.clear();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
@@ -97,87 +83,124 @@ void ServerPool::AddServer(NewsServer* pNewsServer)
{
debug("Adding server to ServerPool");
m_Servers.push_back(pNewsServer);
if (pNewsServer->GetMaxConnections() > 0)
{
m_Servers.push_back(pNewsServer);
}
else
{
delete pNewsServer;
}
}
void ServerPool::NormalizeLevels()
{
if (m_Servers.empty())
{
return;
}
std::sort(m_Servers.begin(), m_Servers.end(), CompareServers);
NewsServer* pNewsServer = m_Servers.front();
m_iMaxLevel = 0;
int iCurLevel = pNewsServer->GetLevel();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() != iCurLevel)
{
m_iMaxLevel++;
}
pNewsServer->SetLevel(m_iMaxLevel);
}
}
bool ServerPool::CompareServers(NewsServer* pServer1, NewsServer* pServer2)
{
return pServer1->GetLevel() < pServer2->GetLevel();
}
void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");
m_iMaxLevel = 0;
NormalizeLevels();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (m_iMaxLevel < pNewsServer->GetLevel())
{
m_iMaxLevel = pNewsServer->GetLevel();
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
if ((int)m_Levels.size() <= pNewsServer->GetLevel())
{
m_Levels.push_back(0);
}
m_Levels[pNewsServer->GetLevel()] += pNewsServer->GetMaxConnections();
}
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
if (m_Levels.empty())
{
int iMaxConnectionsForLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (iLevel == pNewsServer->GetLevel())
{
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
}
}
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
m_Semaphores.push_back(sem);
warn("No news servers defined, download is not possible");
}
}
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
{
bool bWaitVal = false;
if (bWait)
{
debug("Getting connection (wait)");
bWaitVal = m_Semaphores[iLevel]->Wait();
}
else
{
bWaitVal = m_Semaphores[iLevel]->TryWait();
}
if (!bWaitVal)
{
// signal received or wait timeout
return NULL;
}
PooledConnection* pConnection = NULL;
m_mutexConnections.Lock();
PooledConnection* pConnection = NULL;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0)
{
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
// free connection found, take it!
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
PooledConnection* pCandidateConnection = *it;
NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer();
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetLevel() == iLevel &&
(!pWantServer || pCandidateServer == pWantServer ||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
{
// free connection found, check if it's not from the server which should be ignored
bool bUseConnection = true;
if (pIgnoreServers && !pWantServer)
{
for (Servers::iterator it = pIgnoreServers->begin(); it != pIgnoreServers->end(); it++)
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetLevel() == pCandidateServer->GetLevel()))
{
bUseConnection = false;
break;
}
}
}
if (bUseConnection)
{
pConnection = pCandidateConnection;
pConnection->SetInUse(true);
break;
}
}
}
if (pConnection)
{
m_Levels[iLevel]--;
}
}
m_mutexConnections.Unlock();
if (!pConnection)
{
error("ServerPool: serious error, no free connection found, but there should be one.");
}
return pConnection;
}
@@ -195,7 +218,7 @@ void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
m_Levels[pConnection->GetNewsServer()->GetLevel()]++;
m_mutexConnections.Unlock();
}
@@ -211,10 +234,10 @@ void ServerPool::CloseUnusedConnections()
PooledConnection* pConnection = *it;
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = curtime - pConnection->GetFreeTime();
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
debug("Closing unused connection to %s", pConnection->GetHost());
pConnection->Disconnect();
}
}
@@ -235,17 +258,8 @@ void ServerPool::LogDebugInfo()
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
debug(" %s: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetHost(), (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
/*
debug(" Semaphores: %i", m_Semaphores.size());
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
{
sem_t* sem = m_Semaphores[iLevel];
int iSemValue;
sem_getvalue(sem, &iSemValue);
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
}
*/
m_mutexConnections.Unlock();
}

View File

@@ -1,9 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2005 Florian Penzkofer <f.penzkofer@sent.com>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -17,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -37,6 +36,9 @@
class ServerPool
{
public:
typedef std::vector<NewsServer*> Servers;
private:
class PooledConnection : public NNTPConnection
{
@@ -51,25 +53,28 @@ private:
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
};
typedef std::vector<NewsServer*> Servers;
typedef std::vector<Semaphore*> Semaphores;
typedef std::vector<int> Levels;
typedef std::vector<PooledConnection*> Connections;
Servers m_Servers;
Connections m_Connections;
Semaphores m_Semaphores;
Levels m_Levels;
int m_iMaxLevel;
Mutex m_mutexConnections;
int m_iTimeout;
void NormalizeLevels();
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
public:
ServerPool();
~ServerPool();
ServerPool();
~ServerPool();
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
void AddServer(NewsServer *s);
void AddServer(NewsServer* pNewsServer);
void InitConnections();
int GetMaxLevel() { return m_iMaxLevel; }
NNTPConnection* GetConnection(int iLevel, bool bWait);
Servers* GetServers() { return &m_Servers; } // Only for read access (no lockings)
NNTPConnection* GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();

564
TLS.cpp Normal file
View File

@@ -0,0 +1,564 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#ifndef DISABLE_TLS
#ifdef WIN32
#define SKIP_DEFAULT_WINDOWS_HEADERS
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <strings.h>
#endif
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <time.h>
#include <errno.h>
#include <list>
#ifdef WIN32
#include "nzbget.h"
#endif
#ifdef HAVE_LIBGNUTLS
#include <gnutls/gnutls.h>
#if GNUTLS_VERSION_NUMBER <= 0x020b00
#define NEED_GCRYPT_LOCKING
#endif
#ifdef NEED_GCRYPT_LOCKING
#include <gcrypt.h>
#endif /* NEED_GCRYPT_LOCKING */
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif /* HAVE_OPENSSL */
#ifndef WIN32
#include "nzbget.h"
#endif
#include "TLS.h"
#include "Thread.h"
#include "Log.h"
#ifdef HAVE_LIBGNUTLS
#ifdef NEED_GCRYPT_LOCKING
/**
* Mutexes for gcryptlib
*/
typedef std::list<Mutex*> Mutexes;
Mutexes* g_pGCryptLibMutexes;
static int gcry_mutex_init(void **priv)
{
Mutex* pMutex = new Mutex();
g_pGCryptLibMutexes->push_back(pMutex);
*priv = pMutex;
return 0;
}
static int gcry_mutex_destroy(void **lock)
{
Mutex* pMutex = ((Mutex*)*lock);
g_pGCryptLibMutexes->remove(pMutex);
delete pMutex;
return 0;
}
static int gcry_mutex_lock(void **lock)
{
((Mutex*)*lock)->Lock();
return 0;
}
static int gcry_mutex_unlock(void **lock)
{
((Mutex*)*lock)->Unlock();
return 0;
}
static struct gcry_thread_cbs gcry_threads_Mutex =
{ GCRY_THREAD_OPTION_USER, NULL,
gcry_mutex_init, gcry_mutex_destroy,
gcry_mutex_lock, gcry_mutex_unlock,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
#endif /* NEED_GCRYPT_LOCKING */
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
/**
* Mutexes for OpenSSL
*/
Mutex* *g_pOpenSSLMutexes;
static void openssl_locking(int mode, int n, const char *file, int line)
{
Mutex* mutex = g_pOpenSSLMutexes[n];
if (mode & CRYPTO_LOCK)
{
mutex->Lock();
}
else
{
mutex->Unlock();
}
}
/*
static unsigned long openssl_thread_id(void)
{
#ifdef WIN32
return (unsigned long)GetCurrentThreadId();
#else
return (unsigned long)pthread_self();
#endif
}
*/
static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line)
{
return (CRYPTO_dynlock_value*)new Mutex();
}
static void openssl_dynlock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line)
{
Mutex* mutex = (Mutex*)l;
delete mutex;
}
static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
Mutex* mutex = (Mutex*)l;
if (mode & CRYPTO_LOCK)
{
mutex->Lock();
}
else
{
mutex->Unlock();
}
}
#endif /* HAVE_OPENSSL */
void TLSSocket::Init()
{
debug("Initializing TLS library");
#ifdef HAVE_LIBGNUTLS
#ifdef NEED_GCRYPT_LOCKING
g_pGCryptLibMutexes = new Mutexes();
#endif /* NEED_GCRYPT_LOCKING */
int error_code;
#ifdef NEED_GCRYPT_LOCKING
error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex);
if (error_code != 0)
{
error("Could not initialize libcrypt");
return;
}
#endif /* NEED_GCRYPT_LOCKING */
error_code = gnutls_global_init();
if (error_code != 0)
{
error("Could not initialize libgnutls");
return;
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
int iMaxMutexes = CRYPTO_num_locks();
g_pOpenSSLMutexes = (Mutex**)malloc(sizeof(Mutex*)*iMaxMutexes);
for (int i=0; i < iMaxMutexes; i++)
{
g_pOpenSSLMutexes[i] = new 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 /* HAVE_OPENSSL */
}
void TLSSocket::Final()
{
debug("Finalizing TLS library");
#ifdef HAVE_LIBGNUTLS
gnutls_global_deinit();
#ifdef NEED_GCRYPT_LOCKING
// fixing memory leak in gcryptlib
for (Mutexes::iterator it = g_pGCryptLibMutexes->begin(); it != g_pGCryptLibMutexes->end(); it++)
{
delete *it;
}
delete g_pGCryptLibMutexes;
#endif /* NEED_GCRYPT_LOCKING */
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
int iMaxMutexes = CRYPTO_num_locks();
for (int i=0; i < iMaxMutexes; i++)
{
delete g_pOpenSSLMutexes[i];
}
free(g_pOpenSSLMutexes);
#endif /* HAVE_OPENSSL */
}
TLSSocket::TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher)
{
m_iSocket = iSocket;
m_bIsClient = bIsClient;
m_szCertFile = szCertFile ? strdup(szCertFile) : NULL;
m_szKeyFile = szKeyFile ? strdup(szKeyFile) : NULL;
m_szCipher = szCipher && strlen(szCipher) > 0 ? strdup(szCipher) : NULL;
m_pContext = NULL;
m_pSession = NULL;
m_bSuppressErrors = false;
m_bInitialized = false;
m_bConnected = false;
}
TLSSocket::~TLSSocket()
{
if (m_szCertFile)
{
free(m_szCertFile);
}
if (m_szKeyFile)
{
free(m_szKeyFile);
}
if (m_szCipher)
{
free(m_szCipher);
}
Close();
}
void TLSSocket::ReportError(const char* szErrMsg)
{
#ifdef HAVE_LIBGNUTLS
const char* errstr = gnutls_strerror(m_iRetCode);
if (m_bSuppressErrors)
{
debug("%s: %s", szErrMsg, errstr);
}
else
{
error("%s: %s", szErrMsg, errstr);
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
int errcode;
do
{
errcode = ERR_get_error();
char errstr[1024];
ERR_error_string_n(errcode, errstr, sizeof(errstr));
errstr[1024-1] = '\0';
if (m_bSuppressErrors)
{
debug("%s: %s", szErrMsg, errstr);
}
else if (errcode != 0)
{
error("%s: %s", szErrMsg, errstr);
}
else
{
error("%s", szErrMsg);
}
} while (errcode);
#endif /* HAVE_OPENSSL */
}
bool TLSSocket::Start()
{
#ifdef HAVE_LIBGNUTLS
gnutls_certificate_credentials_t cred;
m_iRetCode = gnutls_certificate_allocate_credentials(&cred);
if (m_iRetCode != 0)
{
ReportError("Could not create TLS context");
return false;
}
m_pContext = cred;
if (m_szCertFile && m_szKeyFile)
{
m_iRetCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_pContext,
m_szCertFile, m_szKeyFile, GNUTLS_X509_FMT_PEM);
if (m_iRetCode != 0)
{
ReportError("Could not load certificate or key file");
Close();
return false;
}
}
gnutls_session_t sess;
m_iRetCode = gnutls_init(&sess, m_bIsClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
if (m_iRetCode != 0)
{
ReportError("Could not create TLS session");
Close();
return false;
}
m_pSession = sess;
m_bInitialized = true;
const char* szPriority = m_szCipher ? m_szCipher : "NORMAL";
m_iRetCode = gnutls_priority_set_direct((gnutls_session_t)m_pSession, szPriority, NULL);
if (m_iRetCode != 0)
{
ReportError("Could not select cipher for TLS session");
Close();
return false;
}
m_iRetCode = gnutls_credentials_set((gnutls_session_t)m_pSession, GNUTLS_CRD_CERTIFICATE,
(gnutls_certificate_credentials_t*)m_pContext);
if (m_iRetCode != 0)
{
ReportError("Could not initialize TLS session");
Close();
return false;
}
gnutls_transport_set_ptr((gnutls_session_t)m_pSession, (gnutls_transport_ptr_t)(size_t)m_iSocket);
m_iRetCode = gnutls_handshake((gnutls_session_t)m_pSession);
if (m_iRetCode != 0)
{
ReportError("TLS handshake failed");
Close();
return false;
}
m_bConnected = true;
return true;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
m_pContext = SSL_CTX_new(SSLv23_method());
if (!m_pContext)
{
ReportError("Could not create TLS context");
return false;
}
if (m_szCertFile && m_szKeyFile)
{
if (SSL_CTX_use_certificate_file((SSL_CTX*)m_pContext, m_szCertFile, SSL_FILETYPE_PEM) != 1)
{
ReportError("Could not load certificate file");
Close();
return false;
}
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_pContext, m_szKeyFile, SSL_FILETYPE_PEM) != 1)
{
ReportError("Could not load key file");
Close();
return false;
}
}
m_pSession = SSL_new((SSL_CTX*)m_pContext);
if (!m_pSession)
{
ReportError("Could not create TLS session");
Close();
return false;
}
if (m_szCipher && !SSL_set_cipher_list((SSL*)m_pSession, m_szCipher))
{
ReportError("Could not select cipher for TLS");
Close();
return false;
}
if (!SSL_set_fd((SSL*)m_pSession, m_iSocket))
{
ReportError("Could not set the file descriptor for TLS");
Close();
return false;
}
int error_code = m_bIsClient ? SSL_connect((SSL*)m_pSession) : SSL_accept((SSL*)m_pSession);
if (error_code < 1)
{
ReportError("TLS handshake failed");
Close();
return false;
}
m_bConnected = true;
return true;
#endif /* HAVE_OPENSSL */
}
void TLSSocket::Close()
{
if (m_pSession)
{
#ifdef HAVE_LIBGNUTLS
if (m_bConnected)
{
gnutls_bye((gnutls_session_t)m_pSession, GNUTLS_SHUT_WR);
}
if (m_bInitialized)
{
gnutls_deinit((gnutls_session_t)m_pSession);
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
if (m_bConnected)
{
SSL_shutdown((SSL*)m_pSession);
}
SSL_free((SSL*)m_pSession);
#endif /* HAVE_OPENSSL */
m_pSession = NULL;
}
if (m_pContext)
{
#ifdef HAVE_LIBGNUTLS
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_pContext);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
SSL_CTX_free((SSL_CTX*)m_pContext);
#endif /* HAVE_OPENSSL */
m_pContext = NULL;
}
}
int TLSSocket::Send(const char* pBuffer, int iSize)
{
int ret;
#ifdef HAVE_LIBGNUTLS
ret = gnutls_record_send((gnutls_session_t)m_pSession, pBuffer, iSize);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
ret = SSL_write((SSL*)m_pSession, pBuffer, iSize);
#endif /* HAVE_OPENSSL */
if (ret < 0)
{
#ifdef HAVE_OPENSSL
if (ERR_peek_error() == 0)
{
ReportError("Could not write to TLS-Socket: Connection closed by remote host");
}
else
#endif /* HAVE_OPENSSL */
ReportError("Could not write to TLS-Socket");
return -1;
}
return ret;
}
int TLSSocket::Recv(char* pBuffer, int iSize)
{
int ret;
#ifdef HAVE_LIBGNUTLS
ret = gnutls_record_recv((gnutls_session_t)m_pSession, pBuffer, iSize);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
ret = SSL_read((SSL*)m_pSession, pBuffer, iSize);
#endif /* HAVE_OPENSSL */
if (ret < 0)
{
#ifdef HAVE_OPENSSL
if (ERR_peek_error() == 0)
{
ReportError("Could not read from TLS-Socket: Connection closed by remote host");
}
else
#endif /* HAVE_OPENSSL */
{
ReportError("Could not read from TLS-Socket");
}
return -1;
}
return ret;
}
#endif

62
TLS.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef TLS_H
#define TLS_H
#ifndef DISABLE_TLS
class TLSSocket
{
private:
bool m_bIsClient;
char* m_szCertFile;
char* m_szKeyFile;
char* m_szCipher;
SOCKET m_iSocket;
bool m_bSuppressErrors;
int m_iRetCode;
bool m_bInitialized;
bool m_bConnected;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TLS.h
void* m_pContext;
void* m_pSession;
void ReportError(const char* szErrMsg);
public:
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
~TLSSocket();
static void Init();
static void Final();
bool Start();
void Close();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
};
#endif
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -34,160 +34,136 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <process.h>
#include <process.h>
#else
#include <fcntl.h>
#include <pthread.h>
#endif
#include "Log.h"
#include "Thread.h"
int Thread::m_iThreadCount = 1; // take the main program thread into account
Mutex Thread::m_mutexThread;
Mutex* Thread::m_pMutexThread;
Mutex::Mutex()
{
#ifdef WIN32
InitializeCriticalSection(&m_mutexObj);
m_pMutexObj = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
#else
pthread_mutex_init(&m_mutexObj, NULL);
m_pMutexObj = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
pthread_mutex_init((pthread_mutex_t*)m_pMutexObj, NULL);
#endif
}
Mutex::~ Mutex()
{
#ifdef WIN32
DeleteCriticalSection(&m_mutexObj);
DeleteCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
#else
pthread_mutex_destroy(&m_mutexObj);
pthread_mutex_destroy((pthread_mutex_t*)m_pMutexObj);
#endif
free(m_pMutexObj);
}
void Mutex::Lock()
{
#ifdef WIN32
EnterCriticalSection(&m_mutexObj);
EnterCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
#ifdef DEBUG
// CriticalSections on Windows can be locked many times from the same thread,
// but we do not want this and must treat such situations as errors and detect them.
if (m_mutexObj.RecursionCount > 1)
if (((CRITICAL_SECTION*)m_pMutexObj)->RecursionCount > 1)
{
error("Internal program error: inconsistent thread-lock detected");
}
#endif
#else
pthread_mutex_lock(&m_mutexObj);
pthread_mutex_lock((pthread_mutex_t*)m_pMutexObj);
#endif
}
void Mutex::Unlock()
{
#ifdef WIN32
LeaveCriticalSection(&m_mutexObj);
LeaveCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
#else
pthread_mutex_unlock(&m_mutexObj);
pthread_mutex_unlock((pthread_mutex_t*)m_pMutexObj);
#endif
}
Semaphore::Semaphore()
#ifdef HAVE_SPINLOCK
SpinLock::SpinLock()
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
m_pSpinLockObj = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)m_pSpinLockObj, 0x00FFFFFF);
#else
sem_init(&m_semObj, 0, 0);
m_pSpinLockObj = (pthread_spinlock_t *)malloc(sizeof(pthread_spinlock_t));
pthread_spin_init((pthread_spinlock_t *)m_pSpinLockObj, PTHREAD_PROCESS_PRIVATE);
#endif
}
Semaphore::Semaphore(int iValue)
SpinLock::~SpinLock()
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
DeleteCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
sem_init(&m_semObj, 0, iValue);
pthread_spin_destroy((pthread_spinlock_t *)m_pSpinLockObj);
#endif
free((void*)m_pSpinLockObj);
}
void SpinLock::Lock()
{
#ifdef WIN32
EnterCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
pthread_spin_lock((pthread_spinlock_t *)m_pSpinLockObj);
#endif
}
Semaphore::~ Semaphore()
void SpinLock::Unlock()
{
#ifdef WIN32
CloseHandle(m_semObj);
LeaveCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
sem_destroy(&m_semObj);
pthread_spin_unlock((pthread_spinlock_t *)m_pSpinLockObj);
#endif
}
void Semaphore::Post()
{
#ifdef WIN32
ReleaseSemaphore(m_semObj, 1, NULL);
#else
sem_post(&m_semObj);
#endif
}
bool Semaphore::Wait()
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, INFINITE) == WAIT_OBJECT_0;
#else
return sem_wait(&m_semObj) == 0;
#endif
}
bool Semaphore::TryWait()
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
#else
return sem_trywait(&m_semObj) == 0;
#endif
}
bool Semaphore::TimedWait(int iMSec)
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, iMSec) == WAIT_OBJECT_0;
#else
struct timespec alarm;
alarm.tv_sec = ::time(NULL) + iMSec / 1000;
alarm.tv_nsec = (iMSec % 1000) * 1000;
return sem_timedwait(&m_semObj, &alarm) == 0;
#endif
}
bool Semaphore::IsLocked()
{
#ifdef WIN32
bool bCanLock = WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
if (bCanLock)
{
ReleaseSemaphore(m_semObj, 1, NULL);
}
return !bCanLock;
#else
int iSemValue;
sem_getvalue(&m_semObj, &iSemValue);
return iSemValue <= 0;
#endif
}
void Thread::Init()
{
debug("Initializing global thread data");
m_pMutexThread = new Mutex();
}
void Thread::Final()
{
debug("Finalizing global thread data");
delete m_pMutexThread;
}
Thread::Thread()
{
debug("Creating Thread");
m_Thread = 0;
#ifdef WIN32
m_pThreadObj = NULL;
#else
m_pThreadObj = (pthread_t*)malloc(sizeof(pthread_t));
*((pthread_t*)m_pThreadObj) = 0;
#endif
m_bRunning = false;
m_bStopped = false;
m_bAutoDestroy = false;
@@ -196,6 +172,9 @@ Thread::Thread()
Thread::~Thread()
{
debug("Destroying Thread");
#ifndef WIN32
free(m_pThreadObj);
#endif
}
void Thread::Start()
@@ -204,30 +183,29 @@ void Thread::Start()
m_bRunning = true;
// NOTE: we must garantee, that in a time we setting m_bRunning
// NOTE: we must guarantee, that in a time we set m_bRunning
// to value returned from pthread_create, the thread-object still exists.
// This is not obviously!
// pthread_create could wait long enough before returning result
// back to allow the started thread to complete it job
// and terminate.
// We lock mutex m_mutexThread on calling pthread_create; the started thread
// back to allow the started thread to complete its job and terminate.
// We lock mutex m_pMutexThread on calling pthread_create; the started thread
// then also try to lock the mutex (see thread_handler) and therefore
// must wait until we unlock it
m_mutexThread.Lock();
m_pMutexThread->Lock();
#ifdef WIN32
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
m_bRunning = m_Thread != NULL;
m_pThreadObj = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
m_bRunning = m_pThreadObj != NULL;
#else
pthread_attr_t m_Attr;
pthread_attr_init(&m_Attr);
pthread_attr_setdetachstate(&m_Attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setinheritsched(&m_Attr , PTHREAD_INHERIT_SCHED);
m_bRunning = !pthread_create(&m_Thread, &m_Attr, Thread::thread_handler, (void *) this);
m_bRunning = !pthread_create((pthread_t*)m_pThreadObj, &m_Attr, Thread::thread_handler, (void *) this);
pthread_attr_destroy(&m_Attr);
#endif
m_mutexThread.Unlock();
m_pMutexThread->Unlock();
}
void Thread::Stop()
@@ -241,19 +219,19 @@ bool Thread::Kill()
{
debug("Killing Thread");
m_mutexThread.Lock();
m_pMutexThread->Lock();
#ifdef WIN32
bool terminated = TerminateThread(m_Thread, 0) != 0;
bool terminated = TerminateThread((HANDLE)m_pThreadObj, 0) != 0;
#else
bool terminated = pthread_cancel(m_Thread) == 0;
bool terminated = pthread_cancel(*(pthread_t*)m_pThreadObj) == 0;
#endif
if (terminated)
{
m_iThreadCount--;
}
m_mutexThread.Unlock();
m_pMutexThread->Unlock();
return terminated;
}
@@ -263,9 +241,9 @@ void __cdecl Thread::thread_handler(void* pObject)
void* Thread::thread_handler(void* pObject)
#endif
{
m_mutexThread.Lock();
m_pMutexThread->Lock();
m_iThreadCount++;
m_mutexThread.Unlock();
m_pMutexThread->Unlock();
debug("Entering Thread-func");
@@ -283,9 +261,9 @@ void* Thread::thread_handler(void* pObject)
delete pThread;
}
m_mutexThread.Lock();
m_pMutexThread->Lock();
m_iThreadCount--;
m_mutexThread.Unlock();
m_pMutexThread->Unlock();
#ifndef WIN32
return NULL;
@@ -294,9 +272,8 @@ void* Thread::thread_handler(void* pObject)
int Thread::GetThreadCount()
{
m_mutexThread.Lock();
m_pMutexThread->Lock();
int iThreadCount = m_iThreadCount;
m_mutexThread.Unlock();
m_pMutexThread->Unlock();
return iThreadCount;
}

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -27,19 +27,10 @@
#ifndef THREAD_H
#define THREAD_H
#ifndef WIN32
#include <pthread.h>
#include <semaphore.h>
#endif
class Mutex
{
private:
#ifdef WIN32
CRITICAL_SECTION m_mutexObj;
#else
pthread_mutex_t m_mutexObj;
#endif
void* m_pMutexObj;
public:
Mutex();
@@ -48,38 +39,30 @@ public:
void Unlock();
};
class Semaphore
#ifdef HAVE_SPINLOCK
class SpinLock
{
private:
#ifdef WIN32
HANDLE m_semObj;
void* m_pSpinLockObj;
#else
sem_t m_semObj;
volatile void* m_pSpinLockObj;
#endif
public:
Semaphore();
Semaphore(int iValue);
~Semaphore();
void Post();
bool Wait();
bool TryWait();
bool TimedWait(int iMSec);
bool IsLocked();
SpinLock();
~SpinLock();
void Lock();
void Unlock();
};
#endif
class Thread
{
private:
static Mutex m_mutexThread;
static Mutex* m_pMutexThread;
static int m_iThreadCount;
#ifdef WIN32
HANDLE m_Thread;
#else
pthread_t m_Thread;
#endif
void* m_pThreadObj;
bool m_bRunning;
bool m_bStopped;
bool m_bAutoDestroy;
@@ -91,8 +74,10 @@ private:
#endif
public:
Thread();
Thread();
virtual ~Thread();
static void Init();
static void Final();
virtual void Start();
virtual void Stop();
@@ -105,9 +90,6 @@ public:
void SetAutoDestroy(bool bAutoDestroy) { m_bAutoDestroy = bAutoDestroy; }
static int GetThreadCount();
static void Init();
static void Final();
protected:
virtual void Run() {}; // Virtual function - override in derivatives
};

713
Unpack.cpp Normal file
View File

@@ -0,0 +1,713 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Unpack.h"
#include "Log.h"
#include "Util.h"
#include "ParCoordinator.h"
extern Options* g_pOptions;
extern DownloadQueueHolder* g_pDownloadQueueHolder;
void UnpackController::FileList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
free(*it);
}
clear();
}
bool UnpackController::FileList::Exists(const char* szFilename)
{
for (iterator it = begin(); it != end(); it++)
{
char* szFilename1 = *it;
if (!strcmp(szFilename1, szFilename))
{
return true;
}
}
return false;
}
UnpackController::~UnpackController()
{
m_archiveFiles.Clear();
}
void UnpackController::StartUnpackJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
pUnpackController->m_pPostInfo = pPostInfo;
pUnpackController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pUnpackController);
pUnpackController->Start();
}
void UnpackController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
strncpy(m_szName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
m_szName[1024-1] = '\0';
m_bCleanedUpDisk = false;
bool bUnpack = true;
m_szPassword[0] = '\0';
m_szFinalDir[0] = '\0';
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
if (!strcasecmp(pParameter->GetName(), "*Unpack:") && !strcasecmp(pParameter->GetValue(), "no"))
{
bUnpack = false;
}
if (!strcasecmp(pParameter->GetName(), "*Unpack:Password"))
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
}
}
g_pDownloadQueueHolder->UnlockQueue();
snprintf(m_szInfoName, 1024, "unpack for %s", m_szName);
m_szInfoName[1024-1] = '\0';
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
#ifndef DISABLE_PARCHECK
if (bUnpack && HasBrokenFiles() && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && HasParFiles())
{
info("%s has broken files", m_szName);
RequestParCheck(false);
m_pPostInfo->SetWorking(false);
return;
}
#endif
if (bUnpack)
{
CheckArchiveFiles();
}
if (bUnpack && (m_bHasRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
SetWorkingDir(m_szDestDir);
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
CreateUnpackDir();
m_bUnpackOK = true;
m_bUnpackStartError = false;
if (m_bHasRarFiles)
{
m_pPostInfo->SetProgressLabel("");
ExecuteUnrar();
}
if (m_bHasSevenZipFiles && m_bUnpackOK)
{
m_pPostInfo->SetProgressLabel("");
ExecuteSevenZip(false);
}
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
{
m_pPostInfo->SetProgressLabel("");
ExecuteSevenZip(true);
}
Completed();
}
else
{
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && HasParFiles())
{
RequestParCheck(m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped);
}
else
#endif
{
m_pPostInfo->SetUnpackStatus(PostInfo::usSkipped);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
m_pPostInfo->SetWorking(false);
}
void UnpackController::ExecuteUnrar()
{
// Format:
// unrar x -y -p- -o+ *.rar ./_unpack
char szPasswordParam[1024];
const char* szArgs[8];
szArgs[0] = g_pOptions->GetUnrarCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o+";
szArgs[5] = "*.rar";
szArgs[6] = m_szUnpackDir;
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
SetDefaultKindPrefix("Unrar: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
int iExitCode = Execute();
m_pPostInfo->SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "Unrar error code: %i", iExitCode);
}
}
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
{
// Format:
// 7z x -y -p- -o./_unpack *.7z
// OR
// 7z x -y -p- -o./_unpack *.7z.001
char szPasswordParam[1024];
const char* szArgs[7];
szArgs[0] = g_pOptions->GetSevenZipCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
szArgs[4] = szUnpackDirParam;
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
szArgs[6] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetSevenZipCmd());
SetDefaultKindPrefix("7-Zip: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
PrintMessage(Message::mkInfo, "Executing 7-Zip");
int iExitCode = Execute();
m_pPostInfo->SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "7-Zip error code: %i", iExitCode);
}
}
void UnpackController::Completed()
{
bool bCleanupSuccess = Cleanup();
if (m_bUnpackOK && bCleanupSuccess)
{
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->SetUnpackStatus(PostInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && !m_bUnpackStartError && !GetTerminated() && HasParFiles())
{
RequestParCheck(false);
}
else
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->SetUnpackStatus(PostInfo::usFailure);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck(bool bRename)
{
PrintMessage(Message::mkInfo, "%s requested %s", m_szInfoNameUp, bRename ? "par-rename": "par-check/repair");
if (bRename)
{
m_pPostInfo->SetRequestParRename(true);
}
else
{
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
bool UnpackController::HasParFiles()
{
return ParCoordinator::FindMainPars(m_szDestDir, NULL);
}
bool UnpackController::HasBrokenFiles()
{
char szBrokenLog[1024];
snprintf(szBrokenLog, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_brokenlog.txt");
szBrokenLog[1024-1] = '\0';
return Util::FileExists(szBrokenLog);
}
void UnpackController::CreateUnpackDir()
{
if (strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir())))
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
Util::ForceDirectories(m_szFinalDir);
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
}
else
{
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
}
m_szUnpackDir[1024-1] = '\0';
Util::ForceDirectories(m_szUnpackDir);
}
void UnpackController::CheckArchiveFiles()
{
m_bHasRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExRar.Match(filename))
{
m_bHasRarFiles = true;
}
if (regExSevenZip.Match(filename))
{
m_bHasSevenZipFiles = true;
}
if (regExSevenZipMulti.Match(filename))
{
m_bHasSevenZipMultiFiles = true;
}
}
}
}
bool UnpackController::Cleanup()
{
// By success:
// - move unpacked files to destination dir;
// - remove _unpack-dir;
// - delete archive-files.
// By failure:
// - remove _unpack-dir.
bool bOK = true;
FileList extractedFiles;
if (m_bUnpackOK)
{
// moving files back
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szFinalDir[0] != '\0' ? m_szFinalDir : m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// silently overwrite existing files
remove(szDstFile);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s", szSrcFile, szDstFile);
bOK = false;
}
extractedFiles.push_back(strdup(filename));
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szUnpackDir))
{
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
}
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
{
PrintMessage(Message::mkInfo, "Deleting archive files");
// Delete rar-files (only files which were used by unrar)
for (FileList::iterator it = m_archiveFiles.begin(); it != m_archiveFiles.end(); it++)
{
char* szFilename = *it;
if (!extractedFiles.Exists(szFilename))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFilename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", szFilename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
// Unfortunately 7-Zip doesn't print the processed archive-files to the output.
// Therefore we don't know for sure which files were extracted.
// We just delete all 7z-files in the directory.
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename)
&& regExSevenZip.Match(filename) && !extractedFiles.Exists(filename))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
m_bCleanedUpDisk = true;
}
extractedFiles.Clear();
return bOK;
}
/**
* Unrar prints progress information into the same line using backspace control character.
* In order to print progress continuously we analyze the output after every char
* and update post-job progress information.
*/
bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
bool bPrinted = false;
int i = 0;
for (; i < iBufSize - 1; i++)
{
int ch = fgetc(pStream);
szBuf[i] = ch;
szBuf[i+1] = '\0';
if (ch == EOF)
{
break;
}
if (ch == '\n')
{
i++;
break;
}
char* szBackspace = strrchr(szBuf, '\b');
if (szBackspace)
{
if (!bPrinted)
{
char tmp[1024];
strncpy(tmp, szBuf, 1024);
tmp[1024-1] = '\0';
char* szTmpPercent = strrchr(tmp, '\b');
if (szTmpPercent)
{
*szTmpPercent = '\0';
}
if (strncmp(szBuf, "...", 3))
{
ProcessOutput(tmp);
}
bPrinted = true;
}
if (strchr(szBackspace, '%'))
{
int iPercent = atoi(szBackspace + 1);
m_pPostInfo->SetStageProgress(iPercent * 10);
}
}
}
szBuf[i] = '\0';
if (bPrinted)
{
szBuf[0] = '\0';
}
return i > 0;
}
void UnpackController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
char szMsgText[1024];
strncpy(szMsgText, szText, 1024);
szMsgText[1024-1] = '\0';
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting ", 12) &&
!strncmp(szText + 12, m_szUnpackDir, strlen(m_szUnpackDir)))
{
snprintf(szMsgText, 1024, "Extracting %s", szText + 12 + strlen(m_szUnpackDir) + 1);
szMsgText[1024-1] = '\0';
}
ScriptController::AddMessage(eKind, bDefaultKind, szMsgText);
m_pPostInfo->AppendMessage(eKind, szMsgText);
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Extracting ", 11))
{
m_pPostInfo->SetProgressLabel(szMsgText);
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting from ", 16))
{
const char *szFilename = szText + 16;
debug("Filename: %s", szFilename);
m_archiveFiles.push_back(strdup(szFilename));
m_pPostInfo->SetProgressLabel(szText);
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "All OK", 6)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "Everything is Ok", 16)))
{
m_bAllOKMessageReceived = true;
}
}
void UnpackController::Stop()
{
debug("Stopping unpack");
Thread::Stop();
Terminate();
}
void MoveController::StartMoveJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
pMoveController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pMoveController);
pMoveController->Start();
}
void MoveController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Move: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
m_szDestDir[1024-1] = '\0';
g_pDownloadQueueHolder->UnlockQueue();
info("Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
szInfoName[0] = 'M'; // uppercase
if (bOK)
{
info("%s successful", szInfoName);
// save new dest dir
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
g_pDownloadQueueHolder->UnlockQueue();
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool MoveController::MoveFiles()
{
bool bOK = true;
bOK = Util::ForceDirectories(m_szDestDir);
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// prevent overwriting of existing files
int dupcount = 0;
while (Util::FileExists(szDstFile))
{
dupcount++;
snprintf(szDstFile, 1024, "%s%c%s_duplicate%d", m_szDestDir, PATH_SEPARATOR, filename, dupcount);
szDstFile[1024-1] = '\0';
}
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s! Errcode: %i", szSrcFile, szDstFile, errno);
bOK = false;
}
}
}
Util::RemoveDirectory(m_szInterDir);
return bOK;
}

109
Unpack.h Normal file
View File

@@ -0,0 +1,109 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef UNPACK_H
#define UNPACK_H
#include <deque>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "ScriptController.h"
class UnpackController : public Thread, public ScriptController
{
private:
enum EUnpacker
{
upUnrar,
upSevenZip
};
typedef std::deque<char*> FileListBase;
class FileList : public FileListBase
{
public:
void Clear();
bool Exists(const char* szFilename);
};
private:
PostInfo* m_pPostInfo;
char m_szName[1024];
char m_szInfoName[1024];
char m_szInfoNameUp[1024];
char m_szDestDir[1024];
char m_szFinalDir[1024];
char m_szUnpackDir[1024];
char m_szPassword[1024];
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bCleanedUpDisk;
EUnpacker m_eUnpacker;
FileList m_archiveFiles;
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
void ExecuteUnrar();
void ExecuteSevenZip(bool bMultiVolumes);
void Completed();
void CreateUnpackDir();
bool Cleanup();
bool HasParFiles();
bool HasBrokenFiles();
void CheckArchiveFiles();
#ifndef DISABLE_PARCHECK
void RequestParCheck(bool bRename);
#endif
public:
virtual ~UnpackController();
virtual void Run();
virtual void Stop();
static void StartUnpackJob(PostInfo* pPostInfo);
};
class MoveController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szInterDir[1024];
char m_szDestDir[1024];
bool MoveFiles();
public:
virtual void Run();
static void StartMoveJob(PostInfo* pPostInfo);
};
#endif

455
UrlCoordinator.cpp Normal file
View File

@@ -0,0 +1,455 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "UrlCoordinator.h"
#include "Options.h"
#include "WebDownloader.h"
#include "DiskState.h"
#include "Log.h"
#include "Util.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
extern QueueCoordinator* g_pQueueCoordinator;
UrlDownloader::UrlDownloader() : WebDownloader()
{
m_szCategory = NULL;
}
UrlDownloader::~UrlDownloader()
{
if (m_szCategory)
{
free(m_szCategory);
}
}
void UrlDownloader::ProcessHeader(const char* szLine)
{
WebDownloader::ProcessHeader(szLine);
if (!strncmp(szLine, "X-DNZB-Category: ", 17))
{
if (m_szCategory)
{
free(m_szCategory);
}
const char *szCat = szLine + 17;
int iCatLen = strlen(szCat);
// trim trailing CR/LF/spaces
while (iCatLen > 0 && (szCat[iCatLen-1] == '\n' || szCat[iCatLen-1] == '\r' || szCat[iCatLen-1] == ' ')) iCatLen--;
m_szCategory = (char*)malloc(iCatLen + 1);
strncpy(m_szCategory, szCat, iCatLen);
m_szCategory[iCatLen] = '\0';
debug("Category: %s", m_szCategory);
}
}
UrlCoordinator::UrlCoordinator()
{
debug("Creating UrlCoordinator");
m_bHasMoreJobs = true;
}
UrlCoordinator::~UrlCoordinator()
{
debug("Destroying UrlCoordinator");
// Cleanup
debug("Deleting UrlDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
delete *it;
}
m_ActiveDownloads.clear();
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
{
delete *it;
}
pDownloadQueue->GetUrlQueue()->clear();
g_pQueueCoordinator->UnlockQueue();
debug("UrlCoordinator destroyed");
}
void UrlCoordinator::Run()
{
debug("Entering UrlCoordinator-loop");
int iResetCounter = 0;
while (!IsStopped())
{
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
// start download for next URL
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
{
UrlInfo* pUrlInfo;
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
if (bHasMoreUrls && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
{
StartUrlDownload(pUrlInfo);
}
}
g_pQueueCoordinator->UnlockQueue();
}
int iSleepInterval = 100;
usleep(iSleepInterval * 1000);
iResetCounter += iSleepInterval;
if (iResetCounter >= 1000)
{
// this code should not be called too often, once per second is OK
ResetHangingDownloads();
iResetCounter = 0;
}
}
// waiting for downloads
debug("UrlCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
g_pQueueCoordinator->LockQueue();
completed = m_ActiveDownloads.size() == 0;
g_pQueueCoordinator->UnlockQueue();
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("UrlCoordinator: Downloads are completed");
debug("Exiting UrlCoordinator-loop");
}
void UrlCoordinator::Stop()
{
Thread::Stop();
debug("Stopping UrlDownloads");
g_pQueueCoordinator->LockQueue();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
(*it)->Stop();
}
g_pQueueCoordinator->UnlockQueue();
debug("UrlDownloads are notified");
}
void UrlCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
{
return;
}
g_pQueueCoordinator->LockQueue();
time_t tm = ::time(NULL);
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
UrlDownloader* pUrlDownloader = *it;
if (tm - pUrlDownloader->GetLastUpdateTime() > TimeOut &&
pUrlDownloader->GetStatus() == UrlDownloader::adRunning)
{
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
debug("Terminating hanging download %s", pUrlDownloader->GetInfoName());
if (pUrlDownloader->Terminate())
{
error("Terminated hanging download %s", pUrlDownloader->GetInfoName());
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
}
else
{
error("Could not terminate hanging download %s", pUrlDownloader->GetInfoName());
}
m_ActiveDownloads.erase(it);
// it's not safe to destroy pUrlDownloader, because the state of object is unknown
delete pUrlDownloader;
it = m_ActiveDownloads.begin();
continue;
}
it++;
}
g_pQueueCoordinator->UnlockQueue();
}
void UrlCoordinator::LogDebugInfo()
{
debug(" UrlCoordinator");
debug(" ----------------");
g_pQueueCoordinator->LockQueue();
debug(" Active Downloads: %i", m_ActiveDownloads.size());
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
UrlDownloader* pUrlDownloader = *it;
pUrlDownloader->LogDebugInfo();
}
g_pQueueCoordinator->UnlockQueue();
}
void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
{
debug("Adding NZB-URL to queue");
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
/*
* Returns next URL for download.
*/
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
{
bool bOK = false;
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
{
pUrlInfo = *at;
if (pUrlInfo->GetStatus() == 0)
{
bOK = true;
break;
}
}
return bOK;
}
void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
{
debug("Starting new UrlDownloader");
UrlDownloader* pUrlDownloader = new UrlDownloader();
pUrlDownloader->SetAutoDestroy(true);
pUrlDownloader->Attach(this);
pUrlDownloader->SetUrlInfo(pUrlInfo);
pUrlDownloader->SetURL(pUrlInfo->GetURL());
char tmp[1024];
pUrlInfo->GetName(tmp, 1024);
pUrlDownloader->SetInfoName(tmp);
snprintf(tmp, 1024, "%surl-%i.tmp", g_pOptions->GetTempDir(), pUrlInfo->GetID());
tmp[1024-1] = '\0';
pUrlDownloader->SetOutputFilename(tmp);
pUrlInfo->SetStatus(UrlInfo::aiRunning);
m_ActiveDownloads.push_back(pUrlDownloader);
pUrlDownloader->Start();
}
void UrlCoordinator::Update(Subject* Caller, void* Aspect)
{
debug("Notification from UrlDownloader received");
UrlDownloader* pUrlDownloader = (UrlDownloader*) Caller;
if ((pUrlDownloader->GetStatus() == WebDownloader::adFinished) ||
(pUrlDownloader->GetStatus() == WebDownloader::adFailed) ||
(pUrlDownloader->GetStatus() == WebDownloader::adRetry))
{
UrlCompleted(pUrlDownloader);
}
}
void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
{
debug("URL downloaded");
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
if (pUrlDownloader->GetStatus() == WebDownloader::adFinished)
{
pUrlInfo->SetStatus(UrlInfo::aiFinished);
}
else if (pUrlDownloader->GetStatus() == WebDownloader::adFailed)
{
pUrlInfo->SetStatus(UrlInfo::aiFailed);
}
else if (pUrlDownloader->GetStatus() == WebDownloader::adRetry)
{
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
}
char filename[1024];
if (pUrlDownloader->GetOriginalFilename())
{
strncpy(filename, pUrlDownloader->GetOriginalFilename(), 1024);
filename[1024-1] = '\0';
}
else
{
strncpy(filename, Util::BaseFileName(pUrlInfo->GetURL()), 1024);
filename[1024-1] = '\0';
// TODO: decode URL escaping
}
Util::MakeValidFilename(filename, '_', false);
debug("Filename: [%s]", filename);
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// delete Download from Queue
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
UrlDownloader* pa = *it;
if (pa == pUrlDownloader)
{
m_ActiveDownloads.erase(it);
break;
}
}
bool bDeleteObj = false;
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished || pUrlInfo->GetStatus() == UrlInfo::aiFailed)
{
// delete UrlInfo from Queue
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
{
UrlInfo* pa = *it;
if (pa == pUrlInfo)
{
pDownloadQueue->GetUrlQueue()->erase(it);
break;
}
}
bDeleteObj = true;
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() == UrlInfo::aiFailed)
{
HistoryInfo* pHistoryInfo = new HistoryInfo(pUrlInfo);
pHistoryInfo->SetTime(time(NULL));
pDownloadQueue->GetHistoryList()->push_front(pHistoryInfo);
bDeleteObj = false;
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
}
g_pQueueCoordinator->UnlockQueue();
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
{
// add nzb-file to download queue
AddToNZBQueue(pUrlInfo, pUrlDownloader->GetOutputFilename(), filename, pUrlDownloader->GetCategory());
}
if (bDeleteObj)
{
delete pUrlInfo;
}
}
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
{
info("Queue downloaded collection %s", szOriginalFilename);
NZBFile* pNZBFile = NZBFile::CreateFromFile(szTempFilename, pUrlInfo->GetCategory());
if (pNZBFile)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
pNZBFile->GetNZBInfo()->SetFilename(pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename);
if (strlen(pUrlInfo->GetCategory()) > 0)
{
pNZBFile->GetNZBInfo()->SetCategory(pUrlInfo->GetCategory());
}
else if (szOriginalCategory)
{
pNZBFile->GetNZBInfo()->SetCategory(szOriginalCategory);
}
pNZBFile->GetNZBInfo()->BuildDestDirName();
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(pUrlInfo->GetPriority());
pFileInfo->SetPaused(pUrlInfo->GetAddPaused());
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pUrlInfo->GetAddTop());
delete pNZBFile;
info("Collection %s added to queue", szOriginalFilename);
}
else
{
error("Could not add downloaded collection %s to queue", szOriginalFilename);
}
}

86
UrlCoordinator.h Normal file
View File

@@ -0,0 +1,86 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef URLCOORDINATOR_H
#define URLCOORDINATOR_H
#include <deque>
#include <list>
#include <time.h>
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "Observer.h"
class UrlDownloader;
class UrlCoordinator : public Thread, public Observer, public Subject
{
public:
typedef std::list<UrlDownloader*> ActiveDownloads;
private:
ActiveDownloads m_ActiveDownloads;
bool m_bHasMoreJobs;
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
void StartUrlDownload(UrlInfo* pUrlInfo);
void UrlCompleted(UrlDownloader* pUrlDownloader);
void ResetHangingDownloads();
void AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory);
public:
UrlCoordinator();
virtual ~UrlCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* Caller, void* Aspect);
// Editing the queue
void AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst);
bool HasMoreJobs() { return m_bHasMoreJobs; }
void LogDebugInfo();
};
class UrlDownloader : public WebDownloader
{
private:
UrlInfo* m_pUrlInfo;
char* m_szCategory;
protected:
virtual void ProcessHeader(const char* szLine);
public:
UrlDownloader();
~UrlDownloader();
void SetUrlInfo(UrlInfo* pUrlInfo) { m_pUrlInfo = pUrlInfo; }
UrlInfo* GetUrlInfo() { return m_pUrlInfo; }
const char* GetCategory() { return m_szCategory; }
};
#endif

1314
Util.cpp
View File

File diff suppressed because it is too large Load Diff

233
Util.h
View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -29,7 +29,6 @@
#ifdef WIN32
#include <stdio.h>
#include <io.h>
#include <sys/timeb.h>
#else
#include <dirent.h>
#endif
@@ -58,18 +57,222 @@ public:
const char* Next();
};
char* BaseFileName(const char* filename);
void NormalizePathSeparators(char* szPath);
bool ForceDirectories(const char* szPath);
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
bool SetFileSize(const char* szFilename, int iSize);
void MakeValidFilename(char* szFilename, char cReplaceChar);
class Util
{
public:
long long JoinInt64(unsigned int Hi, unsigned int Lo);
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo);
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool CreateSparseFile(const char* szFilename, int iSize);
static bool TruncateFile(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
static bool FileExists(const char* szFilename);
static bool DirectoryExists(const char* szDirFilename);
static bool CreateDirectory(const char* szDirFilename);
static bool RemoveDirectory(const char* szDirFilename);
static bool DeleteDirectoryWithContent(const char* szDirFilename);
static bool ForceDirectories(const char* szPath);
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
static bool SetCurrentDirectory(const char* szDirFilename);
static long long FileSize(const char* szFilename);
static long long FreeDiskSize(const char* szPath);
static bool DirEmpty(const char* szDirFilename);
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
#ifndef WIN32
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
float EqualTime(_timeval* t1, _timeval* t2);
bool EmptyTime(_timeval* t);
float DiffTime(_timeval* t1, _timeval* t2);
/*
* Split command line int arguments.
* Uses spaces and single quotation marks as separators.
* Returns bool if sucessful or false if bad escaping was detected.
* Parameter "argv" may be NULL if only a syntax check is needed.
* Parsed parameters returned in Array "argv", which contains at least one element.
* The last element in array is NULL.
* Restrictions: the number of arguments is limited to 100 and each arguments must
* be maximum 1024 chars long.
* If these restrictions are exceeded, only first 100 arguments and only first 1024
* for each argument are returned (the functions still returns "true").
*/
static bool SplitCommandLine(const char* szCommandLine, char*** argv);
static long long JoinInt64(unsigned long Hi, unsigned long Lo);
static void SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo);
/**
* Int64ToFloat converts Int64 to float.
* Simple (float)Int64 does not work on all compilers,
* for example on ARM for NSLU2 (unslung).
*/
static float Int64ToFloat(long long Int64);
static void TrimRight(char* szStr);
/*
* Returns program version and revision number as string formatted like "0.7.0-r295".
* If revision number is not available only version is returned ("0.7.0").
*/
static const char* VersionRevision() { return VersionRevisionBuf; };
/*
* Initialize buffer for program version and revision number.
* This function must be called during program initialization before any
* call to "VersionRevision()".
*/
static void InitVersionRevision();
static char VersionRevisionBuf[40];
};
class WebUtil
{
public:
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
/*
* Encodes string to be used as content of xml-tag.
* Returns new string allocated with malloc, it need to be freed by caller.
*/
static char* XmlEncode(const char* raw);
/*
* Decodes string from xml.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void XmlDecode(char* raw);
/*
* Returns pointer to tag-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* XmlFindTag(const char* szXml, const char* szTag, int* pValueLength);
/*
* Parses tag-content into szValueBuf.
*/
static bool XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd);
/*
* Creates JSON-string by replace the certain characters with escape-sequences.
* Returns new string allocated with malloc, it need to be freed by caller.
*/
static char* JsonEncode(const char* raw);
/*
* Decodes JSON-string.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void JsonDecode(char* raw);
/*
* Returns pointer to field-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength);
/*
* Returns pointer to field-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
/*
* Unquote http quoted string.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void HttpUnquote(char* raw);
};
class URL
{
private:
char* m_szAddress;
char* m_szProtocol;
char* m_szUser;
char* m_szPassword;
char* m_szHost;
char* m_szResource;
int m_iPort;
bool m_bTLS;
bool m_bValid;
void ParseURL();
public:
URL(const char* szAddress);
~URL();
bool IsValid() { return m_bValid; }
const char* GetAddress() { return m_szAddress; }
const char* GetProtocol() { return m_szProtocol; }
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
const char* GetHost() { return m_szHost; }
const char* GetResource() { return m_szResource; }
int GetPort() { return m_iPort; }
bool GetTLS() { return m_bTLS; }
};
class RegEx
{
private:
void* m_pContext;
bool m_bValid;
public:
RegEx(const char *szPattern);
~RegEx();
bool IsValid() { return m_bValid; }
bool Match(const char *szStr);
};
#ifndef DISABLE_GZIP
class ZLib
{
public:
/*
* calculates the size required for output buffer
*/
static unsigned int GZipLen(int iInputBufferLength);
/*
* returns the size of bytes written to szOutputBuffer or 0 if the buffer is too small or an error occured.
*/
static unsigned int GZip(const void* szInputBuffer, int iInputBufferLength, void* szOutputBuffer, int iOutputBufferLength);
};
class GUnzipStream
{
public:
enum EStatus
{
zlError,
zlFinished,
zlOK
};
private:
void* m_pZStream;
void* m_pOutputBuffer;
int m_iBufferSize;
public:
GUnzipStream(int BufferSize);
~GUnzipStream();
/*
* set next memory block for uncompression
*/
void Write(const void *pInputBuffer, int iInputBufferLength);
/*
* get next uncompressed memory block.
* iOutputBufferLength - the size of uncompressed block. if it is "0" the next compressed block must be provided via "Write".
*/
EStatus Read(const void **pOutputBuffer, int *iOutputBufferLength);
};
#endif
#endif

679
WebDownloader.cpp Normal file
View File

@@ -0,0 +1,679 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#include <sys/time.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "WebDownloader.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
WebDownloader::WebDownloader()
{
debug("Creating WebDownloader");
m_szURL = NULL;
m_szOutputFilename = NULL;
m_pConnection = NULL;
m_szInfoName = NULL;
m_bConfirmedLength = false;
m_eStatus = adUndefined;
m_szOriginalFilename = NULL;
SetLastUpdateTimeNow();
}
WebDownloader::~WebDownloader()
{
debug("Destroying WebDownloader");
if (m_szURL)
{
free(m_szURL);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
if (m_szOriginalFilename)
{
free(m_szOriginalFilename);
}
}
void WebDownloader::SetOutputFilename(const char* v)
{
m_szOutputFilename = strdup(v);
}
void WebDownloader::SetInfoName(const char* v)
{
m_szInfoName = strdup(v);
}
void WebDownloader::SetURL(const char * szURL)
{
m_szURL = strdup(szURL);
}
void WebDownloader::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void WebDownloader::Run()
{
debug("Entering WebDownloader-loop");
SetStatus(adRunning);
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedConnectRetries = iRemainedDownloadRetries > 10 ? iRemainedDownloadRetries : 10;
EStatus Status = adFailed;
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
{
SetLastUpdateTimeNow();
Status = Download();
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
&& !IsStopped() && !(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
usleep(100 * 1000);
msec += 100;
}
}
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
}
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
{
break;
}
if (Status != adConnectError)
{
iRemainedDownloadRetries--;
}
else
{
iRemainedConnectRetries--;
}
}
if (Status != adFinished && Status != adRetry)
{
Status = adFailed;
}
if (Status == adFailed)
{
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
}
else
{
error("Download %s failed", m_szInfoName);
}
}
if (Status == adFinished)
{
detail("Download %s completed", m_szInfoName);
}
SetStatus(Status);
debug("Exiting WebDownloader-loop");
}
WebDownloader::EStatus WebDownloader::Download()
{
EStatus Status = adRunning;
URL url(m_szURL);
Status = CreateConnection(&url);
if (Status != adRunning)
{
return Status;
}
m_pConnection->SetSuppressErrors(false);
// connection
bool bConnected = m_pConnection->Connect();
if (!bConnected || IsStopped())
{
FreeConnection();
return adConnectError;
}
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s", m_szInfoName);
SendHeaders(&url);
Status = DownloadHeaders();
if (Status == adRunning)
{
Status = DownloadBody();
}
if (IsStopped())
{
Status = adFailed;
}
FreeConnection();
if (Status != adFinished)
{
// Download failed, delete broken output file
remove(m_szOutputFilename);
}
return Status;
}
WebDownloader::EStatus WebDownloader::CreateConnection(URL *pUrl)
{
if (!pUrl->IsValid())
{
error("URL is not valid: %s", pUrl->GetAddress());
return adFatalError;
}
int iPort = pUrl->GetPort();
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "http"))
{
iPort = 80;
}
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "https"))
{
iPort = 443;
}
if (strcasecmp(pUrl->GetProtocol(), "http") && strcasecmp(pUrl->GetProtocol(), "https"))
{
error("Unsupported protocol in URL: %s", pUrl->GetAddress());
return adFatalError;
}
#ifdef DISABLE_TLS
if (!strcasecmp(pUrl->GetProtocol(), "https"))
{
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", pUrl->GetAddress());
return adFatalError;
}
#endif
bool bTLS = !strcasecmp(pUrl->GetProtocol(), "https");
m_pConnection = new Connection(pUrl->GetHost(), iPort, bTLS);
return adRunning;
}
void WebDownloader::SendHeaders(URL *pUrl)
{
char tmp[1024];
// retrieve file
snprintf(tmp, 1024, "GET %s HTTP/1.0\r\n", pUrl->GetResource());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
snprintf(tmp, 1024, "User-Agent: nzbget/%s\r\n", Util::VersionRevision());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
snprintf(tmp, 1024, "Host: %s\r\n", pUrl->GetHost());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
m_pConnection->WriteLine("Accept: */*\r\n");
#ifndef DISABLE_GZIP
m_pConnection->WriteLine("Accept-Encoding: gzip\r\n");
#endif
m_pConnection->WriteLine("Connection: close\r\n");
m_pConnection->WriteLine("\r\n");
}
WebDownloader::EStatus WebDownloader::DownloadHeaders()
{
EStatus Status = adRunning;
m_bConfirmedLength = false;
const int LineBufSize = 1024*10;
char* szLineBuf = (char*)malloc(LineBufSize);
m_iContentLen = -1;
bool bFirstLine = true;
m_bGZip = false;
// Headers
while (!IsStopped())
{
SetLastUpdateTimeNow();
int iLen = 0;
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
if (bFirstLine)
{
Status = CheckResponse(szLineBuf);
if (Status != adRunning)
{
break;
}
bFirstLine = false;
}
// Have we encountered a timeout?
if (!line)
{
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", m_szInfoName);
}
Status = adFailed;
break;
}
debug("Header: %s", line);
// detect body of response
if (*line == '\r' || *line == '\n')
{
if (m_iContentLen == -1 && !m_bGZip)
{
warn("URL %s: Content-Length is not submitted by server, cannot verify whether the file is complete", m_szInfoName);
}
break;
}
ProcessHeader(line);
}
free(szLineBuf);
return Status;
}
WebDownloader::EStatus WebDownloader::DownloadBody()
{
EStatus Status = adRunning;
m_pOutFile = NULL;
bool bEnd = false;
const int LineBufSize = 1024*10;
char* szLineBuf = (char*)malloc(LineBufSize);
int iWrittenLen = 0;
#ifndef DISABLE_GZIP
m_pGUnzipStream = NULL;
if (m_bGZip)
{
m_pGUnzipStream = new GUnzipStream(1024*10);
}
#endif
// Body
while (!IsStopped())
{
SetLastUpdateTimeNow();
char* szBuffer;
int iLen;
m_pConnection->ReadBuffer(&szBuffer, &iLen);
if (iLen == 0)
{
iLen = m_pConnection->TryRecv(szLineBuf, LineBufSize);
szBuffer = szLineBuf;
}
// Have we encountered a timeout?
if (iLen <= 0)
{
if (m_iContentLen == -1)
{
bEnd = true;
break;
}
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", m_szInfoName);
}
Status = adFailed;
break;
}
// write to output file
if (!Write(szBuffer, iLen))
{
Status = adFatalError;
break;
}
iWrittenLen += iLen;
//detect end of file
if (iWrittenLen == m_iContentLen || (m_iContentLen == -1 && m_bGZip && m_bConfirmedLength))
{
bEnd = true;
break;
}
}
free(szLineBuf);
#ifndef DISABLE_GZIP
if (m_pGUnzipStream)
{
delete m_pGUnzipStream;
}
#endif
if (m_pOutFile)
{
fclose(m_pOutFile);
}
if (!bEnd && Status == adRunning && !IsStopped())
{
warn("URL %s failed: file incomplete", m_szInfoName);
Status = adFailed;
}
if (bEnd)
{
Status = adFinished;
}
return Status;
}
WebDownloader::EStatus WebDownloader::CheckResponse(const char* szResponse)
{
if (!szResponse)
{
if (!IsStopped())
{
warn("URL %s: Connection closed by remote host", m_szInfoName);
}
return adConnectError;
}
const char* szHTTPResponse = strchr(szResponse, ' ');
if (strncmp(szResponse, "HTTP", 4) || !szHTTPResponse)
{
warn("URL %s failed: %s", m_szInfoName, szResponse);
return adFailed;
}
szHTTPResponse++;
if (!strncmp(szHTTPResponse, "400", 3) || !strncmp(szHTTPResponse, "499", 3))
{
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
return adConnectError;
}
else if (!strncmp(szHTTPResponse, "404", 3))
{
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
return adNotFound;
}
else if (!strncmp(szHTTPResponse, "200", 3))
{
// OK
return adRunning;
}
else
{
// unknown error, no special handling
warn("URL %s failed: %s", m_szInfoName, szResponse);
return adFailed;
}
}
void WebDownloader::ProcessHeader(const char* szLine)
{
if (!strncmp(szLine, "Content-Length: ", 16))
{
m_iContentLen = atoi(szLine + 16);
m_bConfirmedLength = true;
}
if (!strncmp(szLine, "Content-Encoding: gzip", 22))
{
m_bGZip = true;
}
if (!strncmp(szLine, "Content-Disposition: ", 21))
{
ParseFilename(szLine);
}
}
void WebDownloader::ParseFilename(const char* szContentDisposition)
{
// Examples:
// Content-Disposition: attachment; filename="fname.ext"
// Content-Disposition: attachement;filename=fname.ext
// Content-Disposition: attachement;filename=fname.ext;
const char *p = strstr(szContentDisposition, "filename");
if (!p)
{
return;
}
p = strchr(p, '=');
if (!p)
{
return;
}
p++;
while (*p == ' ') p++;
char fname[1024];
strncpy(fname, p, 1024);
fname[1024-1] = '\0';
char *pe = fname + strlen(fname) - 1;
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
*pe = '\0';
pe--;
}
WebUtil::HttpUnquote(fname);
if (m_szOriginalFilename)
{
free(m_szOriginalFilename);
}
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
debug("OriginalFilename: %s", m_szOriginalFilename);
}
bool WebDownloader::Write(void* pBuffer, int iLen)
{
if (!m_pOutFile && !PrepareFile())
{
return false;
}
#ifndef DISABLE_GZIP
if (m_bGZip)
{
m_pGUnzipStream->Write(pBuffer, iLen);
const void *pOutBuf;
int iOutLen = 1;
while (iOutLen > 0)
{
GUnzipStream::EStatus eGZStatus = m_pGUnzipStream->Read(&pOutBuf, &iOutLen);
if (eGZStatus == GUnzipStream::zlError)
{
error("URL %s: GUnzip failed", m_szInfoName);
return false;
}
if (iOutLen > 0 && fwrite(pOutBuf, 1, iOutLen, m_pOutFile) <= 0)
{
return false;
}
if (eGZStatus == GUnzipStream::zlFinished)
{
m_bConfirmedLength = true;
return true;
}
}
return true;
}
else
#endif
return fwrite(pBuffer, 1, iLen, m_pOutFile) > 0;
}
bool WebDownloader::PrepareFile()
{
// prepare file for writing
const char* szFilename = m_szOutputFilename;
m_pOutFile = fopen(szFilename, "wb");
if (!m_pOutFile)
{
error("Could not %s file %s", "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() > 0)
{
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
}
return true;
}
void WebDownloader::LogDebugInfo()
{
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&m_tLastUpdateTime, szTime, 50);
#else
ctime_r(&m_tLastUpdateTime, szTime);
#endif
debug(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(m_szOutputFilename));
}
void WebDownloader::Stop()
{
debug("Trying to stop WebDownloader");
Thread::Stop();
m_mutexConnection.Lock();
if (m_pConnection)
{
m_pConnection->SetSuppressErrors(true);
m_pConnection->Cancel();
}
m_mutexConnection.Unlock();
debug("WebDownloader stopped successfully");
}
bool WebDownloader::Terminate()
{
Connection* pConnection = m_pConnection;
bool terminated = Kill();
if (terminated && pConnection)
{
debug("Terminating connection");
pConnection->SetSuppressErrors(true);
pConnection->Cancel();
pConnection->Disconnect();
delete pConnection;
}
return terminated;
}
void WebDownloader::FreeConnection()
{
if (m_pConnection)
{
debug("Releasing connection");
m_mutexConnection.Lock();
if (m_pConnection->GetStatus() == Connection::csCancelled)
{
m_pConnection->Disconnect();
}
delete m_pConnection;
m_pConnection = NULL;
m_mutexConnection.Unlock();
}
}

103
WebDownloader.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef WEBDOWNLOADER_H
#define WEBDOWNLOADER_H
#include <time.h>
#include "Observer.h"
#include "Thread.h"
#include "Connection.h"
#include "Util.h"
class WebDownloader : public Thread, public Subject
{
public:
enum EStatus
{
adUndefined,
adRunning,
adFinished,
adFailed,
adRetry,
adNotFound,
adConnectError,
adFatalError
};
private:
char* m_szURL;
char* m_szOutputFilename;
Connection* m_pConnection;
Mutex m_mutexConnection;
EStatus m_eStatus;
time_t m_tLastUpdateTime;
char* m_szInfoName;
FILE* m_pOutFile;
int m_iContentLen;
bool m_bConfirmedLength;
char* m_szOriginalFilename;
bool m_bGZip;
#ifndef DISABLE_GZIP
GUnzipStream* m_pGUnzipStream;
#endif
void SetStatus(EStatus eStatus);
bool Write(void* pBuffer, int iLen);
bool PrepareFile();
void FreeConnection();
EStatus CheckResponse(const char* szResponse);
EStatus Download();
EStatus CreateConnection(URL *pUrl);
void ParseFilename(const char* szContentDisposition);
void SendHeaders(URL *pUrl);
EStatus DownloadHeaders();
EStatus DownloadBody();
protected:
virtual void ProcessHeader(const char* szLine);
public:
WebDownloader();
~WebDownloader();
EStatus GetStatus() { return m_eStatus; }
virtual void Run();
virtual void Stop();
bool Terminate();
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void SetURL(const char* szURL);
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* v);
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
bool GetConfirmedLength() { return m_bConfirmedLength; }
const char* GetOriginalFilename() { return m_szOriginalFilename; }
void LogDebugInfo();
};
#endif

481
WebServer.cpp Normal file
View File

@@ -0,0 +1,481 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "WebServer.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
static const char* ERR_HTTP_BAD_REQUEST = "400 Bad Request";
static const char* ERR_HTTP_NOT_FOUND = "404 Not Found";
static const char* ERR_HTTP_SERVICE_UNAVAILABLE = "503 Service Unavailable";
static const int MAX_UNCOMPRESSED_SIZE = 500;
//*****************************************************************
// WebProcessor
WebProcessor::WebProcessor()
{
m_pConnection = NULL;
m_szRequest = NULL;
m_szUrl = NULL;
m_szOrigin = NULL;
}
WebProcessor::~WebProcessor()
{
if (m_szRequest)
{
free(m_szRequest);
}
if (m_szUrl)
{
free(m_szUrl);
}
if (m_szOrigin)
{
free(m_szOrigin);
}
}
void WebProcessor::SetUrl(const char* szUrl)
{
m_szUrl = strdup(szUrl);
}
void WebProcessor::Execute()
{
m_bGZip =false;
char szAuthInfo[1024];
szAuthInfo[0] = '\0';
// reading http header
char szBuffer[1024];
int iContentLen = 0;
while (char* p = m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
if (char* pe = strrchr(p, '\r')) *pe = '\0';
debug("header=%s", p);
if (!strncasecmp(p, "Content-Length: ", 16))
{
iContentLen = atoi(p + 16);
}
if (!strncasecmp(p, "Authorization: Basic ", 21))
{
char* szAuthInfo64 = p + 21;
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
{
error("invalid-request: auth-info too big");
return;
}
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
}
if (!strncasecmp(p, "Accept-Encoding: ", 17))
{
m_bGZip = strstr(p, "gzip");
}
if (!strncasecmp(p, "Origin: ", 8))
{
m_szOrigin = strdup(p + 8);
}
if (*p == '\0')
{
break;
}
}
debug("URL=%s", m_szUrl);
debug("Authorization=%s", szAuthInfo);
if (m_eHttpMethod == hmPost && iContentLen <= 0)
{
error("invalid-request: content length is 0");
return;
}
if (m_eHttpMethod == hmOptions)
{
SendOptionsResponse();
return;
}
// remove subfolder "nzbget" from the path (if exists)
// http://localhost:6789/nzbget/username:password/jsonrpc -> http://localhost:6789/username:password/jsonrpc
if (!strncmp(m_szUrl, "/nzbget/", 8))
{
char* sz_OldUrl = m_szUrl;
m_szUrl = strdup(m_szUrl + 7);
free(sz_OldUrl);
}
// http://localhost:6789/nzbget -> http://localhost:6789
if (!strcmp(m_szUrl, "/nzbget"))
{
char szRedirectURL[1024];
snprintf(szRedirectURL, 1024, "%s/", m_szUrl);
szRedirectURL[1024-1] = '\0';
SendRedirectResponse(szRedirectURL);
return;
}
// authorization via URL in format:
// http://localhost:6789/username:password/jsonrpc
char* pauth1 = strchr(m_szUrl + 1, ':');
char* pauth2 = strchr(m_szUrl + 1, '/');
if (pauth1 && pauth1 < pauth2)
{
char* pstart = m_szUrl + 1;
int iLen = 0;
char* pend = strchr(pstart + 1, '/');
if (pend)
{
iLen = (int)(pend - pstart < (int)sizeof(szAuthInfo) - 1 ? pend - pstart : (int)sizeof(szAuthInfo) - 1);
}
else
{
iLen = strlen(pstart);
}
strncpy(szAuthInfo, pstart, iLen);
szAuthInfo[iLen] = '\0';
char* sz_OldUrl = m_szUrl;
m_szUrl = strdup(pend);
free(sz_OldUrl);
}
debug("Final URL=%s", m_szUrl);
if (strlen(szAuthInfo) == 0)
{
SendAuthResponse();
return;
}
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if (strcmp(szAuthInfo, "nzbget") || strcmp(pw, g_pOptions->GetControlPassword()))
{
warn("request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
SendAuthResponse();
return;
}
if (m_eHttpMethod == hmPost)
{
// reading http body (request content)
m_szRequest = (char*)malloc(iContentLen + 1);
m_szRequest[iContentLen] = '\0';
if (!m_pConnection->Recv(m_szRequest, iContentLen))
{
free(m_szRequest);
error("invalid-request: could not read data");
return;
}
debug("Request=%s", m_szRequest);
}
debug("request received from %s", m_pConnection->GetRemoteAddr());
Dispatch();
}
void WebProcessor::Dispatch()
{
if (*m_szUrl != '/')
{
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
return;
}
if (XmlRpcProcessor::IsRpcRequest(m_szUrl))
{
XmlRpcProcessor processor;
processor.SetRequest(m_szRequest);
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
processor.SetUrl(m_szUrl);
processor.Execute();
SendBodyResponse(processor.GetResponse(), strlen(processor.GetResponse()), processor.GetContentType());
return;
}
if (!g_pOptions->GetWebDir() || strlen(g_pOptions->GetWebDir()) == 0)
{
SendErrorResponse(ERR_HTTP_SERVICE_UNAVAILABLE);
return;
}
if (m_eHttpMethod != hmGet)
{
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
return;
}
// for security reasons we allow only characters "0..9 A..Z a..z . - _ /" in the URLs
// we also don't allow ".." in the URLs
for (char *p = m_szUrl; *p; p++)
{
if (!((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
*p == '.' || *p == '-' || *p == '_' || *p == '/') || (*p == '.' && p[1] == '.'))
{
SendErrorResponse(ERR_HTTP_NOT_FOUND);
return;
}
}
const char *szDefRes = "";
if (m_szUrl[strlen(m_szUrl)-1] == '/')
{
// default file in directory (if not specified) is "index.html"
szDefRes = "index.html";
}
char disk_filename[1024];
snprintf(disk_filename, sizeof(disk_filename), "%s%s%s", g_pOptions->GetWebDir(), m_szUrl + 1, szDefRes);
disk_filename[sizeof(disk_filename)-1] = '\0';
SendFileResponse(disk_filename);
}
void WebProcessor::SendAuthResponse()
{
const char* AUTH_RESPONSE_HEADER =
"HTTP/1.0 401 Unauthorized\r\n"
"WWW-Authenticate: Basic realm=\"NZBGet\"\r\n"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, AUTH_RESPONSE_HEADER, Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendOptionsResponse()
{
const char* OPTIONS_RESPONSE_HEADER =
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
//"Content-Type: plain/text\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Origin: %s\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Max-Age: 86400\r\n"
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, OPTIONS_RESPONSE_HEADER,
m_szOrigin ? m_szOrigin : "",
Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendErrorResponse(const char* szErrCode)
{
const char* RESPONSE_HEADER =
"HTTP/1.0 %s\r\n"
"Connection: close\r\n"
"Content-Length: %i\r\n"
"Content-Type: text/html\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
warn("Web-Server: %s, Resource: %s", szErrCode, m_szUrl);
char szResponseBody[1024];
snprintf(szResponseBody, 1024, "<html><head><title>%s</title></head><body>Error: %s</body></html>", szErrCode, szErrCode);
int iPageContentLen = strlen(szResponseBody);
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, RESPONSE_HEADER, szErrCode, iPageContentLen, Util::VersionRevision());
// Send the response answer
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
m_pConnection->Send(szResponseBody, iPageContentLen);
}
void WebProcessor::SendRedirectResponse(const char* szURL)
{
const char* REDIRECT_RESPONSE_HEADER =
"HTTP/1.0 301 Moved Permanently\r\n"
"Location: %s\r\n"
"Connection: close\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, REDIRECT_RESPONSE_HEADER, szURL, Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType)
{
const char* RESPONSE_HEADER =
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Origin: %s\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Max-Age: 86400\r\n"
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
"Content-Length: %i\r\n"
"%s" // Content-Type: xxx
"%s" // Content-Encoding: gzip
"Server: nzbget-%s\r\n"
"\r\n";
#ifndef DISABLE_GZIP
char *szGBuf = NULL;
bool bGZip = m_bGZip && iBodyLen > MAX_UNCOMPRESSED_SIZE;
if (bGZip)
{
unsigned int iOutLen = ZLib::GZipLen(iBodyLen);
szGBuf = (char*)malloc(iOutLen);
int iGZippedLen = ZLib::GZip(szBody, iBodyLen, szGBuf, iOutLen);
if (iGZippedLen > 0 && iGZippedLen < iBodyLen)
{
szBody = szGBuf;
iBodyLen = iGZippedLen;
}
else
{
free(szGBuf);
szGBuf = NULL;
bGZip = false;
}
}
#else
bool bGZip = false;
#endif
char szContentTypeHeader[1024];
if (szContentType)
{
snprintf(szContentTypeHeader, 1024, "Content-Type: %s\r\n", szContentType);
}
else
{
szContentTypeHeader[0] = '\0';
}
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, RESPONSE_HEADER,
m_szOrigin ? m_szOrigin : "",
iBodyLen, szContentTypeHeader,
bGZip ? "Content-Encoding: gzip\r\n" : "",
Util::VersionRevision());
// Send the request answer
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
m_pConnection->Send(szBody, iBodyLen);
#ifndef DISABLE_GZIP
if (szGBuf)
{
free(szGBuf);
}
#endif
}
void WebProcessor::SendFileResponse(const char* szFilename)
{
debug("serving file: %s", szFilename);
char *szBody;
int iBodyLen;
if (!Util::LoadFileIntoBuffer(szFilename, &szBody, &iBodyLen))
{
SendErrorResponse(ERR_HTTP_NOT_FOUND);
return;
}
// "LoadFileIntoBuffer" adds a trailing NULL, which we don't need here
iBodyLen--;
SendBodyResponse(szBody, iBodyLen, DetectContentType(szFilename));
free(szBody);
}
const char* WebProcessor::DetectContentType(const char* szFilename)
{
if (const char *szExt = strrchr(szFilename, '.'))
{
if (!strcasecmp(szExt, ".css"))
{
return "text/css";
}
else if (!strcasecmp(szExt, ".html"))
{
return "text/html";
}
else if (!strcasecmp(szExt, ".js"))
{
return "application/javascript";
}
else if (!strcasecmp(szExt, ".png"))
{
return "image/png";
}
else if (!strcasecmp(szExt, ".jpeg"))
{
return "image/jpeg";
}
else if (!strcasecmp(szExt, ".gif"))
{
return "image/gif";
}
}
return NULL;
}

67
WebServer.h Normal file
View File

@@ -0,0 +1,67 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include "Connection.h"
class WebProcessor
{
public:
enum EHttpMethod
{
hmPost,
hmGet,
hmOptions
};
private:
Connection* m_pConnection;
char* m_szRequest;
char* m_szUrl;
EHttpMethod m_eHttpMethod;
bool m_bGZip;
char* m_szOrigin;
void Dispatch();
void SendAuthResponse();
void SendOptionsResponse();
void SendErrorResponse(const char* szErrCode);
void SendFileResponse(const char* szFilename);
void SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType);
void SendRedirectResponse(const char* szURL);
const char* DetectContentType(const char* szFilename);
public:
WebProcessor();
~WebProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetUrl(const char* szUrl);
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
};
#endif

2322
XmlRpc.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

288
XmlRpc.h Normal file
View File

@@ -0,0 +1,288 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef XMLRPC_H
#define XMLRPC_H
#include "Connection.h"
class StringBuilder
{
private:
char* m_szBuffer;
int m_iBufferSize;
int m_iUsedSize;
public:
StringBuilder();
~StringBuilder();
void Append(const char* szStr);
const char* GetBuffer() { return m_szBuffer; }
};
class XmlCommand;
class XmlRpcProcessor
{
public:
enum ERpcProtocol
{
rpUndefined,
rpXmlRpc,
rpJsonRpc,
rpJsonPRpc
};
enum EHttpMethod
{
hmPost,
hmGet
};
private:
char* m_szRequest;
const char* m_szContentType;
ERpcProtocol m_eProtocol;
EHttpMethod m_eHttpMethod;
char* m_szUrl;
StringBuilder m_cResponse;
void Dispatch();
XmlCommand* CreateCommand(const char* szMethodName);
void MutliCall();
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
public:
XmlRpcProcessor();
~XmlRpcProcessor();
void Execute();
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUrl(const char* szUrl);
void SetRequest(char* szRequest) { m_szRequest = szRequest; }
const char* GetResponse() { return m_cResponse.GetBuffer(); }
const char* GetContentType() { return m_szContentType; }
static bool IsRpcRequest(const char* szUrl);
};
class XmlCommand
{
protected:
char* m_szRequest;
char* m_szRequestPtr;
char* m_szCallbackFunc;
StringBuilder m_StringBuilder;
bool m_bFault;
XmlRpcProcessor::ERpcProtocol m_eProtocol;
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
void BuildErrorResponse(int iErrCode, const char* szErrText, ...);
void BuildBoolResponse(bool bOK);
void AppendResponse(const char* szPart);
bool IsJson();
bool CheckSafeMethod();
bool NextParamAsInt(int* iValue);
bool NextParamAsBool(bool* bValue);
bool NextParamAsStr(char** szValueBuf);
const char* BoolToStr(bool bValue);
char* EncodeStr(const char* szStr);
void DecodeStr(char* szStr);
public:
XmlCommand();
virtual ~XmlCommand() {}
virtual void Execute() = 0;
void PrepareParams();
void SetRequest(char* szRequest) { m_szRequest = szRequest; m_szRequestPtr = m_szRequest; }
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
const char* GetCallbackFunc() { return m_szCallbackFunc; }
bool GetFault() { return m_bFault; }
};
class ErrorXmlCommand: public XmlCommand
{
private:
int m_iErrCode;
const char* m_szErrText;
public:
ErrorXmlCommand(int iErrCode, const char* szErrText);
virtual void Execute();
};
class PauseUnpauseXmlCommand: public XmlCommand
{
public:
enum EPauseAction
{
paDownload,
paDownload2,
paPostProcess,
paScan
};
private:
bool m_bPause;
EPauseAction m_eEPauseAction;
public:
PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction);
virtual void Execute();
};
class ScheduleResumeXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ShutdownXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ReloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class VersionXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DumpDebugXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SetDownloadRateXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class StatusXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListFilesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListGroupsXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class EditQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class PostQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class WriteLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ClearLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ScanXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class HistoryXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadUrlXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class UrlQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LoadConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SaveConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
void Save(const char *szFilename);
};
#endif

330
aclocal.m4 vendored
View File

@@ -1,7 +1,7 @@
# generated automatically by aclocal 1.10 -*- Autoconf -*-
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006 Free Software Foundation, Inc.
# 2005 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -11,12 +11,165 @@
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
m4_if(m4_PACKAGE_VERSION, [2.61],,
[m4_fatal([this file was generated for autoconf 2.61.
You have another version of autoconf. If you want to use that,
you should regenerate the build system entirely.], [63])])
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
#
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
# ----------------------------------
AC_DEFUN([PKG_PROG_PKG_CONFIG],
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
fi
if test -n "$PKG_CONFIG"; then
_pkg_min_version=m4_default([$1], [0.9.0])
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
AC_MSG_RESULT([yes])
else
AC_MSG_RESULT([no])
PKG_CONFIG=""
fi
fi[]dnl
])# PKG_PROG_PKG_CONFIG
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
#
# Check to see whether a particular set of modules exists. Similar
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
#
#
# Similar to PKG_CHECK_MODULES, make sure that the first instance of
# this or PKG_CHECK_MODULES is called, or make sure to call
# PKG_CHECK_EXISTS manually
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_EXISTS],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
if test -n "$PKG_CONFIG" && \
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
m4_ifval([$2], [$2], [:])
m4_ifvaln([$3], [else
$3])dnl
fi])
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
# ---------------------------------------------
m4_define([_PKG_CONFIG],
[if test -n "$PKG_CONFIG"; then
if test -n "$$1"; then
pkg_cv_[]$1="$$1"
else
PKG_CHECK_EXISTS([$3],
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
[pkg_failed=yes])
fi
else
pkg_failed=untried
fi[]dnl
])# _PKG_CONFIG
# _PKG_SHORT_ERRORS_SUPPORTED
# -----------------------------
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi[]dnl
])# _PKG_SHORT_ERRORS_SUPPORTED
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
# [ACTION-IF-NOT-FOUND])
#
#
# Note that if there is a possibility the first call to
# PKG_CHECK_MODULES might not happen, you should be sure to include an
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
#
#
# --------------------------------------------------------------
AC_DEFUN([PKG_CHECK_MODULES],
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
pkg_failed=no
AC_MSG_CHECKING([for $1])
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
and $1[]_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.])
if test $pkg_failed = yes; then
_PKG_SHORT_ERRORS_SUPPORTED
if test $_pkg_short_errors_supported = yes; then
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
else
$1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
fi
# Put the nasty error message in config.log where it belongs
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
ifelse([$4], , [AC_MSG_ERROR(dnl
[Package requirements ($2) were not met:
$$1_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
_PKG_TEXT
])],
[AC_MSG_RESULT([no])
$4])
elif test $pkg_failed = untried; then
ifelse([$4], , [AC_MSG_FAILURE(dnl
[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.
_PKG_TEXT
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
[$4])
else
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
AC_MSG_RESULT([yes])
ifelse([$3], , :, [$3])
fi[]dnl
])# PKG_CHECK_MODULES
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -26,29 +179,14 @@ you should regenerate the build system entirely.], [63])])
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.10'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.10], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# Call AM_AUTOMAKE_VERSION so it can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.10])dnl
_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)])
[AM_AUTOMAKE_VERSION([1.9.6])])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
@@ -105,14 +243,14 @@ am_aux_dir=`cd $ac_aux_dir && pwd`
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 8
# serial 7
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
@@ -121,10 +259,8 @@ AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
AC_SUBST([$1_TRUE])
AC_SUBST([$1_FALSE])
if $2; then
$1_TRUE=
$1_FALSE='#'
@@ -138,14 +274,15 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 9
# serial 8
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
@@ -173,7 +310,6 @@ AC_REQUIRE([AM_DEP_TRACK])dnl
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
[$1], CXX, [depcc="$CXX" am_compiler_list=],
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], UPC, [depcc="$UPC" am_compiler_list=],
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
@@ -239,7 +375,6 @@ AC_CACHE_CHECK([dependency style of $depcc],
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
@@ -292,8 +427,7 @@ if test "x$enable_dependency_tracking" != xno; then
AMDEPBACKSLASH='\'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
AC_SUBST([AMDEPBACKSLASH])
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
@@ -318,9 +452,8 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# Grep'ing the whole file is not good either: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then
# So let's grep whole file.
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
@@ -367,8 +500,8 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006 Free Software Foundation, Inc.
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -391,20 +524,16 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.60])dnl
[AC_PREREQ([2.58])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
# test to see if srcdir already configured
if test "`cd $srcdir && pwd`" != "`pwd`" &&
test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
# test whether we have cygpath
@@ -424,9 +553,6 @@ m4_ifval([$2],
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
@@ -462,10 +588,6 @@ AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES(OBJC)],
[define([AC_PROG_OBJC],
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
])
])
@@ -501,7 +623,7 @@ echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
install_sh=${install_sh-"$am_aux_dir/install-sh"}
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
@@ -579,14 +701,14 @@ rm -f confinc confmf
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 5
# serial 4
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
@@ -602,7 +724,6 @@ AC_SUBST($1)])
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
@@ -613,7 +734,7 @@ else
fi
])
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -621,23 +742,60 @@ fi
# AM_PROG_MKDIR_P
# ---------------
# Check for `mkdir -p'.
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
#
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
# created by `make install' are always world readable, even if the
# installer happens to have an overly restrictive umask (e.g. 077).
# This was a mistake. There are at least two reasons why we must not
# use `-m 0755':
# - it causes special bits like SGID to be ignored,
# - it may be too restrictive (some setups expect 775 directories).
#
# Do not use -m 0755 and let people choose whatever they expect by
# setting umask.
#
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
# Some implementations (such as Solaris 8's) are not thread-safe: if a
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
# concurrently, both version can detect that a/ is missing, but only
# one can create it and the other will error out. Consequently we
# restrict ourselves to GNU make (using the --version option ensures
# this.)
AC_DEFUN([AM_PROG_MKDIR_P],
[AC_PREREQ([2.60])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
dnl while keeping a definition of mkdir_p for backward compatibility.
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
dnl Makefile.ins that do not define MKDIR_P, so we do our own
dnl adjustment using top_builddir (which is defined more often than
dnl MKDIR_P).
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
case $mkdir_p in
[[\\/$]]* | ?:[[\\/]]*) ;;
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
esac
])
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
# We used to keeping the `.' as first argument, in order to
# allow $(mkdir_p) to be used without argument. As in
# $(mkdir_p) $(somedir)
# where $(somedir) is conditionally defined. However this is wrong
# for two reasons:
# 1. if the package is installed by a user who cannot write `.'
# make install will fail,
# 2. the above comment should most certainly read
# $(mkdir_p) $(DESTDIR)$(somedir)
# so it does not work when $(somedir) is undefined and
# $(DESTDIR) is not.
# To support the latter case, we have to write
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
# so the `.' trick is pointless.
mkdir_p='mkdir -p --'
else
# On NextStep and OpenStep, the `mkdir' command does not
# recognize any option. It will interpret all options as
# directories to create, and then abort because `.' already
# exists.
for d in ./-p ./--version;
do
test -d $d && rmdir $d
done
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
if test -f "$ac_aux_dir/mkinstalldirs"; then
mkdir_p='$(mkinstalldirs)'
else
mkdir_p='$(install_sh) -d'
fi
fi
AC_SUBST([mkdir_p])])
# Helper functions for option handling. -*- Autoconf -*-
@@ -749,21 +907,9 @@ dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.

View File

@@ -6,16 +6,22 @@
/* Define to 1 to not use curses */
#undef DISABLE_CURSES
/* Define to 1 to disable gzip-support */
#undef DISABLE_GZIP
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* Define to 1 to include support for uulib */
#undef ENABLE_UULIB
/* Define to 1 to not use TLS/SSL */
#undef DISABLE_TLS
/* Define to the name of macro which returns the name of function being
compiled */
#undef FUNCTION_MACRO_NAME
/* Define to 1 to create stacktrace on segmentation faults */
#undef HAVE_BACKTRACE
/* Define to 1 if ctime_r takes 2 arguments */
#undef HAVE_CTIME_R_2
@@ -25,6 +31,15 @@
/* Define to 1 if you have the <curses.h> header file. */
#undef HAVE_CURSES_H
/* Define to 1 if getaddrinfo is supported */
#undef HAVE_GETADDRINFO
/* Define to 1 if gethostbyname_r is supported */
#undef HAVE_GETHOSTBYNAME_R
/* Define to 1 if gethostbyname_r takes 3 arguments */
#undef HAVE_GETHOSTBYNAME_R_3
/* Define to 1 if gethostbyname_r takes 5 arguments */
#undef HAVE_GETHOSTBYNAME_R_5
@@ -37,6 +52,9 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
#undef HAVE_LIBGNUTLS
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
@@ -46,8 +64,23 @@
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 to use pragma pack directive in MessageBase.h */
#undef HAVE_PRAGMA_PACK
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
#undef HAVE_OPENSSL
/* Define to 1 if libpar2 has recent bugfixes-patch (version 2) */
#undef HAVE_PAR2_BUGFIXES_V2
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
/* Define to 1 if you have the <regex.h> header file. */
#undef HAVE_REGEX_H
/* Define to 1 if spinlocks are supported */
#undef HAVE_SPINLOCK
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -61,6 +94,9 @@
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
#undef HAVE_SYS_PRCTL_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
@@ -91,6 +127,12 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 to install an empty signal handler for SIGCHLD */
#undef SIGCHLD_HANDLER
/* Determine what socket length (socklen_t) data type is */
#undef SOCKLEN_T
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS

9498
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,41 +2,18 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 0.3.1, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.3.1)
AC_INIT(nzbget, 10.1, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 10.1)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
dnl Architecture check
AC_CANONICAL_HOST
case "$host" in
*86-*-linux*)
LIBPREF1="/usr"
CFLAGS1="${CFLAGS} -m486"
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
;;
*-linux*)
LIBPREF1="/usr"
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
;;
*-freebsd*)
LIBPREF1="/usr/local"
;;
*-solaris*)
LIBPREF1="/usr"
;;
esac
dnl
dnl Set default library path, if not specified in environment variable "LIBPREF".
dnl
if test "$LIBPREF" = ""; then
LIBPREF="$LIBPREF1"
fi
if test "$CFLAGS" = ""; then
CFLAGS="$CFLAGS1"
fi
if test "$CPPFLAGS" = ""; then
CPPFLAGS="$CPPFLAGS1"
LIBPREF="/usr"
fi
@@ -44,30 +21,23 @@ dnl
dnl Check for programs.
dnl
AC_PROG_CXX
AC_PROG_CC
AC_PROG_GCC_TRADITIONAL
AC_PROG_RANLIB
AC_PROG_MAKE_SET
AC_PATH_PROG(FALSE, false, /usr/bin/false)
AC_PATH_PROG(TRUE, true, /usr/bin/true)
AC_PATH_PROG(RM, rm, $FALSE)
AC_PATH_PROG(LN, ln, $FALSE)
AC_PATH_PROG(TAR, tar, $FALSE)
AC_PATH_PROG(AR, ar, $FALSE)
AC_PATH_PROG(MAKE, make, $FALSE)
AC_PATH_PROG(CXXCPP, cpp, $FALSE)
AC_PATH_PROG(MV, mv, $FALSE)
AC_PATH_PROG(MKDIR, mkdir, $FALSE)
AC_PATH_PROG(CP, cp, $FALSE)
AC_PROG_INSTALL
dnl
dnl Do all tests with c++ compiler.
dnl
AC_LANG(C++)
dnl
dnl Checks for header files.
dnl
dnl AC_CHECK_HEADERS(stdarg.h time.h stdlib.h stdio.h unistd.h errno.h string.h sys/stat.h sys/time.h)
dnl AC_CHECK_HEADERS(libgen.h pwd.h getopt.h dirent.h fcntl.h pthread.h semaphore.h)
dnl AC_CHECK_HEADERS(sys/socket.h sys/types.h netinet/in.h arpa/inet.h netdb.h)
AC_CHECK_HEADERS(sys/prctl.h)
AC_CHECK_HEADERS(regex.h)
dnl
dnl Check for libs
@@ -75,7 +45,6 @@ dnl
AC_SEARCH_LIBS([pthread_create], [pthread])
AC_SEARCH_LIBS([socket], [socket])
AC_SEARCH_LIBS([inet_addr], [nsl])
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
AC_SEARCH_LIBS([hstrerror], [resolv])
@@ -83,8 +52,14 @@ dnl
dnl Getopt
dnl
AC_CHECK_FUNC(getopt_long,
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
dnl
dnl stat64
dnl
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],)
dnl
@@ -95,6 +70,7 @@ AC_TRY_COMPILE(
[#include <time.h>],
[ time_t clock; char buf[26]; ctime_r(&clock, buf, 26); ],
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_CTIME_R_3], 1, [Define to 1 if ctime_r takes 3 arguments]),
FOUND="no")
if test "$FOUND" = "no"; then
@@ -108,93 +84,125 @@ AC_TRY_COMPILE(
fi
if test "$FOUND" = "no"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR("function ctime_r not found.")
AC_MSG_ERROR("function ctime_r not found")
fi
dnl
dnl check gethostbyname_r
dnl check getaddrinfo
dnl
AC_MSG_CHECKING(for gethostbyname_r)
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
FOUND="no")
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
AC_CHECK_FUNC(getaddrinfo,
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
FOUND="no")
fi
dnl
dnl check gethostbyname_r, if getaddrinfo is not available
dnl
if test "$FOUND" = "no"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR("function gethostbyname_r not found.")
AC_MSG_CHECKING(for gethostbyname_r)
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
FOUND="no")
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
FOUND="no")
fi
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
AC_MSG_RESULT([[no]])
FOUND="no")
fi
if test "$FOUND" = "yes"; then
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
fi
fi
dnl
dnl check for __FUNCTION__ or __func__ macro
dnl cCheck if spinlocks are available
dnl
AC_MSG_CHECKING(for __FUNCTION__ macro)
AC_LANG_PUSH(C++)
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
AC_MSG_RESULT([yes])
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
HAVE_FUNCTION_MACRO=yes,
AC_MSG_RESULT([no]))
AC_LANG_POP(C++)
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
AC_MSG_CHECKING(for __func__ macro)
AC_LANG_PUSH(C++)
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
AC_MSG_RESULT([yes])
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
HAVE_FUNCTION_MACRO=yes,
AC_MSG_RESULT([no]))
AC_LANG_POP(C++)
fi
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
fi
AC_CHECK_FUNC(pthread_spin_init,
[AC_DEFINE([HAVE_SPINLOCK], 1, [Define to 1 if spinlocks are supported])]
AC_SEARCH_LIBS([pthread_spin_init], [pthread]),)
dnl
dnl check for pragma pack
dnl Determine what socket length (socklen_t) data type is
dnl
AC_MSG_CHECKING(for pragma pack)
AC_TRY_COMPILE([#pragma pack(1)
#pragma pack()],,
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_PRAGMA_PACK],1,[Define to 1 to use pragma pack directive in MessageBase.h]),
AC_MSG_RESULT([no]))
AC_MSG_CHECKING([for type of socket length (socklen_t)])
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL)],[
AC_MSG_RESULT(socklen_t)
SOCKLEN_T=socklen_t],[
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
AC_MSG_RESULT(size_t)
SOCKLEN_T=size_t],[
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
AC_MSG_RESULT(int)
SOCKLEN_T=int],[
AC_MSG_WARN(could not determine)
SOCKLEN_T=int])])])
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
dnl
dnl checks for libxml2 includes and libraries.
dnl
INCVAL="${LIBPREF}/include/libxml2"
LIBVAL="${LIBPREF}/lib"
dnl
AC_ARG_WITH(libxml2_includes,
[ --with-libxml2-includes=DIR libxml2 include directory],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files were not found."))
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libxml2_libraries,
[ --with-libxml2-libraries=DIR libxml2 library directory],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libxml2, libxml-2.0,
[LIBS="${LIBS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"])
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files not found"))
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
AC_MSG_ERROR("libxml2 library not found in $LIBVAL."))
AC_MSG_ERROR("libxml2 library not found"))
dnl
@@ -202,20 +210,19 @@ dnl Use curses. Deafult: yes
dnl
AC_MSG_CHECKING(whether to use curses)
AC_ARG_ENABLE(curses,
[ --disable-curses do not use curses (removes dependency from curses-library and makes executable smaller)],
[ USECURSES=$enableval ],
[ USECURSES=yes] )
[AS_HELP_STRING([--disable-curses], [do not use curses (removes dependency from curses-library)])],
[USECURSES=$enableval],
[USECURSES=yes] )
AC_MSG_RESULT($USECURSES)
if test "$USECURSES" = "yes"; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libcurses_includes,
[ --with-libcurses-includes=DIR libcurses include directory],
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(libcurses_libraries,
[ --with-libcurses-libraries=DIR libcurses library directory],
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
@@ -236,7 +243,7 @@ if test "$USECURSES" = "yes"; then
FOUND=no)
fi
if test "$FOUND" = "no"; then
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h).])
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h)])
fi
AC_SEARCH_LIBS([refresh], [ncurses curses],,
AC_ERROR([Couldn't find curses library]))
@@ -246,47 +253,11 @@ fi
dnl
dnl Use uulib. Deafult: no
dnl
AC_MSG_CHECKING(whether to use uulib for decoding and joining)
AC_ARG_ENABLE(uulib,
[ --enable-uulib use uulib for decoding and joining],
[ ENABLEUULIB=$enableval ],
[ ENABLEUULIB=no] )
AC_MSG_RESULT($ENABLEUULIB)
if test "$ENABLEUULIB" = "yes"; then
AC_DEFINE([ENABLE_UULIB],1,[Define to 1 to include support for uulib])
dnl
dnl checks for uulib includes and libraries.
dnl
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(uulib_includes,
[ --with-uulib-includes=DIR uulib include directory],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_CHECK_HEADER($INCVAL/uudeview.h,,
AC_MSG_ERROR("uulib header files were not found in $INCVAL."))
AC_ARG_WITH(uulib_libraries,
[ --with-uulib-libraries=DIR uulib library directory],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_SEARCH_LIBS([UUInitialize], [uu],,
AC_MSG_ERROR("uulib library not found in $LIBVAL."))
fi
dnl
dnl Use lib2par for par-checking. Deafult: no
dnl Use libpar2 for par-checking. Deafult: no
dnl
AC_MSG_CHECKING(whether to include code for par-checking)
AC_ARG_ENABLE(parcheck,
[ --enable-parcheck include code for par-checking],
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support (removes dependency from libpar2- and libsigc-libraries)])],
[ ENABLEPARCHECK=$enableval ],
[ ENABLEPARCHECK=yes] )
AC_MSG_RESULT($ENABLEPARCHECK)
@@ -295,23 +266,26 @@ if test "$ENABLEPARCHECK" = "yes"; then
dnl
dnl checks for libsigc++ includes and libraries (required for libpar2).
dnl
INCVAL="${LIBPREF}/include/sigc++-2.0"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libsigc_includes,
[ --with-libsigc-includes=DIR libsigc++-2.0 include directory],
[INCVAL="$withval"])
[AS_HELP_STRING([--with-libsigc-includes=DIR], [libsigc++-2.0 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libsigc_libraries,
[ --with-libsigc-libraries=DIR libsigc++-2.0 library directory],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
CPPFLAGS="${CPPFLAGS} -I${INCVAL} -I${LIBVAL}/sigc++-2.0/include"
AC_LANG_PUSH(C++)
[AS_HELP_STRING([--with-libsigc-libraries=DIR], [libsigc++-2.0 library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
[LIBS="${LIBS} $libsigc_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
fi
AC_CHECK_HEADER(sigc++/type_traits.h,,
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
AC_LANG_POP(C++)
AC_MSG_ERROR("libsigc++-2.0 header files not found"))
dnl
dnl checks for libpar2 includes and libraries.
@@ -319,53 +293,285 @@ if test "$ENABLEPARCHECK" = "yes"; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libpar2_includes,
[ --with-libpar2-includes=DIR libpar2 include directory],
[AS_HELP_STRING([--with-libpar2-includes=DIR], [libpar2 include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_LANG_PUSH(C++)
AC_CHECK_HEADER(libpar2/libpar2.h,,
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL."))
AC_LANG_POP(C++)
AC_MSG_ERROR("libpar2 header files not found"))
AC_ARG_WITH(libpar2_libraries,
[ --with-libpar2-libraries=DIR libpar2 library directory],
[AS_HELP_STRING([--with-libpar2-libraries=DIR], [libpar2 library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
dnl Q: How to check for c++-class in library?
LIBS="${LIBS} -lpar2"
dnl AC_CHECK_LIB(par2, GenerateCRC32Table, , FOUND=no)
dnl if test "$FOUND" = "no"; then
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
dnl fi
AC_SEARCH_LIBS([_ZN12Par2RepairerC1Ev], [par2], ,
AC_MSG_ERROR("libpar2 library not found"))
dnl
dnl check if libpar2 library is linkable
dnl
AC_MSG_CHECKING(for libpar2 linking)
AC_TRY_LINK(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { }; ],
[ Repairer* p = new Repairer(); ],
AC_MSG_RESULT([[yes]]),
AC_MSG_RESULT([[no]])
AC_MSG_ERROR("libpar2 library not found"))
dnl
dnl check if libpar2 has support for cancelling
dnl
AC_MSG_CHECKING(whether libpar2 supports cancelling)
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { cancelled = true; } }; ],
[],
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
AC_MSG_RESULT([[no]]))
dnl
dnl check if libpar2 has recent bugfixes-patch
dnl
AC_MSG_CHECKING(whether libpar2 has recent bugfixes-patch (version 2))
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } }; ],
[],
AC_MSG_RESULT([[yes]])
PAR2PATCHV2=yes
AC_DEFINE([HAVE_PAR2_BUGFIXES_V2], 1, [Define to 1 if libpar2 has recent bugfixes-patch (version 2)]),
AC_MSG_RESULT([[no]])
PAR2PATCHV2=no)
if test "$PAR2PATCHV2" = "no" ; then
AC_ARG_ENABLE(libpar2-bugfixes-check,
[AS_HELP_STRING([--disable-libpar2-bugfixes-check], [do not check if libpar2 has recent bugfixes-patch applied])],
[ PAR2PATCHCHECK=$enableval ],
[ PAR2PATCHCHECK=yes] )
if test "$PAR2PATCHCHECK" = "yes"; then
AC_ERROR([Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!])
fi
fi
else
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
fi
dnl
dnl Use TLS/SSL. Deafult: yes
dnl
AC_MSG_CHECKING(whether to use TLS/SSL)
AC_ARG_ENABLE(tls,
[AS_HELP_STRING([--disable-tls], [do not use TLS/SSL (removes dependency from TLS/SSL-libraries)])],
[ USETLS=$enableval ],
[ USETLS=yes] )
AC_MSG_RESULT($USETLS)
if test "$USETLS" = "yes"; then
AC_ARG_WITH(tlslib,
[AS_HELP_STRING([--with-tlslib=(GnuTLS, OpenSSL)], [TLS/SSL library to use])],
[TLSLIB="$withval"])
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
fi
if test "$TLSLIB" = "GnuTLS" -o "$TLSLIB" = ""; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libgnutls_includes,
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_ARG_WITH(libgnutls_libraries,
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(gnutls/gnutls.h,
FOUND=yes
TLSHEADERS=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
AC_MSG_ERROR([Couldn't find GnuTLS headers (gnutls.h)])
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
FOUND=yes,
FOUND=no),
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
AC_MSG_ERROR([Couldn't find GnuTLS library])
fi
if test "$FOUND" = "yes"; then
TLSLIB="GnuTLS"
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
fi
fi
fi
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
AC_ARG_WITH(openssl_includes,
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(openssl, openssl,
[LIBS="${LIBS} $openssl_LIBS"]
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
fi
AC_CHECK_HEADER(openssl/ssl.h,
FOUND=yes
TLSHEADERS=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; 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],
FOUND=yes,
FOUND=no),
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL library])
fi
if test "$FOUND" = "yes"; then
TLSLIB="OpenSSL"
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support.])
fi
fi
fi
if test "$TLSLIB" = ""; then
if test "$TLSHEADERS" = ""; then
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL headers (gnutls.h or ssl.h)])
else
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL library])
fi
fi
else
AC_DEFINE([DISABLE_TLS],1,[Define to 1 to not use TLS/SSL])
fi
dnl
dnl checks for zlib includes and libraries.
dnl
AC_MSG_CHECKING(whether to use gzip)
AC_ARG_ENABLE(gzip,
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
[USEZLIB=$enableval],
[USEZLIB=yes] )
AC_MSG_RESULT($USEZLIB)
if test "$USEZLIB" = "yes"; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(zlib_includes,
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_ARG_WITH(zlib_libraries,
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(zlib.h,,
AC_MSG_ERROR("zlib header files not found"))
AC_SEARCH_LIBS([deflateBound], [z], ,
AC_MSG_ERROR("zlib library not found"))
else
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
fi
dnl
dnl Some Linux systems require an empty signal handler for SIGCHLD
dnl in order for exit codes to be correctly delivered to parent process.
dnl Some BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to check the target and disable the handler on BSD but keep it enabled on other systems.
dnl
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
AC_ARG_ENABLE(sigchld-handler,
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling is recommended for BSD)])],
[SIGCHLDHANDLER=$enableval],
[SIGCHLDHANDLER=auto])
if test "$SIGCHLDHANDLER" = "auto"; then
SIGCHLDHANDLER=yes
case "$target" in
*bsd*)
SIGCHLDHANDLER=no
;;
esac
fi
AC_MSG_RESULT($SIGCHLDHANDLER)
if test "$SIGCHLDHANDLER" = "yes"; then
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
fi
dnl
dnl Debugging. Default: no
dnl
AC_MSG_CHECKING(whether to include all debugging code)
AC_ARG_ENABLE(debug,
[ --enable-debug enable debugging],
[AS_HELP_STRING([--enable-debug], [enable debugging])],
[ ENABLEDEBUG=$enableval ],
[ ENABLEDEBUG=no] )
if test "$ENABLEDEBUG" = "yes"; then
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
if test "$CC" = "gcc"; then
CXXFLAGS="-g -Wall"
else
CXXFLAGS=""
fi
fi
AC_MSG_RESULT($ENABLEDEBUG)
if test "$ENABLEDEBUG" = "yes"; then
dnl
dnl Begin of debugging code
dnl
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
dnl
dnl Set debug flags for gcc (if gcc is used)
dnl
if test "$CC" = "gcc"; then
CXXFLAGS="-g -Wall"
else
CXXFLAGS=""
fi
dnl
dnl check for __FUNCTION__ or __func__ macro
dnl
AC_MSG_CHECKING(for macro returning current function name)
AC_TRY_COMPILE(
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
AC_MSG_RESULT(__FUNCTION__)
FUNCTION_MACRO_NAME=__FUNCTION__,
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
AC_MSG_RESULT(__func__)
FUNCTION_MACRO_NAME=__func__,
AC_MSG_RESULT(none)))
if test "$FUNCTION_MACRO_NAME" != ""; then
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
fi
dnl
dnl variadic macros
dnl
@@ -380,14 +586,39 @@ AC_COMPILE_IFELSE([
AC_MSG_RESULT([no]))
dnl Substitute flags.
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(TAR)
AC_SUBST(AR)
AC_SUBST(ADDSRCS)
dnl
dnl Backtracing on segmentation faults
dnl
AC_MSG_CHECKING(for backtrace)
AC_TRY_COMPILE(
[#include <execinfo.h>]
[#include <stdio.h>]
[#include <stdlib.h>],
[ void *array[100]; size_t size; char **strings; ]
[ size = backtrace(array, 100); ]
[ strings = backtrace_symbols(array, size); ],
FOUND=yes
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
FOUND=no
AC_MSG_RESULT([[no]]))
dnl
dnl "rdynamic" linker flag
dnl
AC_MSG_CHECKING(for rdynamic linker flag)
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -rdynamic"
AC_TRY_LINK([], [],
AC_MSG_RESULT([[yes]]),
AC_MSG_RESULT([[no]])
[LDFLAGS="$old_LDFLAGS"])
dnl
dnl End of debugging code
dnl
fi
AC_CONFIG_FILES([Makefile])

View File

@@ -425,7 +425,7 @@ diff -urN libpar2-0.2-original/par2cmdline.h libpar2-0.2-modified/par2cmdline.h
#ifdef HAVE_CONFIG_H
diff -urN libpar2-0.2-original/par2repairer.cpp libpar2-0.2-modified/par2repairer.cpp
--- libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
+++ libpar2-0.2-modified/par2repairer.cpp 2008-01-31 14:35:11.110360000 +0100
+++ libpar2-0.2-modified/par2repairer.cpp 2008-02-13 15:37:59.899314300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
@@ -434,3 +434,21 @@ diff -urN libpar2-0.2-original/par2repairer.cpp libpar2-0.2-modified/par2repaire
}
@@ -1261,7 +1262,7 @@
DiskFile::SplitFilename(filename, path, name);
cout << "Target: \"" << name << "\" - missing." << endl;
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name, 0, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
}
}
@@ -1804,7 +1805,7 @@
}
}
}
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name,count, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
sig_progress.emit(1000.0);
return true;
}

View File

@@ -0,0 +1,60 @@
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2012-11-30 14:23:31.000000000 +0100
@@ -76,10 +76,11 @@
++sf;
}
delete mainpacket;
delete creatorpacket;
+ delete headers;
}
Result Par2Repairer::PreProcess(const CommandLine &commandline)
{
@@ -1259,11 +1260,11 @@
string path;
string name;
DiskFile::SplitFilename(filename, path, name);
cout << "Target: \"" << name << "\" - missing." << endl;
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name, 0, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
}
}
++sf;
}
@@ -1802,11 +1803,11 @@
<< "\" - no data found."
<< endl;
}
}
}
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name,count, count>0 && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
sig_progress.emit(1000.0);
return true;
}
// Find out how much data we have found
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2006-01-20 00:38:27.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2012-11-30 14:24:46.000000000 +0100
@@ -34,10 +34,15 @@
sigc::signal<void, std::string> sig_filename;
sigc::signal<void, double> sig_progress;
sigc::signal<void, ParHeaders*> sig_headers;
sigc::signal<void, std::string, int, int> sig_done;
+ // This method allows to determine whether libpar2 includes the patches
+ // ("libpar2-0.2-bugfixes.patch") submitted to libpar2 project.
+ // Use the method in configure scripts for detection.
+ void BugfixesPatchVersion2() { }
+
protected:
// Steps in verifying and repairing files:
// Load packets from the specified file
bool LoadPacketsFromFile(string filename);

236
libpar2-0.2-cancel.patch Normal file
View File

@@ -0,0 +1,236 @@
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2012-12-03 10:48:13.000000000 +0100
@@ -50,10 +50,12 @@
outputbuffer = 0;
noiselevel = CommandLine::nlNormal;
headers = new ParHeaders;
alreadyloaded = false;
+
+ cancelled = false;
}
Par2Repairer::~Par2Repairer(void)
{
delete [] (u8*)inputbuffer;
@@ -404,10 +406,14 @@
{
cout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
progress = offset;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
// Attempt to read the next packet header
PACKET_HEADER header;
@@ -582,10 +588,15 @@
if (noiselevel > CommandLine::nlQuiet)
cout << "No new packets found" << endl;
delete diskfile;
}
+ if (cancelled)
+ {
+ return false;
+ }
+
return true;
}
// Finish loading a recovery packet
bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header)
@@ -831,26 +842,42 @@
// Load packets from each file that was found
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
+ if (cancelled)
+ {
+ break;
+ }
}
delete files;
+ if (cancelled)
+ {
+ return false;
+ }
}
{
string wildcard = name.empty() ? "*.PAR2" : name + ".*.PAR2";
list<string> *files = DiskFile::FindFiles(path, wildcard);
// Load packets from each file that was found
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
+ if (cancelled)
+ {
+ break;
+ }
}
delete files;
+ if (cancelled)
+ {
+ return false;
+ }
}
return true;
}
@@ -864,13 +891,22 @@
// If the filename contains ".par2" anywhere
if (string::npos != filename.find(".par2") ||
string::npos != filename.find(".PAR2"))
{
LoadPacketsFromFile(filename);
+ if (cancelled)
+ {
+ break;
+ }
}
}
+ if (cancelled)
+ {
+ return false;
+ }
+
return true;
}
// Check that the packets are consistent and discard any that are not
bool Par2Repairer::CheckPacketConsistency(void)
@@ -1208,10 +1244,15 @@
// Start verifying the files
sf = sortedfiles.begin();
while (sf != sortedfiles.end())
{
+ if (cancelled)
+ {
+ return false;
+ }
+
// Do we have a source file
Par2RepairerSourceFile *sourcefile = *sf;
// What filename does the file use
string filename = sourcefile->TargetFileName();
@@ -1560,10 +1601,14 @@
if (oldfraction != newfraction)
{
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
// If we fail to find a match, it might be because it was a duplicate of a block
// that we have already found.
@@ -1649,10 +1694,15 @@
return false;
}
}
}
+ if (cancelled)
+ {
+ return false;
+ }
+
// Get the Full and 16k hash values of the file
filechecksummer.GetFileHashes(hashfull, hash16k);
// Did we make any matches at all
if (count > 0)
@@ -2289,14 +2339,23 @@
if (oldfraction != newfraction)
{
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
}
+ if (cancelled)
+ {
+ break;
+ }
+
++inputblock;
++inputindex;
}
}
else
@@ -2346,13 +2405,22 @@
if (oldfraction != newfraction)
{
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
+ if (cancelled)
+ {
+ break;
+ }
+
++copyblock;
++inputblock;
}
}
@@ -2360,10 +2428,15 @@
if (lastopenfile != NULL)
{
lastopenfile->Close();
}
+ if (cancelled)
+ {
+ return false;
+ }
+
if (noiselevel > CommandLine::nlQuiet)
cout << "Writing recovered data\r";
// For each output block that has been recomputed
vector<DataBlock*>::iterator outputblock = outputblocks.begin();
diff -aud -U 5 ../libpar2-0.2-with-bugfixes-patch/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2012-12-03 10:48:13.000000000 +0100
@@ -186,8 +186,9 @@
u64 progress; // How much data has been processed.
u64 totaldata; // Total amount of data to be processed.
u64 totalsize; // Total data size
+ bool cancelled; // repair cancelled
};
#endif // __PAR2REPAIRER_H__

50
nzbget-postprocess.conf Normal file
View File

@@ -0,0 +1,50 @@
#
# This file if part of nzbget
#
# Template configuration file for post-processing script "nzbget-postprocess.sh".
# Please refer to "nzbget-postprocess.sh" for usage instructions.
#
# Copyright (C) 2008-2013 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
##############################################################################
### OPTIONS ###
# Rename img-files to iso (yes, no).
RenameIMG=yes
# Joint TS-files (yes, no).
JoinTS=yes
##############################################################################
### POSTPROCESSING-PARAMETERS ###
# This section defines parameters, which can be set for each nzb-file
# individually using either web-interface or command line.
# Example command line for setting parameter "PostProcess" to value "no" for
# nzb-file with id=2:
# nzbget -E G O PostProcess=no 2
# Perform postprocessing (yes, no).
#
# Set to "no" to skip postprocessing for this nzb-file.
PostProcess=yes
# Destination directory.
DestDir=

231
nzbget-postprocess.sh Executable file
View File

@@ -0,0 +1,231 @@
#!/bin/sh
#
# This file if part of nzbget
#
# Example postprocessing script for NZBGet
#
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
# Copyright (C) 2008 Otmar Werner
# Copyright (C) 2008-2013 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
####################### Usage instructions #######################
# o Script will cleanup, join ts-files and rename img-files to iso.
#
# o To use this script with nzbget set the option "PostProcess" in
# nzbget configuration file to point to this script file. E.g.:
# PostProcess=/home/user/nzbget/nzbget-postprocess.sh
#
# o The script needs a configuration file. An example configuration file
# is provided in file "nzbget-postprocess.conf". Put the configuration file
# into the directory where nzbget's configuration file (nzbget.conf) is located.
# Then edit the configuration file in any text editor to adjust the settings.
#
# o You can also edit the script's configuration via web-interface.
#
# o There are few options, which can be ajdusted for each nzb-file individually.
#
####################### End of Usage instructions #######################
# NZBGet passes following arguments to postprocess-programm as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled.
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
# Name of script's configuration file
SCRIPT_CONFIG_FILE="nzbget-postprocess.conf"
# Exit codes
POSTPROCESS_PARCHECK_CURRENT=91
POSTPROCESS_PARCHECK_ALL=92
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_NONE=95
# Check if the script is called from nzbget 10.0 or later
if [ "$NZBPP_DIRECTORY" = "" -o "$NZBOP_CONFIGFILE" = "" ]; then
echo "*** NZBGet post-processing script ***"
echo "This script is supposed to be called from nzbget (10.0 or later)."
exit $POSTPROCESS_ERROR
fi
if [ "$NZBOP_UNPACK" = "" ]; then
echo "[ERROR] This script requires nzbget version at least 10.0-testing-r555 or 10.0-stable."
exit $POSTPROCESS_ERROR
fi
# Check if postprocessing was disabled in postprocessing parameters
# (for current nzb-file) via web-interface or via command line with
# "nzbget -E G O PostProcess=no <ID>"
if [ "$NZBPR_PostProcess" = "no" ]; then
echo "[WARNING] Post-Process: Post-processing disabled for this nzb-file, exiting"
exit $POSTPROCESS_NONE
fi
echo "[INFO] Post-Process: Post-processing script successfully started"
cd "$NZBPP_DIRECTORY"
# Determine the location of configuration file (it must be stored in
# the directory with nzbget.conf).
ConfigDir="${NZBOP_CONFIGFILE%/*}"
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
if [ ! -f "$ScriptConfigFile" ]; then
echo "[ERROR] Post-Process: Configuration file $ScriptConfigFile not found, exiting"
exit $POSTPROCESS_ERROR
fi
# Readg configuration file
while read line; do eval "$line"; done < $ScriptConfigFile
# Check nzbget.conf options
BadConfig=0
if [ "$NZBOP_ALLOWREPROCESS" = "yes" ]; then
echo "[ERROR] Post-Process: Please disable option \"AllowReProcess\" in nzbget configuration file"
BadConfig=1
fi
if [ "$NZBOP_UNPACK" != "yes" ]; then
echo "[ERROR] Post-Process: Please enable option \"Unpack\" in nzbget configuration file"
BadConfig=1
fi
if [ "$BadConfig" -eq 1 ]; then
echo "[ERROR] Post-Process: Exiting due to incompatible nzbget configuration"
exit $POSTPROCESS_ERROR
fi
# Check par status
if [ "$NZBPP_PARSTATUS" -eq 3 ]; then
echo "[WARNING] Post-Process: Par-check successful, but Par-repair disabled, exiting"
exit $POSTPROCESS_NONE
fi
if [ "$NZBPP_PARSTATUS" -eq 1 ]; then
echo "[WARNING] Post-Process: Par-check failed, exiting"
exit $POSTPROCESS_NONE
fi
# Check unpack status
if [ "$NZBPP_UNPACKSTATUS" -eq 1 ]; then
echo "[WARNING] Post-Process: Unpack failed, exiting"
exit $POSTPROCESS_NONE
fi
if [ "$NZBPP_UNPACKSTATUS" -eq 0 -a "$NZBPP_PARSTATUS" -ne 2 ]; then
# Unpack is disabled or was skipped due to nzb-file properties or due to errors during par-check
if (ls *.rar *.7z *.7z.??? >/dev/null 2>&1); then
echo "[WARNING] Post-Process: Archive files exist but unpack skipped, exiting"
exit $POSTPROCESS_NONE
fi
if (ls *.par2 >/dev/null 2>&1); then
echo "[WARNING] Post-Process: Unpack skipped and par-check skipped (although par2-files exist), exiting"
exit $POSTPROCESS_NONE
fi
if [ -f "_brokenlog.txt" ]; then
echo "[WARNING] Post-Process: _brokenlog.txt exists, download is probably damaged, exiting"
exit $POSTPROCESS_NONE
fi
echo "[INFO] Post-Process: Neither archive- nor par2-files found, _brokenlog.txt doesn't exist, considering download successful"
fi
# Check if destination directory exists (important for reprocessing of history items)
if [ ! -d "$NZBPP_DIRECTORY" ]; then
echo "[ERROR] Post-Process: Nothing to post-process: destination directory $NZBPP_DIRECTORY doesn't exist"
exit $POSTPROCESS_ERROR
fi
# All checks done, now processing the files
# If download contains only nzb-files move them into nzb-directory
# for further download
# Check if command "wc" exists
wc -l . >/dev/null 2>&1
if [ "$?" -ne 127 ]; then
AllFilesCount=`ls -1 2>/dev/null | wc -l`
NZBFilesCount=`ls -1 *.nzb 2>/dev/null | wc -l`
if [ "$AllFilesCount" -eq "$NZBFilesCount" ]; then
echo "[INFO] Moving downloaded nzb-files into incoming nzb-directory for further download"
mv *.nzb $NZBOP_NZBDIR
fi
fi
# Clean up
echo "[INFO] Post-Process: Cleaning up"
chmod -R a+rw .
rm *.nzb >/dev/null 2>&1
rm *.sfv >/dev/null 2>&1
rm *.1 >/dev/null 2>&1
rm _brokenlog.txt >/dev/null 2>&1
rm *.[pP][aA][rR]2 >/dev/null 2>&1
if [ "$JoinTS" = "yes" ]; then
# Join any split .ts files if they are named xxxx.0000.ts xxxx.0001.ts
# They will be joined together to a file called xxxx.0001.ts
if (ls *.ts >/dev/null 2>&1); then
echo "[INFO] Post-Process: Joining ts-files"
tsname=`find . -name "*0001.ts" |awk -F/ '{print $NF}'`
cat *0???.ts > ./$tsname
# Remove all the split .ts files
echo "[INFO] Post-Process: Deleting source ts-files"
rm *0???.ts >/dev/null 2>&1
fi
fi
if [ "$RenameIMG" = "yes" ]; then
# Rename img file to iso
# It will be renamed to .img.iso so you can see that it has been renamed
if (ls *.img >/dev/null 2>&1); then
echo "[INFO] Post-Process: Renaming img-files to iso"
imgname=`find . -name "*.img" |awk -F/ '{print $NF}'`
mv $imgname $imgname.iso
fi
fi
# Check if destination directory was set in postprocessing parameters
# (for current nzb-file) via web-interface or via command line with
# "nzbget -E G O DestDir=/new/path <ID>"
if [ "$NZBPR_DestDir" != "" ]; then
mkdir $NZBPR_DestDir
mv * $NZBPR_DestDir >/dev/null 2>&1
cd ..
rmdir $NZBPP_DIRECTORY
fi
# All OK, requesting cleaning up of download queue
exit $POSTPROCESS_SUCCESS

35
nzbget-shell.bat Normal file
View File

@@ -0,0 +1,35 @@
@echo off
rem
rem Batch file to start nzbget shell
rem
rem Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
rem Copyright (C) 2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
rem
rem This program is free software; you can redistribute it and/or modify
rem it under the terms of the GNU General Public License as published by
rem the Free Software Foundation; either version 2 of the License, or
rem (at your option) any later version.
rem
rem This program is distributed in the hope that it will be useful,
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
rem GNU General Public License for more details.
rem
rem You should have received a copy of the GNU General Public License
rem along with this program; if not, write to the Free Software
rem Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
rem
rem ####################### Usage instructions #######################
rem
rem After starting the batch file you can use all nzbget commands
rem (like nzbget -s, nzbget -L, etc) without typing the full
rem path to nzbget executable.
rem
rem ####################### End of Usage instructions #######################
rem expression "%~dp0" means the location of an executing batch file
set PATH=%PATH%;%~dp0
cmd /U /K "cd %USERPROFILE% & nzbget"

1136
nzbget.conf Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,436 +0,0 @@
# Sample configuration file for nzbget
#
# On POSIX put this file to one of the following locations:
# ~/.nzbget
# /etc/nzbget.conf
# /usr/etc/nzbget.conf
# /usr/local/etc/nzbget.conf
# /opt/etc/nzbget.conf
#
# On Windows put this file in program's directory.
#
# You can also put the file into any location, if you specify the path to it
# using switch "-c", e.g:
# nzbget -c /home/user/myconig.txt
# For quick start change the option MAINDIR and configure one news-server
##############################################################################
### PATHS ###
# Root directory for all related tasks
# MAINDIR is a variable and therefore starts with "$"
# The value "~/download" is example for POSIX. On Windows use
# absolute paths, like "C:\Download".
$MAINDIR=~/download
# Destination-directory to store the downloaded files
DestDir=${MAINDIR}/dst
# Directory to monitor for incoming nzb-jobs
NzbDir=${MAINDIR}/nzb
# Directory to store download queue
QueueDir=${MAINDIR}/queue
# Directory to store temporary files
TempDir=${MAINDIR}/tmp
# Lock-file for daemon-mode, contains process-id (PID) (POSIX only)
LockFile=/tmp/nzbget.lock
# Where to store log file, if it needs to be created (see "CreateLog")
LogFile=${destdir}/nzbget.log
##############################################################################
### NEWS-SERVERS ###
# This section defines which servers nzbget should connect to.
# The servers will be ordered by their level, i.e. nzbget will at
# first try to download an article from the level-0-server.
# If that server fails, nzbget proceeds with the level-1-server, etc.
# A good idea is surely to put your major download-server at level 0
# and your fill-servers at levels 1,2,...
# NOTE: Do not leave out a level in your server-list and start with level 0!
# NOTE: Several servers with the same level may be used, they will have
# the same priority.
# First server, on level 0
# Level of newsserver
server1.level=0
# Host-name of newsserver
server1.host=my1.newsserver.com
# Port to connect to (default 119 if not specified)
server1.port=119
# Username to use for authentication
server1.username=user
# Password to use for authentication
server1.password=pass
# Maximal number of simultaneous connections to this server
server1.connections=4
# Second server, on level 0
#server2.level=0
#server2.host=my2.newsserver.com
#server2.port=119
#server2.username=me
#server2.password=mypass
#server2.connections=4
# Third server, on level 1
#server3.level=1
#server3.host=fills.newsserver.com
#server3.port=119
#server3.username=me2
#server3.password=mypass2
#server3.connections=1
##############################################################################
### PERMISSIONS (POSIX ONLY) ###
# User name for daemon-mode (POSIX in daemon-mode only).
# Set the user that the daemon normally runs at.
# Set $MAINDIR with an absolute path to be sure where it will write.
# This allows nzbget daemon to be launched in rc.local (at boot), and
# download items as a specific user id.
# NOTE: This option has effect only if the program was started from
# root-account, otherwise it is ignored and the daemon runs under
# current user id
DaemonUserName=root
# Specify default umask (affects file permissions) for newly created
# files (POSIX only).
# The value should be written in octal form (the same as for "umask" shell
# command). If umask not specified (or a value greater than 0777 used, useful
# to disable current config-setting via command-line parameter) the umask-mode
# will not be set and current umask-mode (set via shell) will be used
# NOTE: do not forget to uncomment the next line
#UMask=022
##############################################################################
### DOWNLOAD QUEUE ###
# Save download queue to disk. This allows to reload it on next start (yes, no)
SaveQueue=yes
# Reload download queue on start, if it exists (yes, no)
ReloadQueue=yes
# Reuse articles saved in temp-directory from previous program start (yes, no)
# This allows to continue download of file, if program was exited before
# the file was completed.
ContinuePartial=yes
# Create subdirectory with nzb-filename in destination-directory (yes, no)
AppendNzbDir=yes
# How often incoming-directory (option "NzbDir") must be checked for new
# nzb-files, in seconds.
# Value "0" disables the check.
NzbDirInterval=5
# How old nzb-file should at least be for it to be loaded to queue, in seconds.
# Nzbget checks if nzb-file was not modified in last few seconds, defined by
# this option. That safety interval prevents the loading of files, which
# were not yet completely saved to disk, for example if they are still being
# downloaded in web-browser.
NzbDirFileAge=60
# Check for duplicate files (yes, no)
# If this option is enabled the program checks by adding of a new nzb-file:
# 1) if nzb-file contains duplicate entries. This check aims on detecting
# of reposted files (if first file was not fully uploaded);
# If the program find two files with identical names, only the
# biggest of these files will be added to queue;
# 2) if download queue already contains file with the same name;
# 3) if destination file on disk already exists.
# In last two cases: if the file exists it will not be added to queue;
# If this option is disabled, all files are downloaded and duplicate files
# are renamed to "filename_duplicate1".
# Existing files are never deleted or overwritten.
DupeCheck=no
# Visibly rename broken files on download appending "_broken" (yes, no)
# Do not activate this option if par-check is enabled.
RenameBroken=no
# Determine how the articles should be decoded (yenc, uulib, none)
# yenc - use internal yEnc-Decoder. Supports only yEnc-format, but is
# very fast and does not create temporary files with articles' text
# decoding them on the fly.
# uulib - use uulib to decode files. Supports many encoding formats,
# but is slow. Not available in Windows-version.
# none - the articles will not be decoded and joined. External programs
# (like "uudeview") can be used to decode and join downloaded articles.
Decoder=yEnc
# Write decoded articles directly into destination output file (yes, no)
# With this option enabled the program at first creates the output
# destination file with required size (total size of all articles),
# then writes on the fly decoded articles directly to the file
# without creating of any temporary files, even for decoded articles.
# This may results in major performance improvement, but this higly
# depends on OS and filesystem used.
# Can improve performance on a very fast internet connections,
# but you need to test if it works in your case.
# The option works only with internal decoder ("decoder=yenc")
# INFO: Tests showed, that on Linux with EXT3-partition activating of
# this option results in up to 20% better performance, but on Windows with NTFS
# or Linux with FAT32-partitions the performance were decreased.
# The possible reason is that on EXT3-partition Linux can create large files
# very fast (if the content of file does not need to be initialized),
# but Windows on NTFS-partition and also Linux on FAT32-partition need to
# initialize created large file with nulls, resulting in a big performace
# degradation.
# NOTE: for testing try to download few big files (with total size 500-1000MB)
# and measure required time. Do not rely on the program's speed indicator,
# it is not accurate.
# NOTE: if both options "DirectWrite" and "ContinuePartial" are enabled,
# the program will create empty articles-files in temp-directrory. They
# are used to continue download of file on a next program start. To minimize
# disk-io it is recommended to disable option "ContinuePartial", if
# "DirectWrite" is enabled. Especially on a fast connections (where you
# would want to activate "DirectWrite") it should not be a problem to
# redownload the interrupted file.
DirectWrite=no
# Check CRC of downloaded and decoded articles (yes, no)
# Normally this option should be enabled for better detecting of download
# errors. However checking of CRC needs about the same CPU time as
# decoding of articles. On a fast connections with slow CPUs disabling of
# CPU-check may slightly improve performance (if CPU is a limiting factor).
# The option works only with internal decoder ("decoder=yenc")
CrcCheck=yes
# How much retries should be attempted if a download error occurs
Retries=4
# Set the interval between retries, in seconds
RetryInterval=10
# Redownload article if CRC-check fails (yes, no)
# Helps to minimize number of broken files, but may be effective
# only if you have multiple download servers (even from the same provider
# but from different locations (e.g. europe, usa)).
# In any case the option increases your traffic.
# For slow connections loading of extra par-blocks may be more effective
# The option "CrcCheck" must be enabled for option "RetryOnCrcError" to work.
RetryOnCrcError=no
# Set connection timeout, in seconds
ConnectionTimeout=60
# Timeout until a download-thread is killed, in seconds
# This can help on hanging downloads, but is dangerous.
# Do not use small values!
TerminateTimeout=600
# Set the maximum number of threads program may create.
# Connection errors or fast connection with slow cpu can cause
# the creating of many (thousands) threads, which results in program crash.
# Limiting the number of threads helps in such situations.
# The option affects only download threads, so the number of existing threads
# may be slightly more than set by the option.
ThreadLimit=100
# Set the maximum download rate in KB/s, "0" means no speed control
DownloadRate=0
# Set the size of memory buffer used by writing the articles, in Bytes.
# Bigger values decrease disk-io, but increase memory usage.
# Value "0" causes the OS-dependend default value to be used.
# With value "-1" (which means "max/auto") the program sets the size of
# buffer according to the size of current article (typically less than 500K).
# NOTE: the value must be written in bytes, do not use postfixes "K" or "M".
# NOTE: to calculate the memory usage multiply WriteBufferSize by max number
# of connections, configured in section "NEWS-SERVERS".
# NOTE: typical article's size not exceed 500000 bytes, so using bigger values
# (like several megabytes) will just waste memory.
# NOTE: for desktop computers with large amount of memory value "-1" (max/auto)
# is recommended, but for computers with very low memory (routers, NAS)
# value "0" (default OS-dependend size) could be better alternative.
# NOTE: write-buffer is managed by OS (system libraries) and therefore
# the effect of the option is highly OS-dependend.
WriteBufferSize=0
##############################################################################
### LOGGING ###
# Create log file (yes, no)
CreateLog=yes
# Delete log file upon server start (only in server-mode) (yes, no)
ResetLog=no
# How various messages must be printed (screen, log, both, none)
# Debug-messages can be only printed if the programm was compiled in
# debug-mode: "./configure --enable-debug"
InfoTarget=both
WarningTarget=both
ErrorTarget=both
DebugTarget=both
# Number of messages stored in buffer and available for remote clients
LogBufferSize=1000
# Create a log of all broken files (yes ,no)
# It is a text file placed near downloaded files, which contains
# the names of broken files
CreateBrokenLog=yes
# See also option "logfile" in secion "PATHS"
##############################################################################
### DISPLAY ###
# Set screen-outputmode (loggable, colored, curses)
# loggable - only messages will be printed to standard output;
# colored - prints messages (with simple coloring for messages categories)
# and download progress info; uses escape-sequenses to move cursor;
# curses - advanced interactive iterface with the ability to edit
# download queue and variaous output options;
OutputMode=curses
# Shows NZB-Filename in file list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with Z-key
CursesNzbName=yes
# Show files in groups (NZB-files) in queue list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with G-key
CursesGroup=no
# Show timestamps in message list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with T-key
CursesTime=no
# Update interval for Frontend-output in MSec (min value 25)
# Bigger values reduce CPU usage (especially in curses-outputmode)
# and network traffic in remote-client mode
UpdateInterval=200
##############################################################################
### CLIENT/SERVER COMMUNICATION ###
# Set the IP on which the server listen and which client uses to contact
# the server. It could be dns-hostname or ip-address (more effective since
# does not require dns-lookup).
# If you want the server to listen to all interfaces, use "0.0.0.0"
ServerIp=127.0.0.1
# Set the port which the server & client use
ServerPort=6789
# Set the password needed to succesfully queue a request
ServerPassword=tegbzn6789
# See also option "logbuffersize" in section "LOGGING"
##############################################################################
### PAR CHECK AND REPAIR ###
# How many par2-files to load (none, all, one)
# none - all added par2-files must be automatically paused
# all - all added par2-files must be downloaded
# one - only one main par2-file must be dowloaded and other must be paused
# Paused files remain in queue and should be deleted manually,
# when they not needed anymore
LoadPars=one
# Automatic par-verification (yes, no)
# To download only needed par2-files (smart par-files loading) set also
# the option "loadpars" to "one". If option "loadpars" is set to "all",
# all par2-files will be downloaded before verification and repair starts.
# The option "renamebroken" must be set to "no", otherwise the par-checker
# may not find renamed files and fail
ParCheck=no
# Automatic par-repair (yes, no)
# If option "parcheck" is enabled and "parrepair" is not, the program
# only verifies downloaded files and downloads needed par2-files, but do
# not start repair-process. This is useful if the server does not have
# enough CPU power, since repairing of large files may take too much
# resources and time on a slow computers.
# This option has effect only if the option "parcheck" is enabled
ParRepair=yes
# Use only par2-files with matching names (yes, no)
# If par-check needs extra par-blocks it searches for par2-files
# in download queue, which can be unpaused and used for restore.
# These par2-files should have the same base name as the main par2-file,
# currently loaded in par-checker. Sometimes extra par files (especially if
# they were uploaded from a different poster) have not matching names.
# Normally par-checker does not use these files, but you can allow it
# to use these files by setting "strictparname" to "no".
# This has however a side effect: if NZB-file contains more than one collection
# of files (with different par-sets), par-checker may download par-files from
# a wrong collection. This increases you traffic (but not harm par-check).
# NOTE: par-checker always uses only par-files added from the same NZB-file
# and the option "strictparname" does not change this behavior
StrictParName=yes
##############################################################################
### POSTPROCESSING ###
# Set path to program, that must be executed after the download of
# nzb-file or one collection in nzb-file (if par-check enabled)
# is completed and possibly par-checked/repaired.
# Six arguments are being passed to this program:
# - path to destination dir, where downloaded files are located;
# - name of nzb-file processed;
# - name of par-file processed (if par-checked) or empty string (if not);
# - result of par-check:
# 0 - not checked: par-check disabled or nzb-file does not contain any
# par-files;
# 1 - checked and failed to repair;
# 2 - checked and sucessfully repaired;
# 3 - checked and can be repaired but repair is disabled;
# - state of nzb-job:
# 0 - there are more collections in this nzb-file queued;
# 1 - this was the last collection in nzb-file;
# NOTE: if par-check is enabled and nzb-file contains more than one collection
# of files the postprocess-program is called after each collection is completed
# and par-checked. If you want to clean up the directory (delete par-files,
# etc.) there are two possibilities, when you can do this:
# 1) you parse the "name of par-file processed" to find out the base name
# of collection and clean up only files from this collection;
# 2) or you just check the parameter "state of nzb-job" and do clean up,
# only if it is equal to "1" (which means, that this was the last
# collection in nzb-file and all files are now completed);
# NOTE: do not forget to uncomment the next line
#PostProcess=~/myscript.sh
##############################################################################
### PERFORMANCE ###
# On a very fast connection and slow CPU and/or drive the following
# settings may improve performance:
# 1) Disable par-checking and -repairing ("ParCheck=no"). VERY important,
# because par-checking/repairing needs a lot of CPU-power and
# significantly increases disk usage;
# 2) Use internal Decoder ("Decoder=yenc");
# 3) Try to activate option "DirectWrite" ("DirectWrite=yes");
# 4) Disable option "CrcCheck" ("CrcCheck=no");
# 5) Disable option "ContinuePartial" ("ContinuePartial=no");
# 6) Do not limit download rate ("DownloadRate=0"), because the bandwidth
# throttling eats CPU time;
# 7) Disable logging for info- and debug-messages ("InfoTarget=none",
# "DebugTarget=none");
# 8) Run the program in daemon (Posix) or service (Windows) mode and use
# remote client for short periods of time needed for controlling of
# download process on server. Daemon/Service mode eats less CPU
# resources due to not updating of output on screen.
# 9) Increase the value of option "WriteBufferSize" or better set it to
# "-1" (max/auto) if you have spare 5-20 MB of memory.

View File

@@ -1,8 +1,8 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2012 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#include "config.h"
#endif
#ifdef WIN32
@@ -40,12 +40,22 @@
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#include <signal.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#ifndef DISABLE_PARCHECK
#include <iostream>
#endif
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include "nzbget.h"
#include "ServerPool.h"
@@ -56,51 +66,146 @@
#include "ColoredFrontend.h"
#include "NCursesFrontend.h"
#include "QueueCoordinator.h"
#include "UrlCoordinator.h"
#include "RemoteServer.h"
#include "RemoteClient.h"
#include "MessageBase.h"
#include "DiskState.h"
#include "PrePostProcessor.h"
#include "ParChecker.h"
#include "Scheduler.h"
#include "Util.h"
#ifdef WIN32
#include "NTService.h"
#endif
// Prototypes
void Run();
void RunMain();
void Run(bool bReload);
void Reload();
void Cleanup();
void ProcessClientRequest();
#ifndef WIN32
void InstallSignalHandlers();
void Daemonize();
void PrintBacktrace();
#ifdef HAVE_SYS_PRCTL_H
void EnableDumpCore();
#endif
#ifdef DEBUG
void MakeSegFault();
#endif
#endif
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
ServerPool* g_pServerPool = NULL;
QueueCoordinator* g_pQueueCoordinator = NULL;
UrlCoordinator* g_pUrlCoordinator = NULL;
RemoteServer* g_pRemoteServer = NULL;
RemoteServer* g_pRemoteSecureServer = NULL;
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
Scheduler* g_pScheduler = NULL;
int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
bool g_bReloading = true;
/*
* Main loop
*/
int main(int argc, char *argv[])
int main(int argc, char *argv[], char *argp[])
{
#ifdef WIN32
_set_fmode(_O_BINARY);
#ifdef _DEBUG
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
#ifdef DEBUG_CRTMEMLEAKS
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
#endif
);
#endif
#endif
Util::InitVersionRevision();
#ifdef WIN32
InstallUninstallServiceCheck(argc, argv);
#endif
// Init options & get the name of the .nzb file
#ifndef DISABLE_PARCHECK
DisableCout();
#endif
g_iArgumentCount = argc;
g_szArguments = (char*(*)[])argv;
g_szEnvironmentVariables = (char*(*)[])argp;
#ifdef WIN32
for (int i=0; i < argc; i++)
{
if (!strcmp(argv[i], "-D"))
{
StartService(RunMain);
return 0;
}
}
#endif
RunMain();
#ifdef WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}
void RunMain()
{
// we need to save and later restore current directory each time
// the program is reloaded (RPC-Method "reload") in order for
// config to properly load in a case relative paths are used
// in command line
char szCurDir[MAX_PATH + 1];
Util::GetCurrentDirectory(szCurDir, sizeof(szCurDir));
bool bReload = false;
while (g_bReloading)
{
g_bReloading = false;
Util::SetCurrentDirectory(szCurDir);
Run(bReload);
bReload = true;
}
}
void Run(bool bReload)
{
g_pLog = new Log();
debug("nzbget %s", Util::VersionRevision());
if (!bReload)
{
Thread::Init();
}
g_pServerPool = new ServerPool();
debug("Options parsing");
g_pOptions = new Options(argc, argv);
g_pScheduler = new Scheduler();
debug("Reading options");
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
#ifndef WIN32
if (g_pOptions->GetUMask() < 01000)
@@ -112,42 +217,61 @@ int main(int argc, char *argv[])
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
{
debug("deleting old log-file");
debug("Deleting old log-file");
g_pLog->ResetLog();
}
if (g_pOptions->GetDaemonMode())
g_pLog->InitOptions();
if (g_pOptions->GetDaemonMode() && !bReload)
{
#ifdef WIN32
info("nzbget service-mode");
StartService(Run);
return 0;
info("nzbget %s service-mode", Util::VersionRevision());
#else
Daemonize();
info("nzbget daemon-mode");
info("nzbget %s daemon-mode", Util::VersionRevision());
#endif
}
else if (g_pOptions->GetServerMode())
else if (g_pOptions->GetServerMode() && !bReload)
{
info("nzbget server-mode");
info("nzbget %s server-mode", Util::VersionRevision());
}
else if (g_pOptions->GetRemoteClientMode())
{
info("nzbget remote-mode");
info("nzbget %s remote-mode", Util::VersionRevision());
}
Run();
return 0;
}
if (!bReload)
{
Connection::Init();
}
if (!g_pOptions->GetRemoteClientMode())
{
g_pServerPool->InitConnections();
#ifdef DEBUG
g_pServerPool->LogDebugInfo();
#endif
}
void Run()
{
#ifndef WIN32
InstallSignalHandlers();
#ifdef HAVE_SYS_PRCTL_H
if (g_pOptions->GetDumpCore())
{
EnableDumpCore();
}
#endif
#endif
Thread::Init();
Connection::Init();
#ifndef WIN32
InstallSignalHandlers();
#ifdef DEBUG
if (g_pOptions->GetTestBacktrace())
{
MakeSegFault();
}
#endif
#endif
// client request
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
@@ -162,20 +286,28 @@ void Run()
{
g_pQueueCoordinator = new QueueCoordinator();
g_pDownloadSpeedMeter = g_pQueueCoordinator;
g_pDownloadQueueHolder = g_pQueueCoordinator;
g_pUrlCoordinator = new UrlCoordinator();
}
// Setup the network-server
if (g_pOptions->GetServerMode())
{
g_pRemoteServer = new RemoteServer();
g_pRemoteServer = new RemoteServer(false);
g_pRemoteServer->Start();
if (g_pOptions->GetSecureControl())
{
g_pRemoteSecureServer = new RemoteServer(true);
g_pRemoteSecureServer->Start();
}
}
// Starting a thread with the PrePostProcessor
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
g_pPrePostProcessor->Start();
}
// Create the frontend
@@ -203,14 +335,20 @@ void Run()
g_pFrontend->Start();
}
// Starting QueueCoordinator
// Starting QueueCoordinator and PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
// Standalone-mode
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->AddFileToQueue(g_pOptions->GetArgFilename()))
if (!g_pOptions->GetServerMode())
{
abort("FATAL ERROR: Parsing NZB-document %s failed!!\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
return;
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
if (!pNZBFile)
{
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
return;
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
delete pNZBFile;
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
@@ -219,17 +357,28 @@ void Run()
}
g_pQueueCoordinator->Start();
g_pUrlCoordinator->Start();
g_pPrePostProcessor->Start();
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
while (g_pQueueCoordinator->IsRunning() ||
g_pUrlCoordinator->IsRunning() ||
g_pPrePostProcessor->IsRunning())
{
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->HasMoreJobs() && !g_pPrePostProcessor->HasMoreJobs())
if (!g_pOptions->GetServerMode() &&
!g_pQueueCoordinator->HasMoreJobs() &&
!g_pUrlCoordinator->HasMoreJobs() &&
!g_pPrePostProcessor->HasMoreJobs())
{
// Standalone-mode: download completed
if (!g_pQueueCoordinator->IsStopped())
{
g_pQueueCoordinator->Stop();
}
if (!g_pUrlCoordinator->IsStopped())
{
g_pUrlCoordinator->Stop();
}
if (!g_pPrePostProcessor->IsStopped())
{
g_pPrePostProcessor->Stop();
@@ -240,6 +389,7 @@ void Run()
// main program-loop is terminated
debug("QueueCoordinator stopped");
debug("UrlCoordinator stopped");
debug("PrePostProcessor stopped");
}
@@ -261,7 +411,25 @@ void Run()
}
debug("RemoteServer stopped");
}
if (g_pRemoteSecureServer)
{
debug("stopping RemoteSecureServer");
g_pRemoteSecureServer->Stop();
int iMaxWaitMSec = 1000;
while (g_pRemoteSecureServer->IsRunning() && iMaxWaitMSec > 0)
{
usleep(100 * 1000);
iMaxWaitMSec -= 100;
}
if (g_pRemoteSecureServer->IsRunning())
{
debug("Killing RemoteSecureServer");
g_pRemoteSecureServer->Kill();
}
debug("RemoteSecureServer stopped");
}
// Stop Frontend
if (g_pFrontend)
{
@@ -284,46 +452,116 @@ void ProcessClientRequest()
{
RemoteClient* Client = new RemoteClient();
if (g_pOptions->GetClientOperation() == Options::opClientRequestList)
switch (g_pOptions->GetClientOperation())
{
Client->RequestServerList();
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestPause)
{
Client->RequestServerPauseUnpause(true);
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestUnpause)
{
Client->RequestServerPauseUnpause(false);
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestSetRate)
{
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestDumpDebug)
{
Client->RequestServerDumpDebug();
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestEditQueue)
{
Client->RequestServerEditQueue(g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(), true);
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestLog)
{
Client->RequestServerLog(g_pOptions->GetLogLines());
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestShutdown)
{
Client->RequestServerShutdown();
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestDownload)
{
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddTop());
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestVersion)
{
Client->RequestServerVersion();
case Options::opClientRequestListFiles:
Client->RequestServerList(true, false, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
break;
case Options::opClientRequestListGroups:
Client->RequestServerList(false, true, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
break;
case Options::opClientRequestListStatus:
Client->RequestServerList(false, false, NULL);
break;
case Options::opClientRequestDownloadPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload);
break;
case Options::opClientRequestDownloadUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload);
break;
case Options::opClientRequestDownload2Pause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload2);
break;
case Options::opClientRequestDownload2Unpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload2);
break;
case Options::opClientRequestSetRate:
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
break;
case Options::opClientRequestDumpDebug:
Client->RequestServerDumpDebug();
break;
case Options::opClientRequestEditQueue:
Client->RequestServerEditQueue((eRemoteEditAction)g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
g_pOptions->GetEditQueueText(), g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(),
g_pOptions->GetEditQueueNameList(), (eRemoteMatchMode)g_pOptions->GetMatchMode(), true);
break;
case Options::opClientRequestLog:
Client->RequestServerLog(g_pOptions->GetLogLines());
break;
case Options::opClientRequestShutdown:
Client->RequestServerShutdown();
break;
case Options::opClientRequestReload:
Client->RequestServerReload();
break;
case Options::opClientRequestDownload:
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
break;
case Options::opClientRequestVersion:
Client->RequestServerVersion();
break;
case Options::opClientRequestPostQueue:
Client->RequestPostQueue();
break;
case Options::opClientRequestWriteLog:
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
break;
case Options::opClientRequestScanAsync:
Client->RequestScan(false);
break;
case Options::opClientRequestScanSync:
Client->RequestScan(true);
break;
case Options::opClientRequestPostPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionPostProcess);
break;
case Options::opClientRequestPostUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionPostProcess);
break;
case Options::opClientRequestScanPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionScan);
break;
case Options::opClientRequestScanUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionScan);
break;
case Options::opClientRequestHistory:
Client->RequestHistory();
break;
case Options::opClientRequestDownloadUrl:
Client->RequestServerDownloadUrl(g_pOptions->GetLastArg(), g_pOptions->GetAddNZBFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
break;
case Options::opClientRequestUrlQueue:
Client->RequestUrlQueue();
break;
case Options::opClientNoOperation:
break;
}
delete Client;
@@ -331,7 +569,10 @@ void ProcessClientRequest()
void ExitProc()
{
info("Stopping, please wait...");
if (!g_bReloading)
{
info("Stopping, please wait...");
}
if (g_pOptions->GetRemoteClientMode())
{
if (g_pFrontend)
@@ -346,11 +587,19 @@ void ExitProc()
{
debug("Stopping QueueCoordinator");
g_pQueueCoordinator->Stop();
g_pUrlCoordinator->Stop();
g_pPrePostProcessor->Stop();
}
}
}
void Reload()
{
g_bReloading = true;
info("Reloading...");
ExitProc();
}
#ifndef WIN32
#ifdef DEBUG
typedef void(*sighandler)(int);
@@ -366,32 +615,22 @@ void SignalProc(int iSignal)
{
case SIGINT:
signal(SIGINT, SIG_DFL); // Reset the signal handler
debug("SIGINT received");
ExitProc();
break;
case SIGTERM:
signal(SIGTERM, SIG_DFL); // Reset the signal handler
debug("SIGTERM received");
ExitProc();
break;
#ifdef DEBUG
case SIGPIPE:
debug("SIGPIPE received, ignoring");
case SIGCHLD:
// ignoring
break;
#ifdef DEBUG
case SIGSEGV:
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
debug("SIGSEGV received");
break;
default:
debug("Signal %i received", iSignal);
if (SignalProcList[iSignal - 1])
{
SignalProcList[iSignal - 1](iSignal);
}
PrintBacktrace();
break;
#endif
}
@@ -403,27 +642,83 @@ void InstallSignalHandlers()
signal(SIGTERM, SignalProc);
signal(SIGPIPE, SIG_IGN);
#ifdef DEBUG
SignalProcList.clear();
for (int i = 1; i <= 32; i++)
{
SignalProcList.push_back((sighandler)signal(i, SignalProc));
}
signal(SIGWINCH, SIG_DFL);
signal(SIGSEGV, SignalProc);
#endif
#ifdef SIGCHLD_HANDLER
// it could be necessary on some systems to activate a handler for SIGCHLD
// however it make troubles on other systems and is deactivated by default
signal(SIGCHLD, SignalProc);
#endif
}
void PrintBacktrace()
{
#ifdef HAVE_BACKTRACE
printf("Segmentation fault, tracing...\n");
void *array[100];
size_t size;
char **strings;
size_t i;
size = backtrace(array, 100);
strings = backtrace_symbols(array, size);
// first trace to screen
printf("Obtained %zd stack frames\n", size);
for (i = 0; i < size; i++)
{
printf("%s\n", strings[i]);
}
// then trace to log
error("Segmentation fault, tracing...");
error("Obtained %zd stack frames", size);
for (i = 0; i < size; i++)
{
error("%s", strings[i]);
}
free(strings);
#else
error("Segmentation fault");
#endif
}
#ifdef DEBUG
void MakeSegFault()
{
char* N = NULL;
strcpy(N, "");
}
#endif
#ifdef HAVE_SYS_PRCTL_H
/**
* activates the creation of core-files
*/
void EnableDumpCore()
{
rlimit rlim;
rlim.rlim_cur= RLIM_INFINITY;
rlim.rlim_max= RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rlim);
prctl(PR_SET_DUMPABLE, 1);
}
#endif
#endif
void Cleanup()
{
debug("Cleaning up global objects");
debug("Deleting QueueCoordinator");
if (g_pQueueCoordinator)
debug("Deleting UrlCoordinator");
if (g_pUrlCoordinator)
{
delete g_pQueueCoordinator;
g_pQueueCoordinator = NULL;
delete g_pUrlCoordinator;
g_pUrlCoordinator = NULL;
}
debug("QueueCoordinator deleted");
debug("UrlCoordinator deleted");
debug("Deleting RemoteServer");
if (g_pRemoteServer)
@@ -433,6 +728,14 @@ void Cleanup()
}
debug("RemoteServer deleted");
debug("Deleting RemoteSecureServer");
if (g_pRemoteSecureServer)
{
delete g_pRemoteSecureServer;
g_pRemoteSecureServer = NULL;
}
debug("RemoteSecureServer deleted");
debug("Deleting PrePostProcessor");
if (g_pPrePostProcessor)
{
@@ -449,6 +752,14 @@ void Cleanup()
}
debug("Frontend deleted");
debug("Deleting QueueCoordinator");
if (g_pQueueCoordinator)
{
delete g_pQueueCoordinator;
g_pQueueCoordinator = NULL;
}
debug("QueueCoordinator deleted");
debug("Deleting DiskState");
if (g_pDiskState)
{
@@ -460,8 +771,9 @@ void Cleanup()
debug("Deleting Options");
if (g_pOptions)
{
if (g_pOptions->GetDaemonMode())
if (g_pOptions->GetDaemonMode() && !g_bReloading)
{
info("Deleting lock file");
remove(g_pOptions->GetLockFile());
}
delete g_pOptions;
@@ -477,8 +789,19 @@ void Cleanup()
}
debug("ServerPool deleted");
Thread::Final();
Connection::Final();
debug("Deleting Scheduler");
if (g_pScheduler)
{
delete g_pScheduler;
g_pScheduler = NULL;
}
debug("Scheduler deleted");
if (!g_bReloading)
{
Connection::Final();
Thread::Final();
}
debug("Global objects cleaned up");
@@ -513,6 +836,7 @@ void Daemonize()
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
if (pw)
{
fchown(lfp, pw->pw_uid, pw->pw_gid); /* change owner of lock file */
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
setgid(pw->pw_gid); /* Set primary group. */
/* Try setting aux groups correctly - not critical if this fails. */
@@ -531,3 +855,19 @@ void Daemonize()
signal(SIGTTIN, SIG_IGN);
}
#endif
#ifndef DISABLE_PARCHECK
class NullStreamBuf : public std::streambuf
{
public:
int sputc ( char c ) { return (int) c; }
} NullStreamBufInstance;
void DisableCout()
{
// libpar2 prints messages to c++ standard output stream (std::cout).
// However we do not want these messages to be printed.
// Since we do not use std::cout in nzbget we just disable it.
std::cout.rdbuf(&NullStreamBufInstance);
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
@@ -31,43 +31,55 @@
// WIN32
#define snprintf _snprintf
#ifndef strdup
#define strdup _strdup
#endif
#define fdopen _fdopen
#define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep)
#define localtime_r(time, tm) localtime_s(tm, time)
#define int32_t __int32
#define mkdir(dir, flags) _mkdir(dir)
#define rmdir _rmdir
#define strcasecmp(a, b) _stricmp(a, b)
#define strncasecmp(a, b, c) _strnicmp(a, b, c)
#pragma warning(disable:4800)
#pragma warning(disable:4267)
#pragma warning(disable:4244)
#define ssize_t SSIZE_T
#define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
#define S_DIRMODE NULL
#define usleep(usec) Sleep((usec) / 1000)
#define gettimeofday(tm, ignore) _ftime(tm)
#define _timeval _timeb
#define socklen_t int
#define SHUT_WR 0x01
#define SHUT_RDWR 0x02
#define PATH_SEPARATOR '\\'
#define ALT_PATH_SEPARATOR '/'
#define LINE_ENDING "\r\n"
#define pid_t int
#ifndef FSCTL_SET_SPARSE
#define FSCTL_SET_SPARSE 590020
#endif
#pragma warning(disable:4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
#else
// POSIX
#define _timeval timeval
#define closesocket(sock) close(sock)
#define SOCKET int
#define INVALID_SOCKET (-1)
#define PATH_SEPARATOR '/'
#define ALT_PATH_SEPARATOR '\\'
#define MAX_PATH 1024
#define S_DIRMODE (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
#define S_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
#define LINE_ENDING "\n"
#endif
#ifndef SHUT_RDWR
#define SHUT_RDWR 2
#endif
#endif

View File

@@ -6,11 +6,14 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release (no TLS)|Win32 = Release (no TLS)|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Debug|Win32.ActiveCfg = Debug|Win32
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Debug|Win32.Build.0 = Debug|Win32
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release (no TLS)|Win32.ActiveCfg = Release (no TLS)|Win32
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release (no TLS)|Win32.Build.0 = Release (no TLS)|Win32
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release|Win32.ActiveCfg = Release|Win32
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection

View File

@@ -38,7 +38,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
@@ -58,7 +58,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@@ -112,7 +112,8 @@
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403"
ExceptionHandling="1"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
@@ -130,7 +131,82 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
LinkIncremental="0"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release (no TLS)|Win32"
OutputDirectory="..\bin"
IntermediateDirectory="..\bin\Release (no TLS)"
ConfigurationType="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;DISABLE_TLS"
ExceptionHandling="1"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib regex.lib zlib.lib $(NOINHERIT)"
LinkIncremental="0"
GenerateDebugInformation="true"
SubSystem="1"
@@ -175,6 +251,14 @@
RelativePath=".\ArticleDownloader.h"
>
</File>
<File
RelativePath=".\BinRpc.cpp"
>
</File>
<File
RelativePath=".\BinRpc.h"
>
</File>
<File
RelativePath=".\ColoredFrontend.cpp"
>
@@ -251,14 +335,6 @@
RelativePath=".\NCursesFrontend.h"
>
</File>
<File
RelativePath=".\NetAddress.cpp"
>
</File>
<File
RelativePath=".\NetAddress.h"
>
</File>
<File
RelativePath=".\NewsServer.cpp"
>
@@ -315,6 +391,14 @@
RelativePath=".\Options.h"
>
</File>
<File
RelativePath=".\ParCoordinator.cpp"
>
</File>
<File
RelativePath=".\ParCoordinator.h"
>
</File>
<File
RelativePath=".\ParChecker.cpp"
>
@@ -323,6 +407,14 @@
RelativePath=".\ParChecker.h"
>
</File>
<File
RelativePath=".\ParRenamer.cpp"
>
</File>
<File
RelativePath=".\ParRenamer.h"
>
</File>
<File
RelativePath=".\PrePostProcessor.cpp"
>
@@ -363,6 +455,30 @@
RelativePath=".\RemoteServer.h"
>
</File>
<File
RelativePath=".\Scanner.cpp"
>
</File>
<File
RelativePath=".\Scanner.h"
>
</File>
<File
RelativePath=".\Scheduler.cpp"
>
</File>
<File
RelativePath=".\Scheduler.h"
>
</File>
<File
RelativePath=".\ScriptController.cpp"
>
</File>
<File
RelativePath=".\ScriptController.h"
>
</File>
<File
RelativePath=".\ServerPool.cpp"
>
@@ -379,6 +495,30 @@
RelativePath=".\Thread.h"
>
</File>
<File
RelativePath=".\TLS.cpp"
>
</File>
<File
RelativePath=".\TLS.h"
>
</File>
<File
RelativePath=".\Unpack.cpp"
>
</File>
<File
RelativePath=".\Unpack.h"
>
</File>
<File
RelativePath=".\UrlCoordinator.cpp"
>
</File>
<File
RelativePath=".\UrlCoordinator.h"
>
</File>
<File
RelativePath=".\Util.cpp"
>
@@ -387,10 +527,34 @@
RelativePath=".\Util.h"
>
</File>
<File
RelativePath=".\WebDownloader.cpp"
>
</File>
<File
RelativePath=".\WebDownloader.h"
>
</File>
<File
RelativePath=".\WebServer.cpp"
>
</File>
<File
RelativePath=".\WebServer.h"
>
</File>
<File
RelativePath=".\win32.h"
>
</File>
<File
RelativePath=".\XmlRpc.cpp"
>
</File>
<File
RelativePath=".\XmlRpc.h"
>
</File>
</Files>
<Globals>
</Globals>

75
nzbgetd Executable file
View File

@@ -0,0 +1,75 @@
#!/bin/sh
#
# Script used to start and stop the nzbget usenet service
#
# Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
# Copyright (C) 2009-2012 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, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# --- CONFIGURATION -----------------------------------------------
# Location of the nzbget executable
NZBGET_BINARY="/usr/local/bin/nzbget"
# Additional options, e. g. config file location:
# NZBGET_OPTS="-c /mnt/hdd/tools/nzbget/conf/nzbget.conf"
NZBGET_OPTS=""
# -----------------------------------------------------------------
if [ -z "$1" ] ; then
case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
S??*) rc="start" ;;
K??*) rc="stop" ;;
*) rc="usage" ;;
esac
else
rc="$1"
fi
case "$rc" in
start)
"$NZBGET_BINARY" $NZBGET_OPTS -D
;;
stop)
"$NZBGET_BINARY" $NZBGET_OPTS -Q
;;
restart)
"$NZBGET_BINARY" $NZBGET_OPTS -Q
sleep 10 # since stop is backgrounded
"$NZBGET_BINARY" $NZBGET_OPTS -D
;;
status)
"$NZBGET_BINARY" $NZBGET_OPTS -L S
;;
pstatus)
retval=$(pgrep -l -f nzbget > /dev/null ; echo $?)
if [ "$retval" = "0" ] ; then
echo " ------- nzbget *is* running -------"
ps -Ho user,pid,cmd:32,pcpu -C nzbget
exit 0
else
echo " ------- nzbget is *not* running -------"
exit 0
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status|pstatus|usage}"
exit 1
esac
exit 0

1426
webui/config.js Normal file
View File

File diff suppressed because it is too large Load Diff

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