Compare commits

...

375 Commits

Author SHA1 Message Date
Andrey Prygunkov
ba24945bcd version 0.7.0 2010-04-26 21:21:23 +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
Andrey Prygunkov
0204d0849d updated libpar2-patch for msvc to fix a memory leak in libpar2 (windows only) 2008-01-31 19:07:30 +00:00
Andrey Prygunkov
179f9e9ed6 fixed a memory leak in nzb-parser on windows 2008-01-31 19:02:44 +00:00
Andrey Prygunkov
d5b0a8dd8d fixed a memory leak in thread-management-code on windows 2008-01-31 18:58:41 +00:00
Andrey Prygunkov
4744559b46 removed <-testing> from version-string (prepare for release 0.3.1) 2008-01-30 17:06:20 +00:00
Andrey Prygunkov
51b6549671 fixed: compilation error after configuring with <--disable-parcheck> 2008-01-30 17:04:40 +00:00
Andrey Prygunkov
ec61b13269 fixed: trying to move the last group in queue down results in run-time error 2008-01-28 22:04:09 +00:00
Andrey Prygunkov
d898fff913 fixed: long lines (by bad articles) result in seg.fault 2008-01-28 18:29:13 +00:00
Andrey Prygunkov
fc19d48538 added the removing of trailing dot-characters from generated directory names to provide better compatibility with windows 2008-01-24 18:56:03 +00:00
Andrey Prygunkov
fe512e830c added options <NzbDirInterval> and <NzbDirFileAge> to adjust interval and delay by monitoring of incoming-directory for new nzb-files 2008-01-23 21:26:54 +00:00
Andrey Prygunkov
21be45e89a added remote-command <-V> (--serverversion) to print the server's version 2008-01-23 18:17:00 +00:00
Andrey Prygunkov
1eddc76630 fixed: by deleting files from queue the already downloaded articles were not deleted from temp-directory 2008-01-22 18:32:08 +00:00
Andrey Prygunkov
c4cc0cb745 added option <WriteBufferSize> to slightly reduce disk-io (by using of few megabytes of memory) 2008-01-22 18:29:28 +00:00
Andrey Prygunkov
1944ea7273 added gathering of statistical data: uptime, download-time, amount of downloaded data and average session download speed; extended communication protocol to transfer these data; extended ncurses-outputmode to print uptime, download-time and average download speed 2008-01-21 16:58:49 +00:00
Andrey Prygunkov
7da406ed4f removed superfluous trailing CR-character from one warning-message and reformatted few other messages 2008-01-16 18:21:11 +00:00
Andrey Prygunkov
6a0c1031fa added check for message-id of article returned from server, this improves error-detection for bad articles 2008-01-14 19:44:56 +00:00
Andrey Prygunkov
15b016ef7b extended response-messages in communication protocol for better error-reporting on client-side 2008-01-14 19:36:43 +00:00
Andrey Prygunkov
63b8c11ab5 imporved error reporting in remote server on binding errors (previously they were reported only with debug-binaries); fixed: remote server was listening to all interfaces regardless of option <serverip>, now it listen only to the specified address, but the old behaviour is also possible with address <0.0.0.0>, which means <all interfaces> 2008-01-13 17:09:53 +00:00
Andrey Prygunkov
5ca2279af8 improved the download speed meter, new implementation is much more accurate, especially on fast connections; this also means better speed throttling 2008-01-12 18:29:47 +00:00
Andrey Prygunkov
d8cf6263de eliminated few compiler warnings under gcc 2008-01-11 17:36:57 +00:00
Andrey Prygunkov
ebdd0fd1b8 fixed: it was generated way too many debug-messages in ServerPool, even in StandBy-mode 2008-01-11 16:42:28 +00:00
Andrey Prygunkov
9fa4cace8e improved duplicate check: it now works even for files, which subjects could not be parsed - though these files are not detected as duplicates by adding to queue, they will be detected after correct filename is read from the first article's body 2008-01-10 22:05:17 +00:00
Andrey Prygunkov
27d5f058eb better handling of cancelled downloads (important for keeped-alive connections) 2008-01-10 20:55:08 +00:00
Andrey Prygunkov
849db2a9dc authorization errors are now handled like connect errors and not counted as retries (this might cause the lost of articles under heavy load of newsserver) 2008-01-10 18:41:23 +00:00
Andrey Prygunkov
71531b0077 fixed: SmartOrder did not work by moving the files to the top of queue; fixed: DupeCheck did not check files on disk 2008-01-10 18:19:42 +00:00
Andrey Prygunkov
b254a6b6c9 added edit-commands for <pause all pars> and <pause extra pars> (works best on groups but also with file ranges); the key <P> in curses-outputmode in group-view now switches between three states: pause extra pars -> pause all files -> unpause all files; updated ChangeLog and README 2008-01-09 17:45:11 +00:00
Andrey Prygunkov
1cbf435468 improved communication with news-servers: connections are now keeped open until all files are downloaded (or server is paused), this eliminates the need for establishing of connections and authorization for each article; increased maximum possible download speed on fast connection via eliminating of pauses by starting of new articles' downloads (improved the synchronisation mechanism); another speed optimisation in internal decoder (up to 8% faster) 2008-01-08 21:21:16 +00:00
Andrey Prygunkov
abb33b763b fixed: if nzb-parser decides to use subject as a filename (fallback option), this new filename is not saved on disk (and will not be restored on next server's start) 2008-01-05 21:54:01 +00:00
Andrey Prygunkov
f79bdab2cd fixed: par-checking should not be started on deleting of collections from queue 2008-01-05 16:20:22 +00:00
Andrey Prygunkov
1d294f5037 fixed: decoder does not work with enabled option <DirectWrite> 2008-01-04 23:14:29 +00:00
Andrey Prygunkov
40b7b335b1 improved decoding speed (up to 20% faster); added new option <crccheck> to bypass crc-calculation on slow CPUs 2008-01-04 17:08:47 +00:00
Andrey Prygunkov
c18a3b1d7c fixed a compatibility issue (compilation error), detected on one of test linux systems 2008-01-03 17:53:56 +00:00
Andrey Prygunkov
3d782c985f improved parsing of artcile's subject for better extracting of filename part from it and implemented a fallback-option if the parsing was incorrect; improved dupe check for files from the same nzb-request to detect reposted files and download only the best from them; fixed error on parsing of nzb-files containing percent and other special characters in their names (bug appeared on windows only) 2008-01-02 18:41:05 +00:00
Andrey Prygunkov
d35a2d2f04 reformated sample configuration file and changed default optionnames from lowercase to MixedCase for better readability 2007-12-31 17:10:17 +00:00
Andrey Prygunkov
b85a36944d implemented decode-on-the-fly-technique to reduce disk-io; intermediate files with articles' source text are not created anymore, but only intermediate files with decoded data; futher, decoder can write decoded data directly to the destination file (without any intermediate files at all), this eliminates the necessity of joining of articles later (option <directwrite>) 2007-12-31 16:30:47 +00:00
Andrey Prygunkov
e15063208a fixed: destination directory for files was not initialized, caused by changes in revision 50 (greatly reduced...) 2007-12-27 19:24:48 +00:00
Andrey Prygunkov
255ebb8ccd fixed a compilation error on linux 2007-12-26 23:43:42 +00:00
Andrey Prygunkov
07d04d0e65 greatly reduced the memory consumption by keeping articles' info on disk until the file download starts 2007-12-26 23:31:49 +00:00
Andrey Prygunkov
b3cebce074 small changes in curses-outputmode 2007-12-26 13:21:52 +00:00
Andrey Prygunkov
c03f79155d added option <threadlimit> to prevent program from crash if it wants to create too many threads (sometimes may occur in special cases) 2007-12-26 12:06:25 +00:00
Andrey Prygunkov
d449a26ae0 removed test-code from NZBFile 2007-12-26 00:30:02 +00:00
Andrey Prygunkov
904227ebfc added translation of printed messages to oem-codepage to correctly print filenames with non-english characters (windows only) 2007-12-25 22:48:40 +00:00
Andrey Prygunkov
bff48cd97e fixed: deletion of currently downloading files was not always successful and second attempt was neccessary 2007-12-25 16:10:38 +00:00
Andrey Prygunkov
c72c8df5e9 added check to detect special synchonisation issues on windows in debug-mode 2007-12-25 14:37:19 +00:00
Andrey Prygunkov
8a33307b6c improved error-reporting (_brokenlog.txt) on crc-errors 2007-12-25 14:20:27 +00:00
Andrey Prygunkov
5f19177902 fixed: edit-group-commands not worked on posix due synchonisation issue 2007-12-25 01:34:55 +00:00
Andrey Prygunkov
2cb33bafc4 added option <retryoncrcerror> 2007-12-24 23:53:27 +00:00
Andrey Prygunkov
78baf30339 added replacing of CR and LF charachters with spaces in log-window in ncurses-outputmode for better formatting; normalized line-endings in few source files 2007-12-24 22:28:53 +00:00
Andrey Prygunkov
ea4a48dd20 fixed exception occuring if invalid offset was passed to edit-group-command 2007-12-24 21:01:12 +00:00
Andrey Prygunkov
1b8d9e9bfa changed initalization of options to fix conflict between suboption [G] and negative offsets in edit-command 2007-12-24 20:55:49 +00:00
Andrey Prygunkov
ad9a66e971 implemented edit-command <move-offset> for groups 2007-12-24 20:27:22 +00:00
Andrey Prygunkov
bb5c660e1f keywords set for QueueEditor.cpp/h (second attempt) 2007-12-24 17:51:32 +00:00
Andrey Prygunkov
4f29ed2e4c optimzed QueueEditor to edit lists of files and to provide transactional changes to all affected files 2007-12-24 17:37:23 +00:00
Andrey Prygunkov
6d5929a611 implemented edits for lists of groups; extended server/client communication protocol for group-commands; added suboption <G> for command-line switch <-E> to edit groups; Note: group-commands are still limited to pause, resume, delete, move-top, move-bottom 2007-12-23 22:54:10 +00:00
Andrey Prygunkov
c34ad991c4 refactored QueueEditor 2007-12-21 17:23:35 +00:00
Andrey Prygunkov
8eceb70626 added editing of queue for group-view in curses-outputmode: pause, resume, move-to-top, move-to-bottom; currently work only in server-console or standalone-app, not in remote-client 2007-12-20 23:45:05 +00:00
Andrey Prygunkov
f3f609f747 refactored: new class QueueEditor to handle all edit-requests; related code moved from QueueCoordinator and RemoteServer 2007-12-20 19:22:45 +00:00
Andrey Prygunkov
c02074e7ec creation of necessary directories on program's start extended with automatic creation of all parent directories and error reporting if it was not possible 2007-12-19 22:55:03 +00:00
Andrey Prygunkov
6697737405 added options <cursesnzbname>, <cursesgroup> and <cursestime> to define initial state of curses-outputmode 2007-12-19 20:41:06 +00:00
Andrey Prygunkov
dd3ca56564 toggle files/nzbs-list in curses-outputmode with G-key 2007-12-19 19:58:39 +00:00
Andrey Prygunkov
71805e11f7 removed accidentally commited debug-code in ParChecker.cpp 2007-12-19 18:27:26 +00:00
Andrey Prygunkov
e480781905 fixed a segfault appeared on Windows and caused by changes in revision 23 (added par-checker's status...) 2007-12-19 17:53:18 +00:00
Andrey Prygunkov
4df72eafd2 updated ChangeLog 2007-12-19 17:23:38 +00:00
Andrey Prygunkov
cdbfd3b96d added par-checker's status to status line and output of list-command 2007-12-18 23:23:56 +00:00
Andrey Prygunkov
cee6aa53f4 fully implemented SmartOrder-parameter for Edit-commands 2007-12-17 20:13:20 +00:00
Andrey Prygunkov
2d9bca1d1b added option <updateinterval> 2007-12-16 12:55:21 +00:00
Andrey Prygunkov
80685270e0 added option <umask> to specify permissions for newly created files and dirs (POSIX only) 2007-12-15 13:42:06 +00:00
Andrey Prygunkov
5695eaf3f4 1) fixed: file-ids in Edit-command were not converted to/from network byte order; 2) fixed few warnings under gcc 2007-12-14 22:36:10 +00:00
Andrey Prygunkov
8d46ee3236 eliminated configure-option --disable-parprogress (not needed anymore) 2007-12-14 22:19:51 +00:00
Andrey Prygunkov
702aad905f refactored RemoteServer and implemented initial support for SmartOrder in Edit-Command (currently only for MoveTop- and MoveBottom-commands) 2007-12-14 22:04:44 +00:00
Andrey Prygunkov
1da4976fea Edit-command now accepts inverse ranges of file-ids (this allows to keep relative order of files by moving items to the beginning of queue, since edit-command processes files in a giving order), e.g.: nzbget -E T 10-1 2007-12-13 23:56:59 +00:00
Andrey Prygunkov
94b81f9ce4 Edit-command now accepts more than one file-id or range of file-ids and does not require switch <I>, for example: nzbget -E P 2,6-10,33-39 2007-12-13 23:40:33 +00:00
Andrey Prygunkov
eec1a797b3 changed various structures in communication protocol 2007-12-13 19:08:55 +00:00
Andrey Prygunkov
ca7a90adab show/hide timestamps in curses-outputmode with T-key 2007-12-13 18:24:54 +00:00
Andrey Prygunkov
adedd66dac added few comments and renamed one field in MessageBase.h 2007-12-13 00:34:00 +00:00
Andrey Prygunkov
0a43829772 fixed and extended parameters for post-process-scripts (<result of par-check> and <state of nzb-job>) 2007-12-13 00:02:24 +00:00
Andrey Prygunkov
a35ff448b5 revised communication protocol to achieve compatibility between hosts with different endianness 2007-12-08 13:51:54 +00:00
Andrey Prygunkov
74db2f7785 updated version number in configure-script to 0.3.1-testing to differentiate between executables built from svn and published releases 2007-12-08 13:28:27 +00:00
Andrey Prygunkov
4760198c23 added missing Makefile.cvs (needed by KDevelop to rebuild configure-scripts) 2007-12-08 13:23:43 +00:00
Andrey Prygunkov
078c6c037e eliminated few compiler warnings 2007-12-07 22:20:45 +00:00
Andrey Prygunkov
56d4fcf045 fixed segfault when gethostbyname_r returns hinfo=NULL 2007-12-07 22:08:21 +00:00
Andrey Prygunkov
24c4cd87d7 added option daemonusername 2007-12-01 14:36:53 +00:00
Andrey Prygunkov
1b80cdd9b4 fixed seg fault on nzb-names starting with msgid 2007-11-29 18:53:17 +00:00
Andrey Prygunkov
b66266cf82 fixed an endless loop on receiving of SIGSEGV-signal in debug-mode 2007-11-29 18:06:49 +00:00
83 changed files with 29070 additions and 10929 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.*)
Andrei Prygounkov <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 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
@@ -28,9 +28,6 @@
#define ARTICLEDOWNLOADER_H
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#include "Observer.h"
#include "DownloadInfo.h"
@@ -47,9 +44,14 @@ public:
adRunning,
adFinished,
adFailed,
adRetry,
adDecodeError,
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
@@ -63,20 +65,20 @@ private:
char* m_szTempFilename;
char* m_szArticleFilename;
char* m_szInfoName;
char* m_szOutputFilename;
time_t m_tLastUpdateTime;
Decoder* m_pDecoder;
Semaphore m_semInitialized;
Semaphore m_semWaited;
static const char* m_szJobStatus[];
#ifdef WIN32
struct _timeb m_tStartTime;
#else
struct timeval m_tStartTime;
#endif
int m_iBytes;
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
EStatus Download();
void FreeConnection();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
public:
ArticleDownloader();
@@ -87,7 +89,6 @@ public:
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();
@@ -95,17 +96,13 @@ public:
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void CompleteFileParts();
void WaitInit();
#ifdef WIN32
struct _timeb* GetStartTime() { return &m_tStartTime; }
#else
struct timeval* GetStartTime() { return &m_tStartTime; }
#endif
int GetBytes() { return m_iBytes; }
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
void LogDebugInfo();
};
@@ -115,6 +112,7 @@ class DownloadSpeedMeter
public:
virtual ~DownloadSpeedMeter() {};
virtual float CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};
#endif

996
BinRpc.cpp Normal file
View File

@@ -0,0 +1,996 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2010 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 <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "BinRpc.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", "Version", "Post-queue", "Write log", "Scan",
"Pause/Unpause postprocessor", "History" };
const unsigned int g_iMessageRequestSizes[] =
{ 0,
sizeof(SNZBDownloadRequest),
sizeof(SNZBPauseUnpauseRequest),
sizeof(SNZBListRequest),
sizeof(SNZBSetDownloadRateRequest),
sizeof(SNZBDumpDebugRequest),
sizeof(SNZBEditQueueRequest),
sizeof(SNZBLogRequest),
sizeof(SNZBShutdownRequest),
sizeof(SNZBVersionRequest),
sizeof(SNZBPostQueueRequest),
sizeof(SNZBWriteLogRequest),
sizeof(SNZBScanRequest),
sizeof(SNZBHistoryRequest)
};
//*****************************************************************
// BinProcessor
void BinRpcProcessor::Execute()
{
// Read the first package which needs to be a request
int iBytesReceived = recv(m_iSocket, ((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature), 0);
if (iBytesReceived < 0)
{
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 from %s", g_pOptions->GetServerPort(), m_szClientIP);
return;
}
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
{
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetServerPort(), m_szClientIP);
return;
}
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_szClientIP);
Dispatch();
}
void BinRpcProcessor::Dispatch()
{
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestHistory &&
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
{
error("Invalid size of request: expected %i Bytes, but received %i Bytes",
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
return;
}
BinCommand* command = NULL;
switch (ntohl(m_MessageBase.m_iType))
{
case eRemoteRequestDownload:
command = new DownloadBinCommand();
break;
case eRemoteRequestList:
command = new ListBinCommand();
break;
case eRemoteRequestLog:
command = new LogBinCommand();
break;
case eRemoteRequestPauseUnpause:
command = new PauseUnpauseBinCommand();
break;
case eRemoteRequestEditQueue:
command = new EditQueueBinCommand();
break;
case eRemoteRequestSetDownloadRate:
command = new SetDownloadRateBinCommand();
break;
case eRemoteRequestDumpDebug:
command = new DumpDebugBinCommand();
break;
case eRemoteRequestShutdown:
command = new ShutdownBinCommand();
break;
case eRemoteRequestVersion:
command = new VersionBinCommand();
break;
case eRemoteRequestPostQueue:
command = new PostQueueBinCommand();
break;
case eRemoteRequestWriteLog:
command = new WriteLogBinCommand();
break;
case eRemoteRequestScan:
command = new ScanBinCommand();
break;
case eRemoteRequestHistory:
command = new HistoryBinCommand();
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 BinCommand::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 BinCommand::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 PauseUnpauseBinCommand::Execute()
{
SNZBPauseUnpauseRequest PauseUnpauseRequest;
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
{
return;
}
switch (ntohl(PauseUnpauseRequest.m_iAction))
{
case eRemotePauseUnpauseActionDownload:
g_pOptions->SetPauseDownload(ntohl(PauseUnpauseRequest.m_bPause));
break;
case eRemotePauseUnpauseActionDownload2:
g_pOptions->SetPauseDownload2(ntohl(PauseUnpauseRequest.m_bPause));
break;
case eRemotePauseUnpauseActionPostProcess:
g_pOptions->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_bPause));
break;
case eRemotePauseUnpauseActionScan:
g_pOptions->SetPauseScan(ntohl(PauseUnpauseRequest.m_bPause));
break;
}
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
}
void SetDownloadRateBinCommand::Execute()
{
SNZBSetDownloadRateRequest SetDownloadRequest;
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
{
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
SendBoolResponse(true, "Rate-Command completed successfully");
}
void DumpDebugBinCommand::Execute()
{
SNZBDumpDebugRequest DumpDebugRequest;
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
{
return;
}
g_pQueueCoordinator->LogDebugInfo();
SendBoolResponse(true, "Debug-Command completed successfully");
}
void ShutdownBinCommand::Execute()
{
SNZBShutdownRequest ShutdownRequest;
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
{
return;
}
SendBoolResponse(true, "Stopping server");
ExitProc();
}
void VersionBinCommand::Execute()
{
SNZBVersionRequest VersionRequest;
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
{
return;
}
SendBoolResponse(true, Util::VersionRevision());
}
void DownloadBinCommand::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, DownloadRequest.m_szCategory, 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", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(false, tmp);
}
}
free(pRecvBuffer);
}
void ListBinCommand::Execute()
{
SNZBListRequest ListRequest;
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
{
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(SNZBListResponseFileEntry));
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();
// calculate required buffer size for nzbs
int iNrNZBEntries = pDownloadQueue->GetNZBInfoList()->size();
int iNrPPPEntries = 0;
bufsize += iNrNZBEntries * sizeof(SNZBListResponseNZBEntry);
for (NZBInfoList::iterator it = pDownloadQueue->GetNZBInfoList()->begin(); it != pDownloadQueue->GetNZBInfoList()->end(); it++)
{
NZBInfo* pNZBInfo = *it;
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
// calculate required buffer size for pp-parameters
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
bufsize += sizeof(SNZBListResponsePPPEntry);
bufsize += strlen(pNZBParameter->GetName()) + 1;
bufsize += strlen(pNZBParameter->GetValue()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
iNrPPPEntries++;
}
}
// calculate required buffer size for files
int iNrFileEntries = pDownloadQueue->GetFileQueue()->size();
bufsize += iNrFileEntries * sizeof(SNZBListResponseFileEntry);
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
bufsize += strlen(pFileInfo->GetSubject()) + 1;
bufsize += strlen(pFileInfo->GetFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
buf = (char*) malloc(bufsize);
char* bufptr = buf;
// write nzb entries
for (NZBInfoList::iterator it = pDownloadQueue->GetNZBInfoList()->begin(); it != pDownloadQueue->GetNZBInfoList()->end(); it++)
{
unsigned long iSizeHi, iSizeLo;
NZBInfo* pNZBInfo = *it;
SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) bufptr;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1);
pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1);
pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1);
bufptr += sizeof(SNZBListResponseNZBEntry);
strcpy(bufptr, pNZBInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pNZBInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
strcpy(bufptr, pNZBInfo->GetCategory());
bufptr += ntohl(pListAnswer->m_iCategoryLen);
strcpy(bufptr, pNZBInfo->GetQueuedFilename());
bufptr += ntohl(pListAnswer->m_iQueuedFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
// write ppp entries
int iNZBIndex = 1;
for (NZBInfoList::iterator it = pDownloadQueue->GetNZBInfoList()->begin(); it != pDownloadQueue->GetNZBInfoList()->end(); it++, iNZBIndex++)
{
NZBInfo* pNZBInfo = *it;
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
SNZBListResponsePPPEntry* pListAnswer = (SNZBListResponsePPPEntry*) bufptr;
pListAnswer->m_iNZBIndex = htonl(iNZBIndex);
pListAnswer->m_iNameLen = htonl(strlen(pNZBParameter->GetName()) + 1);
pListAnswer->m_iValueLen = htonl(strlen(pNZBParameter->GetValue()) + 1);
bufptr += sizeof(SNZBListResponsePPPEntry);
strcpy(bufptr, pNZBParameter->GetName());
bufptr += ntohl(pListAnswer->m_iNameLen);
strcpy(bufptr, pNZBParameter->GetValue());
bufptr += ntohl(pListAnswer->m_iValueLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iValueLen = htonl(ntohl(pListAnswer->m_iValueLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
}
// write file entries
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
unsigned long iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseFileEntry* pListAnswer = (SNZBListResponseFileEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
int iNZBIndex = 0;
for (unsigned int i = 0; i < pDownloadQueue->GetNZBInfoList()->size(); i++)
{
iNZBIndex++;
if (pDownloadQueue->GetNZBInfoList()->at(i) == pFileInfo->GetNZBInfo())
{
break;
}
}
pListAnswer->m_iNZBIndex = htonl(iNZBIndex);
Util::SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
Util::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_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
bufptr += sizeof(SNZBListResponseFileEntry);
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += ntohl(pListAnswer->m_iSubjectLen);
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iFilenameLen = htonl(ntohl(pListAnswer->m_iFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries);
ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries);
ListResponse.m_iNrTrailingFileEntries = htonl(iNrFileEntries);
ListResponse.m_iTrailingDataLength = htonl(bufsize);
}
if (htonl(ListRequest.m_bServerState))
{
unsigned long iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
Util::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_bDownloadPaused = htonl(g_pOptions->GetPauseDownload());
ListResponse.m_bDownload2Paused = htonl(g_pOptions->GetPauseDownload2());
ListResponse.m_bPostPaused = htonl(g_pOptions->GetPausePostProcess());
ListResponse.m_bScanPaused = htonl(g_pOptions->GetPauseScan());
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
ListResponse.m_iPostJobCount = htonl(pPostQueue->size());
g_pQueueCoordinator->UnlockQueue();
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_bDownloadStandBy = htonl(bStandBy);
Util::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 LogBinCommand::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;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
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((int)pMessage->GetTime());
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
bufptr += sizeof(SNZBLogResponseEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += ntohl(pLogAnswer->m_iTextLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pLogAnswer->m_iTextLen = htonl(ntohl(pLogAnswer->m_iTextLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
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 EditQueueBinCommand::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);
int iTextLen = ntohl(EditQueueRequest.m_iTextLen);
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
if (iNrEntries * sizeof(int32_t) + iTextLen != iBufLength)
{
error("Invalid struct size");
return;
}
if (iNrEntries <= 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
return;
}
char* pBuf = (char*)malloc(iBufLength);
char* szText = NULL;
int32_t* pIDs = NULL;
// Read from the socket until nothing remains
char* pBufPtr = pBuf;
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;
}
bool bOK = NeedBytes == 0;
if (bOK)
{
szText = iTextLen > 0 ? pBuf : NULL;
pIDs = (int32_t*)(pBuf + iTextLen);
}
IDList cIDList;
cIDList.reserve(iNrEntries);
for (int i = 0; i < iNrEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
if (iAction < eRemoteEditActionPostMoveOffset)
{
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
}
free(pBuf);
if (bOK)
{
SendBoolResponse(true, "Edit-Command completed successfully");
}
else
{
SendBoolResponse(false, "Edit-Command failed");
}
}
void PostQueueBinCommand::Execute()
{
SNZBPostQueueRequest PostQueueRequest;
if (!ReceiveRequest(&PostQueueRequest, sizeof(PostQueueRequest)))
{
return;
}
SNZBPostQueueResponse PostQueueResponse;
memset(&PostQueueResponse, 0, sizeof(PostQueueResponse));
PostQueueResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
PostQueueResponse.m_MessageBase.m_iStructSize = htonl(sizeof(PostQueueResponse));
PostQueueResponse.m_iEntrySize = htonl(sizeof(SNZBPostQueueResponseEntry));
char* buf = NULL;
int bufsize = 0;
// Make a data structure and copy all the elements of the list into it
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
int NrEntries = pPostQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBPostQueueResponseEntry);
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
{
PostInfo* pPostInfo = *it;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1;
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1;
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
time_t tCurTime = time(NULL);
buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
{
PostInfo* pPostInfo = *it;
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) bufptr;
pPostQueueAnswer->m_iID = htonl(pPostInfo->GetID());
pPostQueueAnswer->m_iStage = htonl(pPostInfo->GetStage());
pPostQueueAnswer->m_iStageProgress = htonl(pPostInfo->GetStageProgress());
pPostQueueAnswer->m_iFileProgress = htonl(pPostInfo->GetFileProgress());
pPostQueueAnswer->m_iTotalTimeSec = htonl((int)(pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0));
pPostQueueAnswer->m_iStageTimeSec = htonl((int)(pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0));
pPostQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1);
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1);
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
bufptr += sizeof(SNZBPostQueueResponseEntry);
strcpy(bufptr, pPostInfo->GetNZBInfo()->GetFilename());
bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
strcpy(bufptr, pPostInfo->GetParFilename());
bufptr += ntohl(pPostQueueAnswer->m_iParFilename);
strcpy(bufptr, pPostInfo->GetInfoName());
bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen);
strcpy(bufptr, pPostInfo->GetNZBInfo()->GetDestDir());
bufptr += ntohl(pPostQueueAnswer->m_iDestDirLen);
strcpy(bufptr, pPostInfo->GetProgressLabel());
bufptr += ntohl(pPostQueueAnswer->m_iProgressLabelLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pPostQueueAnswer->m_iProgressLabelLen = htonl(ntohl(pPostQueueAnswer->m_iProgressLabelLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
PostQueueResponse.m_iNrTrailingEntries = htonl(NrEntries);
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}
void WriteLogBinCommand::Execute()
{
SNZBWriteLogRequest WriteLogRequest;
if (!ReceiveRequest(&WriteLogRequest, sizeof(WriteLogRequest)))
{
return;
}
char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(WriteLogRequest.m_iTrailingDataLength);
pRecvBuffer[NeedBytes] = '\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;
}
if (NeedBytes == 0)
{
bool OK = true;
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
{
case Message::mkDetail:
detail(pRecvBuffer);
break;
case Message::mkInfo:
info(pRecvBuffer);
break;
case Message::mkWarning:
warn(pRecvBuffer);
break;
case Message::mkError:
error(pRecvBuffer);
break;
case Message::mkDebug:
debug(pRecvBuffer);
break;
default:
OK = false;
}
SendBoolResponse(OK, OK ? "Message added to log" : "Invalid message-kind");
}
free(pRecvBuffer);
}
void ScanBinCommand::Execute()
{
SNZBScanRequest ScanRequest;
if (!ReceiveRequest(&ScanRequest, sizeof(ScanRequest)))
{
return;
}
g_pPrePostProcessor->ScanNZBDir();
SendBoolResponse(true, "Scan-Command scheduled successfully");
}
void HistoryBinCommand::Execute()
{
SNZBHistoryRequest HistoryRequest;
if (!ReceiveRequest(&HistoryRequest, sizeof(HistoryRequest)))
{
return;
}
SNZBHistoryResponse HistoryResponse;
memset(&HistoryResponse, 0, sizeof(HistoryResponse));
HistoryResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
HistoryResponse.m_MessageBase.m_iStructSize = htonl(sizeof(HistoryResponse));
HistoryResponse.m_iEntrySize = htonl(sizeof(SNZBHistoryResponseEntry));
char* buf = NULL;
int bufsize = 0;
// Make a data structure and copy all the elements of the list into it
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// calculate required buffer size for nzbs
int iNrNZBEntries = pDownloadQueue->GetHistoryList()->size();
bufsize += iNrNZBEntries * sizeof(SNZBHistoryResponseEntry);
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
NZBInfo* pNZBInfo = *it;
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
buf = (char*) malloc(bufsize);
char* bufptr = buf;
// write nzb entries
for (NZBInfoList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
unsigned long iSizeHi, iSizeLo;
NZBInfo* pNZBInfo = *it;
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iID = htonl(pNZBInfo->GetID());
pListAnswer->m_tTime = htonl((int)pNZBInfo->GetHistoryTime());
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatus());
pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1);
pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1);
pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1);
bufptr += sizeof(SNZBHistoryResponseEntry);
strcpy(bufptr, pNZBInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pNZBInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
strcpy(bufptr, pNZBInfo->GetCategory());
bufptr += ntohl(pListAnswer->m_iCategoryLen);
strcpy(bufptr, pNZBInfo->GetQueuedFilename());
bufptr += ntohl(pListAnswer->m_iQueuedFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
HistoryResponse.m_iNrTrailingEntries = htonl(iNrNZBEntries);
HistoryResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &HistoryResponse, sizeof(HistoryResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}

143
BinRpc.h Normal file
View File

@@ -0,0 +1,143 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2009 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 BINRPC_H
#define BINRPC_H
#include "Connection.h"
#include "MessageBase.h"
class BinRpcProcessor
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
const char* m_szClientIP;
void Dispatch();
public:
void Execute();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
void SetSignature(int iSignature) { m_MessageBase.m_iSignature = iSignature; }
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
class BinCommand
{
protected:
SOCKET m_iSocket;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
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 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();
};
#endif

808
ChangeLog
View File

@@ -1,188 +1,620 @@
nzbget-0.3.0:
- The download queue now contains newsgroup-files to be downloaded instead of
nzb-jobs. By adding a new job, the nzb-file is immediately parsed and each
newsgroup-file is added to download queue. Each file can therefore be
managed separately (paused, deleted or moved);
- Current queue state is saved after every change (file is completed or the
queue is changed - entries paused, deleted or moved). The state is saved on
disk using internal format, which allows fast loading on next program start
(no need to parse xml-files again);
- The remaining download-size is updated after every article is completed to
indicate the correct remaining size and time for total files in queue;
- Downloaded articles, which are saved in temp-directory, can be reused on
next program start, if the file was not completed (option "continuepartial"
in config-file);
- Along with uulib the program has internal decoder for yEnc-format. This
decoder was necessary, because uulib is so slow, that it prevents using of
the program on not so powerful systems like linux-routers (MIPSEL CPU 200
MHz). The new decoder is very fast. It is controlled over option "decoder"
in config-file;
- The decoder can be completely disabled. In this case all downloaded articles
are saved in unaltered form and can be joined with an external program;
UUDeview is one of them;
- If download of article fails, the program attempts to download it again so
many times, what the option "retries" in config-file says. This works even
if no servers with level higher than "0" defined. After each retry the next
server-level is used, if there are no more levels, the program switches to
level "0" again. The pause between retries can be set with config-option
"retryinterval";
- If despite of a stated connection-timeout (it can be changed via
config-option "connectiontimeout") connection hangs, the program tries to
cancel the connection (after "terminatetimeout" seconds). If it doesn't
work the download thread is killed and the article will be redownloaded in
a new thread. This ensures, that there are no long-time hanging connections
and all articles are downloaded, when a time to rejoin file comes;
- Automatic par-checking and repairing. Only reuired par-files are downloaded.
The program uses libpar2 and does not require any external tools. The big
advantage of library is, that it allows to continue par-check after new
par-blocks were downloaded. This were not possible with external
par2cmdline-tool;
- There is a daemon-mode now (command-line switch "-D" (--daemon)). In this
mode a lock-file (default location "/tmp/nzbget.lock", can be changed via
option "lockfile") contains PID of daemon;
- The format of configuration-file was changed from xml to more common
text-format. It allows also using of variables like
"tempdir=${MAINDIR}/tmp";
- Any option of config-file can be overwritten via command-line switch
"-o" (--option). This includes also the definition of servers.
This means that the program can now be started without a configuration-file
at all (all required options must be passed via command-line);
- The command-line switches were revised. The dedicated switches to change
options in config-file were eliminated, since any option can now be changed
via switch "-o" (--option);
- If the name of configuration-file was not passed via command-line the
program search it in following locations: "~/.nzbget", "/etc/nzbget.conf",
"/usr/etc/nzbget.conf", "/usr/local/etc/nzbget.conf",
"/opt/etc/nzbget.conf";
- The new command-line switch "-n" (--noconfigfile) prevents the loading of
a config-file. All required config-options must be passed via command-line
(switch "-o" (--option));
- To start the program in server mode either "-s" (--server) or
"-D" (--daemon) switch must be used. If the program started without any
parameters it prints help-screen. There is no a dedicated switch to start
in a standalone mode. If switches "-s" and "-D" are omitted and none of
client-request-switches used the standalone mode is default. This usage
of switches is more common to programs like "wget". To add a file to
server's download queue use switch "-A" (--append) and a name of nzb-file
as last command-line parameter;
- There is a new switch "-Q" (--quit) to gracefully stop server. BTW the
SIGKIL-signal is now handled appropriately, so "killall nzbget" is also OK,
where "killall -9 nzbget" terminates server immediately (helpful if it
hangs, but it shouldn't);
- With new switch "-T" (--top) the file will be added to the top of download
queue. Use it with switch "-A" (--append);
- The download queue can be edited via switch "-E" (--edit). It is possible
to pause, unpause, delete and move files in queue. The IDs of file(s)
to be affected are passed via switch "-I" (fileid), either one ID or a
range in a form "IDForm-IDTo". This also means, that every file in queue
have ID now;
- The switch "-L" (--list) prints IDs of files consequently. It prints also
name, size, percentage of completing and state (paused or not) of each file.
Plus summary info: number of files, total remaining size and size of
paused files, server state (paused or running), number of threads on
server, current speed limit;
- With new switch "-G" (--log) the last N lines printed to server's
screen-log, can be printed on client. The max number of lines which can
be returned from servers depends on option "logbuffersize";
- The redesigned Frontends (known as outputmodes "loggable", "colored" and
"curses") can connect to (remote) server and behave as if you were running
server-instance of program itself (command-line switch "-C" (--connect)).
The log-output updates constantly and even all control-functions in
ncurses-mode works: pause/unpause server, set download rate limit, edit of
queue (pause/unpause, delete, move entries). The number of connected
clients is not limited. The "outputmode" on a client can be set
independently from server. The client-mode is especially useful if the
server runs as a daemon;
- The writing to log-file can be disabled via option "createlog".
The location of log-file controls the option "log-file";
- Switch "-p" (--printconfig) prints the name of configuration file being
used and all option/value-pairs, taking into account all used
"-o" (--option) - switches;
- The communication protocol between server and client was optimized to
minimize the size of transferred data. Instead of fixing the size for
Filenames in data-structures to 512 bytes only in fact used data
are transferred;
- Extensions in ncurses-outputmode: scrolling in queue-list works better,
navigation in queue with keys Up, Down, PgUp, PgDn, Home, End.
Keys to move entries are "U" (move up), "N" (move down), "T" (move to top),
"B" (move to bottom). "P" to pause/unpause file. The size, percentage
of completing and state (paused or not) for every file is printed.
The header of queue shows number of total files, number of unpaused
files and size for all and unpaused files. Better using of screen estate
space <20> no more empty lines and separate header for status (total seven
lines gain). The messages are printed on several lines (if they not fill
in one line) without trimming now;
- configure.ac-file updated to work with recent versions of autoconf/automake.
There are new configure-options now: "--disable-uulib" to compile the
program without uulib; "--disable-ncurses" to disable ncurses-support
(eliminates necessity of ncurses-libs), useful on embedded systems with
little resources; "--disable-parcheck" to compile without par-check;
- The algorithm for parsing of nzb-files now uses XMLReader instead of
DOM-Parser to minimize memory usage (no mor needs to build complete DOM-tree
in memory). Thanks to Thierry MERLE <merlum@users.sourceforge.net> for
the patch;
- The log-file contains now thread-ID for all entry-types and additionally
for debug-entries: filename, line number and function's name of source
code, where the message was printed. Debug-messages can be disabled in
config-file (option "debugtarget") like other messages;
- The program is now compatible with windows. Project file for MS Visual
C++ 2005 is included. Use "nzbget -install" and "nzbget -remove" to
install/remove NZBGet-Service. Servers and clients can run on diferrent
operating systems;
- Improved compatibility with POSIX systems; Tested on:
- Linux Debian 3.1 on x86;
- Linux BusyBox with uClibc on MIPSEL;
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
- Solaris 10 on x86;
- Many memory-leaks and thread issues were fixed;
- The program was thoroughly worked over. Almost every line of code was
revised;
nzbget-0.2.3
- Fixed problem with losing connection to newsserver after too long idle time
- Added functionality for dumping lots of debug info
nzbget-0.2.2
- Added Florian Penzkofers fix for FreeBSD, exchanging base functionality in
SingleServerPool.cpp with a more elegant solution
- Added functionality for forcing answer to reloading queue upon startup of
server
+ use -y option to force from command-line
+ use "reloadqueue" option in nzbget.cfg to control behavior
- Added nzbget.cfg options to control where info, warnings and errors get
directed to (either screen, log or both)
- Added option "createbrokenfilelog" in nzbget.cfg
nzbget-0.2.1
- Changed and extended the TCP client/server interface
- Added timeout on sockets which prevents certain types of nzbget hanging
- Added Kristian Hermansen's patch for renaming broken files
nzbget-0.2.0
- Moved 0.1.2-alt4 to a official release as 0.2.0
- Small fixes
nzbget-0.1.2-alt4
- implemented tcp/ip communication between client & server (removing the
rather defunct System V IPC)
- added queue editing functionality in server-mode
nzbget-0.1.2-alt1
- added new ncurses frontend
- added server/client-mode (using System V IPC)
- added functionality for queueing download requests
nzbget-0.1.2
- performance-improvements
- commandline-options
- fixes
nzbget-0.1.1
- new output
- fixes
nzbget-0.1.0a
- compiling-fixes
nzbget-0.1.0
- initial release
nzbget-0.7.0:
- added history: new option <KeepHistory>, new remote subcommand <H> for
commands <L> (list history entries) and <E> (delete history entries,
return history item, postprocess history item), new RPC-command <History>
and subcommands <HistoryDelete>, <HistoryReturn>, <HistoryProcess> for
command <EditQueue>;
- added support for JSON-P (extension of JSON-RPC);
- changed the result code returning status <ERROR> for postprocessing script
from <1> to <94> (needed to show the proper script status in history);
- 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;
- 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;
- improved formatting of groups and added time info in curses output mode;
- 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 interfering 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;
- nzbprocess-script (option <NZBProcess>) can now set category and
post-processing parameters for nzb-file;
- redesigned server pool and par-checker to avoid using of semaphores
(which are very platform specific);
- 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;
- 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;
- improved error reporting while parsing nzb-files;
- added field <NZBID> to NZBInfo; the field is now returned by XML-/JSON-RPC
methods <listfiles>, <listgroups> and <postqueue>;
- improvements in configure script;
- added support for platforms without IPv6 (they do not have <getaddrinfo>);
- 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;
- imporved example postprocessing script: added support for external
configuration file, postprocessing parameters and configuration via
web-interface;
- option <TaskX.Process> now can contain parameters which must be passed
to the script;
- 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>;
- 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>);
- improved example post-processing script: added support for delayed
par-check (try unrar first, par-repair if unrar failed);
- 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;
- 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>;
- 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;
- added estimated remaining time and better distinguishing of server state
in command <--list/-L>;
- 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;
- added readonly options <AppBin>, <ConfigFile> and <Version> for usage
in processing scripts (options are available as environment variables
<NZBOP_APPBIN>, <NZBOP_CONFIGFILE> and <NZBOP_VERSION>);
- renamed ParStatus constant <FAILED> to <FAILURE> for a consistence with
ScriptStatus constant <FAILURE>, that also affects the results of
RPC-command <history>;
- 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>;
- added field <PostTime> to result of RPC-Command <listfiles> and fields
<MinPostTime> and <MaxPostTime> for command <listgroups>;
- 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;
- improvement in example post-processing script: added check for existence
of <unrar> and command <wc>;
- added shell batch file for windows (nzbget-shell.bat);
thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script;
- added debian style init script (nzbgetd);
thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script;
- 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;
- changed the sleep-time during the throttling of bandwidth from 200ms to
10ms in order to achieve better uniformity;
- modified example postprocessing script to not use the command <dirname>,
which is not always available;
thanks to Ger Teunis for the patch;
- 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);
- by saving the queue to disk now using relative paths for the list of
compeled files to reduce the file's size;
- eliminated few compiler warnings on GCC;
- fixed: when option <DaemonUserName> was specified and nzbget was
started as root, the lockfile was not removed;
- fixed: nothing was downloaded when the option <Retries> was set to <0>;
- fixed: base64 decoding function used by RPC-method <append> sometimes
failed, in particular when called from Ruby-language;
- fixed: JSON-RPC-commands failed, if parameters were placed before method
name in the request;
- fixed: RPC-method <append> did not work properly on Posix systems
(it worked only on Windows);
- fixed compilation error when using native curses library on OpenSolaris;
- fixed linking error on OpenSolaris when using GnuTLS;
- fixed: option <ContinuePartial> did not work;
- fixed: seg. fault in service mode on program start (Windows only);
- fixed: environment block was not passed correctly to child process,
what could result in seg faults (windows only);
- fixed: returning the postprocessing exit code <92 - par-check all
collections> when there were no par-files results in endless calling
of postprocessing script;
- fixed compatibility issues with OS/2.
nzbget-0.6.0:
- added scheduler; new options <TaskX.Time>, <TaskX.WeekDays>,
<TaskX.Command>, <TaskX.DownloadRate> and <TaskX.Process>;
- 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;
- 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;
- added new subcommand <M> to edit-command <E> for merging of two (or more)
groups (useful after adding pars from a separate nzb-file);
- added option <MergeNzb> to automatically merge nzb-files with the same
filename (useful by adding pars from a different source);
- added script-processing of files in incoming directory to allow automatic
unpacking and queueing of compressed nzb-files; new option <NzbProcess>;
- added the printing of post-process-parameters for groups in command
<--list G>;
- added the printing of nzbget version into the log-file on start;
- added option <DeleteCleanupDisk> to automatically delete already downloaded
files from disk if nzb-file was deleted from queue (the download was
cancelled);
- added option <ParTimeLimit> to define the max time allowed for par-repair;
- added command <--scan/-S> to execute the scan of nzb-directory on remote
server;
- changed the method to pass arguments to postprocess/nzbprocess: now using
environment variables (old method is still supported for compatibility with
existing scripts);
- added the passing of nzbget-options to postprocess/nzbprocess scripts as
environment variables;
- extended the communication between nzbget and post-process-script:
collections are now detected even if parcheck is disabled;
- added support for delayed par-check/repair: post-process-script can request
par-check/repair using special exit codes to repair current collection or
all collections;
- 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;
- replaced option <PostLogKind> with new option <ProcessLogKind> which is now
used by all scripts (PostProcess, NzbProcess, TaskX.Process);
- improved entering to paused state on connection errors (do not retry failed
downloads if pause was activated);
- improved error reporting on decoding failures;
- improved compatibility of yenc-decoder;
- improved the speed of deleting of groups from download queue (by avoiding
the saving of queue after the deleting of each individual file);
- updated configure-script for better compatibility with FreeBSD;
- 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;
- added workaround to avoid hangs in child processes (by starting of
postprocess or nzbprocess), observed on uClibC based systems;
- fixed: TLS/SSL didn't work in standalone mode;
- fixed compatibility issues with Mac OS X;
- fixed: not all necessary par2-files were unpaused on first request for
par-blocks (although harmless, because additional files were unpaused
later anyway);
- fixed small memory leak appeared if process-script could not be started;
- fixed: configure-script could not detect the right syntax for function
<ctime_r> on OpenSolaris.
- fixed: files downloaded with disabled decoder (option decode=no) sometimes
were malformed and could not be decoded;
- fixed: empty string parameters did not always work in XML-RPC.
nzbget-0.5.1:
- improved the check of server responses to prevent unnecessary retrying
if the article does not exist on server;
- fixed: seg.fault in standalone mode if used without specifying the
category (e.g. without switch <-K>);
- fixed: download speed indicator could report not-null values in
standby-mode (when paused);
- 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;
nzbget-0.5.0:
- added TLS/SSL-support for encrypted communication with news-servers;
- added IPv6-support for communication with news-servers as well as for
communication between nzbget-server and nzbget-client;
- added support for categories to organize downloaded files;
- new option <AppendCategoryDir> to create the subdirectory for each category;
- new switch <-K> for usage with switch <-A> to define a category during
the adding a file to download queue;
- new command <K> in switch <-E> to change the category of nzb-file in
download queue; the already downloaded files are automatically moved to new
directory if the option <AppendCategoryDir> is active;
- new parameter <Category> in XML-/JSON-RPC-command <editqueue> to allow the
changing of category via those protocols;
- new parameter in a call to post-process-script with category name;
- scanning of subdirectories inside incoming nzb-directory to automatically
assign category names; nested categories are supported;
- added option <ServerX.JoinGroup> to connect to servers, that do not accept
<GROUP>-command;
- added example post-process script for unraring of downloaded files
(POSIX only);
- added options <ParPauseQueue> and <PostPauseQueue> useful on slow CPUs;
- added option <NzbCleanupDisk> to delete source nzb-file after successful
download and parcheck;
- switch <-P> can now be used together with switches <-s> and <-D> to start
server/daemon in paused state;
- changed the type of messages logged in a case of connection errors from
<DEBUG> to <ERROR> to provide better error reporting;
- now using OS-specific line-endings in log-file and brokenlog-file: LF on
Posix and CRLF on Windows;
- 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);
- added the printing of stack on segmentation faults (if configured with
<--enable-debug>, POSIX only);
- added option <DumpCore> for better debugging on Linux in a case of abnormal
program termination;
- fixed: configure-script could not automatically find libsigc++ on 64-bit
systems;
- few other small fixes;
nzbget-0.4.1:
- to avoid accidental deletion of file in curses-frontend the key <D>
now must be pressed in uppercase;
- options <username> and <password> in news-server's configuration are now
optional;
- added the server's name to the detail-log-message, displayed on the start
of article's download;
- added the option <AllowReProcess> to help to post-process-scripts, which
make par-check/-repair on it's own;
- improved download-speed-meter: it uses now a little bit less cpu and
calculates the speed for the last 30 seconds (instead of 5 seconds),
providing better accuracy; Thanks to ydrol <ydrol@users.sourceforge.net>
for the patch;
- reduced CPU-usage in curses-outputmode; Thanks to ydrol for the patch
<ydrol@users.sourceforge.net>;
- fixed: line-endings in windows-style (CR-LF) in config-file were not
read properly;
- fixed: trailing spaces in nzb-filenames (before the file's extension)
caused errors on windows. Now they will be trimmed;
- fixed: XML-RPC and JSON-RPC did not work on Big-Endian-CPUs (ARM, PPC, etc),
preventing the using of web-interface;
- fixed: umask-option did not allow to enable write-permissions for <group>
and <others>;
- fixed: in curses-outputmode the remote-client showed on first screen-update
only one item of queue;
- fixed: edit-commands with negative offset did not work via XML-RPC
(but worked via JSON-RPC);
- fixed incompatibility issues with gcc 4.3; Thanks to Paul Bredbury
<brebs@users.sourceforge.net> for the patch;
- fixed: segmentation fault if a file listed in nzb-file does not have any
segments (articles);
nzbget-0.4.0:
- added the support for XML-RPC and JSON-RPC to easier control the server
from other applications;
- added web-interface - it is available for download from NZBGet-project's
home page as a separate package "web-interface";
- added the automatic cleaning up of the download queue (deletion of unneeded
paused par-files) after successful par-check/repair - new
option <ParCleanupQueue>;
- 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>);
- added the gathering of progress-information during par-check; it is
available via XML-RPC or JSON-RPC; it is also showed in web-interface;
- improvements in internal decoder: added support for yEnc-files without
ypart-statement (sometimes used for small files); added support for
UU-format;
- removed support for uulib-decoder (it did not work well anyway);
- replaced the option <decoder (yenc, uulib, none)> with the option
<decode (yes, no)>;
- 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
differ such errors from CrcErrors (better error reporting);
- improved error-reporting on moving of completed files from tmp- to
dst-directory and added code to move files across drives if renaming fails;
- improved handling of nzb-files with multiple collections in par-checker;
- improved the parchecker: added the detection and processing of files
splitted after parring;
- 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) to provide more balanced cpu utilization;
- added the redirecting of post-process-script's output to log; new option
<PostLogKind> to specify the default message-kind for unformatted
log-messages;
- added the returning of script-output by command <postqueue> via XML-RPC
and JSON-RPC; the script-output is also showed in web-interface;
- added the saving and restoring of the post-processor-queue (if server was
stopped before all items were processed); new option <ReloadPostQueue>;
- added new parameter to postprocess-script to indicate if any of par-jobs
for the same nzb-file failed;
- added remote command (switch O/--post) to request the post-processor-queue
from server;
- added remote command (switch -W/--write) to write messages to server's log;
- added option <DiskSpace> to automatically pause the download on low disk
space;
- fixed few incompatibility-issues with unslung-platform on nslu2 (ARM);
- fixed: articles with trailing text after binary data caused the decode
failures and the reporting of CRC-errors;
- fixed: dupecheck could cause seg.faults when all articles for a file failed;
- fixed: by dupe-checking of files contained in nzb-file the files with the
same size were ignored (not deleted from queue);
- updated libpar2-patch for msvc to fix a segfault in libpar2 (windows only);
- 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;
- fixed: the pausing of a group could cause the start of post-processing for
that group;
- fixed: too many info-messages <Need more N blocks> could be printed during
par-check (appeared on posix only);
nzbget-0.3.1:
- Greatly reduced the memory consumption by keeping articles' info on disk
until the file download starts;
- Implemented decode-on-the-fly-technique to reduce disk-io; downloaded
and decoded data can also be saved directly to the destination file
(without any intermediate files at all); this eliminates the necessity
of joining of articles later (option "DirectWrite");
- Improved communication with news-servers: connections are now keeped open
until all files are downloaded (or server paused); this eliminates the
need for establishing of connections and authorizations for each
article and improves overal download speed;
- Significantly better download speed is now possible on fast connection;
it was limited by delays on starting of new articles' downloads;
the synchronisation mechanism was reworked to fix this issue;
- Download speed meter is much more accurate, especially on fast connections;
this also means better speed throttling;
- Speed optimisations in internal decoder (up to 25% faster);
- CRC-calculation can be bypassed to increase performance on slow CPUs
(option "CrcCheck");
- Improved parsing of artcile's subject for better extracting of filename
part from it and implemented a fallback-option if the parsing was incorrect;
- Improved dupe check for files from the same nzb-request to detect reposted
files and download only the best from them (if option "DupeCheck" is on);
- Articles with incorrect CRC can be treated as "possibly recoverable errors"
and relaunched for download (option "RetryOnCrcError"), it is useful if
multiple servers are available;
- Improved error-check for downloaded articles (crc-check and check for
received message-id) decreases the number of broken files;
- Extensions in curses-outputmode: added group-view-mode (key "G") to show
items in download queue as groups, where one group represents all files
from the same nzb-file; the editing of queue works also in group-mode
(for all files in this group): pause/unpause/delete/move of groups;
- Other extensions in curses-outputmode: key "T" toggles timestamps in log;
added output of statistical data: uptime, download-time, average session
download speed;
- Edit-command accepts more than one ID or range of IDs.
E.g: "nzbget -E P 2,6-10,33-39"; The switch "-I" is not used anymore;
- Move-actions in Edit-command affect files in a smart order to guarantee
that the relative order of files in queue is not changed after the moving;
- Extended syntax of edit-command to edit groups (pause/unpause/delete/move
of groups). E.g: "nzbget -E G P 2";
- Added option "DaemonUserName" to set the user that the daemon (POSIX only)
normally runs at. This allows nzbget daemon to be launched in rc.local
(at boot), and download items as a specific user id; Thanks to Thierry
MERLE <merlum@users.sourceforge.net> for the patch;
- Added option "UMask" to specify permissions for newly created files and dirs
(POSIX only);
- Communication protocol used between server and client was revised to define
the byte order for transferred data. This allows hosts with different
endianness to communicate with each other;
- Added options "CursesNzbName", "CursesGroup" and "CursesTime" to define
initial state of curses-outputmode;
- Added option "UpdateInterval" to adjust update interval for Frontend-output
(useful in remote-mode to reduce network usage);
- Added option "WriteBufferSize" to reduce disk-io (but it could slightly
increase memory usage and therefore disabled by default);
- List-command prints additional statistical info: uptime, download-time,
total amount of downloaded data and average session download speed;
- The creation of necessary directories on program's start was extended
with automatic creation of all parent directories or error reporting
if it was not possible;
- Printed messages are now translated to oem-codepage to correctly print
filenames with non-english characters (windows only);
- Added remote-command "-V (--serverversion)" to print the server's version;
- Added option "ThreadLimit" to prevent program from crash if it wants to
create too many threads (sometimes could occur in special cases);
- Added options "NzbDirInterval" and "NzbDirFileAge" to adjust interval and
delay by monitoring of incoming-directory for new nzb-files;
- Fixed error on parsing of nzb-files containing percent and other special
characters in their names (bug appeared on windows only);
- Reformated sample configuration file and changed default optionnames
from lowercase to MixedCase for better readability;
- Few bugs (seg faults) were fixed.
nzbget-0.3.0:
- The download queue now contains newsgroup-files to be downloaded instead of
nzb-jobs. By adding a new job, the nzb-file is immediately parsed and each
newsgroup-file is added to download queue. Each file can therefore be
managed separately (paused, deleted or moved);
- Current queue state is saved after every change (file is completed or the
queue is changed - entries paused, deleted or moved). The state is saved on
disk using internal format, which allows fast loading on next program start
(no need to parse xml-files again);
- The remaining download-size is updated after every article is completed to
indicate the correct remaining size and time for total files in queue;
- Downloaded articles, which are saved in temp-directory, can be reused on
next program start, if the file was not completed (option "continuepartial"
in config-file);
- Along with uulib the program has internal decoder for yEnc-format. This
decoder was necessary, because uulib is so slow, that it prevents using of
the program on not so powerful systems like linux-routers (MIPSEL CPU 200
MHz). The new decoder is very fast. It is controlled over option "decoder"
in config-file;
- The decoder can be completely disabled. In this case all downloaded articles
are saved in unaltered form and can be joined with an external program;
UUDeview is one of them;
- If download of article fails, the program attempts to download it again so
many times, what the option "retries" in config-file says. This works even
if no servers with level higher than "0" defined. After each retry the next
server-level is used, if there are no more levels, the program switches to
level "0" again. The pause between retries can be set with config-option
"retryinterval";
- If despite of a stated connection-timeout (it can be changed via
config-option "connectiontimeout") connection hangs, the program tries to
cancel the connection (after "terminatetimeout" seconds). If it doesn't
work the download thread is killed and the article will be redownloaded in
a new thread. This ensures, that there are no long-time hanging connections
and all articles are downloaded, when a time to rejoin file comes;
- Automatic par-checking and repairing. Only reuired par-files are downloaded.
The program uses libpar2 and does not require any external tools. The big
advantage of library is, that it allows to continue par-check after new
par-blocks were downloaded. This were not possible with external
par2cmdline-tool;
- There is a daemon-mode now (command-line switch "-D" (--daemon)). In this
mode a lock-file (default location "/tmp/nzbget.lock", can be changed via
option "lockfile") contains PID of daemon;
- The format of configuration-file was changed from xml to more common
text-format. It allows also using of variables like
"tempdir=${MAINDIR}/tmp";
- Any option of config-file can be overwritten via command-line switch
"-o" (--option). This includes also the definition of servers.
This means that the program can now be started without a configuration-file
at all (all required options must be passed via command-line);
- The command-line switches were revised. The dedicated switches to change
options in config-file were eliminated, since any option can now be changed
via switch "-o" (--option);
- If the name of configuration-file was not passed via command-line the
program search it in following locations: "~/.nzbget", "/etc/nzbget.conf",
"/usr/etc/nzbget.conf", "/usr/local/etc/nzbget.conf",
"/opt/etc/nzbget.conf";
- The new command-line switch "-n" (--noconfigfile) prevents the loading of
a config-file. All required config-options must be passed via command-line
(switch "-o" (--option));
- To start the program in server mode either "-s" (--server) or
"-D" (--daemon) switch must be used. If the program started without any
parameters it prints help-screen. There is no a dedicated switch to start
in a standalone mode. If switches "-s" and "-D" are omitted and none of
client-request-switches used the standalone mode is default. This usage
of switches is more common to programs like "wget". To add a file to
server's download queue use switch "-A" (--append) and a name of nzb-file
as last command-line parameter;
- There is a new switch "-Q" (--quit) to gracefully stop server. BTW the
SIGKIL-signal is now handled appropriately, so "killall nzbget" is also OK,
where "killall -9 nzbget" terminates server immediately (helpful if it
hangs, but it shouldn't);
- With new switch "-T" (--top) the file will be added to the top of download
queue. Use it with switch "-A" (--append);
- The download queue can be edited via switch "-E" (--edit). It is possible
to pause, unpause, delete and move files in queue. The IDs of file(s)
to be affected are passed via switch "-I" (fileid), either one ID or a
range in a form "IDForm-IDTo". This also means, that every file in queue
have ID now;
- The switch "-L" (--list) prints IDs of files consequently. It prints also
name, size, percentage of completing and state (paused or not) of each file.
Plus summary info: number of files, total remaining size and size of
paused files, server state (paused or running), number of threads on
server, current speed limit;
- With new switch "-G" (--log) the last N lines printed to server's
screen-log, can be printed on client. The max number of lines which can
be returned from servers depends on option "logbuffersize";
- The redesigned Frontends (known as outputmodes "loggable", "colored" and
"curses") can connect to (remote) server and behave as if you were running
server-instance of program itself (command-line switch "-C" (--connect)).
The log-output updates constantly and even all control-functions in
ncurses-mode works: pause/unpause server, set download rate limit, edit of
queue (pause/unpause, delete, move entries). The number of connected
clients is not limited. The "outputmode" on a client can be set
independently from server. The client-mode is especially useful if the
server runs as a daemon;
- The writing to log-file can be disabled via option "createlog".
The location of log-file controls the option "log-file";
- Switch "-p" (--printconfig) prints the name of configuration file being
used and all option/value-pairs, taking into account all used
"-o" (--option) - switches;
- The communication protocol between server and client was optimized to
minimize the size of transferred data. Instead of fixing the size for
Filenames in data-structures to 512 bytes only in fact used data
are transferred;
- Extensions in ncurses-outputmode: scrolling in queue-list works better,
navigation in queue with keys Up, Down, PgUp, PgDn, Home, End.
Keys to move entries are "U" (move up), "N" (move down), "T" (move to top),
"B" (move to bottom). "P" to pause/unpause file. The size, percentage
of completing and state (paused or not) for every file is printed.
The header of queue shows number of total files, number of unpaused
files and size for all and unpaused files. Better using of screen estate
space <20> no more empty lines and separate header for status (total seven
lines gain). The messages are printed on several lines (if they not fill
in one line) without trimming now;
- configure.ac-file updated to work with recent versions of autoconf/automake.
There are new configure-options now: "--disable-uulib" to compile the
program without uulib; "--disable-ncurses" to disable ncurses-support
(eliminates necessity of ncurses-libs), useful on embedded systems with
little resources; "--disable-parcheck" to compile without par-check;
- The algorithm for parsing of nzb-files now uses XMLReader instead of
DOM-Parser to minimize memory usage (no mor needs to build complete DOM-tree
in memory). Thanks to Thierry MERLE <merlum@users.sourceforge.net> for
the patch;
- The log-file contains now thread-ID for all entry-types and additionally
for debug-entries: filename, line number and function's name of source
code, where the message was printed. Debug-messages can be disabled in
config-file (option "debugtarget") like other messages;
- The program is now compatible with windows. Project file for MS Visual
C++ 2005 is included. Use "nzbget -install" and "nzbget -remove" to
install/remove NZBGet-Service. Servers and clients can run on diferrent
operating systems;
- Improved compatibility with POSIX systems; Tested on:
- Linux Debian 3.1 on x86;
- Linux BusyBox with uClibc on MIPSEL;
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
- Solaris 10 on x86;
- Many memory-leaks and thread issues were fixed;
- The program was thoroughly worked over. Almost every line of code was
revised.
nzbget-0.2.3
- Fixed problem with losing connection to newsserver after too long idle time
- Added functionality for dumping lots of debug info
nzbget-0.2.2
- Added Florian Penzkofers fix for FreeBSD, exchanging base functionality in
SingleServerPool.cpp with a more elegant solution
- Added functionality for forcing answer to reloading queue upon startup of
server
+ use -y option to force from command-line
+ use "reloadqueue" option in nzbget.cfg to control behavior
- Added nzbget.cfg options to control where info, warnings and errors get
directed to (either screen, log or both)
- Added option "createbrokenfilelog" in nzbget.cfg
nzbget-0.2.1
- Changed and extended the TCP client/server interface
- Added timeout on sockets which prevents certain types of nzbget hanging
- Added Kristian Hermansen's patch for renaming broken files
nzbget-0.2.0
- Moved 0.1.2-alt4 to a official release as 0.2.0
- Small fixes
nzbget-0.1.2-alt4
- implemented tcp/ip communication between client & server (removing the
rather defunct System V IPC)
- added queue editing functionality in server-mode
nzbget-0.1.2-alt1
- added new ncurses frontend
- added server/client-mode (using System V IPC)
- added functionality for queueing download requests
nzbget-0.1.2
- performance-improvements
- commandline-options
- fixes
nzbget-0.1.1
- new output
- fixes
nzbget-0.1.0a
- compiling-fixes
nzbget-0.1.0
- initial release

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 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
@@ -41,6 +41,7 @@
#include "nzbget.h"
#include "ColoredFrontend.h"
#include "Util.h"
ColoredFrontend::ColoredFrontend()
{
@@ -73,52 +74,49 @@ void ColoredFrontend::PrintStatus()
char tmp[1024];
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (m_fCurrentDownloadSpeed > 0.0f)
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = m_lRemainingSize / ((long long int)(m_fCurrentDownloadSpeed * 1024));
int h = 0;
int m = 0;
int s = 0;
while (remain_sec > 3600)
{
h++;
remain_sec -= 3600;
}
while (remain_sec > 60)
{
m++;
remain_sec -= 60;
}
s = remain_sec;
sprintf(timeString, "(~ %.2d:%.2d:%.2d)", h, m, s);
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
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);
}
const char* szPause[] = { "Paused", "" };
int iPauseIdx = m_bPause ? 0 : 1;
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
{
sprintf(szDownloadLimit, "Limit %.0f KB/S", m_fDownloadLimit);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
}
else
{
szDownloadLimit[0] = 0;
}
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
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 running, %.0f KB/s, %.2f MB remaining %s %s %s%s\n",
m_iThreadCount, m_fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
timeString, szPause[iPauseIdx], szDownloadLimit, szControlSeq);
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
(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;
@@ -126,7 +124,6 @@ void ColoredFrontend::PrintStatus()
void ColoredFrontend::PrintMessage(Message * pMessage)
{
const char* msg = pMessage->GetText();
#ifdef WIN32
switch (pMessage->GetKind())
{
@@ -146,10 +143,18 @@ 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());
CharToOem(msg, msg);
printf(" %s\n", msg);
free(msg);
#else
const char* msg = pMessage->GetText();
switch (pMessage->GetKind())
{
case Message::mkDebug:
@@ -164,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

@@ -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 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
@@ -29,16 +29,24 @@
#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
@@ -46,10 +54,21 @@
#include "nzbget.h"
#include "Connection.h"
#include "Log.h"
#include "TLS.h"
void Connection::Init()
static const int CONNECTION_READBUFFER_SIZE = 1024;
#ifndef DISABLE_TLS
bool Connection::bTLSLibInitialized = false;
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
Mutex* Connection::m_pMutexGetHostByName = NULL;
#endif
#endif
void Connection::Init(bool bTLS)
{
debug("Intiializing global connection data");
debug("Initializing global connection data");
#ifdef WIN32
WSADATA wsaData;
@@ -66,6 +85,30 @@ void Connection::Init()
return;
}
#endif
#ifndef DISABLE_TLS
if (bTLS)
{
debug("Initializing TLS library");
char* szErrStr;
int iRes = tls_lib_init(&szErrStr);
bTLSLibInitialized = iRes == TLS_EOK;
if (!bTLSLibInitialized)
{
error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error");
if (szErrStr)
{
free(szErrStr);
}
}
}
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
m_pMutexGetHostByName = new Mutex();
#endif
#endif
}
void Connection::Final()
@@ -75,8 +118,21 @@ void Connection::Final()
#ifdef WIN32
WSACleanup();
#endif
}
#ifndef DISABLE_TLS
if (bTLSLibInitialized)
{
debug("Finalizing TLS library");
tls_lib_deinit();
}
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
delete m_pMutexGetHostByName;
#endif
#endif
}
Connection::Connection(NetAddress* pNetAddress)
{
@@ -86,49 +142,81 @@ Connection::Connection(NetAddress* pNetAddress)
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_bCanceling = false;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = true;
#ifndef DISABLE_TLS
m_pTLS = NULL;
m_bTLSError = false;
#endif
}
Connection::Connection(SOCKET iSocket, bool bAutoClose)
{
debug("Creating Connection");
m_pNetAddress = NULL;
m_eStatus = csConnected;
m_iSocket = iSocket;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = bAutoClose;
#ifndef DISABLE_TLS
m_pTLS = NULL;
#endif
}
Connection::~Connection()
{
debug("Destroying Connection");
if (m_eStatus == csConnected)
if (m_eStatus == csConnected && m_bAutoClose)
{
Disconnect();
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
if (m_pTLS)
{
free(m_pTLS);
}
#endif
}
int Connection::Connect()
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()
@@ -142,12 +230,15 @@ int Connection::Bind()
int iRes = DoBind();
m_eStatus = csListening;
if (iRes == 0)
{
m_eStatus = csListening;
}
return iRes;
}
int Connection::WriteLine(char* line)
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
@@ -156,15 +247,12 @@ int Connection::WriteLine(char* line)
return -1;
}
int iRes = DoWriteLine(line);
if (iRes == EOF)
Connection::DoDisconnect();
int iRes = DoWriteLine(pBuffer);
return iRes;
}
int Connection::Send(char* pBuffer, int iSize)
int Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
@@ -178,17 +266,14 @@ int Connection::Send(char* pBuffer, int iSize)
return iRes;
}
char* Connection::ReadLine(char* pBuffer, int iSize)
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
if (m_eStatus != csConnected)
{
return NULL;
}
char* res = DoReadLine(pBuffer, iSize);
if (res == NULL)
Connection::DoDisconnect();
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
}
@@ -217,7 +302,7 @@ 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;
@@ -231,6 +316,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)
{
@@ -238,7 +334,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;
@@ -247,35 +343,81 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
return true;
}
int Connection::DoConnect()
bool Connection::DoConnect()
{
debug("Do connecting");
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_pNetAddress->GetPort());
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
ReportError("Could not resolve hostname %s", m_pNetAddress->GetHost(), true, 0);
return false;
}
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)
{
res = connect(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;
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)
{
return -1;
return false;
}
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
return -1;
ReportError("Socket creation failed for %s", m_pNetAddress->GetHost(), 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;
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Connection to %s failed", m_pNetAddress->GetHost(), true, 0);
return false;
}
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
@@ -288,87 +430,54 @@ int Connection::DoConnect()
#endif
if (err != 0)
{
ReportError("setsockopt failed", NULL, 0);
ReportError("setsockopt failed", NULL, true, 0);
}
return 0;
return true;
}
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;
#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);
#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 uaddr;
}
int Connection::DoDisconnect()
bool Connection::DoDisconnect()
{
debug("Do disconnecting");
if (m_iSocket > 0)
if (m_iSocket != INVALID_SOCKET)
{
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
#ifndef DISABLE_TLS
if (m_pTLS)
{
CloseTLS();
}
#endif
}
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)
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
//debug( "Connection::DoReadLine()" );
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, ReadBufLen, 0);
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)
@@ -383,7 +492,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = p - szBufPtr + 1;
len = (int)(p - szBufPtr + 1);
}
else
{
@@ -399,18 +508,24 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
iSize--;
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
@@ -422,30 +537,93 @@ 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_pNetAddress->GetPort());
int res = getaddrinfo(m_pNetAddress->GetHost(), 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_pNetAddress->GetHost() );
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;
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_pNetAddress->GetHost(), 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_pNetAddress->GetHost(), true, 0);
return -1;
}
if (listen(m_iSocket, 10) < 0)
{
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
@@ -454,16 +632,11 @@ int Connection::DoBind()
SOCKET Connection::DoAccept()
{
struct sockaddr_in ClientAddress;
socklen_t SockLen;
SOCKET iSocket = accept(GetSocket(), NULL, NULL);
SockLen = sizeof(ClientAddress);
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
if (iSocket == INVALID_SOCKET && !m_bCanceling)
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, 0);
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
@@ -472,36 +645,226 @@ SOCKET Connection::DoAccept()
void Connection::Cancel()
{
debug("Cancelling connection");
m_bCanceling = true;
if (m_iSocket != INVALID_SOCKET)
{
m_eStatus = csCancelled;
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);
}
m_eStatus = csCancelled;
}
}
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
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
int ErrCode = WSAGetLastError();
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
#else
const char* szErrMsg = hstrerror(ErrCode);
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);
}
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
#endif
}
else
{
if (m_bSuppressErrors)
{
debug(szErrPrefix);
}
else
{
error(szErrPrefix);
}
}
}
#ifndef DISABLE_TLS
bool Connection::CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix)
{
bool bOK = iResultCode == TLS_EOK;
if (!bOK)
{
ReportError(szErrMsgPrefix, szErrStr ? szErrStr : "unknown error", false, 0);
if (szErrStr)
{
free(szErrStr);
}
}
return bOK;
}
bool Connection::StartTLS()
{
debug("Starting TLS");
if (m_pTLS)
{
free(m_pTLS);
}
m_pTLS = malloc(sizeof(tls_t));
tls_t* pTLS = (tls_t*)m_pTLS;
memset(pTLS, 0, sizeof(tls_t));
tls_clear(pTLS);
char* szErrStr;
int iRes;
iRes = tls_init(pTLS, NULL, NULL, NULL, 0, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not initialize TLS-object: %s"))
{
return false;
}
debug("tls_start...");
iRes = tls_start(pTLS, (int)m_iSocket, NULL, 1, NULL, &szErrStr);
debug("tls_start...%i", iRes);
if (!CheckTLSResult(iRes, szErrStr, "Could not establish secure connection: %s"))
{
return false;
}
return true;
}
void Connection::CloseTLS()
{
tls_close((tls_t*)m_pTLS);
free(m_pTLS);
m_pTLS = NULL;
}
int Connection::recv(SOCKET s, char* buf, int len, int flags)
{
size_t iReceived = 0;
if (m_pTLS)
{
m_bTLSError = false;
char* szErrStr;
int iRes = tls_getbuf((tls_t*)m_pTLS, buf, len, &iReceived, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not read from TLS-socket: %s"))
{
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)
{
if (m_pTLS)
{
m_bTLSError = false;
char* szErrStr;
int iRes = tls_putbuf((tls_t*)m_pTLS, buf, len, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not send to TLS-socket: %s"))
{
m_bTLSError = true;
return -1;
}
return 0;
}
else
{
int iRet = ::send(s, buf, len, flags);
return iRet;
}
}
#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

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 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
@@ -28,6 +28,11 @@
#define CONNECTION_H
#include "NetAddress.h"
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
#include "Thread.h"
#endif
#endif
class Connection
{
@@ -43,42 +48,66 @@ public:
protected:
NetAddress* m_pNetAddress;
SOCKET m_iSocket;
static const int ReadBufLen = 1024;
char m_szReadBuf[ReadBufLen + 1];
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
bool m_bCanceling;
int m_iTimeout;
bool m_bSuppressErrors;
bool m_bAutoClose;
#ifndef DISABLE_TLS
void* m_pTLS;
static bool bTLSLibInitialized;
bool m_bTLSError;
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
static Mutex* m_pMutexGetHostByName;
#endif
#endif
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
#endif
#ifndef DISABLE_TLS
bool CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix);
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(SOCKET iSocket, bool bAutoClose);
virtual ~Connection();
static void Init();
static void Init(bool bTLS);
static void Final();
int Connect();
int Disconnect();
bool Connect();
bool Disconnect();
int Bind();
int Send(char* pBuffer, int iSize);
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize);
int WriteLine(char* text);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
int WriteLine(const char* pBuffer);
SOCKET Accept();
void Cancel();
NetAddress* GetServer() { return m_pNetAddress; }
SOCKET GetSocket() { return m_iSocket; }
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
protected:
virtual int DoConnect();
virtual int DoDisconnect();
int DoBind();
int DoWriteLine(char* text);
char* DoReadLine(char* pBuffer, int iSize);
SOCKET DoAccept();
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
bool GetSuppressErrors() { return m_bSuppressErrors; }
#ifndef DISABLE_TLS
bool StartTLS();
#endif
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* 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>
@@ -40,39 +40,12 @@
#endif
#include "nzbget.h"
#ifdef ENABLE_UULIB
#ifndef PROTOTYPES
#define PROTOTYPES
#endif
#include <uudeview.h>
#endif
//#define USEEXTERNALDECODER // not working
//#define DEBUGDECODER
#include "Decoder.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
#ifdef DEBUGDECODER
int g_iDecoderID = 0;
#endif
Mutex Decoder::m_mutexDecoder;
unsigned int Decoder::crc_tab[256];
void Decoder::Init()
{
debug("Initializing global decoder");
crc32gentab();
}
void Decoder::Final()
{
debug("Finalizing global Decoder");
}
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
unsigned int YDecoder::crc_tab[256];
Decoder::Decoder()
{
@@ -81,9 +54,6 @@ Decoder::Decoder()
m_szSrcFilename = NULL;
m_szDestFilename = NULL;
m_szArticleFilename = NULL;
m_eKind = dcYenc;
m_iDebugStatus = 0;
m_iDebugLines = 0;
}
Decoder::~ Decoder()
@@ -96,227 +66,87 @@ Decoder::~ Decoder()
}
}
bool Decoder::Execute()
void Decoder::Clear()
{
if (m_eKind == dcUulib)
if (m_szArticleFilename)
{
return DecodeUulib();
}
else
{
return DecodeYenc();
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
bool Decoder::DecodeUulib()
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
{
bool res = false;
#ifndef ENABLE_UULIB
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
#else
m_mutexDecoder.Lock();
#ifdef DEBUGDECODER
debug("Decoding ID %i (%s)", g_iDecoderID, szSrcFilename);
#endif
#ifndef USEEXTERNALDECODER
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 (!strncmp(buffer, "=ybegin ", 8))
{
uulist* att_tmp = UUGetFileListItem(i);
return efYenc;
}
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
return efUx;
}
if (!att_tmp)
if (!strncmp(buffer, "begin ", 6))
{
bool bOK = true;
buffer += 6; //strlen("begin ")
while (*buffer && *buffer != ' ')
{
break;
}
if ((att_tmp) && (att_tmp->haveparts))
{
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();
#else
execl("/usr/local/bin", "uudeview", szSrcFilename, szDestFilename);
#endif
#ifdef DEBUGDECODER
debug("Finished decoding ID %i (%s)", g_iDecoderID++, szDestFilename);
#endif
m_mutexDecoder.Unlock();
#endif // ENABLE_UULIB
return res;
return efUnknown;
}
/**
* Very primitive (but fast) implementation of yEnc-Decoder
* YDecoder: fast implementation of yEnc-Decoder
*/
bool Decoder::DecodeYenc()
void YDecoder::Init()
{
FILE* infile = fopen(m_szSrcFilename, "r");
if (!infile)
{
error("Could not open file \"%s\"", m_szSrcFilename);
return false;
}
debug("Initializing global decoder");
crc32gentab();
}
FILE* outfile = fopen(m_szDestFilename, "w");
if (!outfile)
{
error("Could not create file \"%s\"", m_szDestFilename);
fclose(infile);
return false;
}
void YDecoder::Final()
{
debug("Finalizing global Decoder");
}
static const int MAX_LINE_LEN = 1024;
char buffer[MAX_LINE_LEN];
bool body = false;
bool end = false;
unsigned long expectedCRC = 0;
unsigned long calculatedCRC = 0xFFFFFFFF;
m_iDebugStatus = 1;
bool eof = !fgets(buffer, sizeof(buffer), infile);
m_iDebugLines++;
m_iDebugStatus = 2;
while (!eof)
{
if (body)
{
if (strstr(buffer, "=yend size="))
{
end = true;
m_iDebugStatus = 3;
char* pc = strstr(buffer, "pcrc32=");
if (pc)
{
pc += 7; //=strlen("pcrc32=")
expectedCRC = strtoul(pc, NULL, 16);
}
break;
}
m_iDebugStatus = 4;
char* iptr = buffer;
char* optr = buffer;
while (*iptr)
{
switch (*iptr)
{
case '=': //escape-sequence
iptr++;
*optr = *iptr - 64 - 42;
*optr++;
break;
case '\n': // ignored char
case '\r': // ignored char
break;
default: // normal char
*optr = *iptr - 42;
*optr++;
break;
}
iptr++;
}
m_iDebugStatus = 5;
calculatedCRC = crc32m(calculatedCRC, (unsigned char *)buffer, optr - buffer);
fwrite(buffer, 1, optr - buffer, outfile);
m_iDebugStatus = 6;
}
else
{
if (strstr(buffer, "=ypart begin="))
{
m_iDebugStatus = 7;
body = true;
}
else if (strstr(buffer, "=ybegin part="))
{
m_iDebugStatus = 8;
char* pb = strstr(buffer, "name=");
if (pb)
{
m_iDebugStatus = 9;
pb += 5; //=strlen("name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
m_iDebugStatus = 10;
}
m_iDebugStatus = 11;
}
}
m_iDebugStatus = 12;
eof = !fgets(buffer, sizeof(buffer), infile);
m_iDebugStatus = 13;
m_iDebugLines++;
}
m_iDebugStatus = 14;
YDecoder::YDecoder()
{
Clear();
}
calculatedCRC ^= 0xFFFFFFFF;
void YDecoder::Clear()
{
Decoder::Clear();
debug("Expected pcrc32=%x", expectedCRC);
debug("Calculated pcrc32=%x", calculatedCRC);
if (expectedCRC != calculatedCRC)
{
warn("CRC-Error for \"%s\"", m_szDestFilename);
}
fclose(infile);
fclose(outfile);
return body && end;
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 = 0xFFFFFFFF;
m_iSize = 0;
m_iEndSize = 0;
m_bAutoSeek = false;
m_bNeedSetPos = false;
m_bCrcCheck = false;
}
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
@@ -329,7 +159,7 @@ bool Decoder::DecodeYenc()
* calculate the crcTable for crc32-checksums.
* it is generated to the polynom [..]
*/
void Decoder::crc32gentab()
void YDecoder::crc32gentab()
{
unsigned long crc, poly;
int i, j;
@@ -363,21 +193,293 @@ void Decoder::crc32gentab()
* reached. the crc32-checksum will be
* the result.
*/
unsigned long Decoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
{
register unsigned long crc;
unsigned long i;
crc = startCrc;
for (i = 0; i < length; i++)
register unsigned long crc = startCrc;
for (unsigned long i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
return crc;
}
void Decoder::LogDebugInfo()
unsigned int YDecoder::DecodeBuffer(char* buffer)
{
debug(" Decoder: status=%i, lines=%i, filename=%s, ArticleFileName=%s",
m_iDebugStatus, m_iDebugLines, BaseFileName(m_szSrcFilename), m_szArticleFilename);
if (m_bBody && !m_bEnd)
{
if (!strncmp(buffer, "=yend ", 6))
{
m_bEnd = true;
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
if (pb)
{
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;
}
char* iptr = buffer;
char* optr = buffer;
while (true)
{
switch (*iptr)
{
case '=': //escape-sequence
iptr++;
*optr = *iptr - 64 - 42;
optr++;
break;
case '\n': // ignored char
case '\r': // ignored char
break;
case '\0':
goto BreakLoop;
default: // normal char
*optr = *iptr - 42;
optr++;
break;
}
iptr++;
}
BreakLoop:
if (m_bCrcCheck)
{
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
}
return (unsigned int)(optr - buffer);
}
else
{
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
{
m_bBegin = true;
char* pb = strstr(buffer, " name=");
if (pb)
{
pb += 6; //=strlen(" name=")
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';
}
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, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer);
if (wcnt > 0)
{
if (m_bNeedSetPos)
{
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
{
return false;
}
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
{
return false;
}
m_bNeedSetPos = false;
}
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus YDecoder::Check()
{
m_lCalculatedCRC ^= 0xFFFFFFFF;
debug("Expected crc32=%x", m_lExpectedCRC);
debug("Calculated crc32=%x", m_lCalculatedCRC);
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;
}

113
Decoder.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-2008 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
@@ -27,46 +27,93 @@
#ifndef DECODER_H
#define DECODER_H
#include "Thread.h"
//#define DECODER_INTERNAL_FGETS
class Decoder
{
public:
enum EKind
enum EStatus
{
dcUulib,
dcYenc
eUnknownError,
eFinished,
eArticleIncomplete,
eCrcError,
eInvalidSize,
eNoBinaryData
};
private:
static Mutex m_mutexDecoder;
static unsigned int crc_tab[256];
EKind m_eKind;
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
int m_iDebugStatus;
int m_iDebugLines;
enum EFormat
{
efUnknown,
efYenc,
efUx,
};
bool DecodeUulib();
bool DecodeYenc();
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
public:
Decoder();
~Decoder();
bool Execute();
void SetKind(EKind eKind) { m_eKind = eKind; }
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
const char* GetArticleFilename() { return m_szArticleFilename; }
void LogDebugInfo();
Decoder();
virtual ~Decoder();
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; }
static EFormat DetectFormat(const char* buffer, int len);
};
static void Init();
static void Final();
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;
unsigned int DecodeBuffer(char* buffer);
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
public:
YDecoder();
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; }
static void Init();
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-2009 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
@@ -31,15 +31,29 @@
class DiskState
{
private:
int ParseFormatVersion(const char* szFormatSignature);
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(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);
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile);
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
public:
bool Exists();
bool Save(DownloadQueue* pDownloadQueue, bool OnlyOrder);
bool Load(DownloadQueue* pDownloadQueue);
bool Discard();
bool DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(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-2010 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
@@ -34,10 +34,9 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "DownloadInfo.h"
@@ -48,6 +47,359 @@
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
int NZBInfo::m_iIDGen = 0;
int PostInfo::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_iFileCount = 0;
m_iParkedFileCount = 0;
m_lSize = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_eParStatus = prNone;
m_eScriptStatus = srNone;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
m_szQueuedFilename = strdup("");
m_tHistoryTime = 0;
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_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);
}
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::GetNiceNZBName(char* szBuffer, int iSize)
{
MakeNiceNZBName(m_szFilename, szBuffer, iSize);
}
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
{
char postname[1024];
const char* szBaseName = Util::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';
Util::MakeValidFilename(postname, '_', false);
// if the resulting name is empty, use basename without cleaning 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';
Util::MakeValidFilename(postname, '_', false);
// 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 NZBInfo::BuildDestDirName()
{
char szBuffer[1024];
char szCategory[1024];
bool bHasCategory = m_szCategory && m_szCategory[0] != '\0';
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
strncpy(szCategory, m_szCategory, 1024);
szCategory[1024 - 1] = '\0';
Util::MakeValidFilename(szCategory, '_', true);
}
if (g_pOptions->GetAppendNZBDir())
{
char szNiceNZBName[1024];
GetNiceNZBName(szNiceNZBName, 1024);
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, szNiceNZBName);
}
else
{
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
}
szBuffer[1024-1] = '\0';
}
else
{
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szCategory);
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
}
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
SetDestDir(szBuffer);
}
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);
}
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_mutexLog.Lock();
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()
{
@@ -79,6 +431,10 @@ void ArticleInfo::SetMessageID(const char * szMessageID)
void ArticleInfo::SetResultFilename(const char * v)
{
if (m_szResultFilename)
{
free(m_szResultFilename);
}
m_szResultFilename = strdup(v);
}
@@ -92,13 +448,14 @@ FileInfo::FileInfo()
m_szSubject = NULL;
m_szFilename = 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_iIDGen++;
m_iID = m_iIDGen;
}
@@ -115,26 +472,28 @@ FileInfo::~ FileInfo()
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
{
delete *it;
}
m_Articles.clear();
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
free(*it);
}
m_Groups.clear();
ClearArticles();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void FileInfo::ClearArticles()
{
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
{
delete *it;
}
m_Articles.clear();
}
void FileInfo::SetID(int s)
@@ -146,67 +505,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)
{
strncpy(szBuffer, BaseFileName(szNZBFilename), iSize);
szBuffer[iSize-1] = '\0';
if (char* p = strrchr(szBuffer, '.')) *p = '\0';
}
void FileInfo::ParseSubject()
{
char* fnstart = strstr(m_szSubject, "\"");
char* fnend = NULL;
if (fnstart)
{
fnstart++;
fnend = strstr(fnstart, "\"");
}
if (fnend)
{
char fn[1024];
strncpy(fn, fnstart, fnend - fnstart);
fn[fnend - fnstart] = '\0';
m_szFilename = strdup(fn);
}
else
{
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", m_szSubject);
m_szFilename = strdup(m_szSubject);
}
//replace bad chars in filename
char* p = m_szFilename;
while (*p)
{
if (strchr("\\/:*?\"><'\n\r\t", *p))
{
*p = '_';
}
p++;
}
}
void FileInfo::SetFilename(const char* szFilename)
{
if (m_szFilename)
@@ -216,57 +529,232 @@ void FileInfo::SetFilename(const char* szFilename)
m_szFilename = strdup(szFilename);
}
void FileInfo::BuildDestDirName(const char* szNZBFilename)
void FileInfo::MakeValidFilename()
{
char szBuffer[1024];
if (g_pOptions->GetAppendNZBDir())
{
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)
{
// wipe out certain structure
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
}
else
{
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
}
// wipe out ".nzb"
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), postname);
szBuffer[1024-1] = '\0';
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
m_szDestDir = strdup(szBuffer);
Util::MakeValidFilename(m_szFilename, '_', false);
}
bool FileInfo::IsDupe()
void FileInfo::LockOutputFile()
{
debug("Checking if the file was already downloaded or queued");
m_mutexOutputFile.Lock();
}
struct stat buffer;
void FileInfo::UnlockOutputFile()
{
m_mutexOutputFile.Unlock();
}
bool FileInfo::IsDupe(const char* szFilename)
{
char fileName[1024];
bool exists = false;
snprintf(fileName, 1024, "%s%c%s", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
if (!exists)
if (Util::FileExists(fileName))
{
snprintf(fileName, 1024, "%s%c%s_broken", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
return exists;
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;
}
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_bParCheck = false;
m_eParStatus = psNone;
m_eRequestParCheck = rpNone;
m_eScriptStatus = srNone;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
m_tStartTime = 0;
m_tStageTime = 0;
m_eStage = ptQueued;
m_pScriptThread = 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)
{
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_mutexLog.Lock();
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)
{
for (FileQueue::iterator it = GetFileQueue()->begin(); it != GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo* pGroupInfo = NULL;
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
{
GroupInfo* pGroupInfo1 = *itg;
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
}
}
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();
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();
}
}
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++;
}
}
}

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 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
@@ -29,6 +29,13 @@
#include <vector>
#include <deque>
#include <time.h>
#include "Log.h"
#include "Thread.h"
class NZBInfo;
class DownloadQueue;
class ArticleInfo
{
@@ -71,18 +78,20 @@ 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;
Mutex m_mutexOutputFile;
static int m_iIDGen;
@@ -91,35 +100,340 @@ 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; }
void SetFilename(const char* szFilename);
void MakeValidFilename();
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
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; }
void BuildDestDirName(const char* szNZBFilename);
const char* GetDestDir() { return m_szDestDir; }
void SetDestDir(const char* szDestDir);
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int s) { m_iCompleted = s; }
void ParseSubject();
bool IsDupe();
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
};
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;
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; }
};
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 EParStatus
{
prNone,
prFailure,
prRepairPossible,
prSuccess
};
enum EScriptStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
};
typedef std::vector<char*> Files;
typedef std::deque<Message*> Messages;
private:
int m_iID;
int m_iRefCount;
char* m_szFilename;
char* m_szDestDir;
char* m_szCategory;
int m_iFileCount;
int m_iParkedFileCount;
long long m_lSize;
Files m_completedFiles;
bool m_bPostProcess;
EParStatus m_eParStatus;
EScriptStatus m_eScriptStatus;
char* m_szQueuedFilename;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bCleanupDisk;
time_t m_tHistoryTime;
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);
void GetNiceNZBName(char* szBuffer, int iSize);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
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)
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();
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
void ClearCompletedFiles();
bool GetPostProcess() { return m_bPostProcess; }
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
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; }
time_t GetHistoryTime() { return m_tHistoryTime; }
void SetHistoryTime(time_t tHistoryTime) { m_tHistoryTime = tHistoryTime; }
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,
ptExecutingScript,
ptFinished
};
enum EParStatus
{
psNone,
psFailure,
psSuccess,
psRepairPossible
};
enum ERequestParCheck
{
rpNone,
rpCurrent,
rpAll
};
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;
bool m_bParCheck;
EParStatus m_eParStatus;
EScriptStatus m_eScriptStatus;
ERequestParCheck m_eRequestParCheck;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pScriptThread;
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; }
bool GetParCheck() { return m_bParCheck; }
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetScriptThread() { return m_pScriptThread; }
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
Messages* LockMessages();
void UnlockMessages();
};
typedef std::deque<PostInfo*> PostQueue;
typedef std::vector<int> IDList;
typedef std::deque<NZBInfo*> HistoryList;
class DownloadQueue
{
protected:
NZBInfoList m_NZBInfoList;
FileQueue m_FileQueue;
PostQueue m_PostQueue;
HistoryList m_HistoryList;
FileQueue m_ParkedFiles;
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; }
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 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
@@ -34,8 +34,12 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
@@ -46,6 +50,7 @@
#include "MessageBase.h"
#include "QueueCoordinator.h"
#include "RemoteClient.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
@@ -60,11 +65,16 @@ Frontend::Frontend()
m_bFileList = false;
m_fCurrentDownloadSpeed = 0;
m_lRemainingSize = 0;
m_bPause = false;
m_bPauseDownload = false;
m_bPauseDownload2 = false;
m_fDownloadLimit = 0;
m_iThreadCount = 0;
m_RemoteMessages.clear();
m_RemoteQueue.clear();
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
m_iDnTimeSec = 0;
m_iAllBytes = 0;
m_bStandBy = 0;
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
}
bool Frontend::PrepareData()
@@ -77,7 +87,7 @@ bool Frontend::PrepareData()
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("Unable to send request to nzbserver 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->GetServerIP(), g_pOptions->GetServerPort());
Stop();
return false;
}
@@ -88,9 +98,14 @@ bool Frontend::PrepareData()
{
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
m_bPause = g_pOptions->GetPause();
m_bPauseDownload = g_pOptions->GetPauseDownload();
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
m_iPostJobCount = pPostQueue->size();
g_pQueueCoordinator->UnlockQueue();
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
}
}
return true;
@@ -106,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();
}
}
@@ -134,7 +149,7 @@ void Frontend::UnlockMessages()
}
}
DownloadQueue * Frontend::LockQueue()
DownloadQueue* Frontend::LockQueue()
{
if (IsRemoteMode())
{
@@ -159,15 +174,22 @@ 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);
if (bSecondRegister)
{
g_pOptions->SetPauseDownload2(bPause);
}
else
{
g_pOptions->SetPauseDownload(bPause);
}
}
}
@@ -195,68 +217,24 @@ void Frontend::ServerDumpDebug()
}
}
bool Frontend::ServerEditQueue(EEditAction eAction, int iEntry)
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
{
DownloadQueue* pDownloadQueue = LockQueue();
int ID = 0;
bool bPause = false;
if (iEntry >= 0 && iEntry < (int)pDownloadQueue->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[iEntry];
ID = pFileInfo->GetID();
bPause = !pFileInfo->GetPaused();
}
UnlockQueue();
if (ID == 0)
{
return false;
}
if (IsRemoteMode())
{
switch (eAction)
{
case eaPauseUnpause:
return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID, ID);
case eaDelete:
return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID, ID);
case eaMoveUp:
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID, ID);
case eaMoveDown:
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID, ID);
case eaMoveTop:
return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID, ID);
case eaMoveBottom:
return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID, ID);
}
return RequestEditQueue((eRemoteEditAction)eAction, iOffset, iID);
}
else
{
switch (eAction)
{
case eaPauseUnpause:
return g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, bPause);
case eaDelete:
return g_pQueueCoordinator->EditQueueDeleteEntry(ID);
case eaMoveUp:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1, false);
case eaMoveDown:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1, false);
case eaMoveTop:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
case eaMoveBottom:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1000000, true);
}
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset, NULL);
}
return false;
}
void Frontend::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
{
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
pMessageBase->m_iType = iRequest;
pMessageBase->m_iSize = iSize;
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -266,18 +244,18 @@ bool Frontend::RequestMessages()
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect() >= 0;
bool OK = connection.Connect();
if (!OK)
{
return false;
}
SNZBLogRequest LogRequest;
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = m_iNeededLogEntries;
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
if (m_iNeededLogEntries == 0)
{
LogRequest.m_iIDFrom = m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1;
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
}
else
{
@@ -290,17 +268,20 @@ bool Frontend::RequestMessages()
}
// Now listen for the returned log
SNZBLogRequestAnswer LogRequestAnswer;
if (connection.Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
SNZBLogResponse LogResponse;
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
return false;
}
char* pBuf = NULL;
if (LogRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
if (!connection.RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -309,19 +290,19 @@ bool Frontend::RequestMessages()
connection.Disconnect();
if (LogRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
{
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
Message* pMessage = new Message(pLogAnswer->m_iID, (Message::EKind)pLogAnswer->m_iKind, pLogAnswer->m_tTime, szText);
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
m_RemoteMessages.push_back(pMessage);
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
}
free(pBuf);
@@ -335,16 +316,16 @@ bool Frontend::RequestFileList()
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect() >= 0;
bool OK = connection.Connect();
if (!OK)
{
return false;
}
SNZBListRequest ListRequest;
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = m_bFileList;
ListRequest.m_bServerState = m_bSummary;
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
@@ -352,17 +333,20 @@ bool Frontend::RequestFileList()
}
// Now listen for the returned list
SNZBListRequestAnswer ListRequestAnswer;
if (connection.Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
SNZBListResponse ListResponse;
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
return false;
}
char* pBuf = NULL;
if (ListRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
if (!connection.RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -373,42 +357,26 @@ bool Frontend::RequestFileList()
if (m_bSummary)
{
m_bPause = ListRequestAnswer.m_bServerPaused;
m_lRemainingSize = ListRequestAnswer.m_lRemainingSize;
m_fCurrentDownloadSpeed = ListRequestAnswer.m_fDownloadRate;
m_fDownloadLimit = ListRequestAnswer.m_fDownloadLimit;
m_iThreadCount = ListRequestAnswer.m_iThreadCount;
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_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
m_bStandBy = ntohl(ListResponse.m_bDownloadStandBy);
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
}
if (m_bFileList && ListRequestAnswer.m_iTrailingDataLength > 0)
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
{
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
char* szSubject = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen;
char* szFileName = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
char* szDestDir = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen + pListAnswer->m_iFilenameLen;
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(pListAnswer->m_iID);
pFileInfo->SetSize(pListAnswer->m_iFileSize);
pFileInfo->SetRemainingSize(pListAnswer->m_iRemainingSize);
pFileInfo->SetPaused(pListAnswer->m_bPaused);
pFileInfo->SetNZBFilename(szNZBFilename);
pFileInfo->SetSubject(szSubject);
pFileInfo->SetFilename(szFileName);
pFileInfo->SetFilenameConfirmed(pListAnswer->m_bFilenameConfirmed);
pFileInfo->SetDestDir(szDestDir);
m_RemoteQueue.push_back(pFileInfo);
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
}
RemoteClient client;
client.SetVerbose(false);
client.BuildFileList(&ListResponse, pBuf, &m_RemoteQueue);
}
if (pBuf)
{
free(pBuf);
@@ -417,11 +385,11 @@ 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)
@@ -438,9 +406,9 @@ bool Frontend::RequestDumpDebug()
return client.RequestServerDumpDebug();
}
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo);
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, 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 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
@@ -31,20 +31,10 @@
#include "Log.h"
#include "DownloadInfo.h"
#include "MessageBase.h"
#include "QueueEditor.h"
class Frontend : public Thread
{
public:
enum EEditAction
{
eaPauseUnpause,
eaDelete,
eaMoveUp,
eaMoveDown,
eaMoveTop,
eaMoveBottom
};
private:
Log::Messages m_RemoteMessages;
DownloadQueue m_RemoteQueue;
@@ -55,15 +45,22 @@ private:
protected:
bool m_bSummary;
bool m_bFileList;
unsigned int m_iNeededLogEntries;
unsigned int m_iNeededLogFirstID;
unsigned int m_iNeededLogEntries;
unsigned int m_iNeededLogFirstID;
int m_iUpdateInterval;
// summary
float m_fCurrentDownloadSpeed;
long long m_lRemainingSize;
bool m_bPause;
bool m_bPauseDownload;
bool m_bPauseDownload2;
float m_fDownloadLimit;
int m_iThreadCount;
int m_iPostJobCount;
int m_iUpTimeSec;
int m_iDnTimeSec;
long long m_iAllBytes;
bool m_bStandBy;
bool PrepareData();
void FreeData();
@@ -72,15 +69,15 @@ protected:
DownloadQueue* LockQueue();
void UnlockQueue();
bool IsRemoteMode();
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
void ServerPauseUnpause(bool bPause);
bool RequestPauseUnpause(bool bPause);
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
void ServerPauseUnpause(bool bPause, bool bSecondRegister);
bool RequestPauseUnpause(bool bPause, bool bSecondRegister);
void ServerSetDownloadRate(float fRate);
bool RequestSetDownloadRate(float fRate);
void ServerDumpDebug();
bool RequestDumpDebug();
bool ServerEditQueue(EEditAction eAction, int iEntry);
bool RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
bool RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID);
public:
Frontend();

151
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 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
@@ -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,8 +53,7 @@ Log::Log()
m_iIDGen = 0;
m_szLogFilename = NULL;
#ifdef DEBUG
struct stat buffer;
m_bExtraDebug = !stat("extradebug", &buffer);
m_bExtraDebug = Util::FileExists("extradebug");
#endif
}
@@ -70,18 +72,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 +94,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 +103,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 +116,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 +124,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 +136,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 +149,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 +165,8 @@ void debug(const char* msg, ...)
}
g_pLog->m_mutexLog.Unlock();
#endif
}
#endif
void error(const char* msg, ...)
{
@@ -194,7 +180,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 +205,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 +230,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 +243,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];
@@ -269,7 +280,7 @@ void abort(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
printf("%s", tmp2);
printf("\n%s", tmp2);
g_pLog->Filelog(tmp2);
@@ -309,11 +320,14 @@ 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 +346,60 @@ 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 two 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).
*/
void Log::InitOptions()
{
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
}
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
{
i++;
}
}
}

33
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 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
@@ -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:
@@ -91,26 +94,30 @@ 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 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

@@ -59,7 +59,7 @@ void LoggableFrontend::Run()
while (!IsStopped())
{
Update();
usleep(200 * 1000);
usleep(m_iUpdateInterval * 1000);
}
// Printing the last messages
Update();
@@ -106,25 +106,36 @@ void LoggableFrontend::Update()
void LoggableFrontend::PrintMessage(Message * pMessage)
{
#ifdef WIN32
char* msg = strdup(pMessage->GetText());
CharToOem(msg, msg);
#else
const char* msg = pMessage->GetText();
#endif
switch (pMessage->GetKind())
{
case Message::mkDebug:
fprintf(stdout, "[DEBUG] %s\n", msg);
printf("[DEBUG] %s\n", msg);
break;
case Message::mkError:
fprintf(stdout, "[ERROR] %s\n", msg);
printf("[ERROR] %s\n", msg);
break;
case Message::mkWarning:
fprintf(stdout, "[WARNING] %s\n", msg);
printf("[WARNING] %s\n", msg);
break;
case Message::mkInfo:
fprintf(stdout, "[INFO] %s\n", msg);
printf("[INFO] %s\n", msg);
break;
case Message::mkDetail:
printf("[DETAIL] %s\n", msg);
break;
}
#ifdef WIN32
free(msg);
#endif
}
void LoggableFrontend::PrintSkip()
{
fprintf(stdout, ".....\n");
printf(".....\n");
}

View File

@@ -1,18 +1,59 @@
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 RemoteServer.cpp RemoteServer.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
RemoteClient.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 ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
nzbget.cpp nzbget.h
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 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 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 nzbget.cpp nzbget.h
EXTRA_DIST = nzbget.conf.example \
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
win32.h NTService.cpp NTService.h \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
nzbget.kdevelop nzbget.sln nzbget.vcproj
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
nzbgetd nzbget-shell.bat
# 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 *~

8
Makefile.cvs Normal file
View File

@@ -0,0 +1,8 @@
default: all
all:
aclocal
autoheader
automake
autoconf

View File

@@ -1,4 +1,4 @@
# Makefile.in generated by automake 1.9.5 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,
@@ -14,8 +14,6 @@
@SET_MAKE@
SOURCES = $(nzbget_SOURCES)
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
@@ -56,17 +54,19 @@ CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
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) \
RemoteServer.$(OBJEXT) NCursesFrontend.$(OBJEXT) \
NNTPConnection.$(OBJEXT) RemoteClient.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
ServerPool.$(OBJEXT) Thread.$(OBJEXT) Util.$(OBJEXT) \
nzbget.$(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) nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
@@ -106,11 +106,7 @@ AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CP = @CP@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
@@ -124,7 +120,7 @@ ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FALSE = @FALSE@
GREP = @GREP@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
@@ -132,12 +128,9 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LN = @LN@
LTLIBOBJS = @LTLIBOBJS@
MAKE = @MAKE@
MAKEINFO = @MAKEINFO@
MKDIR = @MKDIR@
MV = @MV@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
@@ -146,20 +139,13 @@ 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@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_RANLIB = @ac_ct_RANLIB@
ac_ct_STRIP = @ac_ct_STRIP@
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
@@ -174,42 +160,57 @@ build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
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@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
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 RemoteServer.cpp RemoteServer.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
RemoteClient.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 ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
nzbget.cpp nzbget.h
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 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 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 nzbget.cpp nzbget.h
EXTRA_DIST = nzbget.conf.example \
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
win32.h NTService.cpp NTService.h \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
nzbget.kdevelop nzbget.sln nzbget.vcproj
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
nzbgetd nzbget-shell.bat
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -300,6 +301,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@
@@ -318,12 +320,19 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.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)/Util.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@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@@ -612,6 +621,43 @@ uninstall-am: uninstall-binPROGRAMS uninstall-info-am
uninstall-info-am
# 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 *~
# 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.

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-2010 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
@@ -27,161 +27,433 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6201; // = "nzb"-version-1
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6208; // = "nzb8" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
// 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
/**
* 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 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.
*/
namespace NZBMessageRequest
// Possible values for field "m_iType" of struct "SNZBRequestBase":
enum eRemoteRequest
{
enum
{
eRequestDownload = 1,
eRequestPauseUnpause,
eRequestList,
eRequestSetDownloadRate,
eRequestDumpDebug,
eRequestEditQueue,
eRequestLog,
eRequestShutdown
eRemoteRequestDownload = 1,
eRemoteRequestPauseUnpause,
eRemoteRequestList,
eRemoteRequestSetDownloadRate,
eRemoteRequestDumpDebug,
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog,
eRemoteRequestScan,
eRemoteRequestHistory
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
enum
// File-Actions affect one file, Group-Actions affect all files in group.
// Group is a list of files, added to queue from one NZB-File.
enum eRemoteEditAction
{
eActionMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eActionMoveTop, // move to top of queue
eActionMoveBottom, // move to bottom of queue
eActionPause, // pause
eActionResume, // resume (unpause)
eActionDelete // 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 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)
eRemoteEditActionGroupSetCategory, // set or change category for a group
eRemoteEditActionGroupMerge, // merge group
eRemoteEditActionGroupSetParameter, // set post-process parameter for 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
};
}
// The basic NZBMessageBase struct
struct SNZBMessageBase
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
enum eRemotePauseUnpauseAction
{
int32_t m_iId; // Id must be 'nzbg' in integer-value
int32_t m_iType; // message type, must be > 0
int32_t m_iSize; // Size of the entire struct
char m_szPassword[ NZBREQUESTPASSWORDSIZE ]; // Password needs to be in every request
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
};
// The basic SNZBRequestBase struct, used in all requests
struct SNZBRequestBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
};
// The basic SNZBResposneBase struct, used in all responses
struct SNZBResponseBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
};
// A download request
struct SNZBDownloadRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
char m_szFilename[ NZBREQUESTFILENAMESIZE ];
int32_t m_bAddFirst;
int32_t m_iTrailingDataLength;
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, be empty
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
// A list request
// A download response
struct SNZBDownloadResponse
{
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 list and status request
struct SNZBListRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList;
int32_t m_bServerState;
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
};
// A list request-answer
struct SNZBListRequestAnswer
// A list response
struct SNZBListResponse
{
int32_t m_iSize; // Size of the entire struct
int32_t m_iEntrySize; // Size of the SNZBListRequestAnswerEntry-struct
long long m_lRemainingSize;
float m_fDownloadRate;
float m_fDownloadLimit;
int32_t m_bServerPaused;
int32_t m_iThreadCount;
int32_t m_iNrTrailingEntries;
int32_t m_iTrailingDataLength;
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
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_iDownloadRate; // Current download speed, in Bytes pro Second
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
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_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_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 request-answer entry
struct SNZBListRequestAnswerEntry
// A list response nzb entry
struct SNZBListResponseNZBEntry
{
int32_t m_iNZBFilenameLen;
int32_t m_iSubjectLen;
int32_t m_iFilenameLen;
int32_t m_iDestDirLen;
int32_t m_iFileSize;
int32_t m_bFilenameConfirmed;
int32_t m_iRemainingSize;
int32_t m_iID;
int32_t m_bPaused;
//char m_szNZBFilename[0]; // variable sized
//char m_szSubject[0]; // variable sized
//char m_szFilename[0]; // variable sized
//char m_szDestDir[0]; // variable sized
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_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
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_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_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
//char m_szSubject[m_iSubjectLen]; // variable sized
//char m_szFilename[m_iFilenameLen]; // variable sized
};
// A log request
struct SNZBLogRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // Only one of these two parameters
int32_t m_iLines; // can be set. The another one must be set to "0".
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // Only one of these two parameters
int32_t m_iLines; // can be set. The another one must be set to "0".
};
// A log request-answer
struct SNZBLogRequestAnswer
// A log response
struct SNZBLogResponse
{
int32_t m_iSize; // Size of the entire struct
int32_t m_iEntrySize; // Size of the SNZBLogRequestAnswerEntry-struct
int32_t m_iNrTrailingEntries;
int32_t m_iTrailingDataLength;
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A log request-answer entry
struct SNZBLogRequestAnswerEntry
// A log response entry
struct SNZBLogResponseEntry
{
int32_t m_iTextLen;
int32_t m_iID;
int32_t m_iKind; // see Message::Kind in "Log.h"
time_t m_tTime;
//char m_szText[0]; // variable sized
int32_t m_iID; // ID of Log-entry
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTextLen]; // variable sized
};
// A Pause/Unpause request
struct SNZBPauseUnpauseRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_bPause; // The value g_bPause should be set to
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
struct SNZBPauseUnpauseResponse
{
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
};
// Request setting the download rate
struct SNZBSetDownloadRateRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
float m_fDownloadRate;
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
};
// A download request
// A setting download rate response
struct SNZBSetDownloadRateResponse
{
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
};
// An edit queue request
struct SNZBEditQueueRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // ID of the first file in the range
int32_t m_iIDTo; // ID of the last file in the range, not used yet, must be same as m_iIDFrom
int32_t m_iAction; // action to be done, see later
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
SNZBRequestBase m_MessageBase; // Must be the first in the struct
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_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_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
};
// An edit queue response
struct SNZBEditQueueResponse
{
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
};
// Request dumping of debug info
struct SNZBDumpDebugRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iLevel; // Future use
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
// Dumping of debug response
struct SNZBDumpDebugResponse
{
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
};
// Shutdown server request
struct SNZBShutdownRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Shutdown server response
struct SNZBShutdownResponse
{
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
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Server version response
struct SNZBVersionResponse
{
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
};
// 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
};
// 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
};
// A 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_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
};
// A list response nzb entry
struct SNZBHistoryResponseEntry
{
int32_t m_iID; // NZBID
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_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
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
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_szDestDir[m_iDestDirLen]; // variable sized
//char m_szCategory[m_iCategoryLen]; // variable sized
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
};
#endif

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 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
@@ -30,6 +30,7 @@
#ifndef DISABLE_CURSES
#include <vector>
#include <time.h>
#include "Frontend.h"
#include "Log.h"
@@ -46,22 +47,31 @@ private:
eDownloadRate
};
bool m_bUseColor;
int m_iSkipUpdateData;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
int m_iQueueWinHeight;
int m_iQueueWinClientHeight;
int m_iMessagesWinTop;
int m_iMessagesWinHeight;
int m_iMessagesWinClientHeight;
int m_iSelectedQueueEntry;
int m_iQueueScrollOffset;
bool m_bUseColor;
int m_iDataUpdatePos;
bool m_bUpdateNextTime;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
int m_iQueueWinHeight;
int m_iQueueWinClientHeight;
int m_iMessagesWinTop;
int m_iMessagesWinHeight;
int m_iMessagesWinClientHeight;
int m_iSelectedQueueEntry;
int m_iLastEditEntry;
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
int m_iInputNumberIndex;
int m_iInputValue;
// Inputting numbers
int m_iInputNumberIndex;
int m_iInputValue;
#ifdef WIN32
CHAR_INFO* m_pScreenBuffer;
@@ -69,12 +79,14 @@ private:
int m_iScreenBufferSize;
std::vector<WORD> m_ColorAttr;
#else
void* m_pWindow; // WINDOW*
void* m_pWindow; // WINDOW*
#endif
EInputMode m_eInputMode;
bool m_bShowNZBname;
float m_QueueWindowPercentage;
EInputMode m_eInputMode;
bool m_bShowNZBname;
bool m_bShowTimestamp;
bool m_bGroupFiles;
float m_QueueWindowPercentage;
#ifdef WIN32
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
@@ -83,17 +95,27 @@ private:
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
void PrintMessages();
void PrintQueue();
void PrintFileQueue();
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
void PrintGroupQueue();
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 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
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
@@ -41,42 +42,37 @@
#include "Connection.h"
#include "NewsServer.h"
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
{
m_UnavailableGroups.clear();
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(LineBufSize);
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
}
NNTPConnection::~NNTPConnection()
{
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
{
free(m_UnavailableGroups[i]);
m_UnavailableGroups[i] = NULL;
}
m_UnavailableGroups.clear();
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
if (m_szLineBuf)
{
free(m_szLineBuf);
}
free(m_szLineBuf);
}
char* NNTPConnection::Request(char* req)
const char* NNTPConnection::Request(const char* req)
{
if (!req)
{
return NULL;
}
m_bAuthError = false;
WriteLine(req);
char* answer = ReadLine(m_szLineBuf, LineBufSize);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
@@ -88,56 +84,56 @@ char* NNTPConnection::Request(char* req)
debug("%s requested authorization", m_pNetAddress->GetHost());
//authentication required!
if (Authenticate() < 0)
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, LineBufSize);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
}
int NNTPConnection::Authenticate()
bool NNTPConnection::Authenticate()
{
if ((!((NewsServer*)m_pNetAddress)->GetUser()) ||
(!((NewsServer*)m_pNetAddress)->GetPassword()))
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
!((NewsServer*)m_pNetAddress)->GetPassword())
{
return -1;
return true;
}
return AuthInfoUser();
}
int NNTPConnection::AuthInfoUser(int iRecur)
bool NNTPConnection::AuthInfoUser(int iRecur)
{
if (iRecur > 10)
{
return -1;
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* answer = ReadLine(m_szLineBuf, LineBufSize);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
return -1;
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (!strncmp(answer, "281", 3))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
return 0;
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
{
@@ -145,130 +141,151 @@ int NNTPConnection::AuthInfoUser(int iRecur)
}
else if (!strncmp(answer, "480", 3))
{
return AuthInfoUser();
return AuthInfoUser(++iRecur);
}
return -1;
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
int NNTPConnection::AuthInfoPass(int iRecur)
bool NNTPConnection::AuthInfoPass(int iRecur)
{
if (iRecur > 10)
{
return -1;
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* szAnswer = ReadLine(m_szLineBuf, LineBufSize);
if (!szAnswer)
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);
return -1;
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
else if (!strncmp(szAnswer, "2", 1))
else if (!strncmp(answer, "2", 1))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
return 0;
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(szAnswer, "381", 3))
else if (!strncmp(answer, "381", 3))
{
return AuthInfoPass(++iRecur);
}
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), szAnswer);
return -1;
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
int NNTPConnection::DoConnect()
const char* NNTPConnection::JoinGroup(const char* grp)
{
debug("Opening connection to %s", GetServer()->GetHost());
int res = Connection::DoConnect();
if (res < 0)
return res;
char* answer = DoReadLine(m_szLineBuf, LineBufSize);
if (!answer)
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
{
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
return -1;
}
if (strncmp(answer, "2", 1))
{
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
return -1;
}
debug("Connection to %s established", GetServer()->GetHost());
return 0;
}
int NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
Request("quit\r\n");
}
return Connection::DoDisconnect();
}
int NNTPConnection::JoinGroup(char* grp)
{
if (!grp)
{
debug("joinGroup called with NULL-pointer!!");
return -1;
}
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
return 0;
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
{
if (!strcmp(grp, m_UnavailableGroups[i]))
{
debug("Group %s unavailable on %s.", grp, this->GetServer()->GetHost());
return -1;
}
// already in group
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 answer;
}
if ((answer) && (!strncmp(answer, "2", 1)))
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
if (m_szActiveGroup)
{
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
return 0;
}
if (!answer)
{
warn("Error changing group on %s: Connection closed by remote host.",
GetServer()->GetHost());
return -1;
}
else
{
warn("Error changing group on %s to %s: Answer was \"%s\".",
GetServer()->GetHost(), grp, answer);
m_UnavailableGroups.push_back(strdup(grp));
debug("Error changing group on %s to %s: %s.",
GetServer()->GetHost(), grp, answer);
}
return -1;
return answer;
}
bool NNTPConnection::DoConnect()
{
debug("Opening connection to %s", GetServer()->GetHost());
bool res = Connection::DoConnect();
if (!res)
{
return res;
}
#ifndef DISABLE_TLS
if (GetNewsServer()->GetTLS())
{
if (!StartTLS())
{
return false;
}
}
#endif
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
return false;
}
debug("Connection to %s established", GetServer()->GetHost());
return true;
}
bool NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
Request("quit\r\n");
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
}
return Connection::DoDisconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, m_pNetAddress->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 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
@@ -27,32 +27,31 @@
#ifndef NNTPCONNECTION_H
#define NNTPCONNECTION_H
#include <stdio.h>
#include <vector>
#include "NewsServer.h"
#include "Connection.h"
class NNTPConnection : public Connection
{
private:
std::vector <char*> m_UnavailableGroups;
char* m_szActiveGroup;
static const int LineBufSize = 1024*10;
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();
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
char* Request(char* req);
int Authenticate();
int AuthInfoUser(int iRecur = 0);
int AuthInfoPass(int iRecur = 0);
int JoinGroup(char* grp);
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -38,148 +38,152 @@
extern void ExitProc();
RunProc Run = NULL;
char* strServiceName = "NZBGet";
SERVICE_STATUS_HANDLE nServiceStatusHandle;
DWORD nServiceCurrentStatus;
char* strServiceName = "NZBGet";
SERVICE_STATUS_HANDLE nServiceStatusHandle;
DWORD nServiceCurrentStatus;
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS nServiceStatus;
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
nServiceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
{
nServiceStatus.dwControlsAccepted = 0;
}
else
{
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
}
nServiceStatus.dwWin32ExitCode = NO_ERROR;
nServiceStatus.dwServiceSpecificExitCode = 0;
nServiceStatus.dwCheckPoint = 0;
nServiceStatus.dwWaitHint = dwWaitHint;
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
return success;
}
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
{
BOOL success;
SERVICE_STATUS nServiceStatus;
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
nServiceStatus.dwCurrentState = dwCurrentState;
if (dwCurrentState == SERVICE_START_PENDING)
{
nServiceStatus.dwControlsAccepted = 0;
}
else
{
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
}
nServiceStatus.dwWin32ExitCode = NO_ERROR;
nServiceStatus.dwServiceSpecificExitCode = 0;
nServiceStatus.dwCheckPoint = 0;
nServiceStatus.dwWaitHint = dwWaitHint;
void ServiceCtrlHandler(DWORD nControlCode)
{
switch(nControlCode)
{
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
nServiceCurrentStatus = SERVICE_STOP_PENDING;
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
ExitProc();
return;
default:
break;
}
UpdateServiceStatus(nServiceCurrentStatus, 0);
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
return success;
}
void ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
if(!nServiceStatusHandle)
{
return;
}
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
if(!success)
{
return;
}
nServiceCurrentStatus=SERVICE_RUNNING;
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
if(!success)
{
return;
}
Run();
UpdateServiceStatus(SERVICE_STOPPED, 0);
}
void ServiceCtrlHandler(DWORD nControlCode)
{
switch(nControlCode)
{
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
nServiceCurrentStatus = SERVICE_STOP_PENDING;
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
ExitProc();
return;
default:
break;
}
UpdateServiceStatus(nServiceCurrentStatus, 0);
}
void ServiceMain(DWORD argc, LPTSTR *argv)
{
BOOL success;
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
if(!nServiceStatusHandle)
{
return;
}
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
if(!success)
{
return;
}
nServiceCurrentStatus=SERVICE_RUNNING;
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
if(!success)
{
return;
}
Run();
UpdateServiceStatus(SERVICE_STOPPED, 0);
}
void StartService(RunProc RunProcPtr)
{
Run = RunProcPtr;
SERVICE_TABLE_ENTRY servicetable[]=
{
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL,NULL}
};
BOOL success = StartServiceCtrlDispatcher(servicetable);
if(!success)
{
error("Could not start service");
}
SERVICE_TABLE_ENTRY servicetable[]=
{
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL,NULL}
};
BOOL success = StartServiceCtrlDispatcher(servicetable);
if(!success)
{
error("Could not start service");
}
}
void InstallService(int argc, char *argv[])
{
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
if(!scm)
{
printf("Could not install service\n");
return;
}
char szCmdLine[1024];
snprintf(szCmdLine, 1024, "%s -D", argv[0]);
szCmdLine[1024-1] = '\0';
SC_HANDLE hService = CreateService(scm, strServiceName,
strServiceName,
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
szCmdLine,
0,0,0,0,0);
if(!hService)
{
CloseServiceHandle(scm);
printf("Could not install service\n");
return;
}
CloseServiceHandle(hService);
CloseServiceHandle(scm);
printf("Service \"%s\" sucessfully installed\n", strServiceName);
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
if(!scm)
{
printf("Could not install service\n");
return;
}
char szExeName[1024];
GetModuleFileName(NULL, szExeName, 1024);
szExeName[1024-1] = '\0';
char szCmdLine[1024];
snprintf(szCmdLine, 1024, "%s -D", szExeName);
szCmdLine[1024-1] = '\0';
SC_HANDLE hService = CreateService(scm, strServiceName,
strServiceName,
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
szCmdLine,
0,0,0,0,0);
if(!hService)
{
CloseServiceHandle(scm);
printf("Could not install service\n");
return;
}
CloseServiceHandle(hService);
CloseServiceHandle(scm);
printf("Service \"%s\" sucessfully installed\n", strServiceName);
}
void UnInstallService()
{
BOOL success;
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
if(!scm)
{
printf("Could not uninstall service\n");
return;
}
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
if(!hService)
{
CloseServiceHandle(scm);
printf("Could not uninstall service\n");
return;
}
success = DeleteService(hService);
if(!success)
{
error("Could not uninstall service");
}
CloseServiceHandle(hService);
CloseServiceHandle(scm);
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
BOOL success;
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
if(!scm)
{
printf("Could not uninstall service\n");
return;
}
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
if(!hService)
{
CloseServiceHandle(scm);
printf("Could not uninstall service\n");
return;
}
success = DeleteService(hService);
if(!success)
{
error("Could not uninstall service");
}
CloseServiceHandle(hService);
CloseServiceHandle(scm);
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
}
void InstallUninstallServiceCheck(int argc, char *argv[])

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*

View File

@@ -1,5 +1,5 @@
/*
* 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>
@@ -32,9 +32,8 @@
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <list>
#ifdef WIN32
#include <comutil.h>
#import "MSXML.dll" named_guids
@@ -42,23 +41,47 @@ using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlerror.h>
#endif
#include "nzbget.h"
#include "NZBFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "DiskState.h"
#include "Util.h"
bool ArticleGreater(ArticleInfo* elem1, ArticleInfo* elem2)
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#ifndef WIN32
static void libxml_errorhandler(void *ebuf, const char *fmt, ...)
{
return elem1->GetPartNumber() > elem2->GetPartNumber();
}
va_list argp;
va_start(argp, fmt);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), fmt, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
NZBFile::NZBFile(const char* szFileName)
// 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
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();
m_FileInfos.clear();
}
@@ -73,11 +96,16 @@ NZBFile::~NZBFile()
free(m_szFileName);
}
for (std::vector<FileInfo*>::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
delete *it;
}
m_FileInfos.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void NZBFile::LogDebugInfo()
@@ -90,58 +118,14 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
bool NZBFile::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
FILE* pFile = fopen(szFileName, "r");
if (!pFile)
{
return false;
}
// obtain file size.
fseek(pFile , 0 , SEEK_END);
int iSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file.
*pBuffer = (char*) malloc(iSize + 1);
if (!*pBuffer)
{
return false;
}
// copy the file into the buffer.
fread(*pBuffer, 1, iSize, pFile);
fclose(pFile);
(*pBuffer)[iSize] = 0;
*pBufferLength = iSize + 1;
return true;
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
{
//return Create(szFileName, NULL, 0, false);
// /*
//TEST
int iBufferLength = 0;
char* szBuffer = NULL;
if (!NZBFile::LoadFileIntoBuffer(szFileName, &szBuffer, &iBufferLength))
{
return false;
}
return Create(szFileName, szBuffer, iBufferLength, true);
// */
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
@@ -153,8 +137,9 @@ void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
}
void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
// deleting empty articles
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
int i = 0;
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
@@ -170,10 +155,161 @@ void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
i++;
}
}
if (!pArticles->empty())
{
ParseSubject(pFileInfo);
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
else
{
delete pFileInfo;
}
}
void NZBFile::ParseSubject(FileInfo* pFileInfo)
{
// tokenize subject, considering spaces as separators and quotation
// marks as non separatable token delimiters.
// then take the last token containing dot (".") as a filename
typedef std::list<char*> TokenList;
TokenList tokens;
tokens.clear();
// tokenizing
char* p = (char*)pFileInfo->GetSubject();
char* start = p;
bool quot = false;
while (true)
{
char ch = *p;
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
if (sep)
{
// end of token
int len = (int)(p - start);
if (len > 0)
{
char* token = (char*)malloc(len + 1);
strncpy(token, start, len);
token[len] = '\0';
tokens.push_back(token);
}
start = p;
if (ch != '\"' || quot)
{
start++;
}
quot = *start == '\"';
if (quot)
{
start++;
char* q = strchr(start, '\"');
if (q)
{
p = q - 1;
}
else
{
quot = false;
}
}
}
if (ch == '\0')
{
break;
}
p++;
}
if (!tokens.empty())
{
// finding the best candidate for being a filename
char* besttoken = tokens.back();
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
{
char* s = *it;
char* p = strchr(s, '.');
if (p && (p[1] != '\0'))
{
besttoken = s;
break;
}
}
pFileInfo->SetFilename(besttoken);
// free mem
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
{
free(*it);
}
}
else
{
// subject is empty or contains only separators?
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
pFileInfo->MakeValidFilename();
}
/**
* Check if the parsing of subject was correct
*/
void NZBFile::CheckFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo1 = *it;
int iDupe = 0;
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
{
iDupe++;
}
}
// If more than two files have the same parsed filename but different subjects,
// this means, that the parsing was not correct.
// in this case we take subjects as filenames to prevent
// false "duplicate files"-alarm.
// It's Ok for just two files to have the same filename, this is
// 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();
}
}
}
}
}
#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);
@@ -197,7 +333,12 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
}
else
{
_variant_t v(szFileName);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
success = doc->load(v);
}
if (success == VARIANT_FALSE)
@@ -208,8 +349,12 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName);
if (!pFile->parseNZB(doc))
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
@@ -218,7 +363,29 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return pFile;
}
bool NZBFile::parseNZB(IUnknown* nzb)
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
{
while (char ch = *szFilename++)
{
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') )
{
*szURL++ = ch;
}
else
{
*szURL++ = '%';
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
}
}
*szURL = NULL;
}
bool NZBFile::ParseNZB(IUnknown* nzb)
{
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
@@ -231,9 +398,14 @@ 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);
pFileInfo->ParseSubject();
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++)
@@ -274,20 +446,21 @@ bool NZBFile::parseNZB(IUnknown* nzb)
}
}
DeleteEmptyArticles(pFileInfo);
m_FileInfos.push_back(pFileInfo);
AddFileInfo(pFileInfo);
}
return true;
}
#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)
{
xmlSetGenericErrorFunc(NULL, libxml_errorhandler);
xmlTextReaderPtr doc;
if (bFromBuffer)
{
doc = xmlReaderForMemory(szBuffer,iSize-1, "", NULL, 0);
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
}
else
{
@@ -295,11 +468,16 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
}
if (!doc)
{
error("Could not create XML-Reader");
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName);
if (!pFile->parseNZB(doc))
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
@@ -310,7 +488,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return pFile;
}
bool NZBFile::parseNZB(void* nzb)
bool NZBFile::ParseNZB(void* nzb)
{
FileInfo* pFileInfo = NULL;
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
@@ -334,7 +512,7 @@ bool NZBFile::parseNZB(void* nzb)
if (!strcmp("file", (char*)name))
{
pFileInfo = new FileInfo();
pFileInfo->SetNZBFilename(m_szFileName);
pFileInfo->SetFilename(m_szFileName);
while (xmlTextReaderMoveToNextAttribute(node))
{
@@ -345,7 +523,12 @@ bool NZBFile::parseNZB(void* nzb)
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetSubject((char*)value);
pFileInfo->ParseSubject();
}
if (!strcmp("date",(char*)name))
{
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetTime(atoi((char*)value));
}
}
}
@@ -396,6 +579,11 @@ bool NZBFile::parseNZB(void* nzb)
ret = xmlTextReaderRead(node);
xmlFree(value);
value = xmlTextReaderValue(node);
if (!pFileInfo)
{
// error: bad nzb-file
break;
}
pFileInfo->GetGroups()->push_back(strdup((char*)value));
}
}
@@ -405,8 +593,7 @@ bool NZBFile::parseNZB(void* nzb)
/* Close the file element, add the new file to file-list */
if (!strcmp("file",(char*)name))
{
DeleteEmptyArticles(pFileInfo);
m_FileInfos.push_back(pFileInfo);
AddFileInfo(pFileInfo);
}
}
@@ -417,7 +604,7 @@ bool NZBFile::parseNZB(void* nzb)
}
if (ret != 0)
{
error("Failed to parse nzb-file\n");
error("Failed to parse nzb-file");
return false;
}
return true;

View File

@@ -1,5 +1,5 @@
/*
* 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>
@@ -38,25 +38,29 @@ 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 DeleteEmptyArticles(FileInfo* pFileInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo);
void CheckFilenames();
#ifdef WIN32
bool parseNZB(IUnknown* nzb);
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
bool parseNZB(void* nzb);
bool ParseNZB(void* nzb);
#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 bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
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

@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NetAddress.h"
NetAddress::NetAddress(const char* szHost, int iPort)

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 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
@@ -35,30 +35,37 @@
#include <stdlib.h>
#include <string.h>
#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(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel) : NetAddress(szHost, iPort)
{
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = level;
m_iMaxConnections = maxConnections;
m_iLevel = iLevel;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
if (pass)
if (szUser)
{
m_szPassword = strdup(pass);
m_szUser = strdup(szUser);
}
if (user)
if (szPass)
{
m_szUser = strdup(user);
m_szPassword = strdup(szPass);
}
}
NewsServer::~NewsServer()
{
free(m_szUser);
m_szUser = NULL;
free(m_szPassword);
m_szPassword = NULL;
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
}

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 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
@@ -32,18 +32,22 @@
class NewsServer : public NetAddress
{
private:
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
bool m_bJoinGroup;
bool m_bTLS;
public:
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
virtual ~NewsServer();
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
};
#endif

View File

File diff suppressed because it is too large Load Diff

226
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-2010 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
@@ -28,57 +28,90 @@
#define OPTIONS_H
#include <vector>
#include "Thread.h"
class Options
{
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestList,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown
opClientNoOperation,
opClientRequestDownload,
opClientRequestListFiles,
opClientRequestListGroups,
opClientRequestListStatus,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScan,
opClientRequestDownloadPause,
opClientRequestDownloadUnpause,
opClientRequestDownload2Pause,
opClientRequestDownload2Unpause,
opClientRequestPostPause,
opClientRequestPostUnpause,
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory
};
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 EScriptLogKind
{
slNone,
slDetail,
slInfo,
slWarning,
slError,
slDebug
};
private:
struct OptEntry
class OptEntry
{
char* name;
char* value;
private:
char* m_szName;
char* m_szValue;
void SetName(const char* szName);
void SetValue(const char* szValue);
friend class Options;
public:
OptEntry();
~OptEntry();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
std::vector< struct OptEntry > optEntries;
typedef std::vector<OptEntry*> OptEntries;
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
// Options
char* m_szConfigFilename;
@@ -90,12 +123,14 @@ private:
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;
@@ -106,8 +141,10 @@ private:
char* m_szServerPassword;
int m_szServerPort;
char* m_szLockFile;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadPostQueue;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
@@ -115,8 +152,34 @@ private:
bool m_bParCheck;
bool m_bParRepair;
char* m_szPostProcess;
char* m_szNZBProcess;
bool m_bStrictParName;
bool m_bNoConfig;
int m_iUMask;
int m_iUpdateInterval;
bool m_bCursesNZBName;
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;
// Parsed command-line parameters
bool m_bServerMode;
@@ -124,43 +187,56 @@ private:
bool m_bRemoteClientMode;
int m_iEditQueueAction;
int m_iEditQueueOffset;
int m_iEditQueueIDFrom;
int m_iEditQueueIDTo;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szCategory;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iLogLines;
bool m_bTest;
int m_iWriteLogKind;
bool m_bTestBacktrace;
// Current state
bool m_bPause;
bool m_bPauseDownload;
bool m_bPauseDownload2;
bool m_bPausePostProcess;
bool m_bPauseScan;
float m_fDownloadRate;
EClientOperation m_eClientOperation;
void InitDefault();
void InitOptFile(int argc, char* argv[]);
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitScheduler();
void CheckOptions();
void PrintUsage(char* com);
void Dump();
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
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 CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty);
void ParseFileIDList(int argc, char* argv[], int optind);
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
public:
Options(int argc, char* argv[]);
~Options();
Options(int argc, char* argv[]);
~Options();
// Options
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetDestDir() { return m_szDestDir; }
const char* GetTempDir() { return m_szTempDir; }
const char* GetQueueDir() { return m_szQueueDir; }
@@ -171,49 +247,89 @@ public:
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; }
const char* GetServerIP() { return m_szServerIP; }
const char* GetServerPassword() { return m_szServerPassword; }
int GetServerPort() { return m_szServerPort; }
char* GetLockFile() { return m_szLockFile; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUserName() { return m_szDaemonUserName; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
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; }
const char* GetPostProcess() { return m_szPostProcess; }
const char* GetNZBProcess() { return m_szNZBProcess; }
bool GetStrictParName() { return m_bStrictParName; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
bool GetCursesNZBName() { return m_bCursesNZBName; }
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; }
// 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 GetEditQueueIDFrom() { return m_iEditQueueIDFrom; }
int GetEditQueueIDTo() { return m_iEditQueueIDTo; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetCategory() { return m_szCategory; }
const char* GetLastArg() { return m_szLastArg; }
bool GetAddTop() { return m_bAddTop; }
float GetSetRate() { return m_fSetRate; }
int GetLogLines() { return m_iLogLines; }
bool GetTest() { return m_bTest; }
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 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(float fRate) { m_fDownloadRate = fRate; }
float GetDownloadRate() const { return m_fDownloadRate; }
};

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 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
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
@@ -47,11 +48,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,20 +63,28 @@ const char* Par2CmdLineErrStr[] = { "OK",
"internal error occurred",
"out of memory" };
class Repairer : public Par2Repairer
{
friend class ParChecker;
};
ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psUndefined;
m_szParFilename = NULL;
m_szNZBFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
m_eStage = ptLoadingPars;
m_QueuedParFiles.clear();
}
@@ -89,10 +96,6 @@ ParChecker::~ParChecker()
{
free(m_szParFilename);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
@@ -101,6 +104,7 @@ ParChecker::~ParChecker()
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
@@ -127,15 +131,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,12 +139,19 @@ void ParChecker::SetStatus(EStatus eStatus)
void ParChecker::Run()
{
info("Verifying %s", m_szInfoName);
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
CommandLine commandLine;
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
@@ -158,19 +160,29 @@ void ParChecker::Run()
}
Result res;
Repairer* repairer = new Repairer();
#ifdef ENABLE_PARPROGRESS
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
#endif
res = repairer->PreProcess(commandLine);
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(commandLine);
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");
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;
return;
}
@@ -182,15 +194,25 @@ void ParChecker::Run()
}
m_szErrMsg = NULL;
m_bRepairNotNeeded = false;
m_bRepairing = false;
res = repairer->Process(commandLine, false);
m_eStage = ptVerifyingSources;
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, 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();
@@ -200,9 +222,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)
@@ -215,7 +245,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);
}
}
}
@@ -224,18 +262,19 @@ 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(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
SetStatus(psFailed);
delete repairer;
delete pRepairer;
return;
}
@@ -249,8 +288,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(commandLine, true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
@@ -264,7 +312,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);
}
@@ -278,214 +332,10 @@ void ParChecker::Run()
SetStatus(psFailed);
}
delete repairer;
delete pRepairer;
}
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;
@@ -496,37 +346,212 @@ void ParChecker::LoadMorePars(void* repairer)
for (QueuedParFiles::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 (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);
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;
}
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());
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 (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 used version of libpar2 does not support the cancelling of par-repair. Libpar2 needs to be patched for that feature to work.");
#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 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
@@ -32,7 +32,6 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
class ParChecker : public Thread, public Subject
{
@@ -44,40 +43,63 @@ 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;
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;
Mutex m_mutexQueuedParFiles;
Semaphore m_semNeedMoreFiles;
bool m_bRepairing;
bool m_bQueuedParFilesChanged;
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);
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
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 +108,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

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-2009 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
@@ -31,6 +31,7 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "Scanner.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
@@ -38,6 +39,18 @@
class PrePostProcessor : public Thread
{
public:
enum EEditAction
{
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
eaPostMoveTop,
eaPostMoveBottom,
eaPostDelete,
eaHistoryDelete,
eaHistoryReturn,
eaHistoryProcess
};
private:
typedef std::deque<char*> FileList;
@@ -55,47 +68,81 @@ private:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class QueuedFile
class PostParChecker: public ParChecker
{
private:
char* m_szNZBFilename;
char* m_szParFilename;
char* m_szInfoName;
PrePostProcessor* m_Owner;
PostInfo* m_pPostInfo;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
public:
QueuedFile(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
~QueuedFile();
const char* GetNZBFilename() { return m_szNZBFilename; }
const char* GetParFilename() { return m_szParFilename; }
const char* GetInfoName() { return m_szInfoName; }
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class PrePostProcessor;
};
typedef std::deque<QueuedFile*> ParQueue;
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
#endif
private:
bool m_bCheckIncomingNZBs;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
Scanner m_Scanner;
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnoreFirstInPostQueue, bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
void ApplySchedulerState();
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 FindMainPars(const char* szPath, FileList* pFileList);
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bPostScript, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
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 PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
void CheckIncomingNZBs();
bool WasLastUnpausedInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, bool bParOK);
#ifndef DISABLE_PARCHECK
ParChecker m_ParChecker;
Mutex m_mutexParChecker;
ParQueue m_ParQueue;
PostParChecker 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);
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 UpdateParProgress();
void StartParJob(PostInfo* pPostInfo);
#endif
public:
@@ -105,6 +152,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
void ScanNZBDir();
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
};
#endif

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-2010 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
@@ -29,69 +29,94 @@
#include <deque>
#include <list>
#include <time.h>
#include "Thread.h"
#include "NZBFile.h"
#include "ArticleDownloader.h"
#include "DownloadInfo.h"
#include "Observer.h"
#include "DiskState.h"
#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:
DownloadQueue m_DownloadQueue;
ActiveDownloads m_ActiveDownloads;
DiskState m_DiskState;
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
// statistics
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;
int m_iSpeedBytesIndex;
long long m_iAllBytes;
time_t m_tStartServer;
time_t m_tLastCheck;
time_t m_tStartDownload;
time_t m_tPausedFrom;
bool m_bStandBy;
Mutex m_mutexStat;
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
FileInfo* FindFileInfo(int iID);
int GetFileInfoEntry(int iID);
void DeleteFileInfo(FileInfo* pFileInfo);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
void AdjustStartTime();
public:
QueueCoordinator();
virtual ~QueueCoordinator();
virtual void Run();
virtual void Stop();
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
void Update(Subject* Caller, void* Aspect);
// statistics
long long CalcRemainingSize();
virtual float 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);
bool EditQueuePauseUnpauseEntry(int iID, bool bPause);
bool EditQueueDeleteEntry(int iID);
bool EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection);
int GetFileInfoID(unsigned int iEntry);
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 MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
void DiscardDiskFile(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
void LogDebugInfo();
};

768
QueueEditor.cpp Normal file
View File

@@ -0,0 +1,768 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 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 <cctype>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "DownloadInfo.h"
#include "QueueEditor.h"
#include "QueueCoordinator.h"
#include "DiskState.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
const int MAX_ID = 100000000;
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
{
m_pFileInfo = pFileInfo;
m_iOffset = iOffset;
}
QueueEditor::QueueEditor()
{
debug("Creating QueueEditor");
}
QueueEditor::~QueueEditor()
{
debug("Destroying QueueEditor");
}
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
{
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetID() == iID)
{
return pFileInfo;
}
}
return NULL;
}
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
{
int iEntry = 0;
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (pFileInfo2 == pFileInfo)
{
return iEntry;
}
iEntry ++;
}
return -1;
}
/*
* Set the pause flag of the specific entry in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
{
pFileInfo->SetPaused(bPause);
}
/*
* Removes entry with index iEntry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
}
/*
* Moves entry identified with iID in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
{
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
if (iEntry > -1)
{
int iNewEntry = iEntry + iOffset;
if (iNewEntry < 0)
{
iNewEntry = 0;
}
if ((unsigned int)iNewEntry > pDownloadQueue->GetFileQueue()->size() - 1)
{
iNewEntry = (int)pDownloadQueue->GetFileQueue()->size() - 1;
}
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->GetFileQueue()->size() - 1)
{
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, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return EditList(&cIDList, bSmartOrder, eAction, iOffset, szText);
}
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, szText);
}
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
return bOK;
}
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)
{
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
}
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
{
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
}
else if (eAction == eaGroupMerge)
{
MergeGroups(pDownloadQueue, &cItemList);
}
else
{
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
switch (eAction)
{
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
}
}
return cItemList.size() > 0;
}
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
EEditAction EEditAction, int iOffset)
{
if (EEditAction == eaFileMoveTop)
{
iOffset = -MAX_ID;
}
else if (EEditAction == eaFileMoveBottom)
{
iOffset = MAX_ID;
}
pItemList->reserve(pIDList->size());
if (bSmartOrder && iOffset != 0 &&
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
{
//add IDs to list in order they currently have in download queue
int iLastDestPos = -1;
int iStart, iEnd, iStep;
if (iOffset < 0)
{
iStart = 0;
iEnd = pDownloadQueue->GetFileQueue()->size();
iStep = 1;
}
else
{
iStart = pDownloadQueue->GetFileQueue()->size() - 1;
iEnd = -1;
iStep = -1;
}
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
{
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iIndex);
int iID = pFileInfo->GetID();
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
{
if (iID == *it)
{
int iWorkOffset = iOffset;
int iDestPos = iIndex + iWorkOffset;
if (iLastDestPos == -1)
{
if (iDestPos < 0)
{
iWorkOffset = -iIndex;
}
else if (iDestPos > int(pDownloadQueue->GetFileQueue()->size()) - 1)
{
iWorkOffset = int(pDownloadQueue->GetFileQueue()->size()) - 1 - iIndex;
}
}
else
{
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
{
iWorkOffset = iLastDestPos - iIndex + 1;
}
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
{
iWorkOffset = iLastDestPos - iIndex - 1;
}
}
iLastDestPos = iIndex + iWorkOffset;
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
break;
}
}
}
}
else
{
// check ID range
int iMaxID = 0;
int iMinID = MAX_ID;
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
int ID = pFileInfo->GetID();
if (ID > iMaxID)
{
iMaxID = ID;
}
if (ID < iMinID)
{
iMinID = ID;
}
}
//add IDs to list in order they were transmitted in command
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
{
int iID = *it;
if (iMinID <= iID && iID <= iMaxID)
{
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
if (pFileInfo)
{
pItemList->push_back(new EditItem(pFileInfo, iOffset));
}
}
}
}
}
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
{
IDList cIDList;
cIDList.clear();
// collecting files belonging to group
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
}
}
if (eAction == eaGroupMoveOffset)
{
// calculating offset in terms of files
FileList cGroupList;
BuildGroupList(pDownloadQueue, &cGroupList);
unsigned int iNum = 0;
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pGroupInfo = *it;
if (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
break;
}
}
int iFileOffset = 0;
if (iOffset > 0)
{
if (iNum + iOffset >= cGroupList.size() - 1)
{
eAction = eaGroupMoveBottom;
}
else
{
for (unsigned int i = iNum + 2; i < cGroupList.size() && iOffset > 0; i++, iOffset--)
{
iFileOffset += FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
}
}
}
else
{
if (iNum + iOffset <= 0)
{
eAction = eaGroupMoveTop;
}
else
{
for (unsigned int i = iNum; i > 0 && iOffset < 0; i--, iOffset++)
{
iFileOffset -= FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
}
}
}
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)0, (EEditAction)0, (EEditAction)0 };
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, NULL);
}
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
{
pGroupList->clear();
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 (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupList->push_back(pFileInfo);
}
}
}
bool QueueEditor::ItemExists(FileList* pFileList, FileInfo* pFileInfo)
{
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
if (*it == pFileInfo)
{
return true;
}
}
return false;
}
void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset)
{
// Build list of all groups; List contains first file of each group
FileList cGroupList;
BuildGroupList(pDownloadQueue, &cGroupList);
// Find affected groups. It includes groups being moved and groups directly
// above or under of these groups (those order is also changed)
FileList cAffectedGroupList;
cAffectedGroupList.clear();
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eaFileMoveOffset, iOffset);
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
unsigned int iNum = 0;
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pFileInfo = *it;
if (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (!ItemExists(&cAffectedGroupList, pFileInfo))
{
cAffectedGroupList.push_back(pFileInfo);
}
if (iOffset < 0)
{
for (int i = iNum - 1; i >= -iOffset-1; i--)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
cAffectedGroupList.push_back(cGroupList[i]);
}
}
}
if (iOffset > 0)
{
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
cAffectedGroupList.push_back(cGroupList[i]);
}
}
if (iNum + 1 < cGroupList.size())
{
cAffectedGroupList.push_back(cGroupList[iNum + 1]);
}
}
break;
}
}
delete pItem;
}
cGroupList.clear();
// Aligning groups
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
{
FileInfo* pFileInfo = *it;
AlignGroup(pDownloadQueue, pFileInfo);
}
}
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
{
FileInfo* pLastFileInfo = NULL;
unsigned int iLastNum = 0;
unsigned int iNum = 0;
while (iNum < pDownloadQueue->GetFileQueue()->size())
{
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (pLastFileInfo && iNum - iLastNum > 1)
{
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iNum);
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iLastNum + 1, pFileInfo);
iLastNum++;
}
else
{
iLastNum = iNum;
}
pLastFileInfo = pFileInfo;
}
iNum++;
}
}
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
{
while (true)
{
FileList GroupFileList;
GroupFileList.clear();
FileInfo* pFirstFileInfo = NULL;
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
{
EditItem* pItem = *it;
if (!pFirstFileInfo ||
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
{
GroupFileList.push_back(pItem->m_pFileInfo);
if (!pFirstFileInfo)
{
pFirstFileInfo = pItem->m_pFileInfo;
}
delete pItem;
pItemList->erase(it);
it = pItemList->begin();
continue;
}
it++;
}
if (!GroupFileList.empty())
{
PausePars(&GroupFileList, bExtraParsOnly);
}
else
{
break;
}
}
}
/**
* If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files.
* If the parameter "bExtraParsOnly" is set to "true", we use the following strategy:
* At first we find all par-files, which do not have "vol" in their names, then we pause
* all vols and do not affect all just-pars.
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
* and do not affect it, but pause all other pars.
*/
void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
{
debug("QueueEditor: Pausing pars");
FileList Pars, Vols;
Pars.clear();
Vols.clear();
for (FileList::iterator it = pFileList->begin(); it != pFileList->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"))
{
if (!bExtraParsOnly)
{
pFileInfo->SetPaused(true);
}
else
{
if (strstr(szLoFileName, ".vol"))
{
Vols.push_back(pFileInfo);
}
else
{
Pars.push_back(pFileInfo);
}
}
}
}
if (bExtraParsOnly)
{
if (!Pars.empty())
{
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPaused(true);
}
}
else
{
// pausing all Vol-files except the smallest one
FileInfo* pSmallest = NULL;
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
{
FileInfo* pFileInfo = *it;
if (!pSmallest)
{
pSmallest = pFileInfo;
}
else if (pSmallest->GetSize() > pFileInfo->GetSize())
{
pSmallest->SetPaused(true);
pSmallest = pFileInfo;
}
else
{
pFileInfo->SetPaused(true);
}
}
}
}
}
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);
}
/**
* 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" needs the first file item as parameter)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pDestItem->m_pFileInfo->GetNZBInfo())
{
AlignGroup(pDownloadQueue, pFileInfo);
break;
}
}
delete pDestItem;
}
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", Util::BaseFileName(pNZBInfo->GetFilename()), szParamString);
}
free(szStr);
}

104
QueueEditor.h Normal file
View File

@@ -0,0 +1,104 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2007-2009 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 QUEUEEDITOR_H
#define QUEUEEDITOR_H
#include <vector>
#include "DownloadInfo.h"
class QueueEditor
{
public:
enum EEditAction
{
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eaFileMoveTop,
eaFileMoveBottom,
eaFilePause,
eaFileResume,
eaFileDelete,
eaFilePauseAllPars,
eaFilePauseExtraPars,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
eaGroupPause,
eaGroupResume,
eaGroupDelete,
eaGroupPauseAllPars,
eaGroupPauseExtraPars,
eaGroupSetCategory,
eaGroupMerge,
eaGroupSetParameter
};
private:
class EditItem
{
public:
int m_iOffset;
FileInfo* m_pFileInfo;
EditItem(FileInfo* pFileInfo, int iOffset);
};
typedef std::vector<EditItem*> ItemList;
typedef std::vector<FileInfo*> FileList;
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, 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);
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 PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
void PausePars(FileList* pFileList, bool bExtraParsOnly);
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void MergeGroups(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);
public:
QueueEditor();
~QueueEditor();
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
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

262
README
View File

@@ -30,23 +30,23 @@ Then you use client to send requests to server. The sample requests
are: download nzb-file, list files in queue, etc.
Standalone-tool, server and client are all contained in only one
executable file "NZBGet". The mode in which the program works
executable file "nzbget". The mode in which the program works
depends on command-line parameters passed to the program.
=====================================
2. Supported OS
=====================================
NZBGet is written in C++ and was initialy developen on Linux.
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.0) should run at least on:
- Linux Debian 3.1 on x86;
- Linux BusyBox with uClibc on MIPSEL;
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
- Solaris 10 on x86;
The current version (0.7.0) should run at least on:
- Linux Debian 4.0 on x86;
- Linux with uClibc and BusyBox on MIPSEL and ARM;
- FreeBSD 7.0 on x86;
- OpenSolaris 2008.11 on x86;
- Mac OS X;
- Windows XP SP2 on x86.
Clients and servers running on different OS'es may communicate with
@@ -54,8 +54,9 @@ each other. For example, you can use NZBGet as client on Windows to
control your NZBGet-server running on Linux.
The download-section of NZBGet web-site provides binary files
for few platforms (including Windows), but for most POSIX-systems
you need to compile the program yourself.
for Windows. The binary packages for many routers and NAS devices are
also available in OPTWARE repository (http://www.nslu2-linux.org),
but for most POSIX-systems you need to compile the program yourself.
If you have downloaded binaries you can just jump to section
"Configuration".
@@ -65,8 +66,7 @@ If you have downloaded binaries you can just jump to section
=====================================
NZBGet is developed on a linux-system, but it should run on other
POSIX platforms (tested on FreeBSD and Solaris for x86; although
the par-support and uulib were not tested).
POSIX platforms (see the list of tested platforms above).
NZBGet absolutely needs the following libraries:
@@ -75,22 +75,25 @@ NZBGet absolutely needs the following libraries:
And the following libraries are optional:
- for curses-output-mode:
- for curses-output-mode (enabled by default):
- libcurses (usually part of commercial systems)
or (better)
- libncurses (http://invisible-island.net/ncurses)
- for par-check and -repair:
- for par-check and -repair (enabled by default):
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for support of encoding-formats other than yEnc:
- libuu (http://www.fpx.de/fp/Software/UUDeview)
- for encrypted connections (TLS/SSL):
- GnuTLS (http://www.gnu.org/software/gnutls)
or
- OpenSSL (http://www.openssl.org)
All these libraries are included in modern Linux distributions and
should be available as installable packages. On other systems you
may need to download the libraries at the given URLs and compile
them (see hints below).
should be available as installable packages. Please note that you also
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
@@ -105,11 +108,10 @@ Well, the usual stuff:
- configure it via
./configure
(maybe you have to tell configure, where to find some libraries.
./configure --help is your friend! ;-)
./configure --help is your friend!)
also see "Configure-options" later.)
- compile it via
make
(you may get some warnings concerning 'mktemp', simply ignore them!)
- become root via
su
- install it via
@@ -119,80 +121,30 @@ 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-parprogress - to not show progress during par-check. This option
may be needed on some systems, where sigc++ does not work very well
(uClibc on MIPSEL is one of them). If par-check segfaults right after
start, then you probably need this option.
--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 +153,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 +187,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,15 +223,23 @@ 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
@@ -277,6 +279,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,41 +312,61 @@ 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:
nzbget -C
The client have three different (display) outputmodes, which you can select
in configuration file (on client computer) or in command line. Try them:
nzbget -o outputmode=log -C
nzbget -o outputmode=color -C
nzbget -o outputmode=curses -C
To list files in server's queue:
nzbget -L
It prints something like:
[1] nzbname\filename1.rar (50.00 MB)
[2] nzbname\filename1.r01 (50.00 MB)
[1] nzbname\filename1.rar (50.00 MB)
[2] nzbname\filename1.r01 (50.00 MB)
The numbers in square braces are ID's of files in queue. They can be used
in edit-command. For example to move file with ID 2 to the top of queue:
nzbget -E T -I 2
nzbget -E T 2
or to pause files with IDs from 10 to 20:
nzbget -E P -I 10-20
nzbget -E P 10-20
or to delete file from queue:
or to delete files from queue:
nzbget -E D -I 3
nzbget -E D 3 10-15 20-21 16
The edit-command has also a group-mode which affects all files from the
same nzb-request. You need to pass one ID of any file in the group. For
example to delete all files from the first nzb-request:
nzbget -E G D 1
The switch "o" is useful to override options in configuration files.
For example:
@@ -371,34 +399,58 @@ 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.
Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-process-script,
defined in configuration file. See example configuration file for the
description of parameters passed to the script.
An example script for unraring of downloaded files is provided in file
postprocess-example.sh. The usage instructions are included in the file,
please open the file in any text editor to read them.
NOTE: That example script is for POSIX systems (not for Windows).
=====================================
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).
Up to version 0.2.3 it was 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).
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).
=====================================
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)
libpar2 is distributed under GPL and libsigc++ under LGPL.
libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
=====================================
10. Contact
=====================================
If you encounter any problem, feel free to use tracker/forums on
If you encounter any problem, feel free to use forums on
sourceforge.net/projects/nzbget

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 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
@@ -30,6 +30,7 @@
#include "Options.h"
#include "MessageBase.h"
#include "Connection.h"
#include "DownloadInfo.h"
class RemoteClient
{
@@ -39,23 +40,29 @@ private:
bool m_bVerbose;
bool InitConnection();
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
void ReceiveCommandResult();
void printf(char* msg, ...);
void perror(char* msg);
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
bool ReceiveBoolResponse();
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 RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst);
bool RequestServerList(bool bFiles, bool bGroups);
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder);
bool RequestServerLog(int iLines);
bool RequestServerShutdown();
bool RequestServerVersion();
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
bool RequestScan();
bool RequestHistory();
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-2009 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
@@ -43,19 +43,12 @@
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
#include "QueueCoordinator.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern void ExitProc();
const char* g_szMessageRequests[] =
{ "N/A", "Download", "Pause/Unpause", "List",
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
};
//*****************************************************************
// RemoteServer
@@ -65,49 +58,64 @@ RemoteServer::RemoteServer()
debug("Creating RemoteServer");
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection = NULL;
}
RemoteServer::~RemoteServer()
{
debug("Destroying RemoteServer");
if (m_pConnection)
{
delete m_pConnection;
}
delete m_pNetAddress;
delete m_pConnection;
}
void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
m_pConnection->Bind();
while (!IsStopped())
{
// Accept connections and store the "new" socket value
SOCKET iSocket = m_pConnection->Accept();
if (iSocket == INVALID_SOCKET)
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(m_pNetAddress);
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;
if (bBind)
{
iSocket = m_pConnection->Accept();
}
if (!bBind || iSocket == INVALID_SOCKET)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
{
break;
}
// error binding on port. wait 0.5 sec. and retry
usleep(500 * 1000);
delete m_pConnection;
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->Bind();
m_pConnection = NULL;
continue;
}
MessageCommand* commandThread = new MessageCommand();
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetSocket(iSocket);
commandThread->Start();
}
m_pConnection->Disconnect();
if (m_pConnection)
{
m_pConnection->Disconnect();
}
debug("Exiting RemoteServer-loop");
}
@@ -115,413 +123,108 @@ void RemoteServer::Run()
void RemoteServer::Stop()
{
Thread::Stop();
m_pConnection->Cancel();
if (m_pConnection)
{
m_pConnection->SetSuppressErrors(true);
m_pConnection->Cancel();
#ifdef WIN32
m_pConnection->Disconnect();
m_pConnection->Disconnect();
#endif
}
}
//*****************************************************************
// MessageCommand
// RequestProcessor
void MessageCommand::SendResponse(char* szAnswer)
void RequestProcessor::Run()
{
send(m_iSocket, szAnswer, strlen(szAnswer), 0);
}
void MessageCommand::ProcessRequest()
{
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
switch (pMessageBase->m_iType)
// Read the first 4 bytes to determine request type
bool bOK = false;
int iSignature = 0;
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
if (iBytesReceived < 0)
{
case NZBMessageRequest::eRequestDownload:
{
RequestDownload();
break;
}
case NZBMessageRequest::eRequestList:
{
RequestList();
break;
}
case NZBMessageRequest::eRequestLog:
{
RequestLog();
break;
}
case NZBMessageRequest::eRequestPauseUnpause:
{
SNZBPauseUnpauseRequest* pPauseUnpauseRequest = (SNZBPauseUnpauseRequest*) & m_RequestBuffer;
g_pOptions->SetPause(pPauseUnpauseRequest->m_bPause);
SendResponse("Pause-/Unpause-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestEditQueue:
{
RequestEditQueue();
break;
}
case NZBMessageRequest::eRequestSetDownloadRate:
{
SNZBSetDownloadRateRequest* pSetDownloadRequest = (SNZBSetDownloadRateRequest*) & m_RequestBuffer;
g_pOptions->SetDownloadRate(pSetDownloadRequest->m_fDownloadRate);
SendResponse("Rate-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestDumpDebug:
{
g_pQueueCoordinator->LogDebugInfo();
SendResponse("Debug-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestShutdown:
{
SendResponse("Stopping server");
ExitProc();
break;
}
default:
error("NZB-Request not yet supported");
break;
}
}
void MessageCommand::RequestDownload()
{
SNZBDownloadRequest* pDownloadRequest = (SNZBDownloadRequest*) & m_RequestBuffer;
const char* pExtraData = (m_iExtraDataLength > 0) ? ((char*)pDownloadRequest + pDownloadRequest->m_MessageBase.m_iSize) : NULL;
int NeedBytes = pDownloadRequest->m_iTrailingDataLength - m_iExtraDataLength;
char* pRecvBuffer = (char*)malloc(pDownloadRequest->m_iTrailingDataLength + 1);
memcpy(pRecvBuffer, pExtraData, m_iExtraDataLength);
char* pBufPtr = pRecvBuffer + m_iExtraDataLength;
// Read from the socket until nothing remains
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;
}
if (NeedBytes == 0)
{
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(pDownloadRequest->m_szFilename, pRecvBuffer, pDownloadRequest->m_iTrailingDataLength);
if (pNZBFile)
{
info("Request: Queue collection %s", pDownloadRequest->m_szFilename);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pDownloadRequest->m_bAddFirst);
delete pNZBFile;
char tmp[1024];
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(pDownloadRequest->m_szFilename));
tmp[1024-1] = '\0';
SendResponse(tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(pDownloadRequest->m_szFilename));
tmp[1024-1] = '\0';
SendResponse(tmp);
}
}
free(pRecvBuffer);
}
void MessageCommand::RequestList()
{
SNZBListRequest* pListRequest = (SNZBListRequest*) & m_RequestBuffer;
SNZBListRequestAnswer ListRequestAnswer;
memset(&ListRequestAnswer, 0, sizeof(ListRequestAnswer));
ListRequestAnswer.m_iSize = sizeof(ListRequestAnswer);
ListRequestAnswer.m_iEntrySize = sizeof(SNZBListRequestAnswerEntry);
char* buf = NULL;
int bufsize = 0;
if (pListRequest->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(SNZBListRequestAnswerEntry);
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++)
{
FileInfo* pFileInfo = *it;
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) bufptr;
pListAnswer->m_iID = pFileInfo->GetID();
pListAnswer->m_iFileSize = (int)pFileInfo->GetSize();
pListAnswer->m_bFilenameConfirmed = pFileInfo->GetFilenameConfirmed();
pListAnswer->m_iRemainingSize = (int)pFileInfo->GetRemainingSize();
pListAnswer->m_bPaused = pFileInfo->GetPaused();
pListAnswer->m_iNZBFilenameLen = strlen(pFileInfo->GetNZBFilename()) + 1;
pListAnswer->m_iSubjectLen = strlen(pFileInfo->GetSubject()) + 1;
pListAnswer->m_iFilenameLen = strlen(pFileInfo->GetFilename()) + 1;
pListAnswer->m_iDestDirLen = strlen(pFileInfo->GetDestDir()) + 1;
bufptr += sizeof(SNZBListRequestAnswerEntry);
strcpy(bufptr, pFileInfo->GetNZBFilename());
bufptr += pListAnswer->m_iNZBFilenameLen;
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += pListAnswer->m_iSubjectLen;
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += pListAnswer->m_iFilenameLen;
strcpy(bufptr, pFileInfo->GetDestDir());
bufptr += pListAnswer->m_iDestDirLen;
}
g_pQueueCoordinator->UnlockQueue();
ListRequestAnswer.m_iNrTrailingEntries = NrEntries;
ListRequestAnswer.m_iTrailingDataLength = bufsize;
}
if (pListRequest->m_bServerState)
{
ListRequestAnswer.m_fDownloadRate = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
ListRequestAnswer.m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
ListRequestAnswer.m_fDownloadLimit = g_pOptions->GetDownloadRate();
ListRequestAnswer.m_bServerPaused = g_pOptions->GetPause();
ListRequestAnswer.m_iThreadCount = Thread::GetThreadCount() - 1; // not counting itself
}
// Send the request answer
send(m_iSocket, (char*) &ListRequestAnswer, sizeof(ListRequestAnswer), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
if (buf)
{
free(buf);
}
}
void MessageCommand::RequestLog()
{
SNZBLogRequest* pLogRequest = (SNZBLogRequest*) & m_RequestBuffer;
Log::Messages* pMessages = g_pLog->LockMessages();
int iNrEntries = pLogRequest->m_iLines;
unsigned int iIDFrom = pLogRequest->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(SNZBLogRequestAnswerEntry);
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];
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) bufptr;
pLogAnswer->m_iID = pMessage->GetID();
pLogAnswer->m_iKind = pMessage->GetKind();
pLogAnswer->m_tTime = pMessage->GetTime();
pLogAnswer->m_iTextLen = strlen(pMessage->GetText()) + 1;
bufptr += sizeof(SNZBLogRequestAnswerEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += pLogAnswer->m_iTextLen;
}
g_pLog->UnlockMessages();
SNZBLogRequestAnswer LogRequestAnswer;
LogRequestAnswer.m_iSize = sizeof(LogRequestAnswer);
LogRequestAnswer.m_iEntrySize = sizeof(SNZBLogRequestAnswerEntry);
LogRequestAnswer.m_iNrTrailingEntries = iNrEntries;
LogRequestAnswer.m_iTrailingDataLength = bufsize;
// Send the request answer
send(m_iSocket, (char*) &LogRequestAnswer, sizeof(LogRequestAnswer), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}
void MessageCommand::RequestEditQueue()
{
SNZBEditQueueRequest* pEditQueueRequest = (SNZBEditQueueRequest*) & m_RequestBuffer;
int From = pEditQueueRequest->m_iIDFrom;
int To = pEditQueueRequest->m_iIDTo;
int Step = 1;
if ((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveTop) ||
((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveOffset) &&
(pEditQueueRequest->m_iOffset < 0)))
{
Step = -1;
int tmp = From; From = To; To = tmp;
}
for (int ID = From; ID != To + Step; ID += Step)
{
switch (pEditQueueRequest->m_iAction)
{
case NZBMessageRequest::eActionPause:
case NZBMessageRequest::eActionResume:
{
g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, pEditQueueRequest->m_iAction == NZBMessageRequest::eActionPause);
break;
}
case NZBMessageRequest::eActionMoveOffset:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, pEditQueueRequest->m_iOffset, true);
break;
}
case NZBMessageRequest::eActionMoveTop:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
break;
}
case NZBMessageRequest::eActionMoveBottom:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, + 1000000, true);
break;
}
case NZBMessageRequest::eActionDelete:
{
g_pQueueCoordinator->EditQueueDeleteEntry(ID);
break;
}
}
}
SendResponse("Edit-Command completed successfully");
}
void MessageCommand::SetSocket(SOCKET iSocket)
{
m_iSocket = iSocket;
}
void MessageCommand::Run()
{
int iRequestReceived = 0;
// Read the first package which needs to be a request
iRequestReceived = recv(m_iSocket, (char*) & m_RequestBuffer, sizeof(m_RequestBuffer), 0);
if (iRequestReceived < 0)
{
return;
}
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
// Make sure this is a nzbget request from a client
if (pMessageBase->m_iId != NZBMESSAGE_SIGNATURE)
{
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
if (strcmp(pMessageBase->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
#ifdef WIN32
char* ip = NULL;
#else
char ip[20];
#endif
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
char* ip = inet_ntoa(PeerName.sin_addr);
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_szMessageRequests[pMessageBase->m_iType], ip);
}
m_iExtraDataLength = iRequestReceived - pMessageBase->m_iSize;
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
{
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
{
// XML-RPC or JSON-RPC request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
eHttpMethod = XmlRpcProcessor::hmPost;
szUrl++;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
ProcessRequest();
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
{
eProtocol = XmlRpcProcessor::rpXmlRpc;
}
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
{
eProtocol = XmlRpcProcessor::rpJsonRpc;
}
else if (!strcmp(szUrl, "/jsonprpc") || !strncmp(szUrl, "/jsonprpc/", 10))
{
eProtocol = XmlRpcProcessor::rpJsonPRpc;
}
if (eProtocol != XmlRpcProcessor::rpUndefined)
{
XmlRpcProcessor processor;
processor.SetConnection(&con);
processor.SetClientIP(ip);
processor.SetProtocol(eProtocol);
processor.SetHttpMethod(eHttpMethod);
processor.SetUrl(szUrl);
processor.Execute();
bOK = true;
}
}
}
if (!bOK)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
}
// Close the socket
closesocket(m_iSocket);
}

View File

@@ -1,5 +1,5 @@
/*
* 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>
@@ -30,9 +30,6 @@
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
#include "MessageBase.h"
static int const REQUESTBUFFERSIZE = 8192;
class RemoteServer : public Thread
{
@@ -47,23 +44,14 @@ public:
virtual void Stop();
};
class MessageCommand : public Thread
class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
char m_RequestBuffer[REQUESTBUFFERSIZE];
int m_iExtraDataLength;
void ProcessRequest();
void RequestDownload();
void RequestList();
void RequestLog();
void RequestEditQueue();
void SendResponse(char* szAnswer);
public:
void SetSocket(SOCKET iSocket);
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
#endif

363
Scanner.cpp Normal file
View File

@@ -0,0 +1,363 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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 <fstream>
#ifdef WIN32
#include <direct.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_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;
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
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;
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
{
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory, &szNZBCategory, 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, pParameterList);
}
else
{
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
AddFileToQueue(szFullFilename, szNZBCategory, 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, 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());
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
info("Collection %s added to queue", szBasename);
}
if (pNZBFile)
{
delete pNZBFile;
}
}
void Scanner::ScanNZBDir()
{
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
// but it's not critical here.
m_bRequestedNZBDirScan = true;
}

76
Scanner.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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 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;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
void AddFileToQueue(const char* szFilename, const char* szCategory, 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();
void Check();
};
#endif

246
Scheduler.cpp Normal file
View File

@@ -0,0 +1,246 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2009 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"
#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 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 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

917
ScriptController.cpp Normal file
View File

@@ -0,0 +1,917 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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 <ctype.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include "nzbget.h"
#include "ScriptController.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
extern char* (*szEnvironmentVariables)[];
extern DownloadQueueHolder* g_pDownloadQueueHolder;
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
static const int POSTPROCESS_PARCHECK_ALL = 92;
static const int POSTPROCESS_SUCCESS = 93;
static const int POSTPROCESS_ERROR = 94;
static const int POSTPROCESS_NONE = 95;
#ifndef WIN32
#define CHILD_WATCHDOG 1
#endif
#ifdef CHILD_WATCHDOG
/**
* Sometimes the forked child process doesn't start properly and hangs
* just during the starting. I didn't find any explanation about what
* could cause that problem except of a general advice, that
* "a forking in a multithread application is not recommended".
*
* Workaround:
* 1) child process prints a line into stdout directly after the start;
* 2) parent process waits for a line for 60 seconds. If it didn't receive it
* the cild process assumed to hang and will be killed. Another attempt
* will be made.
*/
class ChildWatchDog : public Thread
{
private:
pid_t m_hProcessID;
protected:
virtual void Run();
public:
void SetProcessID(pid_t hProcessID) { m_hProcessID = hProcessID; }
};
void ChildWatchDog::Run()
{
static const int WAIT_SECONDS = 60;
time_t tStart = time(NULL);
while (!IsStopped() && (time(NULL) - tStart) < WAIT_SECONDS)
{
usleep(10 * 1000);
}
if (!IsStopped())
{
info("Restarting hanging child process");
kill(m_hProcessID, SIGKILL);
}
}
#endif
EnvironmentStrings::EnvironmentStrings()
{
}
EnvironmentStrings::~EnvironmentStrings()
{
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
free(*it);
}
m_strings.clear();
}
void EnvironmentStrings::InitFromCurrentProcess()
{
for (int i = 0; (*szEnvironmentVariables)[i]; i++)
{
char* szVar = (*szEnvironmentVariables)[i];
Append(strdup(szVar));
}
}
void EnvironmentStrings::Append(char* szString)
{
m_strings.push_back(szString);
}
#ifdef WIN32
/*
* Returns environment block in format suitable for using with CreateProcess.
* The allocated memory must be freed by caller using "free()".
*/
char* EnvironmentStrings::GetStrings()
{
int iSize = 1;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
iSize += strlen(szVar) + 1;
}
char* szStrings = (char*)malloc(iSize);
char* szPtr = szStrings;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
strcpy(szPtr, szVar);
szPtr += strlen(szVar) + 1;
}
*szPtr = '\0';
return szStrings;
}
#else
/*
* Returns environment block in format suitable for using with execve
* The allocated memory must be freed by caller using "free()".
*/
char** EnvironmentStrings::GetStrings()
{
char** pStrings = (char**)malloc((m_strings.size() + 1) * sizeof(char*));
char** pPtr = pStrings;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
*pPtr = szVar;
pPtr++;
}
*pPtr = NULL;
return pStrings;
}
#endif
ScriptController::ScriptController()
{
m_szScript = NULL;
m_szWorkingDir = NULL;
m_szArgs = NULL;
m_bFreeArgs = false;
m_szInfoName = NULL;
m_szDefaultKindPrefix = NULL;
m_bTerminated = false;
m_environmentStrings.InitFromCurrentProcess();
}
ScriptController::~ScriptController()
{
if (m_bFreeArgs)
{
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
free((char*)*szArgPtr);
}
free(m_szArgs);
}
}
void ScriptController::SetEnvVar(const char* szName, const char* szValue)
{
int iLen = strlen(szName) + strlen(szValue) + 2;
char* szVar = (char*)malloc(iLen);
snprintf(szVar, iLen, "%s=%s", szName, szValue);
m_environmentStrings.Append(szVar);
}
void ScriptController::PrepareEnvironmentStrings()
{
Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries();
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
{
Options::OptEntry* pOptEntry = *it;
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBOP_%s", pOptEntry->GetName());
// convert to upper case; replace "." with "_".
for (char* szPtr = szVarname; *szPtr; szPtr++)
{
if (*szPtr == '.')
{
*szPtr = '_';
}
*szPtr = toupper(*szPtr);
}
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pOptEntry->GetValue());
}
g_pOptions->UnlockOptEntries();
}
int ScriptController::Execute()
{
if (!Util::FileExists(m_szScript))
{
error("Could not start %s: could not find file %s", m_szInfoName, m_szScript);
return -1;
}
PrepareEnvironmentStrings();
int iExitCode = 0;
int pipein;
#ifdef CHILD_WATCHDOG
bool bChildConfirmed = false;
while (!bChildConfirmed && !m_bTerminated)
{
#endif
#ifdef WIN32
// build command line
char szCmdLine[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
snprintf(szCmdLine + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
}
szCmdLine[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
// create pipes to write and read data
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES SecurityAttributes;
memset(&SecurityAttributes, 0, sizeof(SecurityAttributes));
SecurityAttributes.nLength = sizeof(SecurityAttributes);
SecurityAttributes.bInheritHandle = TRUE;
CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0);
STARTUPINFO StartupInfo;
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
StartupInfo.hStdInput = 0;
StartupInfo.hStdOutput = hWritePipe;
StartupInfo.hStdError = hWritePipe;
PROCESS_INFORMATION ProcessInfo;
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
char* szEnvironmentStrings = m_environmentStrings.GetStrings();
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, szEnvironmentStrings, m_szWorkingDir, &StartupInfo, &ProcessInfo);
if (!bOK)
{
DWORD dwErrCode = GetLastError();
char szErrMsg[255];
szErrMsg[255-1] = '\0';
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL, dwErrCode, 0, szErrMsg, 255, NULL))
{
error("Could not start %s: %s", m_szInfoName, szErrMsg);
}
else
{
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
}
free(szEnvironmentStrings);
return -1;
}
free(szEnvironmentStrings);
debug("Child Process-ID: %i", (int)ProcessInfo.dwProcessId);
m_hProcess = ProcessInfo.hProcess;
// close unused "write" end
CloseHandle(hWritePipe);
pipein = _open_osfhandle((intptr_t)hReadPipe, _O_RDONLY);
#else
int p[2];
int pipeout;
// create the pipe
if (pipe(p))
{
error("Could not open pipe: errno %i", errno);
return -1;
}
char** pEnvironmentStrings = m_environmentStrings.GetStrings();
pipein = p[0];
pipeout = p[1];
debug("forking");
pid_t pid = fork();
if (pid == -1)
{
error("Could not start %s: errno %i", m_szInfoName, errno);
free(pEnvironmentStrings);
return -1;
}
else if (pid == 0)
{
// here goes the second instance
// create new process group (see Terminate() where it is used)
setsid();
// close up the "read" end
close(pipein);
// make the pipeout to be the same as stdout and stderr
dup2(pipeout, 1);
dup2(pipeout, 2);
close(pipeout);
#ifdef CHILD_WATCHDOG
fwrite("\n", 1, 1, stdout);
fflush(stdout);
#endif
execve(m_szScript, (char* const*)m_szArgs, (char* const*)pEnvironmentStrings);
fprintf(stdout, "[ERROR] Could not start script: %s", strerror(errno));
fflush(stdout);
_exit(-1);
}
// continue the first instance
debug("forked");
debug("Child Process-ID: %i", (int)pid);
free(pEnvironmentStrings);
m_hProcess = pid;
// close unused "write" end
close(pipeout);
#endif
// open the read end
FILE* readpipe = fdopen(pipein, "r");
if (!readpipe)
{
error("Could not open pipe to %s", m_szInfoName);
return -1;
}
#ifdef CHILD_WATCHDOG
debug("Creating child watchdog");
ChildWatchDog* pWatchDog = new ChildWatchDog();
pWatchDog->SetAutoDestroy(false);
pWatchDog->SetProcessID(pid);
pWatchDog->Start();
#endif
char* buf = (char*)malloc(10240);
debug("Entering pipe-loop");
while (!feof(readpipe) && !m_bTerminated)
{
if (fgets(buf, 10240, readpipe))
{
#ifdef CHILD_WATCHDOG
if (!bChildConfirmed)
{
bChildConfirmed = true;
pWatchDog->Stop();
debug("Child confirmed");
}
#endif
ProcessOutput(buf);
}
}
debug("Exited pipe-loop");
#ifdef CHILD_WATCHDOG
debug("Destroying WatchDog");
if (!bChildConfirmed)
{
pWatchDog->Stop();
}
while (pWatchDog->IsRunning())
{
usleep(1 * 1000);
}
delete pWatchDog;
#endif
free(buf);
fclose(readpipe);
if (m_bTerminated)
{
warn("Interrupted %s", m_szInfoName);
}
iExitCode = 0;
#ifdef WIN32
WaitForSingleObject(m_hProcess, INFINITE);
DWORD dExitCode = 0;
GetExitCodeProcess(m_hProcess, &dExitCode);
iExitCode = dExitCode;
#else
int iStatus = 0;
waitpid(m_hProcess, &iStatus, 0);
if (WIFEXITED(iStatus))
{
iExitCode = WEXITSTATUS(iStatus);
}
#endif
#ifdef CHILD_WATCHDOG
} // while (!bChildConfirmed && !m_bTerminated)
#endif
if (!m_bTerminated)
{
info("Completed %s", m_szInfoName);
debug("Exit code %i", iExitCode);
}
return iExitCode;
}
void ScriptController::Terminate()
{
debug("Stopping %s", m_szInfoName);
m_bTerminated = true;
#ifdef WIN32
BOOL bOK = TerminateProcess(m_hProcess, -1);
#else
pid_t hKillProcess = m_hProcess;
if (getpgid(hKillProcess) == hKillProcess)
{
// if the child process has its own group (setsid() was successful), kill the whole group
hKillProcess = -hKillProcess;
}
bool bOK = kill(hKillProcess, SIGKILL) == 0;
#endif
if (bOK)
{
debug("Terminated %s", m_szInfoName);
}
else
{
error("Could not terminate %s", m_szInfoName);
}
debug("Stopped %s", m_szInfoName);
}
void ScriptController::ProcessOutput(char* szText)
{
debug("Processing output received from script");
for (char* pend = szText + strlen(szText) - 1; pend >= szText && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
if (strlen(szText) == 0)
{
// skip empty lines
return;
}
if (!strncmp(szText, "[INFO] ", 7))
{
AddMessage(Message::mkInfo, false, g_pOptions->GetInfoTarget(), szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
AddMessage(Message::mkWarning, false, g_pOptions->GetWarningTarget(), szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
AddMessage(Message::mkError, false, g_pOptions->GetErrorTarget(), szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
AddMessage(Message::mkDetail, false, g_pOptions->GetDetailTarget(), szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
AddMessage(Message::mkDebug, false, g_pOptions->GetDebugTarget(), szText + 8);
}
else
{
switch (m_eDefaultLogKind)
{
case Options::slNone:
break;
case Options::slDetail:
AddMessage(Message::mkDetail, true, g_pOptions->GetDetailTarget(), szText);
break;
case Options::slInfo:
AddMessage(Message::mkInfo, true, g_pOptions->GetInfoTarget(), szText);
break;
case Options::slWarning:
AddMessage(Message::mkWarning, true, g_pOptions->GetWarningTarget(), szText);
break;
case Options::slError:
AddMessage(Message::mkError, true, g_pOptions->GetErrorTarget(), szText);
break;
case Options::slDebug:
AddMessage(Message::mkDebug, true, g_pOptions->GetDebugTarget(), szText);
break;
}
}
debug("Processing output received from script - completed");
}
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
{
switch (eKind)
{
case Message::mkDetail:
detail("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkInfo:
info("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkWarning:
warn("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkError:
error("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkDebug:
debug("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
}
}
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
{
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
PostScriptController* pScriptController = new PostScriptController();
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->SetScript(szScript);
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
pScriptController->SetAutoDestroy(false);
pPostInfo->SetScriptThread(pScriptController);
pScriptController->Start();
}
void PostScriptController::Run()
{
// the locking is needed for accessing the memebers of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szParStatus[10];
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
szParStatus[10-1] = '\0';
char szCollectionCompleted[10];
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
szCollectionCompleted[10-1] = '\0';
char szHasFailedParJobs[10];
snprintf(szHasFailedParJobs, 10, "%i", (int)m_bHasFailedParJobs);
szHasFailedParJobs[10-1] = '\0';
char szDestDir[1024];
strncpy(szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
szDestDir[1024-1] = '\0';
char szNZBFilename[1024];
strncpy(szNZBFilename, m_pPostInfo->GetNZBInfo()->GetFilename(), 1024);
szNZBFilename[1024-1] = '\0';
char szParFilename[1024];
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
szParFilename[1024-1] = '\0';
char szCategory[1024];
strncpy(szCategory, m_pPostInfo->GetNZBInfo()->GetCategory(), 1024);
szCategory[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script for %s", m_pPostInfo->GetInfoName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Post-Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[9];
szArgs[0] = GetScript();
szArgs[1] = szDestDir;
szArgs[2] = szNZBFilename;
szArgs[3] = szParFilename;
szArgs[4] = szParStatus;
szArgs[5] = szCollectionCompleted;
szArgs[6] = szHasFailedParJobs;
szArgs[7] = szCategory;
szArgs[8] = NULL;
SetArgs(szArgs, false);
SetEnvVar("NZBPP_DIRECTORY", szDestDir);
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
SetEnvVar("NZBPP_NZBCOMPLETED", szCollectionCompleted);
SetEnvVar("NZBPP_PARFAILED", szHasFailedParJobs);
SetEnvVar("NZBPP_CATEGORY", szCategory);
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pParameter->GetValue());
}
g_pDownloadQueueHolder->UnlockQueue();
int iResult = Execute();
switch (iResult)
{
case POSTPROCESS_SUCCESS:
info("%s sucessful", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
break;
case POSTPROCESS_ERROR:
info("%s failed", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
break;
case POSTPROCESS_NONE:
info("%s skipped", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srNone);
break;
#ifndef DISABLE_PARCHECK
case POSTPROCESS_PARCHECK_ALL:
if (m_pPostInfo->GetParCheck())
{
error("%s requested par-check/repair for all collections, but they were already checked", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else if (!m_bNZBFileCompleted)
{
error("%s requested par-check/repair for all collections, but it was not the call for the last collection", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else
{
info("%s requested par-check/repair for all collections", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
}
break;
case POSTPROCESS_PARCHECK_CURRENT:
if (m_pPostInfo->GetParCheck())
{
error("%s requested par-check/repair for current collection, but it was already checked", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else if (strlen(m_pPostInfo->GetParFilename()) == 0)
{
error("%s requested par-check/repair for current collection, but it doesn't have any par-files", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else
{
info("%s requested par-check/repair for current collection", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpCurrent);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
}
break;
#endif
default:
info("%s terminated with unknown status", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srUnknown);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
{
if (!strncmp(szText, "[HISTORY] ", 10))
{
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
}
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(eKind, szText);
}
}
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = m_pPostInfo->GetStageTime();
time_t tStartTime = m_pPostInfo->GetStartTime();
time_t tWaitTime = time(NULL);
// wait until Post-processor is unpaused
while (g_pOptions->GetPausePostProcess() && !IsStopped())
{
usleep(100 * 1000);
// update time stamps
time_t tDelta = time(NULL) - tWaitTime;
if (tStageTime > 0)
{
m_pPostInfo->SetStageTime(tStageTime + tDelta);
}
if (tStartTime > 0)
{
m_pPostInfo->SetStartTime(tStartTime + tDelta);
}
}
}
}
void PostScriptController::Stop()
{
debug("Stopping post-process-script");
Thread::Stop();
Terminate();
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename,
const char* szDirectory, char** pCategory, NZBParameterList* pParameterList)
{
info("Executing nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
NZBScriptController* pScriptController = new NZBScriptController();
pScriptController->SetScript(szScript);
pScriptController->m_pCategory = pCategory;
pScriptController->m_pParameterList = pParameterList;
char szInfoName[1024];
snprintf(szInfoName, 1024, "nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
szInfoName[1024-1] = '\0';
pScriptController->SetInfoName(szInfoName);
// remove trailing slash
char szDir[1024];
strncpy(szDir, szDirectory, 1024);
szDir[1024-1] = '\0';
int iLen = strlen(szDir);
if (szDir[iLen-1] == PATH_SEPARATOR)
{
szDir[iLen-1] = '\0';
}
pScriptController->SetDefaultKindPrefix("NZB-Process: ");
pScriptController->SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[4];
szArgs[0] = szScript;
szArgs[1] = szDir;
szArgs[2] = szNZBFilename;
szArgs[3] = NULL;
pScriptController->SetArgs(szArgs, false);
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->Execute();
delete pScriptController;
}
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
{
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strncmp(szText + 6, "CATEGORY=", 9))
{
free(*m_pCategory);
*m_pCategory = strdup(szText + 6 + 9);
}
else if (!strncmp(szText + 6, "NZBPR_", 6))
{
char* szParam = strdup(szText + 6 + 6);
char* szValue = strchr(szParam, '=');
if (szValue)
{
*szValue = '\0';
m_pParameterList->SetParameter(szParam, szValue + 1);
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
}
free(szParam);
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
}
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
}
}
void SchedulerScriptController::StartScript(const char* szCommandLine)
{
char** argv = NULL;
if (!Util::SplitCommandLine(szCommandLine, &argv))
{
error("Could not execute scheduled process-script, failed to parse command line: %s", szCommandLine);
return;
}
info("Executing scheduled process-script %s", Util::BaseFileName(argv[0]));
SchedulerScriptController* pScriptController = new SchedulerScriptController();
pScriptController->SetScript(argv[0]);
pScriptController->SetArgs((const char**)argv, true);
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void SchedulerScriptController::Run()
{
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduled process-script %s", Util::BaseFileName(GetScript()));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Scheduled Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
Execute();
}

133
ScriptController.h Normal file
View File

@@ -0,0 +1,133 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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 SCRIPTCONTROLLER_H
#define SCRIPTCONTROLLER_H
#include <list>
#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 ProcessOutput(char* szText);
void PrepareEnvironmentStrings();
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
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, ScriptController
{
private:
PostInfo* m_pPostInfo;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
{
private:
char** m_pCategory;
NZBParameterList* m_pParameterList;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, NZBParameterList* pParameterList);
};
class SchedulerScriptController : public Thread, 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 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
@@ -23,14 +23,6 @@
*
*/
/*
*
* 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>
@@ -51,34 +43,39 @@
#include "ServerPool.h"
#include "Log.h"
static const int CONNECTION_HOLD_SECODNS = 5;
ServerPool::PooledConnection::PooledConnection(NewsServer* server) : NNTPConnection(server)
{
m_bInUse = false;
m_tFreeTime = 0;
}
ServerPool::ServerPool()
{
debug("Creating ServerPool");
m_iMaxLevel = 0;
m_iTimeout = 60;
m_Servers.clear();
m_FreeConnections.clear();
m_Semaphores.clear();
}
ServerPool::~ ServerPool()
{
debug("Destroying ServerPool");
m_FreeConnections.clear();
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++)
{
delete *it;
}
m_Servers.clear();
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
delete *it;
}
m_Connections.clear();
}
void ServerPool::AddServer(NewsServer* pNewsServer)
@@ -102,7 +99,9 @@ void ServerPool::InitConnections()
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
m_FreeConnections.push_back(pNewsServer);
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
}
@@ -118,68 +117,83 @@ void ServerPool::InitConnections()
}
}
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
m_Semaphores.push_back(sem);
m_Levels.push_back(iMaxConnectionsForLevel);
}
}
NNTPConnection* ServerPool::GetConnection(int level)
NNTPConnection* ServerPool::GetConnection(int iLevel)
{
debug("Getting connection");
PooledConnection* pConnection = NULL;
debug("sem_wait...");
// decrease m_Semaphore counter or block
bool bWaitVal = m_Semaphores[level]->Wait();
debug("sem_wait...OK");
m_mutexConnections.Lock();
if (!bWaitVal)
if (m_Levels[iLevel] > 0)
{
debug("semaphore error: %i", errno);
return NULL;
}
m_mutexFree.Lock();
NNTPConnection* pConnection = NULL;
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
{
NewsServer* server = *it;
if (server->GetLevel() == level)
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
// free connection found, take it!
pConnection = new NNTPConnection(server);
pConnection->SetTimeout(m_iTimeout);
m_FreeConnections.erase(it);
break;
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
{
// free connection found, take it!
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
}
}
m_Levels[iLevel]--;
if (!pConnection)
{
error("ServerPool: internal error, no free connection found, but there should be one");
}
}
m_mutexFree.Unlock();
if (!pConnection)
{
error("ServerPool: serious error, no free connection found, but there should be one.");
}
m_mutexConnections.Unlock();
return pConnection;
}
void ServerPool::FreeConnection(NNTPConnection* pConnection)
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
debug("Freeing connection");
if (bUsed)
{
debug("Freeing used connection");
}
// give back free connection
m_mutexFree.Lock();
m_FreeConnections.push_back(pConnection->GetNewsServer());
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
m_mutexFree.Unlock();
m_mutexConnections.Lock();
delete pConnection;
((PooledConnection*)pConnection)->SetInUse(false);
if (bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
m_Levels[pConnection->GetNewsServer()->GetLevel()]++;
m_mutexConnections.Unlock();
}
bool ServerPool::HasFreeConnection()
void ServerPool::CloseUnusedConnections()
{
return !m_Semaphores[0]->IsLocked();
m_mutexConnections.Lock();
time_t curtime = ::time(NULL);
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
pConnection->Disconnect();
}
}
}
m_mutexConnections.Unlock();
}
void ServerPool::LogDebugInfo()
@@ -189,22 +203,13 @@ void ServerPool::LogDebugInfo()
debug(" Max-Level: %i", m_iMaxLevel);
m_mutexFree.Lock();
m_mutexConnections.Lock();
debug(" Free Connections: %i", m_FreeConnections.size());
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
debug(" Free Connection: level=%i", (*it)->GetLevel());
debug(" Connection: Level=%i, InUse:%i", (*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_mutexFree.Unlock();
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 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
@@ -28,7 +27,8 @@
#ifndef SERVERPOOL_H
#define SERVERPOOL_H
#include <list>
#include <vector>
#include <time.h>
#include "Thread.h"
#include "NewsServer.h"
@@ -37,26 +37,40 @@
class ServerPool
{
private:
class PooledConnection : public NNTPConnection
{
private:
bool m_bInUse;
time_t m_tFreeTime;
public:
PooledConnection(NewsServer* server);
bool GetInUse() { return m_bInUse; }
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
time_t GetFreeTime() { return m_tFreeTime; }
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;
Servers m_FreeConnections;
Semaphores m_Semaphores;
Connections m_Connections;
Levels m_Levels;
int m_iMaxLevel;
Mutex m_mutexFree;
Mutex m_mutexConnections;
int m_iTimeout;
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 level);
bool HasFreeConnection();
void FreeConnection(NNTPConnection* con);
NNTPConnection* GetConnection(int iLevel);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();
void LogDebugInfo();
};

1566
TLS.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

212
TLS.h Normal file
View File

@@ -0,0 +1,212 @@
/*
* This file is part of nzbget
*
* Based on "tls.h" from project "mpop" by Martin Lambers
* Original source code available on http://sourceforge.net/projects/mpop/
*
* Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
* Martin Lambers <marlam@marlam.de>
*
* Copyright (C) 2008-2009 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 TLS_H
#define TLS_H
#ifndef DISABLE_TLS
#include <time.h>
#ifdef HAVE_LIBGNUTLS
# include <gnutls/gnutls.h>
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
# include <openssl/ssl.h>
#endif /* HAVE_OPENSSL */
/*
* If a function with an 'errstr' argument returns a value != TLS_EOK,
* '*errstr' either points to an allocates string containing an error
* description or is NULL.
* If such a function returns TLS_EOK, 'errstr' will not be changed.
*/
#define TLS_EOK 0 /* no error */
#define TLS_ELIBFAILED 1 /* The underlying library failed */
#define TLS_ESEED 2 /* Cannot seed pseudo random number generator */
#define TLS_ECERT 3 /* Certificate check or verification failed */
#define TLS_EIO 4 /* Input/output error */
#define TLS_EFILE 5 /* A file does not exist/cannot be read */
#define TLS_EHANDSHAKE 6 /* TLS handshake failed */
/*
* Always use tls_clear() before using a tls_t!
* Never call a tls_*() function with tls_t NULL!
*/
typedef struct
{
int have_trust_file;
int is_active;
#ifdef HAVE_LIBGNUTLS
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
SSL_CTX *ssl_ctx;
SSL *ssl;
#endif /* HAVE_OPENSSL */
} tls_t;
/*
* Information about a X509 certificate.
* The 6 owner_info and issuer_info fields are:
* Common Name
* Organization
* Organizational unit
* Locality
* State/Province
* Country
* Each of these entries may be NULL if it was not provided.
*/
typedef struct
{
unsigned char sha1_fingerprint[20];
unsigned char md5_fingerprint[16];
time_t activation_time;
time_t expiration_time;
char *owner_info[6];
char *issuer_info[6];
} tls_cert_info_t;
/*
* tls_lib_init()
*
* Initialize underlying TLS library. If this function returns TLS_ELIBFAILED,
* *errstr will always point to an error string.
* Used error codes: TLS_ELIBFAILED
*/
int tls_lib_init(char **errstr);
/*
* tls_clear()
*
* Clears a tls_t type (marks it inactive).
*/
void tls_clear(tls_t *tls);
/*
* tls_init()
*
* Initializes a tls_t. If both 'key_file' and 'ca_file' are not NULL, they are
* set to be used when the peer request a certificate. If 'trust_file' is not
* NULL, it will be used to verify the peer certificate.
* All files must be in PEM format.
* If 'force_sslv3' is set, then only the SSLv3 protocol will be accepted. This
* option might be needed to talk to some obsolete broken servers. Only use this
* if you have to.
* Used error codes: TLS_ELIBFAILED, TLS_EFILE
*/
int tls_init(tls_t *tls,
const char *key_file, const char *ca_file, const char *trust_file,
int force_sslv3, char **errstr);
/*
* tls_start()
*
* Starts TLS encryption on a socket.
* 'tls' must be initialized using tls_init().
* If 'no_certcheck' is true, then no checks will be performed on the peer
* certificate. If it is false and no trust file was set with tls_init(),
* only sanity checks are performed on the peer certificate. If it is false
* and a trust file was set, real verification of the peer certificate is
* performed.
* 'hostname' is the host to start TLS with. It is needed for sanity checks/
* verification.
* 'tci' must be allocated with tls_cert_info_new(). Information about the
* peer's certificata will be stored in it. It can later be freed with
* tls_cert_info_free(). 'tci' is allowed to be NULL; no certificate
* information will be passed in this case.
* Used error codes: TLS_ELIBFAILED, TLS_ECERT, TLS_EHANDSHAKE
*/
int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck,
tls_cert_info_t *tci, char **errstr);
/*
* tls_is_active()
*
* Returns whether 'tls' is an active TLS connection.
*/
int tls_is_active(tls_t *tls);
/*
* tls_cert_info_new()
* Returns a new tls_cert_info_t
*/
tls_cert_info_t *tls_cert_info_new(void);
/*
* tls_cert_info_free()
* Frees a tls_cert_info_t
*/
void tls_cert_info_free(tls_cert_info_t *tci);
/*
* tls_cert_info_get()
*
* Extracts certificate information from the TLS connection 'tls' and stores
* it in 'tci'. See the description of tls_cert_info_t above.
* Used error codes: TLS_ECERT
*/
int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr);
/*
* tls_getbuf()
*
* Reads a buffer using TLS and stores it in 's'.
* Used error codes: TLS_EIO
*/
int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr);
/*
* tls_putbuf()
*
* Writes 'len' characters from the string 's' using TLS.
* Used error codes: TLS_EIO
*/
int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr);
/*
* tls_close()
*
* Close a TLS connection and mark it inactive
*/
void tls_close(tls_t *tls);
/*
* tls_lib_deinit()
*
* Deinit underlying TLS library.
*/
void tls_lib_deinit(void);
#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-2009 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
@@ -34,120 +34,66 @@
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#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 (((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);
#endif
}
Semaphore::Semaphore()
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
#else
sem_init(&m_semObj, 0, 0);
#endif
}
Semaphore::Semaphore(int iValue)
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
#else
sem_init(&m_semObj, 0, iValue);
#endif
}
Semaphore::~ Semaphore()
{
#ifdef WIN32
CloseHandle(m_semObj);
#else
sem_destroy(&m_semObj);
#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::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;
pthread_mutex_unlock((pthread_mutex_t*)m_pMutexObj);
#endif
}
@@ -155,18 +101,27 @@ bool Semaphore::IsLocked()
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;
@@ -175,6 +130,9 @@ Thread::Thread()
Thread::~Thread()
{
debug("Destroying Thread");
#ifndef WIN32
free(m_pThreadObj);
#endif
}
void Thread::Start()
@@ -183,31 +141,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
DWORD ThreadId;
m_Thread = CreateThread(NULL, 0, Thread::thread_handler, (void *) this, 0, &ThreadId);
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()
@@ -221,31 +177,31 @@ 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;
}
#ifdef WIN32
DWORD WINAPI Thread::thread_handler(void* pObject)
void __cdecl Thread::thread_handler(void* pObject)
#else
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");
@@ -263,18 +219,19 @@ 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;
#endif
}
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-2009 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
@@ -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();
@@ -49,49 +40,27 @@ public:
};
class Semaphore
{
private:
#ifdef WIN32
HANDLE m_semObj;
#else
sem_t m_semObj;
#endif
public:
Semaphore();
Semaphore(int iValue);
~Semaphore();
void Post();
bool Wait();
bool TimedWait(int iMSec);
bool IsLocked();
};
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;
#ifdef WIN32
static DWORD WINAPI thread_handler(void* pObject);
static void __cdecl thread_handler(void* pObject);
#else
static void *thread_handler(void* pObject);
#endif
public:
Thread();
Thread();
virtual ~Thread();
static void Init();
static void Final();
virtual void Start();
virtual void Stop();
@@ -104,9 +73,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
};

1061
Util.cpp
View File

File diff suppressed because it is too large Load Diff

117
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 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
@@ -57,8 +57,117 @@ public:
const char* Next();
};
char* BaseFileName(const char* filename);
class Util
{
public:
void NormalizePathSeparators(char* Path);
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SetFileSize(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 ForceDirectories(const char* szPath);
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);
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
#endif
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
/*
* 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 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);
/*
* 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];
};
#endif

1908
XmlRpc.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

238
XmlRpc.h Normal file
View File

@@ -0,0 +1,238 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 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 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:
Connection* m_pConnection;
const char* m_szClientIP;
char* m_szRequest;
ERpcProtocol m_eProtocol;
EHttpMethod m_eHttpMethod;
char* m_szUrl;
void Dispatch();
void SendAuthResponse();
void SendResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
XmlCommand* CreateCommand(const char* szMethodName);
void MutliCall();
public:
XmlRpcProcessor();
~XmlRpcProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetProtocol(ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUrl(const char* szUrl);
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
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);
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 ShutdownXmlCommand: 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 ScanXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class HistoryXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
#endif

162
aclocal.m4 vendored
View File

@@ -1,4 +1,4 @@
# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005 Free Software Foundation, Inc.
@@ -11,6 +11,164 @@
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# 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.
# 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
@@ -28,7 +186,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# 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.9.5])])
[AM_AUTOMAKE_VERSION([1.9.6])])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-

View File

@@ -9,17 +9,16 @@
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* Define to 1 to show progress during par-check (it must be disabled if
sigc++ doesn't work correctly) */
#undef ENABLE_PARPROGRESS
/* 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
@@ -29,6 +28,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
@@ -41,6 +49,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
@@ -50,8 +61,14 @@
/* 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 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
/* 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
@@ -65,6 +82,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
@@ -95,6 +115,9 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* 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

12555
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,41 +2,34 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 0.3.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.3.0)
AC_INIT(nzbget, 0.7.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.7.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
dnl Architecture check
dnl
dnl Architecture check.
dnl
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"
case "$host" in
*-linux*)
LIBPREF="/usr"
;;
*-freebsd*)
LIBPREF="/usr/local"
;;
*-solaris*)
LIBPREF="/usr"
;;
esac
fi
@@ -44,30 +37,22 @@ 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)
dnl
dnl Check for libs
@@ -75,7 +60,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 +67,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 +85,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 +99,119 @@ 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 Determine what socket length (socklen_t) data type is
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
dnl
dnl check for pragma pack
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}"]
[CFLAGS="${CFLAGS} -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,
[LDFLAGS="${LDFLAGS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"]
[CFLAGS="${CFLAGS} $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 +219,20 @@ 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 +253,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 +263,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 +276,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,
[LDFLAGS="${LDFLAGS} $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,67 +303,199 @@ 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 Enable par-check-progress (via sigc++)? Deafult: yes
dnl It doesn't work on WL500gP (uClibc), so we need a way to disable it
dnl check if libpar2 library is linkable
dnl
AC_MSG_CHECKING(whether to show progress during par-check )
AC_ARG_ENABLE(parprogress,
[ --disable-parprogress do not show progress during par-check (it must be disabled if sigc++ doesn't work correctly)],
[ USEPARPROGRESS=$enableval ],
[ USEPARPROGRESS=yes] )
AC_MSG_RESULT($USEPARPROGRESS)
if test "$USEPARPROGRESS" = "yes"; then
AC_DEFINE([ENABLE_PARPROGRESS],1,[Define to 1 to show progress during par-check (it must be disabled if sigc++ doesn't work correctly)])
fi
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]]))
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}"
CFLAGS="${CFLAGS} -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
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(openssl_includes,
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
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([SSL_library_init], [ssl],
FOUND=yes,
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 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
@@ -394,6 +510,41 @@ AC_COMPILE_IFELSE([
AC_MSG_RESULT([no]))
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
dnl Substitute flags.
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)

View File

@@ -423,3 +423,32 @@ diff -urN libpar2-0.2-original/par2cmdline.h libpar2-0.2-modified/par2cmdline.h
#else // WIN32
#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-02-13 15:37:59.899314300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
delete creatorpacket;
+ delete headers;
}
@@ -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,29 @@
diff -aud ../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 2008-02-06 12:02:53.226050300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
delete creatorpacket;
+ delete headers;
}
@@ -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->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
}
}
@@ -1804,7 +1805,7 @@
}
}
}
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name,count, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
sig_progress.emit(1000.0);
return true;
}

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

@@ -0,0 +1,187 @@
diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2008-10-26 19:54:33.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2008-10-29 10:24:48.000000000 +0100
@@ -52,6 +52,8 @@
noiselevel = CommandLine::nlNormal;
headers = new ParHeaders;
alreadyloaded = false;
+
+ cancelled = false;
}
Par2Repairer::~Par2Repairer(void)
@@ -406,6 +408,10 @@
progress = offset;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
@@ -584,6 +590,11 @@
delete diskfile;
}
+ if (cancelled)
+ {
+ return false;
+ }
+
return true;
}
@@ -833,9 +844,17 @@
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
+ if (cancelled)
+ {
+ break;
+ }
}
delete files;
+ if (cancelled)
+ {
+ return false;
+ }
}
{
@@ -846,9 +865,17 @@
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;
@@ -866,9 +893,18 @@
string::npos != filename.find(".PAR2"))
{
LoadPacketsFromFile(filename);
+ if (cancelled)
+ {
+ break;
+ }
}
}
+ if (cancelled)
+ {
+ return false;
+ }
+
return true;
}
@@ -1210,6 +1246,11 @@
sf = sortedfiles.begin();
while (sf != sortedfiles.end())
{
+ if (cancelled)
+ {
+ return false;
+ }
+
// Do we have a source file
Par2RepairerSourceFile *sourcefile = *sf;
@@ -1562,6 +1603,10 @@
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
@@ -1651,6 +1696,11 @@
}
}
+ if (cancelled)
+ {
+ return false;
+ }
+
// Get the Full and 16k hash values of the file
filechecksummer.GetFileHashes(hashfull, hash16k);
@@ -2291,10 +2341,19 @@
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
}
+ if (cancelled)
+ {
+ break;
+ }
+
++inputblock;
++inputindex;
}
@@ -2348,9 +2407,18 @@
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
+ if (cancelled)
+ {
+ break;
+ }
}
}
+ if (cancelled)
+ {
+ break;
+ }
+
++copyblock;
++inputblock;
}
@@ -2362,6 +2430,11 @@
lastopenfile->Close();
}
+ if (cancelled)
+ {
+ return false;
+ }
+
if (noiselevel > CommandLine::nlQuiet)
cout << "Writing recovered data\r";
diff -aud ../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 2008-10-26 19:01:08.000000000 +0100
@@ -183,6 +183,7 @@
u64 totaldata; // Total amount of data to be processed.
u64 totalsize; // Total data size
+ bool cancelled; // repair cancelled
};
#endif // __PAR2REPAIRER_H__

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 Andrei Prygounkov <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., 675 Mass Ave, Cambridge, MA 02139, 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"

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-2010 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
@@ -38,17 +38,23 @@
#include <winsvc.h>
#else
#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>
#ifdef DEBUG
#ifndef DISABLE_PARCHECK
#include <sigc++/sigc++.h>
#include <iostream>
#endif
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include "nzbget.h"
@@ -63,8 +69,11 @@
#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
@@ -76,62 +85,135 @@ void ProcessClientRequest();
#ifndef WIN32
void InstallSignalHandlers();
void Daemonize();
void PrintBacktrace();
#ifdef HAVE_SYS_PRCTL_H
void EnableDumpCore();
#endif
#ifdef DEBUG
void DoTest();
void MakeSegFault();
#endif
#endif
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
ServerPool* g_pServerPool = NULL;
QueueCoordinator* g_pQueueCoordinator = NULL;
RemoteServer* g_pRemoteServer = NULL;
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor;
PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
Scheduler* g_pScheduler = NULL;
char* (*szEnvironmentVariables)[] = NULL;
/*
* 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
g_pLog = new Log();
g_pServerPool = new ServerPool();
debug("Options parsing");
g_pOptions = new Options(argc, argv);
#ifndef DISABLE_PARCHECK
DisableCout();
#endif
g_pLog = new Log();
debug("nzbget %s", Util::VersionRevision());
g_pServerPool = new ServerPool();
g_pScheduler = new Scheduler();
Thread::Init();
debug("Reading options");
g_pOptions = new Options(argc, argv);
szEnvironmentVariables = (char*(*)[])argp;
#ifndef WIN32
if (g_pOptions->GetUMask() < 01000)
{
/* set newly created file permissions */
umask(g_pOptions->GetUMask());
}
#endif
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
{
debug("deleting old log-file");
debug("Deleting old log-file");
g_pLog->ResetLog();
}
g_pLog->InitOptions();
if (g_pOptions->GetDaemonMode())
{
info("nzbget daemon-mode");
#ifdef WIN32
StartService(Run);
return 0;
info("nzbget %s service-mode", Util::VersionRevision());
#else
Daemonize();
info("nzbget %s daemon-mode", Util::VersionRevision());
#endif
}
else if (g_pOptions->GetServerMode())
{
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());
}
if (!g_pOptions->GetRemoteClientMode())
{
g_pServerPool->InitConnections();
#ifdef DEBUG
g_pServerPool->LogDebugInfo();
#endif
}
#ifdef WIN32
if (g_pOptions->GetDaemonMode())
{
StartService(Run);
return 0;
}
#else
#ifdef HAVE_SYS_PRCTL_H
if (g_pOptions->GetDumpCore())
{
EnableDumpCore();
}
#endif
#endif
Run();
#ifdef WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}
@@ -139,10 +221,16 @@ void Run()
{
#ifndef WIN32
InstallSignalHandlers();
#ifdef DEBUG
if (g_pOptions->GetTestBacktrace())
{
MakeSegFault();
}
#endif
Thread::Init();
Connection::Init();
#endif
Connection::Init(g_pOptions->GetTLS() && !g_pOptions->GetRemoteClientMode() &&
(g_pOptions->GetClientOperation() == Options::opClientNoOperation));
// client request
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
@@ -153,10 +241,11 @@ void Run()
}
// Create the queue coordinator
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
if (!g_pOptions->GetRemoteClientMode())
{
g_pQueueCoordinator = new QueueCoordinator();
g_pDownloadSpeedMeter = g_pQueueCoordinator;
g_pDownloadQueueHolder = g_pQueueCoordinator;
}
// Setup the network-server
@@ -166,7 +255,13 @@ void Run()
g_pRemoteServer->Start();
}
// Create the front-end
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
}
// Create the frontend
if (!g_pOptions->GetDaemonMode())
{
switch (g_pOptions->GetOutputMode())
@@ -191,21 +286,29 @@ void Run()
g_pFrontend->Start();
}
// Start QueueCoordinator
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
// Starting QueueCoordinator and PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
g_pPrePostProcessor->Start();
// 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->GetCategory() ? g_pOptions->GetCategory() : "");
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())
{
g_pDiskState = new DiskState();
}
g_pQueueCoordinator->Start();
g_pPrePostProcessor->Start();
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
@@ -249,14 +352,6 @@ void Run()
debug("RemoteServer stopped");
}
#ifdef DEBUG
if (g_pOptions->GetTest())
{
DoTest();
}
#endif
// Stop Frontend
if (g_pFrontend)
{
@@ -279,42 +374,99 @@ 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->GetEditQueueIDFrom(), g_pOptions->GetEditQueueIDTo());
}
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());
case Options::opClientRequestListFiles:
Client->RequestServerList(true, false);
break;
case Options::opClientRequestListGroups:
Client->RequestServerList(false, true);
break;
case Options::opClientRequestListStatus:
Client->RequestServerList(false, false);
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(), true);
break;
case Options::opClientRequestLog:
Client->RequestServerLog(g_pOptions->GetLogLines());
break;
case Options::opClientRequestShutdown:
Client->RequestServerShutdown();
break;
case Options::opClientRequestDownload:
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetCategory(), g_pOptions->GetAddTop());
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::opClientRequestScan:
Client->RequestScan();
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::opClientNoOperation:
break;
}
delete Client;
@@ -357,23 +509,30 @@ 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;
case SIGCHLD:
// ignoring
break;
#ifdef DEBUG
case SIGPIPE:
debug("SIGPIPE received, ignoring");
// ignoring
break;
case SIGSEGV:
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
PrintBacktrace();
break;
default:
debug("Signal %i received", iSignal);
// printf("Signal %i received\n", iSignal);
if (SignalProcList[iSignal - 1])
{
SignalProcList[iSignal - 1](iSignal);
@@ -387,6 +546,7 @@ void InstallSignalHandlers()
{
signal(SIGINT, SignalProc);
signal(SIGTERM, SignalProc);
signal(SIGCHLD, SignalProc);
signal(SIGPIPE, SIG_IGN);
#ifdef DEBUG
SignalProcList.clear();
@@ -397,6 +557,62 @@ void InstallSignalHandlers()
signal(SIGWINCH, SIG_DFL);
#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()
@@ -435,11 +651,20 @@ void Cleanup()
}
debug("Frontend deleted");
debug("Deleting DiskState");
if (g_pDiskState)
{
delete g_pDiskState;
g_pDiskState = NULL;
}
debug("DiskState deleted");
debug("Deleting Options");
if (g_pOptions)
{
if (g_pOptions->GetDaemonMode())
{
info("Deleting lock file");
remove(g_pOptions->GetLockFile());
}
delete g_pOptions;
@@ -455,9 +680,18 @@ void Cleanup()
}
debug("ServerPool deleted");
Thread::Final();
debug("Deleting Scheduler");
if (g_pScheduler)
{
delete g_pScheduler;
g_pScheduler = NULL;
}
debug("Scheduler deleted");
Connection::Final();
Thread::Final();
debug("Global objects cleaned up");
if (g_pLog)
@@ -480,11 +714,27 @@ void Daemonize()
setsid(); /* obtain a new process group */
for (i = getdtablesize();i >= 0;--i) close(i); /* close all descriptors */
i = open("/dev/null", O_RDWR); dup(i); dup(i); /* handle standart I/O */
umask(027); /* set newly created file permissions */
chdir(g_pOptions->GetDestDir()); /* change running directory */
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0) exit(1); /* can not open */
if (lockf(lfp, F_TLOCK, 0) < 0) exit(0); /* can not lock */
/* Drop user if there is one, and we were run as root */
if ( getuid() == 0 || geteuid() == 0 )
{
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. */
initgroups( g_pOptions->GetDaemonUserName(),pw->pw_gid);
/* Finally, set uid. */
setuid(pw->pw_uid);
}
}
/* first instance continues */
sprintf(str, "%d\n", getpid());
write(lfp, str, strlen(str)); /* record pid to lockfile */
@@ -495,35 +745,18 @@ void Daemonize()
}
#endif
#ifdef DEBUG
#ifndef DISABLE_PARCHECK
/*
class T1 { public:
sigc::signal<int, std::string> sig_filename;
int Test1() { std::string str = "test"; return sig_filename.emit(str); }
};
class T2 { public:
void Test1() {
T1 t1;
t1.sig_filename.connect(sigc::mem_fun(*this, &T2::signal_filename));
if (t1.Test1() == 4) {
printf("ok\n");
//exit(0);
} else {
printf("error\n");
//exit(-1);
}
}
int signal_filename(std::string str) {
printf("%s\n", str.c_str());
return str.length();
}
};
*/
#endif
void DoTest()
class NullStreamBuf : public std::streambuf
{
printf("testing\n");
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-2009 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
@@ -31,40 +31,51 @@
// 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_RDWR 0x02
#define PATH_SEPARATOR '\\'
#define ALT_PATH_SEPARATOR '/'
#define LINE_ENDING "\r\n"
#define pid_t int
#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_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

@@ -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 $(NOINHERIT)"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@@ -113,6 +113,7 @@
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
ExceptionHandling="1"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
@@ -130,8 +131,83 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib"
LinkIncremental="2"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib libgnutls-26.lib libgcrypt-11.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;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 $(NOINHERIT)"
LinkIncremental="0"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
@@ -175,6 +251,14 @@
RelativePath=".\ArticleDownloader.h"
>
</File>
<File
RelativePath=".\BinRpc.cpp"
>
</File>
<File
RelativePath=".\BinRpc.h"
>
</File>
<File
RelativePath=".\ColoredFrontend.cpp"
>
@@ -339,6 +423,14 @@
RelativePath=".\QueueCoordinator.h"
>
</File>
<File
RelativePath=".\QueueEditor.cpp"
>
</File>
<File
RelativePath=".\QueueEditor.h"
>
</File>
<File
RelativePath=".\RemoteClient.cpp"
>
@@ -355,6 +447,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"
>
@@ -371,6 +487,14 @@
RelativePath=".\Thread.h"
>
</File>
<File
RelativePath=".\TLS.cpp"
>
</File>
<File
RelativePath=".\TLS.h"
>
</File>
<File
RelativePath=".\Util.cpp"
>
@@ -383,6 +507,14 @@
RelativePath=".\win32.h"
>
</File>
<File
RelativePath=".\XmlRpc.cpp"
>
</File>
<File
RelativePath=".\XmlRpc.h"
>
</File>
</Files>
<Globals>
</Globals>

79
nzbgetd Executable file
View File

@@ -0,0 +1,79 @@
#!/bin/sh
#
# Script used to start and stop the nzbget usenet service
#
# Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
# Copyright (C) 2009 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.
#
#
# Location of the nzbget executable
export NZBGET_BINARY="/usr/local/bin/nzbget"
# -----------------------------------------------------------------
# start/stop section
execCommand() {
"$NZBGET_BINARY" $@
sleep 1 # allows prompt to return
}
start() {
execCommand "--daemon"
}
stop() {
execCommand "--quit"
}
status() {
execCommand "--log 5"
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart)
stop
sleep 10 # since stop is backgrounded
start
;;
pstatus)
retval=$(pgrep -l -f "$NZBGET_BINARY --daemon" > /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
;;
istatus)
status
;;
*)
echo "Usage $0 {start|stop|restart|pstatus|istatus}"
exit 1
esac
exit 0

45
postprocess-example.conf Normal file
View File

@@ -0,0 +1,45 @@
# Template configuration file for postprocessing script "postprocess-example.sh".
# Please refer to "postprocess-example.sh" for usage instructions.
##############################################################################
### PATHS ###
# Set the full path to unrar if it is not in your PATH.
UnrarCmd=unrar
##############################################################################
### OPTIONS ###
# Delete rar-files after unpacking (yes, no).
DeleteRarFiles=yes
# Rename img-files to iso (yes, no).
RenameIMG=yes
# Joint TS-files (yes, no).
JoinTS=no
##############################################################################
### 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 "password" to value "123" for
# nzb-file with id=2:
# nzbget -E G O Password=123 2
# Perform postprocessing (yes, no).
#
# Set to "no" to skip postprocessing for this nzb-file.
PostProcess=yes
# Password for encrypted posts.
#
# If the post requires a password for unpacking.
Password=
# Destination directory.
#
# NOTE: NZBGet must have write-access-rights for that directory.
DestDir=

326
postprocess-example.sh Executable file
View File

@@ -0,0 +1,326 @@
#!/bin/sh
#
# Example postprocessing script for NZBGet
#
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
# Copyright (C) 2008 Otmar Werner
# Copyright (C) 2008-2009 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.
#
#
####################### Usage instructions #######################
# o Script will unrar downloaded rar files, 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 "postprocess-example.conf". Put the configuration file
# into the directory where nzbget's configuration file (nzbget.conf) or where
# this script itself 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 (requires
# NZBGetWeb 1.4 or later). Set the options "PostProcessConfigFile" and
# "PostProcessConfigTemplate" to point to "postprocess-example.conf"
# (including full path). The both options are under the section
# "CONFIGURATION OF POSTPROCESSING-SCRIPT" in NZBGetWeb.
#
# o There are few options, which can be ajdusted for each nzb-file
# individually. To view/edit them in web-interface click on a spanner icon
# near the name of nzb-file.
#
# o The script supports the feature called "delayed par-check".
# That means it can try to unpack downloaded files without par-checking
# them fisrt. Only if unpack fails, the script schedules par-check,
# then unpacks again.
# To use delayed par-check set following options in nzbget configuration file:
# ParCheck=no
# ParRepair=yes
# LoadPars=one (or) LoadPars=all
#
# o If you want to par-check/repair all files before trying to unpack them,
# set option "ParCheck=yes".
#
####################### End of Usage instructions #######################
# NZBGet passes following arguments to postprocess-programm as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBFILENAME - name of processed nzb-file;
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
# found);
# NZBPP_PARSTATUS - 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 successfully repaired;
# 3 = checked and can be repaired but repair is disabled;
# NZBPP_NZBCOMPLETED - state of nzb-job:
# 0 = there are more collections in this nzb-file queued;
# 1 = this was the last collection in nzb-file;
# NZBPP_PARFAILED - indication of failed par-jobs for current nzb-file:
# 0 = no failed par-jobs;
# 1 = current par-job or any of the previous par-jobs for
# the same nzb-files failed;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string).
# Name of script's configuration file
SCRIPT_CONFIG_FILE="postprocess-example.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
if [ "$NZBPP_DIRECTORY" = "" -o "$NZBOP_CONFIGFILE" = "" ]; then
echo "*** NZBGet post-process script ***"
echo "This script is supposed to be called from nzbget (0.7.0 or later)."
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: Postprocessing disabled for this nzb-file, exiting"
exit $POSTPROCESS_NONE
fi
echo "[INFO] Post-Process: Post-process script successfully started"
# Determine the location of configuration file (it must be stored in
# the directory with nzbget.conf or in this script's directory).
ConfigDir="${NZBOP_CONFIGFILE%/*}"
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
if [ ! -f "$ScriptConfigFile" ]; then
ConfigDir="${0%/*}"
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
fi
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_LOADPARS" = "none" ]; then
echo "[ERROR] Post-Process: Please set option \"LoadPars\" to \"One\" or \"All\" in nzbget configuration file"
BadConfig=1
fi
if [ "$NZBOP_PARREPAIR" = "no" ]; then
echo "[ERROR] Post-Process: Please set option \"ParRepair\" to \"Yes\" in nzbget configuration file"
BadConfig=1
fi
if [ "$BadConfig" -eq 1 ]; then
echo "[ERROR] Post-Process: Existing because of not compatible nzbget configuration"
exit $POSTPROCESS_ERROR
fi
# Check if all collections in nzb-file were downloaded
if [ ! "$NZBPP_NZBCOMPLETED" -eq 1 ]; then
echo "[INFO] Post-Process: Not the last collection in nzb-file, exiting"
exit $POSTPROCESS_SUCCESS
fi
# Check par status
if [ "$NZBPP_PARSTATUS" -eq 1 -o "$NZBPP_PARSTATUS" -eq 3 -o "$NZBPP_PARFAILED" -eq 1 ]; then
if [ "$NZBPP_PARSTATUS" -eq 3 ]; then
echo "[WARNING] Post-Process: Par-check successful, but Par-repair disabled, exiting"
else
echo "[WARNING] Post-Process: Par-check failed, exiting"
fi
exit $POSTPROCESS_ERROR
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
cd "$NZBPP_DIRECTORY"
# If not just repaired and file "_brokenlog.txt" exists, the collection is damaged
# exiting with returning code $POSTPROCESS_PARCHECK_ALL to request par-repair
if [ ! "$NZBPP_PARSTATUS" -eq 2 ]; then
if [ -f "_brokenlog.txt" ]; then
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
echo "[INFO] Post-Process: Brokenlog found, requesting par-repair"
exit $POSTPROCESS_PARCHECK_ALL
fi
fi
fi
# All checks done, now processing the files
# Flag indicates that something was unrared
Unrared=0
# Unrar the files (if any) to the temporary directory, if there are no rar files this will do nothing
if (ls *.rar >/dev/null 2>&1); then
# Check if unrar exists
$UnrarCmd >/dev/null 2>&1
if [ "$?" -eq 127 ]; then
echo "[ERROR] Post-Process: Unrar not found. Set the path to unrar in script's configuration"
exit $POSTPROCESS_ERROR
fi
# Make a temporary directory to store the unrarred files
ExtractedDirExists=0
if [ -d extracted ]; then
ExtractedDirExists=1
else
mkdir extracted
fi
echo "[INFO] Post-Process: Unraring"
rarpasswordparam=""
if [ "$NZBPR_Password" != "" ]; then
rarpasswordparam="-p$NZBPR_Password"
fi
$UnrarCmd x -y -p- "$rarpasswordparam" -o+ "*.rar" ./extracted/
if [ "$?" -eq 3 ]; then
echo "[ERROR] Post-Process: Unrar failed"
if [ "$ExtractedDirExists" -eq 0 ]; then
rm -R extracted
fi
# for delayed par-check/-repair at least one par-file must be already downloaded
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
echo "[INFO] Post-Process: Requesting par-repair"
exit $POSTPROCESS_PARCHECK_ALL
fi
exit $POSTPROCESS_ERROR
fi
Unrared=1
# Remove the rar files
if [ "$DeleteRarFiles" = "yes" ]; then
echo "[INFO] Post-Process: Deleting rar-files"
rm *.r[0-9][0-9] >/dev/null 2>&1
rm *.rar >/dev/null 2>&1
rm *.s[0-9][0-9] >/dev/null 2>&1
fi
# Go to the temp directory and try to unrar again.
# If there are any rars inside the extracted rars then these will no also be unrarred
cd extracted
if (ls *.rar >/dev/null 2>&1); then
echo "[INFO] Post-Process: Unraring (second pass)"
$UnrarCmd x -y -p- -o+ "*.rar"
if [ "$?" -eq 3 ]; then
echo "[INFO] Post-Process: Unrar (second pass) failed"
exit $POSTPROCESS_ERROR
fi
# Delete the Rar files
if [ "$DeleteRarFiles" = "yes" ]; then
echo "[INFO] Post-Process: Deleting rar-files (second pass)"
rm *.r[0-9][0-9] >/dev/null 2>&1
rm *.rar >/dev/null 2>&1
rm *.s[0-9][0-9] >/dev/null 2>&1
fi
fi
# Move everything back to the Download folder
mv * ..
cd ..
rmdir extracted
fi
# 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
if [ "$Unrared" -eq 1 ]; then
# Delete par2-file only if there were files for unpacking.
rm *.[pP][aA][rR]2 >/dev/null 2>&1
fi
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
fi
# Remove all the split .ts files
echo "[INFO] Post-Process: Deleting source ts-files"
rm *0???.ts >/dev/null 2>&1
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

43
win32.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-2010 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
@@ -25,17 +25,18 @@
/* win32.h - Defines and standard includes for MS Windows / Visual C++ 2005 */
/* Define to 1 to not use curses */
#undef DISABLE_CURSES
//#define DISABLE_CURSES
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
//#define DISABLE_PARCHECK
/* Define to 1 to show progress during par-check (it must be disabled if
sigc++ doesn't work correctly) */
#define ENABLE_PARPROGRESS
/* Define to 1 to disable TLS/SSL-support. */
//#define DISABLE_TLS
/* Define to 1 to include support for uulib */
#undef ENABLE_UULIB
#ifndef DISABLE_TLS
/* Define to 1 to use GnuTLS library for TLS/SSL-support */
#define HAVE_LIBGNUTLS
#endif
/* Define to the name of macro which returns the name of function being
compiled */
@@ -50,13 +51,19 @@
/* Define to 1 if getopt_long is supported */
#undef HAVE_GETOPT_LONG
/* Define to 1 to use pragma pack directive in MessageBase.h */
#define HAVE_PRAGMA_PACK
/* Define to 1 if variadic macros are supported */
#define HAVE_VARIADIC_MACROS
#define VERSION "0.3.0"
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#define HAVE_PAR2_CANCEL
/* Define to 1 if function GetAddrInfo is supported */
#define HAVE_GETADDRINFO
/* Determine what socket length (socklen_t) data type is */
#define SOCKLEN_T socklen_t
#define VERSION "0.7.0"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE
@@ -66,6 +73,14 @@
#define _USE_32BIT_TIME_T
#ifdef _DEBUG
// detection of memory leaks
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#ifndef SKIP_DEFAULT_WINDOWS_HEADERS
#include <windows.h>
#include <winbase.h>
#endif