Compare commits

..

180 Commits
v9.0 ... v11.0

Author SHA1 Message Date
Andrey Prygunkov
3b87cf1b23 version 11.0 2013-07-01 20:03:52 +00:00
Andrey Prygunkov
ee5a2a320e updated version string (preparing to release 11.0) 2013-07-01 19:18:41 +00:00
Andrey Prygunkov
738fd3da58 updated ChangeLog 2013-07-01 18:48:07 +00:00
Andrey Prygunkov
decc08934c removed a superfluous page scroll after clicking on option in web-interface settings 2013-06-26 20:52:10 +00:00
Andrey Prygunkov
5f5b7f92cf improved configure-script: defining of symbol "FILE_OFFSET_BITS=64", required on some systems, is not necessary anymore 2013-06-20 18:18:05 +00:00
Andrey Prygunkov
20fa280171 imporved error message in web-interface displayed when the template configuration file could not be found 2013-06-19 18:24:50 +00:00
Andrey Prygunkov
e5fa2ef750 fixed: support for splitted files (.001, .002, etc.) were broken 2013-06-18 21:56:23 +00:00
Andrey Prygunkov
bd31e25757 configure-script now defines "SIGCHLD_HANDLER" by default on all systems including BSD; this eliminates the need of configure-parameter "--enable-sigchld-handler" on 64-Bit BSD; the trade-off: 32-Bit BSD now requires "--disable-sigchld-handler" 2013-06-18 19:06:11 +00:00
Andrey Prygunkov
5b3113d96b 1) when a nzb-file is added via web-interface or via remote call the file is now put into incoming nzb-directory (option "NzbDir") and then scanned; this has two advantages over the old behavior when the file was parsed directly in memory: the file serves as a backup for troubleshootings and the file is processed by nzbprocess-script (if defined in option "NzbProcess") making the pre-processing much easier; 2) new env-var parameters are passed to NzbProcess-script: NZBNP_NZBNAME, NZBNP_CATEGORY, NZBNP_PRIORITY, NZBNP_TOP, NZBNP_PAUSED; 3) new commands for use in NzbProcess-scripts: "[NZB] TOP=1" to add nzb to the top of queue and "[NZB] PAUSED=1" to add nzb-file in paused state 2013-06-17 20:39:46 +00:00
Andrey Prygunkov
84b4f7695b when a nzb-file whose name ends with ".queued" is added via web-interface the ".queued"-part is automatically removed 2013-06-16 13:00:57 +00:00
Andrey Prygunkov
fc8ea3bcd0 fixed: if an error occurs when a RPC-client or web-browser communicates with nzbget the program could crash 2013-06-15 15:07:11 +00:00
Andrey Prygunkov
9051a4df4d fixed: if the last file of collection was detected as duplicate after the download of the first article the file was deleted from queue (that's OK) but the post-processing was not triggered (that's a bug) 2013-06-13 20:51:38 +00:00
Andrey Prygunkov
a7b42b6c97 fixed: pp-scripts "Logger.py" and "EMail.py" failed trying to get post-processing log from nzbget if option "ControlUsername" were set to a non-default value 2013-06-12 20:25:06 +00:00
Andrey Prygunkov
f7675b1e46 addition to r693: fixed: unicode characters were not properly encoded in JSON-RPC response 2013-06-12 20:14:45 +00:00
Andrey Prygunkov
db1117d892 new option "ControlUsername" to define login user name (if you don't like default username "nzbget") 2013-06-05 21:09:28 +00:00
Andrey Prygunkov
4b14e19229 removed option "RenameBroken"; it caused problems in par-checker (the option existed since early program versions before the par-check was added) 2013-06-04 21:04:00 +00:00
Andrey Prygunkov
606021fb8a removed option "AppendNzbDir"; if it was disabled that caused problems in par-checker and unpacker; the option is now assumed always active 2013-06-04 20:56:17 +00:00
Andrey Prygunkov
b15499c1dd removed option "ProcessLogKind"; scripts should use prefixes ([INFO], [DETAIL], etc); messages printed without prefixes are added as [INFO] 2013-06-03 20:56:56 +00:00
Andrey Prygunkov
950588cb65 addition to r698: if options of the section "Terminal" were missed in the config file, they were written with empty values causing warnings on program start 2013-06-03 20:35:18 +00:00
Andrey Prygunkov
7991c06543 fixed: in the option "NzbAddedProcess" the env-var parameter with nzb-name was passed in "NZBNA_NAME", should be "NZBNA_NZBNAME"; the old parameter name "NZBNA_NAME" is still supported for compatibility 2013-06-02 21:11:09 +00:00
Andrey Prygunkov
b335c4ca05 updated svn log URL 2013-06-02 21:10:14 +00:00
Andrey Prygunkov
cf3773dd28 added functions to backup and restore settings from web-interface; when restoring it's possible to choose what sections to restore (for example only news servers settings or only settings of a certain pp-script) or restore the whole configuration 2013-06-02 19:20:31 +00:00
Andrey Prygunkov
2a3740e49f added check for directory existence in pp-script <Logger> to avoid script failure if the directory was deleted by one of the previous scripts 2013-05-28 20:22:59 +00:00
Andrey Prygunkov
bcbd30ff6e addition to r694: fixed: a directory check/creation could fail if the directory was just created in another thread 2013-05-26 21:10:08 +00:00
Andrey Prygunkov
571ab9602f 1) additional comment to r693 (ArticleDownloader.cpp, line 632): fixed: the program could hang if the destination file could not be created; 2) improved thread synchronisation to avoid (short-time) lockings of the program during creation of destination files 2013-05-26 20:42:15 +00:00
Andrey Prygunkov
cfab6a3bb6 more detailed error message if a directory could not be created (<DstDir>, <NzbDir>, etc.); the message includes error text reported by OS such as <permission denied> or similar 2013-05-26 13:47:23 +00:00
Andrey Prygunkov
7c9ab59aff addition to r649 (unicode support in XML-RPC and JSON-RPC): fixed a typo which could prevent the filtering of invalid xml-characters 2013-05-23 20:47:42 +00:00
Andrey Prygunkov
7b1d1129a8 when unpacking the unpack start time is now measured after receiving of unrar copyright message; this provides better unpack time estimation in a case when user uses unpack-script to do some things before executing unrar (for example sending Wake-On-Lan message to the destination NAS); it works with unrar only, it's not possible with 7-Zip because it buffers printed messages 2013-05-23 20:40:46 +00:00
Andrey Prygunkov
bf3e8fe3a9 the maximum number of download threads are now managed automatically taking into account the number of allowed connections to news servers; removed option <ThreadLimit> 2013-05-22 20:25:19 +00:00
Andrey Prygunkov
baeac17d5b when the program is reloaded, a message with version number is printed like on start 2013-05-22 20:09:21 +00:00
Andrey Prygunkov
68ce6dea4b if a communication error occurs in web-interface, it retries multiple times before giving up with an error message 2013-05-21 20:41:08 +00:00
Andrey Prygunkov
00df4b8920 small correction in a log-message: removed <Request:> from message <Request: Queue collection...> 2013-05-21 20:35:42 +00:00
Andrey Prygunkov
36814514b7 new parameter (env. var) <NZBPP_NZBID> is passed to pp_scripts and contains an internal ID of NZB-file 2013-05-21 20:34:42 +00:00
Andrey Prygunkov
381a9a28b0 pp-scripts terminated with unknown status are now considered failed (status=FAILURE instead of status=UNKNOWN) 2013-05-21 20:32:41 +00:00
Andrey Prygunkov
5c364896d3 added support for rar-files with non-standard extensions (such as .001, etc.) 2013-05-21 20:21:52 +00:00
Andrey Prygunkov
07ce1d44a9 fixed: remote command <--list> for history items may fail with segfault on certain par-status 2013-05-16 20:57:04 +00:00
Andrey Prygunkov
1348ac86f7 added setting of post-processing parameters for history items; pp-parameters can now be viewed and changed in history dialog in web-interface; useful before post-processing again; new action <HistorySetParameter> in RPC-method <editqueue>; new action <O> in remote command <--edit/-E> for history items (subcommand <H>) 2013-05-16 20:54:13 +00:00
Andrey Prygunkov
b4c4855a9b option <ControlPassword> can now be set to en empty value to disable authentication; useful if nzbget works behind other web-server with its own authentication 2013-05-15 19:45:48 +00:00
Andrey Prygunkov
7a1001a70b fixed: error in IE when loading web-interface (bug introduced in r673) 2013-05-15 16:51:13 +00:00
Andrey Prygunkov
340a8130e9 addition to r677: added missing headers, causing compilation error in newer gcc versions 2013-05-15 16:45:36 +00:00
Andrey Prygunkov
9eb8de27d2 addition to r676: fixed crash on Linux with uClibc 2013-05-14 20:52:39 +00:00
Andrey Prygunkov
476a43a5bf configuration can now be saved in web-interface even if there were no changes made but if obsolete or invalid options were detected in the config file; the saving removes invalid entries from config file 2013-05-14 20:27:23 +00:00
Andrey Prygunkov
9ab955d026 refactor: more consistent using of c-headers 2013-05-14 20:20:52 +00:00
Andrey Prygunkov
bcedb32cf0 1) fixed compilation error on Linux (bug introduced in r675); 2) refactor: small corrections in class <ParCoordinator> 2013-05-14 20:18:17 +00:00
Andrey Prygunkov
6bc266f13c Par-checker and renamer now add messages into the log of pp-item (like unpack- and pp-scripts-messages); these message now appear in the log created by scripts Logger.py and EMail.py 2013-05-13 21:00:08 +00:00
Andrey Prygunkov
ed36feeb0a fixed: by deleting of a partially downloaded nzb-file from queue, when the option <DeleteCleanupDisk> was active, the file <_brokenlog.txt> was not deleted preventing the directory from automatic deletion 2013-05-13 20:13:28 +00:00
Andrey Prygunkov
85400cd8f6 fixed: for pp-scripts saved using windows line endings (CR,LF) the descriptions of options were not displayed correctly on settings page 2013-05-08 21:53:18 +00:00
Andrey Prygunkov
2866697b32 fixed: crash when adding malformed nzb-files with certain structure (Windows only) 2013-05-08 21:51:48 +00:00
Andrey Prygunkov
f1a99e1194 when deleting downloads via web-interface a proper hint regarding deleting of already downloaded files from disk depending on option <DeleteCleanupDisk> is displayed 2013-05-08 21:50:47 +00:00
Andrey Prygunkov
db47ddf3dc when download is resumed in web-interface the option <ParCheck=Force> is respected and all par2-files are resumed (not only main par2-file) 2013-05-08 21:45:22 +00:00
Andrey Prygunkov
2cc4dbd2ba made pp-scripts EMail.py and Logger.py compatible with python3 (python2 is OK too) 2013-05-07 18:02:15 +00:00
Andrey Prygunkov
a38eef2971 fixed: symbol <DISABLE_TLS> must be defined in project settings, defining it in <win32.h> didn't work properly (Windows only) 2013-05-06 19:52:54 +00:00
Andrey Prygunkov
e3e197a917 1) option <ExtCleanupDisk> now checks not only file extensions but any substring at the end of file name (in particular this allows to delete file _brokenlog.txt); 2) fixed: when option <InterDir> was used the files extracted from archives were not processed/deleted by option <ExtCleanupDisk> 2013-05-06 18:58:18 +00:00
Andrey Prygunkov
361d0befb6 addition to r665: fixed: crash on starting of download 2013-05-06 18:32:16 +00:00
Andrey Prygunkov
ba35c662ea small improvement in multithread synchronisation: do not create mutexex for each file-object but instead only for active objects (which are being downloaded at the moment) 2013-05-05 21:17:23 +00:00
Andrey Prygunkov
45ce763c71 small improvement in multithread synchronisation of download queue 2013-05-05 21:16:02 +00:00
Andrey Prygunkov
73c85a0013 fixed: when a duplicate file was detected during download the program could hang 2013-05-05 20:59:22 +00:00
Andrey Prygunkov
26361630c2 addition to r660: fixed: downloads were always checked when option <ParCheck> was set to <Auto> 2013-05-04 07:45:00 +00:00
Andrey Prygunkov
96c30c509b fixed: spaces in option <ExtCleanupDisk> prevented its correct operation 2013-05-02 20:48:32 +00:00
Andrey Prygunkov
d9b9786486 improved par-check: added support for manual par-check; if option <ParCheck> is set to <Manual> and a damaged download is detected the program downloads all par2-files but doesn't perform par-check; the user must perform par-check/repair manually then (possibly on another, faster computer); old values <yes/no> of option <ParCheck> renamed to <Force> and <Auto> respectively; when set to <Force> all par2-files are always downloaded; removed option <LoadPars> since its functionality is now covered by option <ParCheck>; Result of par-check can now have new value <Manual repair necessary>; field <ParStatus> in RPC-method <history> can have new value <MANUAL>; parameter <NZBPP_PARSTATUS> for pp-script can have new value <4 = manual repair necessary>; extended pp-script <EMail.py> to handle ParStatus=4 (manual) 2013-05-02 20:40:36 +00:00
Andrey Prygunkov
bb9cea260d small improvements in formatting of option descriptions in web-interface 2013-05-02 20:03:51 +00:00
Andrey Prygunkov
958c2f97ec refactor: discarding of download queue is now less complicated and not depend on diskstate version 2013-04-30 20:12:07 +00:00
Andrey Prygunkov
27651f17bf improvement in JSON-/XML-RPC: all ID fields including NZBID are now persistent and remain their values after restart; this allows for third-party software to identify nzb-files by ID; method <history> now returns ID of NZB-file in the field <NZBID>; in versions up to 0.8.0 the field <NZBID> was used to identify history items in the edit-commands <HistoryDelete>, <HistoryReturn>, <HistoryProcess>; since version 9 field <ID> is used for this purpose; in versions 9-10 field <NZBID> still existed and had the same value as field <ID> for compatibility with version 0.8.0; the compatibility is not provided anymore; this change was needed to provide a consistent using of field <NZBID> across all RPC-methods 2013-04-30 20:10:10 +00:00
Andrey Prygunkov
71621f7bb5 eliminated a compiler warning 2013-04-30 20:06:54 +00:00
Andrey Prygunkov
d8add46215 automatic deletion of backup-source files after successful par-repair; important when repairing renamed rar-files since this could cause failure during unpack 2013-04-29 20:46:09 +00:00
Andrey Prygunkov
e459f570d5 improved unicode support in pp-script Logger.py 2013-04-29 19:25:59 +00:00
Andrey Prygunkov
5b5057dee0 fixed: failed to read download queue from disk if post-processing queue was not empty 2013-04-29 19:24:44 +00:00
Andrey Prygunkov
f21becb37d added link to catalog of pp-scripts to web-interface 2013-04-29 18:21:06 +00:00
Andrey Prygunkov
9d03eb1ad4 fixed: when option <InterDir> was active and the download after unpack contained rar-file with the same name as one of original files (sometimes happen with included subtitles) the original rar-file was kept with name <.rar_duplicate1> even if the option <UnpackCleanupDisk> was active 2013-04-28 20:29:19 +00:00
Andrey Prygunkov
cb90c5e616 when logging messages from a post-processing script, a short name of the script is now used as prefix if possible; a short name doesn't include subdirectory name or file extension; RPC-method <configtemplates> returns new field <DisplayName> representing the short name of the script which is recommended for using in UI 2013-04-26 20:01:53 +00:00
Andrey Prygunkov
77059f2db0 improved unicode support in XML-RPC and JSON-RPC 2013-04-26 19:56:41 +00:00
Andrey Prygunkov
fb72c36a48 if a news-server returns empty or bad article (this may be caused by errors on the news server), the program tries again from the same or other servers (in previous versions the article was marked as failed without other download attempts) 2013-04-26 19:55:30 +00:00
Andrey Prygunkov
b2b215a061 updated ChangeLog 2013-04-24 20:26:05 +00:00
Andrey Prygunkov
8d313e4cf8 removed pp-script Cleanup.sh (its functionality is now part of the main program) 2013-04-24 20:24:30 +00:00
Andrey Prygunkov
6bb760375e added option <ExtCleanupDisk> to automatically delete unwanted files (with specified extensions) after successful par-check or unpack 2013-04-24 20:16:04 +00:00
Andrey Prygunkov
025cd043d3 history dialog now shows status of every script 2013-04-23 18:20:52 +00:00
Andrey Prygunkov
3f368d4a8e fixed: download time in statistics were incorrect if the computer were put into standby (thanks Frank Kuypers for the patch) 2013-04-21 20:23:32 +00:00
Andrey Prygunkov
449e41e435 removed unused file from repository 2013-04-21 19:56:28 +00:00
Andrey Prygunkov
bf0062be52 addition to r639: eliminated a compiler warning 2013-04-21 19:43:14 +00:00
Andrey Prygunkov
3c025c8b52 updated forum URL in about dialog in web-interface 2013-04-19 18:53:56 +00:00
Andrey Prygunkov
6dc3d954c5 fixed: authorization to news-server was forced even when username/password were empty (bug introduced in r634) 2013-04-19 18:44:32 +00:00
Andrey Prygunkov
c9b7a11a89 fixed: by adding nzb-files with assigned category and empty option <CategoryX.DefScript> the global option <DefScript> should be used but it wasn't 2013-04-19 17:34:17 +00:00
Andrey Prygunkov
33cb2d108e fixed: if a download didn't have any par-files and the option <ParCheck> was active, the par-check was started anyway and then failed 2013-04-18 21:04:48 +00:00
Andrey Prygunkov
e053e74b58 fixed: scripts containg spaces in their names were not assigned to nzb-files by adding to queue (when defined in option <DefScript>) 2013-04-18 20:28:31 +00:00
Andrey Prygunkov
4e35fc2fbe improved multiscripts: 1) first level subfolders in the ppscripts-directory (option <ScriptDir>) are now scanned for scripts too; 2) only files containing script definition signature are considered scripts and are shown in web-interface; 3) these changes allows to easily install collections of scripts (scripts bundles by just putting a folder with multiple scripts into ppscripts-directory); 2013-04-18 20:24:30 +00:00
Andrey Prygunkov
4768d8e459 if username and password are defined for a news-server the authentication is now forced (in previous versions the authentication was performed only if requested by server); needed for servers supporting both anonimous (restricted) and authorized (full access) accounts 2013-04-17 20:34:15 +00:00
Andrey Prygunkov
3598cc1d85 refactor: restructured class <Connection> 2013-04-17 20:21:46 +00:00
Andrey Prygunkov
e3a895b88c updated README 2013-04-15 20:22:16 +00:00
Andrey Prygunkov
61eff3ddf0 removed old example post-processing script 2013-04-15 20:17:24 +00:00
Andrey Prygunkov
e9268984ae added post-processing scripts EMail.py and Logger.py 2013-04-15 20:16:06 +00:00
Andrey Prygunkov
f28b35bd28 reworked concept of post-processing scripts: multiple scripts can be assigned to each nzb-file; all assigned scripts are executed after the nzb-file is downloaded and internally processed (unpack, repair); option <PostProcess> is obsolete; new option <ScriptDir> sets directory where all pp-scripts must be stored; new option <DefScript> sets the default list of pp-scripts to be assigned to nzb-file when it's added to queue; new option <CategoryX.DefScript> to set the default list of pp-scripts on a category basis; the execution order of pp-scripts can be set using new option <ScriptOrder>; there are no separate configuration files for pp-scripts; configuration options and pp-parameters are defined in the pp-scripts; script configuration options are saved in nzbget configuration file (nzbget.conf); changed parameters list of RPC-methods <loadconfig> and <saveconfig>; new RPC-method <configtemplates> returns configuration descriptions for the program and for all pp-scripts; configuration of all scripts can be done in web-interface; the pp-scripts assigned to a particular nzb-file can be viewed and changed in web-interface on page <pp-parameters> in the edit download dialog; option <PostPauseQueue> renamed to <ScriptPauseQueue> (the old name is still recognized); new option <ConfigTemplate> to define the location of template configuration file (in previous versions it must be always stored in <WebDir>) 2013-04-15 20:06:05 +00:00
Andrey Prygunkov
a86618c2c2 fixed: when options <DirectWrite> and <ContinuePartial> were both active, a restart or reload of the program during download may cause damaged files in the active download 2013-04-08 20:19:05 +00:00
Andrey Prygunkov
1f1a4b8fb8 fixed potential segfault which could happen with file paths longer than 1024 characters 2013-04-07 15:14:20 +00:00
Andrey Prygunkov
a1d0be34c2 updated README 2013-04-07 15:10:42 +00:00
Andrey Prygunkov
ef0a04cc1c improved unicode (utf8) support: non-ascii characters are now correctly transferred via JSON-RPC; correct displaying of nzb-names and paths in web-interface; it is now possible to use non-ascii characters on settings page for option values (such as paths or category names) 2013-04-06 21:03:05 +00:00
Andrey Prygunkov
284262b7da added new feature <split download> which creates new download from selected files of source download; new command <Split> in web-interface in edit download dialog on page <Files>; new action <S> in remote command <--edit/-E>; new action <FileSplit> in JSON-/XML-RPC method <editqueue> 2013-04-06 20:54:00 +00:00
Andrey Prygunkov
58b0a17986 reworked post-processor queue: 1) only one job is created for each nzb-file; no more separate jobs are created for par-collections within one nzb-file; 2) option <AllowReProcess> removed; a post-processing script is called only once per nzb-file, this behavior cannot be altered anymore; 3) with a new feature <Split> (see next commits) individual par-collections can be processed separately in a more effective way than before 2013-04-06 20:25:07 +00:00
Andrey Prygunkov
57abe00c62 updated version string to 11.0-testing 2013-04-06 12:58:50 +00:00
Andrey Prygunkov
d014407ba4 updated ChangeLog 2013-03-31 14:37:57 +00:00
Andrey Prygunkov
2e8bfa16f9 fixed: articles with decoding errors (incomplete or damaged posts) caused infinite retry-loop in downloader 2013-03-31 14:36:15 +00:00
Andrey Prygunkov
bf34713b0c updated version string to 10.1 2013-03-31 14:10:57 +00:00
Andrey Prygunkov
c46c1a96cd updated version string (preparing to release 10.0) 2013-03-29 21:07:24 +00:00
Andrey Prygunkov
2693b62de4 if an obsolete option is found in the config file a warning is printed instead of an error and the program is not paused anymore 2013-03-23 15:07:25 +00:00
Andrey Prygunkov
18387f6d98 fixed: when the option <ContinuePartial> is active and there are partially downloaded files in queue, after reloading/restarting of the program the file may stuck with status <downloading>; trying to reload or quit the program in this state resulted in a crash (bug introduced in r599) 2013-03-22 22:07:23 +00:00
Andrey Prygunkov
08b7356184 adding of local files via web-interface now works in IE10 2013-03-18 21:43:01 +00:00
Andrey Prygunkov
e30cdfc176 addition to r602: fixed: if news servers from different levels were defined with the same group (bad config actually), download could hang when waiting for a free connection to a higher level server 2013-03-17 20:24:25 +00:00
Andrey Prygunkov
cc0ed38e68 refactor: removed dead code 2013-03-17 12:43:11 +00:00
Andrey Prygunkov
5ec0d20286 improvement in news-server/connection management: new option <ServerX.Group> allows more flexible configuration of news servers when using multiple accounts on the same server; with this option it's also possible to imitate the old server management behavior regarding levels as it was before r599 2013-03-17 12:21:46 +00:00
Andrey Prygunkov
e0aa69f605 improvement in news-server/connection management: do not reconnect on <article/group not found> errors since this doesn't help but unnecessary increases CPU load and network traffic 2013-03-16 15:24:01 +00:00
Andrey Prygunkov
c859f39036 addition to r599: fixed: download could be cancelled when waiting for a free connection to news server 2013-03-16 13:32:35 +00:00
Andrey Prygunkov
5251f62665 major improvement in news-server/connection management (main and fill servers): if download of article fails, the program tries all servers of the same level before trying higher level servers; this ensures that fill servers are used only if all main servers fail; this makes the configuring of multiple servers much easier than before: in most cases the simple configuration of level 0 for all main servers and level 1 for all fill servers suffices; in previous versions the level was increased immediately after the first tried server of the level failed; to make sure all main servers were tried before downloading from fill servers it was required to create complex server configurations with duplicates; these configurations were still not as effective as now 2013-03-14 22:30:59 +00:00
Andrey Prygunkov
2b87e2b221 fixed: download could be cancelled when waiting for a free connection on a second-(or higher)-level news server 2013-03-12 22:24:29 +00:00
Andrey Prygunkov
c185e9b487 removed unneeded code from configure-script 2013-03-12 21:33:45 +00:00
Andrey Prygunkov
b5d9a99f10 fixed: special characters (quotation marks, etc.) in unpack password and in configuration options were not displayed properly and could be discarded on saving 2013-03-12 20:28:03 +00:00
Andrey Prygunkov
fec67fe0ea fixed: some characters with codes below 32 were not properly encoded in JSON-RPC; sometimes output from unrar contained such characters and could break web-interface 2013-03-12 20:22:08 +00:00
Andrey Prygunkov
987997a986 fixed: when option <UnpackCleanupDisk> is active the unpacked archive-files (second level archives) could be deleled too (mostly affected 7-Zip archives but sometimes also rar-archives if the second level rar-files had same names as the first level rars) 2013-03-11 20:03:47 +00:00
Andrey Prygunkov
27ef79ca27 immediately clearing post-process progress label after unpack to avoid status update lag in web-interface 2013-03-11 20:00:49 +00:00
Andrey Prygunkov
bcc1932a37 addition to r586/r591: added automatic speed meter recalibration to recover after possible synchronisation errors which can occur when the option <AccurateRate> is not active; this makes the default (less accurate but fast) speed meter almost as good as the accurate one; important when speed throttling is active 2013-03-11 19:54:14 +00:00
Andrey Prygunkov
2e163e9986 reverted r586 <automatic recovery after synchronisation errors>: needs more testing 2013-03-10 22:50:34 +00:00
Andrey Prygunkov
5473e57b10 fixed: if an external program (unrar, pp-script, etc.) could not be started, the execute-function has returned code 255 although the code -1 were expected in this case; this could break designed post-processing flow 2013-03-10 22:19:46 +00:00
Andrey Prygunkov
b970bad058 imporved configure-script: 1) libs which are added via pkgconfig are now put into LIBS instead of LDFLAGS - improves compatibility with newer Linux linkers; 2) OpenSSL libs/includes are now added using pkgconfig to better handle dependencies; 3) additional check for libcrypto (part of OpenSSL) ensures the library is added to linker command even if pkgconfig is not used 2013-03-10 15:20:41 +00:00
Andrey Prygunkov
bea1814cb9 removed a line of unused code (introduced in r579) 2013-03-09 22:29:18 +00:00
Andrey Prygunkov
45e58f29b4 corrections in code formatting; no actual code changes 2013-03-09 22:23:58 +00:00
Andrey Prygunkov
f7a3df635a added automatic recovery after synchronisation errors which can occur in speed meter when the option <AccurateRate> is not active; this makes the <inaccurate/fast/default> speed meter much more reliable; important when speed throttling is active; a warning is printed to log to indicate detected error and reset of speed meter 2013-03-09 22:21:44 +00:00
Andrey Prygunkov
3f67984929 changed default value for option <ServerX.JoinGroup> to <no>; most news servers nowadays do not require joining the group and many servers do not keep headers for many groups making the join-command fail even if the articles still can be successfully downloaded 2013-03-09 14:51:48 +00:00
Andrey Prygunkov
bf82171baa removed hint <Post-processing script may have moved files elsewhere> from history dialog since it caused more questions than helped 2013-03-08 19:21:43 +00:00
Andrey Prygunkov
4c49a7f003 added link to wiki-article <Performance tips> to settings tab on web-interface 2013-03-08 19:10:34 +00:00
Andrey Prygunkov
57b5d40851 when post-processing-parameters are passed to the post-processing script a second version of each parameter with a normalized parameter-name is passed in addition to the original parameter name; in the normalized name the special characters <*> and <:> are replaced with <_> and all characters are passed in upper case; this is important for internal post-processing-parameters (*Unpack:=yes/no) which includes special characters 2013-03-07 16:45:41 +00:00
Andrey Prygunkov
ecde2d1627 improved post-processing script: better handling of nzb-files not having archive files 2013-03-07 16:45:08 +00:00
Andrey Prygunkov
b48e9a31f6 updated description of option <ServerX.Cipher> and added link to wiki article <Choosing a cipher> 2013-03-06 21:52:15 +00:00
Andrey Prygunkov
f64e5241ed improved the handling of hanging connections: if a connection hangs longer than defined by option <ConnectionTimeout> the program tries to gracefully close connection first (this is new); if it still hangs after <TerminateTimeout> the download thread is terminated as a last resort (as in previous versions) 2013-03-06 21:36:09 +00:00
Andrey Prygunkov
4e9d01055a news servers configuration is now less error-prone: 1) the option <ServerX.Level> is not required to start from <0> and when several news servers are configured the Levels can be any integers - the program sorts the servers and corrects the Levels to 0,1,2,etc. automatically if needed; 2) when option <ServerX.Connections> is set to <0> the server is ignored (in previous version such a server could cause hanging when the program was trying to go to the next level); 3) if no news servers are defined (or all definitions are invalid) a warning is printed to inform that the download is not possible 2013-03-06 21:35:31 +00:00
Andrey Prygunkov
9d4ca25499 updated VC++ project file 2013-03-04 22:16:25 +00:00
Andrey Prygunkov
f1ddf9dc2b addition to r568: corrected saving of diskstate for post-processor queue 2013-03-04 22:14:15 +00:00
Andrey Prygunkov
e99b790d58 added new option <ServerX.Cipher> to manually select cipher for encrypted communication with news server; manually choosing a faster cipher (such as <RC4>) can significantly improve performance (if CPU is a limiting factor) 2013-03-04 22:01:14 +00:00
Andrey Prygunkov
5e4a99c1ad addition to r572: changed the log-messages for deleting of 7-zip-files to <info> too 2013-03-04 21:28:37 +00:00
Andrey Prygunkov
c89824bf25 the log-messages <deleting file *file*> (when option <UnpackCleanupDisk> is active) and <moving file *file* to *destination*> are now printed as <info> instead of <detail> (since <detail> is for article related messages whereas <info> is more suitable for file related messages) 2013-03-04 20:28:27 +00:00
Andrey Prygunkov
1230d9cdd4 added fast renaming of intentionally misnamed (rar-) files; the new renaming algorithm doesn't require full par-scan and restores original filenames in just a few seconds, even on very slow computers (NAS, media players, etc.); the fast renaming is performed automatically when requested by the built-in unpacker (option <Unpack> must be active) 2013-03-04 19:55:36 +00:00
Andrey Prygunkov
d3dd8dc686 fixed a compilation warning 2013-03-03 20:48:14 +00:00
Andrey Prygunkov
382faa49cb added new option <InterDir> to put intermediate files during download into a separate directory (instead of storing them directly in destination directory (option <DestDir>); when nzb-file is completely (successfully) downloaded, repaired (if neccessary) and unpacked the files are moved to destination directory (option <DestDir> or <CategoryX.DestDir>); intermediate directory can significantly improve unpack performance if it is located on a separate physical hard drive 2013-03-01 20:32:17 +00:00
Andrey Prygunkov
5cf4b4663f addition to r568: corrected diskstate version check 2013-02-28 20:47:30 +00:00
Andrey Prygunkov
749b4d3083 when a history item is post-processed again and the archive files were previously deleted because of option <UnpackCleanupDisk> the post-processing goes directly to script stage; if the archive files were kept, the full post-processing including unpack is performed instead 2013-02-28 20:23:50 +00:00
Andrey Prygunkov
02835d057e fixed: remote commands <--list/-L> and <--connect/-C> showed download speed and speed limit in Bytes instead of KiloBytes (bug introduced in r544) 2013-02-15 08:04:23 +00:00
Andrey Prygunkov
f5aaaecc48 fixed: RPC-method <history> returned incorrect Par-Status (bug introduced in r563) 2013-02-14 15:06:10 +00:00
Andrey Prygunkov
184ff84f92 small change in example post-processing script: message <Deleting source ts-files> are now printed only if ts-files really existed 2013-02-13 19:47:48 +00:00
Andrey Prygunkov
ef56bc1f55 fixed: par-status <FAILED> was not correctly checked in the example post-processing script (bug introduced in r558) 2013-02-13 17:05:26 +00:00
Andrey Prygunkov
9846f7509e fixed: parameter <NZBPP_PARSTATUS> was not correctly passed to post-processing script when the download was repaired 2013-02-13 16:54:13 +00:00
Andrey Prygunkov
1cf7cefe83 updated VC++ project file 2013-02-12 22:12:43 +00:00
Andrey Prygunkov
b5f1dbc47b changed formatting of remaining time for post-processing to short format (as used for remaining download time) 2013-02-12 14:10:44 +00:00
Andrey Prygunkov
37b85491c3 fixed: RPC-method <history> returned bad results for URLs; impacts history tab in web-interface (bug introduced in r555) 2013-02-10 21:35:46 +00:00
Andrey Prygunkov
30d792b35b when running external programs (such as unrar or post-processing script) the full path to the program is not neccessary since the search in system PATH is now performed 2013-02-09 10:58:14 +00:00
Andrey Prygunkov
ac29412b2f updated example post-processing script: added check for nzbget version (at least 10.0) and option <Unpack>, small other corrections 2013-02-08 13:16:29 +00:00
Andrey Prygunkov
01c170afaf fixed a compilation warning 2013-02-07 19:32:07 +00:00
Andrey Prygunkov
539e0811c9 fixed compilation error on Linux 2013-02-07 19:30:11 +00:00
Andrey Prygunkov
940448ffae added built-in unpack: 1) rar and 7-zip formats are supported (via external Unrar and 7-Zip executables); 2) new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>, <UnrarCmd>, <SevenZipCmd>; 3) web-interface now shows progress and estimated time during unpack (rar only; for 7-Zip progress is not available due to limitations of 7-Zip) 4) when built-in unpack is enabled, the post-processing script is called after unpack and possibly par-check/repair (if needed); 5) for nzb-files containing multiple collections (par-sets) the post-processing script is called only once, after the last par-set; 6) new parameter <NZBPP_UNPACKSTATUS> passed to post-processing script; 7) if the option <AllowReProcess> is enabled the post-processing-script is called after each par-set (as in previous versions); 8) example post-processing script updated: removed unrar-code, added check for unpack status; 9) new field <UnpackStatus> in result of RPC-method <history>; 10) history-dialog in web-interface shows three status: par-status, unpack-status, script-status; 11) with two built-in special post-processing parameters <*Unpack:> and <*Unpack:Password> the unpack can be disabled for individual nzb-file or the password can be set; 12) built-in special post-processing parameters can be set via web-interface on page <PP-Parameters> (when built-in unpack is enabled). 2013-02-06 22:04:50 +00:00
Andrey Prygunkov
68a73f96c4 warning <Non-nzbget request received> now is not printed when the connection was aborted before the request signature was read 2013-01-31 20:49:52 +00:00
Andrey Prygunkov
87a93745cb when the par-checked requests more par-files, they get an extra priority and are downloaded before other files regardless of their priorities; this is needed to avoid hanging of par-checker-job if a file with a higher priority gets added to queue during par-check 2013-01-28 23:22:15 +00:00
Andrey Prygunkov
3b08abca10 refactor: extracted par-related code from module <PrePostProcessor> into new module <ParCoordinator> 2013-01-23 21:32:36 +00:00
Andrey Prygunkov
5e68096a2e added validation for option <CategoryX.DestDir>; removed a superfluous slash in the generated destination path 2013-01-22 22:16:18 +00:00
Andrey Prygunkov
e3ef11ceae improved error reporting for connection errors (especially on Windows) 2013-01-21 21:29:45 +00:00
Andrey Prygunkov
575fe8379f improved error reporting for connection errors when using OpenSSL 2013-01-21 21:28:51 +00:00
Andrey Prygunkov
60feae7e5b new feature <Pause for X Minutes> in web-interface; new XML-/JSON-RPC method <scheduleresume> 2013-01-21 21:19:04 +00:00
Andrey Prygunkov
2b45ecaea1 fixed: some XML-/JSON-RPC methods may return negative values for file sizes between 2-4GB; this had also influence on web-interface 2013-01-21 20:44:30 +00:00
Andrey Prygunkov
7a3a430137 fixed warning <file glyphicons-halflings.png not found> 2013-01-20 11:19:10 +00:00
Andrey Prygunkov
11c0563fe5 refactor: download speed and speed limit are now internally integers (Bytes) instead of floats (KB) 2013-01-18 21:36:17 +00:00
Andrey Prygunkov
00018b3e89 improved the automatic par-scan (option <ParScan=auto>) to significantly reduce the verify-time in some common cases with renamed rar-files: 1) the extra files are scanned in an optimized order; 2) the scan stops when all missings files are found 2013-01-17 22:08:01 +00:00
Andrey Prygunkov
de787a069d added support for HTTPS to the built-in web-server (web-interface and XML/JSON-RPC); new options <SecureControl>, <SecurePort>, <SecureCert> and <SecureKey>; Module <TLS.c/h> completely rewritten with support for servers-side sockets, newer versions of GnuTLS, proper thread lockings in OpenSSL 2013-01-17 19:07:13 +00:00
Andrey Prygunkov
5f33ea6013 merge from 9-branch: improved the post-processing script to better handle renamed rar-files 2013-01-15 22:00:44 +00:00
Andrey Prygunkov
ee74c4c17f refactor: reformatted TLS.c/.h 2013-01-13 17:46:11 +00:00
Andrey Prygunkov
b031f52ee2 replaced a browser error message when trying to add local files in IE9 with a better message dialog 2013-01-06 19:50:30 +00:00
Andrey Prygunkov
77bb01b18c small changes in libpar2-patches for compatibility with optware 2013-01-06 13:00:13 +00:00
Andrey Prygunkov
d2fdc28c85 refactor: reworked Connection-class: fully encapsulted sockets; better read/write methods 2012-12-30 15:27:38 +00:00
Andrey Prygunkov
d34e985a92 addition to r525: updated config.h.in 2012-12-20 22:04:24 +00:00
Andrey Prygunkov
f0c2c834c3 fixed: segfault if a category didn't have a destination directory defined (bug introduced in r524) 2012-12-20 22:00:46 +00:00
Andrey Prygunkov
ca0cce9401 addition to r525: corrected and updated configure-script 2012-12-19 21:52:24 +00:00
Andrey Prygunkov
2a0e211daf fixed: the reported line numbers for configuration errors were sometimes inaccurate 2012-12-19 20:37:07 +00:00
Andrey Prygunkov
ea89983e45 added full par-scan feature needed to par-check/repair files which were renamed after creation of par-files; new option <ParScan> to activate full par-scan (always or automatic); the automatic full par-scan activates if missing files are detected during par-check, this avoids unnecessary full scan for normal (not renamed) par sets 2012-12-19 20:16:17 +00:00
Andrey Prygunkov
e4ed1c8fd7 categories can now have their own destination directories 2012-12-18 22:29:24 +00:00
Andrey Prygunkov
d46155bf32 updated version string to 10.0-testing 2012-12-18 22:09:09 +00:00
Andrey Prygunkov
9bdf0d8937 updated version string in windows version 2012-12-18 21:45:45 +00:00
92 changed files with 11787 additions and 6932 deletions

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
@@ -110,17 +110,35 @@ void ArticleDownloader::SetInfoName(const char * v)
m_szInfoName = strdup(v);
}
void ArticleDownloader::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
/*
* How server management (for one particular article) works:
- there is a list of failed servers which is initially empty;
- level is initially 0;
<loop>
- request a connection from server pool for current level;
Exception: this step is skipped for the very first download attempt, because a
level-0 connection is initially passed from queue manager;
- try to download from server;
- if connection to server cannot be established or download fails due to interrupted connection,
try again (as many times as needed without limit) the same server until connection is OK;
- if download fails with error "Not-Found" (article or group not found) or with CRC error,
add the server to failed server list;
- if download fails with general failure error (article incomplete, other unknown error
codes), try the same server again as many times as defined by option <Retries>; if all attempts
fail, add the server to failed server list;
- if all servers from current level were tried, increase level;
- if all servers from all levels were tried, break the loop with failure status.
<end-loop>
*/
void ArticleDownloader::Run()
{
debug("Entering ArticleDownloader-loop");
SetStatus(adRunning);
BuildOutputFilename();
m_szResultFilename = m_pArticleInfo->GetResultFilename();
if (g_pOptions->GetContinuePartial())
@@ -129,59 +147,44 @@ void ArticleDownloader::Run()
{
// file exists from previous program's start
detail("Article %s already downloaded, skipping", m_szInfoName);
SetStatus(adFinished);
FreeConnection(true);
SetStatus(adFinished);
Notify(NULL);
return;
}
}
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
#ifdef THREADCONNECT_WORKAROUND
// NOTE: about iRemainedConnectRetries:
// Sometimes connections just do not want to work in a particular thread,
// regardless of retry count. However they work in other threads.
// If ArticleDownloader can't start download after many attempts, it terminates
// and let QueueCoordinator retry the article in a new thread.
// It wasn't confirmed that this workaround actually helps.
// Therefore it is disabled by default. Define symbol "THREADCONNECT_WORKAROUND"
// to activate the workaround.
int iRemainedConnectRetries = iRemainedDownloadRetries > 5 ? iRemainedDownloadRetries * 2 : 10;
#endif
EStatus Status = adFailed;
int iMaxLevel = g_pServerPool->GetMaxLevel();
int* LevelStatus = (int*)malloc((iMaxLevel + 1) * sizeof(int));
for (int i = 0; i <= iMaxLevel; i++)
{
LevelStatus[i] = 0;
}
int level = 0;
while (!IsStopped() && iRemainedDownloadRetries > 0)
{
SetLastUpdateTimeNow();
int iRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedRetries = iRetries;
ServerPool::Servers failedServers;
failedServers.reserve(g_pServerPool->GetServers()->size());
NewsServer* pWantServer = NULL;
NewsServer* pLastServer = NULL;
int iLevel = 0;
while (!IsStopped())
{
Status = adFailed;
SetStatus(adWaiting);
while (!IsStopped() && !m_pConnection)
{
m_pConnection = g_pServerPool->GetConnection(level);
m_pConnection = g_pServerPool->GetConnection(iLevel, pWantServer, &failedServers);
usleep(5 * 1000);
}
SetLastUpdateTimeNow();
SetStatus(adRunning);
if (IsStopped())
{
Status = adFailed;
break;
}
if (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
}
pLastServer = m_pConnection->GetNewsServer();
m_pConnection->SetSuppressErrors(false);
// test connection
@@ -189,7 +192,8 @@ void ArticleDownloader::Run()
if (bConnected && !IsStopped())
{
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s @ %s", m_szInfoName, m_pConnection->GetHost());
detail("Downloading %s @ server%i (%s)", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
Status = Download();
}
@@ -200,9 +204,6 @@ void ArticleDownloader::Run()
m_pConnection->Disconnect();
bConnected = false;
Status = adFailed;
#ifdef THREADCONNECT_WORKAROUND
iRemainedConnectRetries--;
#endif
}
else
{
@@ -212,28 +213,32 @@ void ArticleDownloader::Run()
// free the connection, to prevent starting of thousands of threads
// (cause each of them will also free it's connection after the
// same connect-error).
FreeConnection(Status == adFinished);
FreeConnection(Status == adFinished || Status == adNotFound);
}
}
#ifdef THREADCONNECT_WORKAROUND
else
{
iRemainedConnectRetries--;
}
if (iRemainedConnectRetries == 0)
if (Status == adFinished || Status == adFatalError)
{
debug("Can't connect from this thread, retry later from another");
Status = adRetry;
break;
}
#endif
if (((Status == adFailed) || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
(iRemainedDownloadRetries > 1 || !bConnected) && !IsStopped() &&
pWantServer = NULL;
if (bConnected && Status == adFailed)
{
iRemainedRetries--;
}
if (!bConnected || (Status == adFailed && iRemainedRetries > 0))
{
pWantServer = pLastServer;
}
if (pWantServer && !IsStopped() &&
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
SetStatus(adWaiting);
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
@@ -241,62 +246,70 @@ void ArticleDownloader::Run()
usleep(100 * 1000);
msec += 100;
}
SetLastUpdateTimeNow();
SetStatus(adRunning);
}
if (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
}
if (IsStopped())
if (!pWantServer)
{
Status = adFailed;
break;
}
if ((Status == adFinished) || (Status == adFatalError) ||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
{
break;
}
failedServers.push_back(pLastServer);
LevelStatus[level] = Status;
// if all servers from current level were tried, increase level
// if all servers from all levels were tried, break the loop with failure status
bool bAllLevelNotFound = true;
for (int lev = 0; lev <= iMaxLevel; lev++)
{
if (LevelStatus[lev] != adNotFound)
bool bAllServersOnLevelFailed = true;
for (ServerPool::Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
bAllLevelNotFound = false;
break;
NewsServer* pCandidateServer = *it;
if (pCandidateServer->GetLevel() == iLevel)
{
bool bServerFailed = false;
for (ServerPool::Servers::iterator it = failedServers.begin(); it != failedServers.end(); it++)
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetLevel() == pCandidateServer->GetLevel()))
{
bServerFailed = true;
break;
}
}
if (!bServerFailed)
{
bAllServersOnLevelFailed = false;
break;
}
}
}
}
if (bAllLevelNotFound)
{
if (iMaxLevel > 0)
{
warn("Article %s @ all servers failed: Article not found", m_szInfoName);
}
break;
}
// do not count connect-errors, only article- and group-errors
if (bConnected)
{
level++;
if (level > iMaxLevel)
if (bAllServersOnLevelFailed)
{
level = 0;
if (iLevel < g_pServerPool->GetMaxLevel())
{
detail("Article %s @ all level %i servers failed, increasing level", m_szInfoName, iLevel);
iLevel++;
}
else
{
warn("Article %s @ all servers failed", m_szInfoName);
Status = adFailed;
break;
}
}
iRemainedDownloadRetries--;
iRemainedRetries = iRetries;
}
}
FreeConnection(Status == adFinished);
free(LevelStatus);
if (m_bDuplicate)
{
Status = adFinished;
@@ -307,19 +320,19 @@ void ArticleDownloader::Run()
Status = adFailed;
}
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
Status = adRetry;
}
if (Status == adFailed)
{
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
}
else
{
warn("Download %s failed", m_szInfoName);
}
warn("Download %s failed", m_szInfoName);
}
SetStatus(Status);
Notify(NULL);
debug("Exiting ArticleDownloader-loop");
}
@@ -356,7 +369,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
for (int retry = 3; retry > 0; retry--)
{
szResponse = m_pConnection->Request(tmp);
if (szResponse && !strncmp(szResponse, "2", 1))
if ((szResponse && !strncmp(szResponse, "2", 1)) || m_pConnection->GetAuthError())
{
break;
}
@@ -407,7 +420,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
{
if (!IsStopped())
{
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetHost());
warn("Article %s @ server%i (%s) failed: Unexpected end of article", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
}
Status = adFailed;
break;
@@ -441,7 +455,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
{
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
warn("Article %s @ server%i (%s) failed: Wrong message-id, expected %s, returned %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
Status = adFailed;
break;
}
@@ -469,7 +484,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (!bEnd && Status == adRunning && !IsStopped())
{
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetHost());
warn("Article %s @ server%i (%s) failed: article incomplete", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
Status = adFailed;
}
@@ -496,18 +512,21 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
{
if (!IsStopped())
{
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetHost(), szComment);
warn("Article %s @ server%i (%s) failed, %s: Connection closed by remote host", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment);
}
return adConnectError;
}
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
{
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adConnectError;
}
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2) || !strncmp(szResponse, "43", 2))
{
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adNotFound;
}
else if (!strncmp(szResponse, "2", 1))
@@ -518,7 +537,8 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
else
{
// unknown error, no special handling
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adFailed;
}
}
@@ -571,7 +591,8 @@ bool ArticleDownloader::PrepareFile(char* szLine)
if (g_pOptions->GetDupeCheck())
{
m_pFileInfo->LockOutputFile();
if (!m_pFileInfo->GetOutputInitialized())
bool bOutputInitialized = m_pFileInfo->GetOutputInitialized();
if (!bOutputInitialized)
{
char* pb = strstr(szLine, " name=");
if (pb)
@@ -585,11 +606,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
}
if (m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
}
if (!g_pOptions->GetDirectWrite())
@@ -597,6 +613,11 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pFileInfo->SetOutputInitialized(true);
}
m_pFileInfo->UnlockOutputFile();
if (!bOutputInitialized && m_szArticleFilename && m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
if (g_pOptions->GetDirectWrite())
@@ -609,9 +630,9 @@ bool ArticleDownloader::PrepareFile(char* szLine)
{
pb += 6; //=strlen(" size=")
long iArticleFilesize = atol(pb);
if (!Util::CreateSparseFile(m_szOutputFilename, iArticleFilesize))
if (!CreateOutputFile(iArticleFilesize))
{
error("Could not create file %s!", m_szOutputFilename);
m_pFileInfo->UnlockOutputFile();
return false;
}
m_pFileInfo->SetOutputInitialized(true);
@@ -654,6 +675,77 @@ bool ArticleDownloader::PrepareFile(char* szLine)
return true;
}
/* creates output file and subdirectores */
bool ArticleDownloader::CreateOutputFile(int iSize)
{
if (g_pOptions->GetDirectWrite() && Util::FileExists(m_szOutputFilename) &&
Util::FileSize(m_szOutputFilename) == iSize)
{
// keep existing old file from previous program session
return true;
}
// delete eventually existing old file from previous program session
remove(m_szOutputFilename);
// ensure the directory exist
char szDestDir[1024];
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
strncpy(szDestDir, m_szOutputFilename, iMaxlen);
szDestDir[iMaxlen] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", szDestDir, szErrBuf);
return false;
}
if (!Util::CreateSparseFile(m_szOutputFilename, iSize))
{
error("Could not create file %s", m_szOutputFilename);
return false;
}
return true;
}
void ArticleDownloader::BuildOutputFilename()
{
char szFilename[1024];
snprintf(szFilename, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), m_pFileInfo->GetID(), m_pArticleInfo->GetPartNumber());
szFilename[1024-1] = '\0';
m_pArticleInfo->SetResultFilename(szFilename);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", szFilename);
tmpname[1024-1] = '\0';
SetTempFilename(tmpname);
if (g_pOptions->GetDirectWrite())
{
m_pFileInfo->LockOutputFile();
if (m_pFileInfo->GetOutputFilename())
{
strncpy(szFilename, m_pFileInfo->GetOutputFilename(), 1024);
szFilename[1024-1] = '\0';
}
else
{
snprintf(szFilename, 1024, "%s%c%i.out.tmp", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetID());
szFilename[1024-1] = '\0';
m_pFileInfo->SetOutputFilename(szFilename);
}
m_pFileInfo->UnlockOutputFile();
SetOutputFilename(szFilename);
}
}
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
{
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
@@ -675,7 +767,7 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
else
{
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFatalError;
return adFailed;
}
Decoder::EStatus eStatus = pDecoder->Check();
@@ -853,9 +945,10 @@ void ArticleDownloader::CompleteFileParts()
}
// Ensure the DstDir is created
if (!Util::ForceDirectories(szNZBDestDir))
char szErrBuf[1024];
if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s! Errcode: %i", szNZBDestDir, errno);
error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
SetStatus(adJoined);
return;
}
@@ -985,6 +1078,24 @@ void ArticleDownloader::CompleteFileParts()
{
error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
}
// if destination directory was changed delete the old directory (if empty)
int iLen = strlen(szNZBDestDir);
if (!(!strncmp(szNZBDestDir, m_szOutputFilename, iLen) &&
(m_szOutputFilename[iLen] == PATH_SEPARATOR || m_szOutputFilename[iLen] == ALT_PATH_SEPARATOR)))
{
debug("Checking old dir for: %s", m_szOutputFilename);
char szOldDestDir[1024];
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
strncpy(szOldDestDir, m_szOutputFilename, iMaxlen);
szOldDestDir[iMaxlen] = '\0';
if (Util::DirEmpty(szOldDestDir))
{
debug("Deleting old dir: %s", szOldDestDir);
rmdir(szOldDestDir);
}
}
}
if (!bDirectWrite || g_pOptions->GetContinuePartial())
@@ -1004,27 +1115,6 @@ void ArticleDownloader::CompleteFileParts()
{
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
if (g_pOptions->GetRenameBroken())
{
char brokenfn[1024];
snprintf(brokenfn, 1024, "%s_broken", ofn);
brokenfn[1024-1] = '\0';
if (Util::MoveFile(ofn, brokenfn))
{
detail("Renaming broken file from %s to %s", ofn, brokenfn);
}
else
{
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
}
strncpy(ofn, brokenfn, 1024);
ofn[1024-1] = '\0';
}
else
{
detail("Not renaming broken file %s", ofn);
}
if (g_pOptions->GetCreateBrokenLog())
{
char szBrokenLogName[1024];
@@ -1057,9 +1147,10 @@ bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldD
}
// Ensure the DstDir is created
if (!Util::ForceDirectories(pNZBInfo->GetDestDir()))
char szErrBuf[1024];
if (!Util::ForceDirectories(pNZBInfo->GetDestDir(), szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s! Errcode: %i", pNZBInfo->GetDestDir(), errno);
error("Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
return false;
}

View File

@@ -42,10 +42,10 @@ public:
{
adUndefined,
adRunning,
adWaiting,
adFinished,
adFailed,
adRetry,
adDecodeError,
adCrcError,
adDecoding,
adJoining,
@@ -76,9 +76,15 @@ private:
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
bool CreateOutputFile(int iSize);
void BuildOutputFilename();
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
public:
ArticleDownloader();
@@ -87,16 +93,12 @@ public:
FileInfo* GetFileInfo() { return m_pFileInfo; }
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
virtual void Run();
virtual void Stop();
bool Terminate();
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
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; }
@@ -111,7 +113,7 @@ class DownloadSpeedMeter
{
public:
virtual ~DownloadSpeedMeter() {};
virtual float CalcCurrentDownloadSpeed() = 0;
virtual int CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,8 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
@@ -53,11 +52,13 @@
#include "PrePostProcessor.h"
#include "Util.h"
#include "DownloadInfo.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern UrlCoordinator* g_pUrlCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Scanner* g_pScanner;
extern void ExitProc();
extern void Reload();
@@ -89,29 +90,28 @@ const unsigned int g_iMessageRequestSizes[] =
//*****************************************************************
// BinProcessor
BinRpcProcessor::BinRpcProcessor()
{
m_MessageBase.m_iSignature = (int)NZBMESSAGE_SIGNATURE;
}
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)
if (!m_pConnection->Recv(((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature)))
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
// Make sure this is a nzbget request from a client
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(m_MessageBase.m_szUsername, g_pOptions->GetControlUsername())) ||
strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_szClientIP);
warn("nzbget request received on port %i from %s, but username or password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
{
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_szClientIP);
return;
}
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_szClientIP);
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_pConnection->GetRemoteAddr());
Dispatch();
}
@@ -202,7 +202,7 @@ void BinRpcProcessor::Dispatch()
if (command)
{
command->SetSocket(m_iSocket);
command->SetConnection(m_pConnection);
command->SetMessageBase(&m_MessageBase);
command->Execute();
delete command;
@@ -224,8 +224,8 @@ void BinCommand::SendBoolResponse(bool bSuccess, const char* szText)
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
// Send the request answer
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
send(m_iSocket, (char*)szText, iTextLen, 0);
m_pConnection->Send((char*) &BoolResponse, sizeof(BoolResponse));
m_pConnection->Send((char*)szText, iTextLen);
}
bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
@@ -234,8 +234,7 @@ bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
iSize -= sizeof(SNZBRequestBase);
if (iSize > 0)
{
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
if (iBytesReceived != iSize)
if (!m_pConnection->Recv(((char*)pBuffer) + sizeof(SNZBRequestBase), iSize))
{
error("invalid request");
return false;
@@ -252,6 +251,8 @@ void PauseUnpauseBinCommand::Execute()
return;
}
g_pOptions->SetResumeTime(0);
switch (ntohl(PauseUnpauseRequest.m_iAction))
{
case eRemotePauseUnpauseActionDownload:
@@ -282,7 +283,7 @@ void SetDownloadRateBinCommand::Execute()
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate));
SendBoolResponse(true, "Rate-Command completed successfully");
}
@@ -342,59 +343,29 @@ void DownloadBinCommand::Execute()
return;
}
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
int iBufLen = ntohl(DownloadRequest.m_iTrailingDataLength);
char* pRecvBuffer = (char*)malloc(iBufLen);
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
while (NeedBytes > 0)
if (!m_pConnection->Recv(pRecvBuffer, iBufLen))
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
error("invalid request");
free(pRecvBuffer);
return;
}
int iPriority = ntohl(DownloadRequest.m_iPriority);
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
bool bAddTop = ntohl(DownloadRequest.m_bAddFirst);
if (NeedBytes == 0)
{
int iPriority = ntohl(DownloadRequest.m_iPriority);
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
bool bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory,
iPriority, NULL, bAddTop, bAddPaused, NULL, pRecvBuffer, iBufLen, true);
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
char tmp[1024];
snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s",
Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
if (pNZBFile)
{
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
pFileInfo->SetPaused(bAddPaused);
}
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);
}
}
SendBoolResponse(bOK, tmp);
free(pRecvBuffer);
}
@@ -606,11 +577,11 @@ void ListBinCommand::Execute()
if (htonl(ListRequest.m_bServerState))
{
unsigned long iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
ListResponse.m_iDownloadRate = htonl(g_pQueueCoordinator->CalcCurrentDownloadSpeed());
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_iDownloadLimit = htonl(g_pOptions->GetDownloadRate());
ListResponse.m_bDownloadPaused = htonl(g_pOptions->GetPauseDownload());
ListResponse.m_bDownload2Paused = htonl(g_pOptions->GetPauseDownload2());
ListResponse.m_bPostPaused = htonl(g_pOptions->GetPausePostProcess());
@@ -633,12 +604,12 @@ void ListBinCommand::Execute()
}
// Send the request answer
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
m_pConnection->Send((char*) &ListResponse, sizeof(ListResponse));
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
m_pConnection->Send(buf, bufsize);
}
if (buf)
@@ -724,12 +695,12 @@ void LogBinCommand::Execute()
LogResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
m_pConnection->Send((char*) &LogResponse, sizeof(LogResponse));
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
m_pConnection->Send(buf, bufsize);
}
free(buf);
@@ -760,24 +731,13 @@ void EditQueueBinCommand::Execute()
}
char* pBuf = (char*)malloc(iBufLength);
// Read from the socket until nothing remains
char* pBufPtr = pBuf;
int NeedBytes = iBufLength;
int iResult = 0;
while (NeedBytes > 0)
if (!m_pConnection->Recv(pBuf, iBufLength))
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
error("invalid request");
free(pBuf);
return;
}
bool bOK = NeedBytes == 0;
if (iNrIDEntries <= 0 && iNrNameEntries <= 0)
{
@@ -785,45 +745,44 @@ void EditQueueBinCommand::Execute()
return;
}
if (bOK)
char* szText = iTextLen > 0 ? pBuf : NULL;
int32_t* pIDs = (int32_t*)(pBuf + iTextLen);
char* pNames = (pBuf + iTextLen + iNrIDEntries * sizeof(int32_t));
IDList cIDList;
NameList cNameList;
if (iNrIDEntries > 0)
{
char* szText = iTextLen > 0 ? pBuf : NULL;
int32_t* pIDs = (int32_t*)(pBuf + iTextLen);
char* pNames = (pBuf + iTextLen + iNrIDEntries * sizeof(int32_t));
IDList cIDList;
NameList cNameList;
if (iNrIDEntries > 0)
cIDList.reserve(iNrIDEntries);
for (int i = 0; i < iNrIDEntries; i++)
{
cIDList.reserve(iNrIDEntries);
for (int i = 0; i < iNrIDEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
cIDList.push_back(ntohl(pIDs[i]));
}
}
if (iNrNameEntries > 0)
if (iNrNameEntries > 0)
{
cNameList.reserve(iNrNameEntries);
for (int i = 0; i < iNrNameEntries; i++)
{
cNameList.reserve(iNrNameEntries);
for (int i = 0; i < iNrNameEntries; i++)
{
cNameList.push_back(pNames);
pNames += strlen(pNames) + 1;
}
cNameList.push_back(pNames);
pNames += strlen(pNames) + 1;
}
}
if (iAction < eRemoteEditActionPostMoveOffset)
{
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(
iNrIDEntries > 0 ? &cIDList : NULL,
iNrNameEntries > 0 ? &cNameList : NULL,
(QueueEditor::EMatchMode)iMatchMode, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
}
bool bOK = false;
if (iAction < eRemoteEditActionPostMoveOffset)
{
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(
iNrIDEntries > 0 ? &cIDList : NULL,
iNrNameEntries > 0 ? &cNameList : NULL,
(QueueEditor::EMatchMode)iMatchMode, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset, szText);
}
free(pBuf);
@@ -873,7 +832,6 @@ void PostQueueBinCommand::Execute()
{
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;
@@ -896,15 +854,12 @@ void PostQueueBinCommand::Execute()
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());
@@ -926,12 +881,12 @@ void PostQueueBinCommand::Execute()
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
m_pConnection->Send((char*) &PostQueueResponse, sizeof(PostQueueResponse));
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
m_pConnection->Send(buf, bufsize);
}
free(buf);
@@ -946,50 +901,36 @@ void WriteLogBinCommand::Execute()
}
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)
if (!m_pConnection->Recv(pRecvBuffer, ntohl(WriteLogRequest.m_iTrailingDataLength)))
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
error("invalid request");
free(pRecvBuffer);
return;
}
bool OK = true;
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
{
case Message::mkDetail:
detail(pRecvBuffer);
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");
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);
}
@@ -1004,7 +945,7 @@ void ScanBinCommand::Execute()
bool bSyncMode = ntohl(ScanRequest.m_bSyncMode);
g_pPrePostProcessor->ScanNZBDir(bSyncMode);
g_pScanner->ScanNZBDir(bSyncMode);
SendBoolResponse(true, bSyncMode ? "Scan-Command completed" : "Scan-Command scheduled successfully");
}
@@ -1066,7 +1007,7 @@ void HistoryBinCommand::Execute()
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_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus());
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
{
@@ -1092,12 +1033,12 @@ void HistoryBinCommand::Execute()
HistoryResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &HistoryResponse, sizeof(HistoryResponse), 0);
m_pConnection->Send((char*) &HistoryResponse, sizeof(HistoryResponse));
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
m_pConnection->Send(buf, bufsize);
}
free(buf);
@@ -1202,12 +1143,12 @@ void UrlQueueBinCommand::Execute()
UrlQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &UrlQueueResponse, sizeof(UrlQueueResponse), 0);
m_pConnection->Send((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
m_pConnection->Send(buf, bufsize);
}
free(buf);

View File

@@ -33,23 +33,21 @@
class BinRpcProcessor
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
const char* m_szClientIP;
Connection* m_pConnection;
void Dispatch();
public:
BinRpcProcessor();
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; }
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
};
class BinCommand
{
protected:
SOCKET m_iSocket;
Connection* m_pConnection;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
@@ -58,7 +56,7 @@ protected:
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};

391
ChangeLog
View File

@@ -1,3 +1,394 @@
nzbget-11.0:
- reworked concept of post-processing scripts:
- multiple scripts can be assigned to each nzb-file;
- all assigned scripts are executed after the nzb-file is
downloaded and internally processed (unpack, repair);
- option <PostProcess> is obsolete;
- new option <ScriptDir> sets directory where all pp-scripts must
be stored;
- new option <DefScript> sets the default list of pp-scripts to
be assigned to nzb-file when it's added to queue;
- new option <CategoryX.DefScript> to set the default list of
pp-scripts on a category basis;
- the execution order of pp-scripts can be set using new option
<ScriptOrder>;
- there are no separate configuration files for pp-scripts;
- configuration options and pp-parameters are defined in the
pp-scripts;
- script configuration options are saved in nzbget configuration
file (nzbget.conf);
- changed parameters list of RPC-methods <loadconfig> and
<saveconfig>;
- new RPC-method <configtemplates> returns configuration
descriptions for the program and for all pp-scripts;
- configuration of all scripts can be done in web-interface;
- the pp-scripts assigned to a particular nzb-file can be viewed
and changed in web-interface on page <pp-parameters> in the
edit download dialog;
- option <PostPauseQueue> renamed to <ScriptPauseQueue> (the old
name is still recognized);
- new option <ConfigTemplate> to define the location of template
configuration file (in previous versions it must be always
stored in <WebDir>);
- history dialog shows status of every script;
- the old example post-processing script replaced with two new scripts:
- EMail.py - sends E-Mail notification;
- Logger.py - saves the full post-processing log of the job into
file _postprocesslog.txt;
- both pp-scripts are written in python and work on Windows too
(in addition to Linux, Mac, etc.);
- added possibility to set post-processing parameters for history items:
- pp-parameters can now be viewed and changed in history dialog
in web-interface;
- useful before post-processing again;
- new action <HistorySetParameter> in RPC-method <editqueue>;
- new action <O> in remote command <--edit/-E> for history items
(subcommand <H>);
- added new feature <split download> which creates new download from
selected files of source download;
- new command <Split> in web-interface in edit download dialog
on page <Files>;
- new action <S> in remote command <--edit/-E>;
- new action <FileSplit> in JSON-/XML-RPC method <editqueue>;
- added support for manual par-check:
- if option <ParCheck> is set to <Manual> and a damaged download
is detected the program downloads all par2-files but doesn't
perform par-check; the user must perform par-check/repair
manually then (possibly on another, faster computer);
- old values <yes/no> of option <ParCheck> renamed to <Force>
and <Auto> respectively;
- when set to <Force> all par2-files are always downloaded;
- removed option <LoadPars> since its functionality is now
covered by option <ParCheck>;
- result of par-check can now have new value <Manual repair
necessary>;
- field <ParStatus> in RPC-method <history> can have new value
<MANUAL>;
- parameter <NZBPP_PARSTATUS> for pp-script can have new value
<4 = manual repair necessary>;
- when download is resumed in web-interface the option <ParCheck=Force>
is respected and all par2-files are resumed (not only main par2-file);
- automatic deletion of backup-source files after successful par-repair;
important when repairing renamed rar-files since this could cause
failure during unpack;
- par-checker and renamer now add messages into the log of pp-item
(like unpack- and pp-scripts-messages); these message now appear in
the log created by scripts Logger.py and EMail.py;
- when a nzb-file is added via web-interface or via remote call the
file is now put into incoming nzb-directory (option "NzbDir") and
then scanned; this has two advantages over the old behavior when the
file was parsed directly in memory:
- the file serves as a backup for troubleshootings;
- the file is processed by nzbprocess-script (if defined in
option "NzbProcess") making the pre-processing much easier;
- new env-var parameters are passed to NzbProcess-script: NZBNP_NZBNAME,
NZBNP_CATEGORY, NZBNP_PRIORITY, NZBNP_TOP, NZBNP_PAUSED;
- new commands for use in NzbProcess-scripts: "[NZB] TOP=1" to add nzb
to the top of queue and "[NZB] PAUSED=1" to add nzb-file in paused state;
- reworked post-processor queue:
- only one job is created for each nzb-file; no more separate
jobs are created for par-collections within one nzb-file;
- option <AllowReProcess> removed; a post-processing script is
called only once per nzb-file, this behavior cannot be altered
anymore;
- with a new feature <Split> individual par-collections can be
processed separately in a more effective way than before
- improved unicode (utf8) support:
- non-ascii characters are now correctly transferred via JSON-RPC;
- correct displaying of nzb-names and paths in web-interface;
- it is now possible to use non-ascii characters on settings page
for option values (such as paths or category names);
- improved unicode support in XML-RPC and JSON-RPC;
- if username and password are defined for a news-server the
authentication is now forced (in previous versions the authentication
was performed only if requested by server); needed for servers
supporting both anonymous (restricted) and authorized (full access)
accounts;
- added option <ExtCleanupDisk> to automatically delete unwanted files
(with specified extensions or names) after successful par-check or unpack;
- improvement in JSON-/XML-RPC:
- all ID fields including NZBID are now persistent and remain
their values after restart;
- this allows for third-party software to identify nzb-files by
ID;
- method <history> now returns ID of NZB-file in the field
<NZBID>;
- in versions up to 0.8.0 the field <NZBID> was used to identify
history items in the edit-commands <HistoryDelete>,
<HistoryReturn>, <HistoryProcess>; since version 9 field <ID>
is used for this purpose; in versions 9-10 field <NZBID> still
existed and had the same value as field <ID> for compatibility
with version 0.8.0; the compatibility is not provided anymore;
this change was needed to provide a consistent using of field
<NZBID> across all RPC-methods;
- added support for rar-files with non-standard extensions (such as
.001, etc.);
- added functions to backup and restore settings from web-interface;
when restoring it's possible to choose what sections to restore
(for example only news servers settings or only settings of a
certain pp-script) or restore the whole configuration;
- new option "ControlUsername" to define login user name (if you don't
like default username "nzbget");
- if a communication error occurs in web-interface, it retries multiple
times before giving up with an error message;
- the maximum number of download threads are now managed automatically
taking into account the number of allowed connections to news servers;
removed option <ThreadLimit>;
- pp-scripts terminated with unknown status are now considered failed
(status=FAILURE instead of status=UNKNOWN);
- new parameter (env. var) <NZBPP_NZBID> is passed to pp_scripts and
contains an internal ID of NZB-file;
- improved thread synchronisation to avoid (short-time) lockings of
the program during creation of destination files;
- more detailed error message if a directory could not be created
(<DstDir>, <NzbDir>, etc.); the message includes error text reported
by OS such as <permission denied> or similar;
- when unpacking the unpack start time is now measured after receiving
of unrar copyright message; this provides better unpack time
estimation in a case when user uses unpack-script to do some things
before executing unrar (for example sending Wake-On-Lan message to
the destination NAS); it works with unrar only, it's not possible
with 7-Zip because it buffers printed messages;
- when the program is reloaded, a message with version number is
printed like on start;
- configuration can now be saved in web-interface even if there were
no changes made but if obsolete or invalid options were detected in
the config file; the saving removes invalid entries from config file;
- option <ControlPassword> can now be set to en empty value to disable
authentication; useful if nzbget works behind other web-server with
its own authentication;
- when deleting downloads via web-interface a proper hint regarding
deleting of already downloaded files from disk depending on option
<DeleteCleanupDisk> is displayed;
- if a news-server returns empty or bad article (this may be caused
by errors on the news server), the program tries again from the same
or other servers (in previous versions the article was marked as
failed without other download attempts);
- when a nzb-file whose name ends with ".queued" is added via web-
interface the ".queued"-part is automatically removed;
- small improvement in multithread synchronization of download queue;
- added link to catalog of pp-scripts to web-interface;
- updated forum URL in about dialog in web-interface;
- small correction in a log-message: removed <Request:> from message
<Request: Queue collection...>;
- removed option "ProcessLogKind"; scripts should use prefixes ([INFO],
[DETAIL], etc); messages printed without prefixes are added as [INFO];
- removed option "AppendNzbDir"; if it was disabled that caused problems
in par-checker and unpacker; the option is now assumed always active;
- removed option "RenameBroken"; it caused problems in par-checker
(the option existed since early program versions before the par-check
was added);
- configure-script now defines "SIGCHLD_HANDLER" by default on all
systems including BSD; this eliminates the need of configure-
parameter "--enable-sigchld-handler" on 64-Bit BSD; the trade-off:
32-Bit BSD now requires "--disable-sigchld-handler";
- improved configure-script: defining of symbol "FILE_OFFSET_BITS=64",
required on some systems, is not necessary anymore;
- fixed: in the option "NzbAddedProcess" the env-var parameter with
nzb-name was passed in "NZBNA_NAME", should be "NZBNA_NZBNAME";
the old parameter name "NZBNA_NAME" is still supported for
compatibility;
- fixed: download time in statistics were incorrect if the computer
was put into standby (thanks Frank Kuypers for the patch);
- fixed: when option <InterDir> was active and the download after
unpack contained rar-file with the same name as one of original
files (sometimes happen with included subtitles) the original
rar-file was kept with name <.rar_duplicate1> even if the option
<UnpackCleanupDisk> was active;
- fixed: failed to read download queue from disk if post-processing
queue was not empty;
- fixed: when a duplicate file was detected during download the
program could hang;
- fixed: symbol <DISABLE_TLS> must be defined in project settings;
defining it in <win32.h> didn't work properly (Windows only);
- fixed: crash when adding malformed nzb-files with certain
structure (Windows only);
- fixed: by deleting of a partially downloaded nzb-file from queue,
when the option <DeleteCleanupDisk> was active, the file
<_brokenlog.txt> was not deleted preventing the directory from
automatic deletion;
- fixed: if an error occurs when a RPC-client or web-browser
communicates with nzbget the program could crash;
- fixed: if the last file of collection was detected as duplicate
after the download of the first article the file was deleted from
queue (that's OK) but the post-processing was not triggered
(that's a bug);
- fixed: support for splitted files (.001, .002, etc.) were broken.
nzbget-10.2:
- fixed potential segfault which could happen with file paths longer
than 1024 characters;
- fixed: when options <DirectWrite> and <ContinuePartial> were both
active, a restart or reload of the program during download may cause
damaged files in the active download;
- increased width of speed indication ui-element to avoid layout
breaking on some linux-browsers;
- fixed a race condition in unpacker which could lead to a segfault
(although the chances were low because the code wasn't executed often).
nzbget-10.1:
- fixed: articles with decoding errors (incomplete or damaged posts)
caused infinite retry-loop in downloader.
nzbget-10.0:
- added built-in unpack:
- rar and 7-zip formats are supported (via external Unrar and
7-Zip executables);
- new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>,
<UnrarCmd>, <SevenZipCmd>;
- web-interface now shows progress and estimated time during
unpack (rar only; for 7-Zip progress is not available due to
limitations of 7-Zip);
- when built-in unpack is enabled, the post-processing script is
called after unpack and possibly par-check/repair (if needed);
- for nzb-files containing multiple collections (par-sets) the
post-processing script is called only once, after the last
par-set;
- new parameter <NZBPP_UNPACKSTATUS> passed to post-processing
script;
- if the option <AllowReProcess> is enabled the post-processing-
script is called after each par-set (as in previous versions);
- example post-processing script updated: removed unrar-code,
added check for unpack status;
- new field <UnpackStatus> in result of RPC-method <history>;
- history-dialog in web-interface shows three status: par-status,
unpack-status, script-status;
- with two built-in special post-processing parameters <*Unpack:>
and <*Unpack:Password> the unpack can be disabled for individual
nzb-file or the password can be set;
- built-in special post-processing parameters can be set via web-
interface on page <PP-Parameters> (when built-in unpack is
enabled);
- added support for HTTPS to the built-in web-server (web-interface and
XML/JSON-RPC):
- new options <SecureControl>, <SecurePort>, <SecureCert> and
<SecureKey>;
- module <TLS.c/h> completely rewritten with support for server-
side sockets, newer versions of GnuTLS, proper thread lockings
in OpenSSL;
- improved the automatic par-scan (option <ParScan=auto>) to
significantly reduce the verify-time in some common cases with renamed
rar-files:
- the extra files are scanned in an optimized order;
- the scan stops when all missings files are found;
- added fast renaming of intentionally misnamed (rar-) files:
- the new renaming algorithm doesn't require full par-scan and
restores original filenames in just a few seconds, even on very
slow computers (NAS, media players, etc.);
- the fast renaming is performed automatically when requested by
the built-in unpacker (option <Unpack> must be active);
- added new option <InterDir> to put intermediate files during download
into a separate directory (instead of storing them directly in
destination directory (option <DestDir>):
- when nzb-file is completely (successfully) downloaded, repaired
(if neccessary) and unpacked the files are moved to destination
directory (option <DestDir> or <CategoryX.DestDir>);
- intermediate directory can significantly improve unpack
performance if it is located on a separate physical hard drive;
- added new option <ServerX.Cipher> to manually select cipher for
encrypted communication with news server:
- manually choosing a faster cipher (such as <RC4>) can
significantly improve performance (if CPU is a limiting factor);
- major improvements in news-server/connection management (main and fill
servers):
- if download of article fails, the program tries all servers of
the same level before trying higher level servers;
- this ensures that fill servers are used only if all main servers
fail;
- this makes the configuring of multiple servers much easier than
before: in most cases the simple configuration of level 0 for
all main servers and level 1 for all fill servers suffices;
- in previous versions the level was increased immediately after
the first tried server of the level failed; to make sure all
main servers were tried before downloading from fill servers it
was required to create complex server configurations with
duplicates; these configurations were still not as effective as
now;
- do not reconnect on <article/group not found> errors since this
doesn't help but unnecessary increases CPU load and network
traffic;
- removed option <RetryOnCrcError>; it's not required anymore;
- new option <ServerX.Group> allows more flexible configuration
of news servers when using multiple accounts on the same server;
with this option it's also possible to imitate the old server
management behavior regarding levels;
- news servers configuration is now less error-prone:
- the option <ServerX.Level> is not required to start from <0> and
when several news servers are configured the Levels can be any
integers - the program sorts the servers and corrects the Levels
to 0,1,2,etc. automatically if needed;
- when option <ServerX.Connections> is set to <0> the server is
ignored (in previous version such a server could cause hanging
when the program was trying to go to the next level);
- if no news servers are defined (or all definitions are invalid)
a warning is printed to inform that the download is not
possible;
- categories can now have their own destination directories; new option
<CategoryX.DestDir>;
- new feature <Pause for X Minutes> in web-interface; new XML-/JSON-RPC
method <scheduleresume>;
- improved the handling of hanging connections: if a connection hangs
longer than defined by option <ConnectionTimeout> the program tries to
gracefully close connection first (this is new); if it still hangs
after <TerminateTimeout> the download thread is terminated as a last
resort (as in previous versions);
- added automatic speed meter recalibration to recover after possible
synchronization errors which can occur when the option <AccurateRate>
is not active; this makes the default (less accurate but fast) speed
meter almost as good as the accurate one; important when speed
throttling is active;
- when the par-checked requests more par-files, they get an extra
priority and are downloaded before other files regardless of their
priorities; this is needed to avoid hanging of par-checker-job if a
file with a higher priority gets added to queue during par-check;
- when post-processing-parameters are passed to the post-processing
script a second version of each parameter with a normalized parameter-
name is passed in addition to the original parameter name; in the
normalized name the special characters <*> and <:> are replaced with
<_> and all characters are passed in upper case; this is important for
internal post-processing-parameters (*Unpack:=yes/no) which include
special characters;
- warning <Non-nzbget request received> now is not printed when the
connection was aborted before the request signature was read;
- changed formatting of remaining time for post-processing to short
format (as used for remaining download time);
- added link to article <Performance tips> to settings tab on web-
interface;
- removed hint <Post-processing script may have moved files elsewhere>
from history dialog since it caused more questions than helped;
- changed default value for option <ServerX.JoinGroup> to <no>; most
news servers nowadays do not require joining the group and many
servers do not keep headers for many groups making the join-command
fail even if the articles still can be successfully downloaded;
- small change in example post-processing script: message <Deleting
source ts-files> are now printed only if ts-files really existed;
- improved configure-script:
- libs which are added via pkgconfig are now put into LIBS instead
of LDFLAGS - improves compatibility with newer Linux linkers;
- OpenSSL libs/includes are now added using pkgconfig to better
handle dependencies;
- additional check for libcrypto (part of OpenSSL) ensures the
library is added to linker command even if pkgconfig is not
used;
- adding of local files via web-interface now works in IE10;
- if an obsolete option is found in the config file a warning is printed
instead of an error and the program is not paused anymore;
- fixed: the reported line numbers for configuration errors were
sometimes inaccurate;
- fixed warning <file glyphicons-halflings.png not found>;
- fixed: some XML-/JSON-RPC methods may return negative values for file
sizes between 2-4GB; this had also influence on web-interface.
- fixed: if an external program (unrar, pp-script, etc.) could not be
started, the execute-function has returned code 255 although the code
-1 were expected in this case; this could break designed post-
processing flow;
- fixed: some characters with codes below 32 were not properly encoded
in JSON-RPC; sometimes output from unrar contained such characters
and could break web-interface;
- fixed: special characters (quotation marks, etc.) in unpack password
and in configuration options were not displayed properly and could be
discarded on saving;
nzbget-9.0:
- changed version naming scheme by removing the leading zero: current
version is now called 9.0 instead of 0.9.0 (it's really the 9th major

View File

@@ -74,11 +74,11 @@ void ColoredFrontend::PrintStatus()
char tmp[1024];
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
@@ -86,9 +86,9 @@ void ColoredFrontend::PrintStatus()
}
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
}
else
{
@@ -113,7 +113,7 @@ void ColoredFrontend::PrintStatus()
#endif
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, szControlSeq);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,7 +37,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -54,18 +54,15 @@
#include "nzbget.h"
#include "Connection.h"
#include "Log.h"
#include "TLS.h"
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()
{
debug("Initializing global connection data");
@@ -87,18 +84,7 @@ void Connection::Init()
#endif
#ifndef DISABLE_TLS
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);
}
}
TLSSocket::Init();
#endif
#ifndef HAVE_GETADDRINFO
@@ -117,11 +103,7 @@ void Connection::Final()
#endif
#ifndef DISABLE_TLS
if (bTLSLibInitialized)
{
debug("Finalizing TLS library");
tls_lib_deinit();
}
TLSSocket::Final();
#endif
#ifndef HAVE_GETADDRINFO
@@ -138,15 +120,15 @@ Connection::Connection(const char* szHost, int iPort, bool bTLS)
m_szHost = NULL;
m_iPort = iPort;
m_bTLS = bTLS;
m_szCipher = NULL;
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = true;
#ifndef DISABLE_TLS
m_pTLS = NULL;
m_pTLSSocket = NULL;
m_bTLSError = false;
#endif
@@ -156,22 +138,23 @@ Connection::Connection(const char* szHost, int iPort, bool bTLS)
}
}
Connection::Connection(SOCKET iSocket, bool bAutoClose)
Connection::Connection(SOCKET iSocket, bool bTLS)
{
debug("Creating Connection");
m_szHost = NULL;
m_iPort = 0;
m_bTLS = false;
m_bTLS = bTLS;
m_szCipher = NULL;
m_eStatus = csConnected;
m_iSocket = iSocket;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = bAutoClose;
#ifndef DISABLE_TLS
m_pTLS = NULL;
m_pTLSSocket = NULL;
m_bTLSError = false;
#endif
}
@@ -179,23 +162,46 @@ Connection::~Connection()
{
debug("Destroying Connection");
Disconnect();
if (m_szHost)
{
free(m_szHost);
}
if (m_bAutoClose)
if (m_szCipher)
{
Disconnect();
free(m_szCipher);
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
if (m_pTLS)
if (m_pTLSSocket)
{
free(m_pTLS);
delete m_pTLSSocket;
}
#endif
}
void Connection::SetSuppressErrors(bool bSuppressErrors)
{
m_bSuppressErrors = bSuppressErrors;
#ifndef DISABLE_TLS
if (m_pTLSSocket)
{
m_pTLSSocket->SetSuppressErrors(bSuppressErrors);
}
#endif
}
void Connection::SetCipher(const char* szCipher)
{
if (m_szCipher)
{
free(m_szCipher);
}
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
bool Connection::Connect()
{
debug("Connecting");
@@ -213,7 +219,7 @@ bool Connection::Connect()
}
else
{
Connection::DoDisconnect();
DoDisconnect();
}
return bRes;
@@ -237,51 +243,145 @@ bool Connection::Disconnect()
return bRes;
}
int Connection::Bind()
bool Connection::Bind()
{
debug("Binding");
if (m_eStatus == csListening)
{
return 0;
return true;
}
int iRes = DoBind();
if (iRes == 0)
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
m_eStatus = csListening;
error("Could not resolve hostname %s", m_szHost);
return false;
}
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));
sSocketAddress.sin_family = AF_INET;
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return false;
}
}
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return false;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return false;
}
if (listen(m_iSocket, 100) < 0)
{
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return false;
}
m_eStatus = csListening;
return iRes;
return true;
}
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
//debug("Connection::WriteLine");
if (m_eStatus != csConnected)
{
return -1;
}
int iRes = DoWriteLine(pBuffer);
int iRes = send(m_iSocket, pBuffer, strlen(pBuffer), 0);
return iRes;
}
int Connection::Send(const char* pBuffer, int iSize)
bool Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
if (m_eStatus != csConnected)
{
return -1;
return false;
}
int iRes = send(m_iSocket, pBuffer, iSize, 0);
int iBytesSent = 0;
while (iBytesSent < iSize)
{
int iRes = send(m_iSocket, pBuffer + iBytesSent, iSize-iBytesSent, 0);
if (iRes <= 0)
{
return false;
}
iBytesSent += iRes;
}
return iRes;
return true;
}
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
@@ -291,26 +391,100 @@ char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
return NULL;
}
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
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, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*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;
}
return pBuffer;
}
SOCKET Connection::Accept()
Connection* Connection::Accept()
{
debug("Accepting connection");
if (m_eStatus != csListening)
{
return INVALID_SOCKET;
return NULL;
}
SOCKET iRes = DoAccept();
SOCKET iSocket = accept(m_iSocket, NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
if (iSocket == INVALID_SOCKET)
{
return NULL;
}
Connection* pCon = new Connection(iSocket, m_bTLS);
return iRes;
return pCon;
}
int Connection::Recv(char* pBuffer, int iSize)
int Connection::TryRecv(char* pBuffer, int iSize)
{
debug("Receiving data");
@@ -326,7 +500,7 @@ int Connection::Recv(char* pBuffer, int iSize)
return iReceived;
}
bool Connection::RecvAll(char * pBuffer, int iSize)
bool Connection::Recv(char * pBuffer, int iSize)
{
debug("Receiving data (full buffer)");
@@ -386,6 +560,7 @@ bool Connection::DoConnect()
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
bool bLastAddr = !addr->ai_next;
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
@@ -396,15 +571,28 @@ bool Connection::DoConnect()
break;
}
// Connection failed
if (bLastAddr)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
}
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
else if (bLastAddr)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
}
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
{
return false;
}
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
@@ -425,18 +613,13 @@ bool Connection::DoConnect()
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
ReportError("Connection to %s failed", m_szHost, true, 0);
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
return false;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
return false;
}
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
@@ -448,11 +631,11 @@ bool Connection::DoConnect()
#endif
if (err != 0)
{
ReportError("setsockopt failed", NULL, true, 0);
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
}
#ifndef DISABLE_TLS
if (m_bTLS && !StartTLS())
if (m_bTLS && !StartTLS(true, NULL, NULL))
{
return false;
}
@@ -467,214 +650,23 @@ bool Connection::DoDisconnect()
if (m_iSocket != INVALID_SOCKET)
{
#ifndef DISABLE_TLS
CloseTLS();
#endif
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
#ifndef DISABLE_TLS
if (m_pTLS)
{
CloseTLS();
}
#endif
}
m_eStatus = csDisconnected;
return true;
}
int Connection::DoWriteLine(const char* pBuffer)
{
//debug("Connection::doWrite()");
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
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, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*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;
}
return pBuffer;
}
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
{
*iBufLen = m_iBufAvail;
*pBuffer = m_szBufPtr;
m_iBufAvail = 0;
};
int Connection::DoBind()
{
debug("Do binding");
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error("Could not resolve hostname %s", m_szHost);
return -1;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return -1;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return -1;
}
if (listen(m_iSocket, 10) < 0)
{
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return -1;
}
return 0;
}
SOCKET Connection::DoAccept()
{
SOCKET iSocket = accept(GetSocket(), NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
}
};
void Connection::Cancel()
{
@@ -709,14 +701,9 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
#ifdef WIN32
int ErrCode = WSAGetLastError();
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
char szErrMsg[1024];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrCode, 0, szErrMsg, 1024, NULL);
szErrMsg[1024-1] = '\0';
#else
const char *szErrMsg = NULL;
int ErrCode = herrno;
@@ -729,7 +716,7 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
szErrMsg = hstrerror(ErrCode);
}
#endif
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
@@ -738,7 +725,6 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
#endif
}
else
{
@@ -754,71 +740,40 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
}
#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()
bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile)
{
debug("Starting TLS");
if (m_pTLS)
if (m_pTLSSocket)
{
free(m_pTLS);
delete m_pTLSSocket;
}
m_pTLS = malloc(sizeof(tls_t));
tls_t* pTLS = (tls_t*)m_pTLS;
memset(pTLS, 0, sizeof(tls_t));
tls_clear(pTLS);
m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher);
m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors);
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;
return m_pTLSSocket->Start();
}
void Connection::CloseTLS()
{
tls_close((tls_t*)m_pTLS);
free(m_pTLS);
m_pTLS = NULL;
if (m_pTLSSocket)
{
m_pTLSSocket->Close();
delete m_pTLSSocket;
m_pTLSSocket = NULL;
}
}
int Connection::recv(SOCKET s, char* buf, int len, int flags)
{
size_t iReceived = 0;
int iReceived = 0;
if (m_pTLS)
if (m_pTLSSocket)
{
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"))
iReceived = m_pTLSSocket->Recv(buf, len);
if (iReceived < 0)
{
m_bTLSError = true;
return -1;
@@ -833,22 +788,23 @@ int Connection::recv(SOCKET s, char* buf, int len, int flags)
int Connection::send(SOCKET s, const char* buf, int len, int flags)
{
if (m_pTLS)
int iSent = 0;
if (m_pTLSSocket)
{
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"))
iSent = m_pTLSSocket->Send(buf, len);
if (iSent < 0)
{
m_bTLSError = true;
return -1;
}
return 0;
return iSent;
}
else
{
int iRet = ::send(s, buf, len, flags);
return iRet;
iSent = ::send(s, buf, len, flags);
return iSent;
}
}
#endif
@@ -902,3 +858,20 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
return uaddr;
}
#endif
const char* Connection::GetRemoteAddr()
{
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
strncpy(m_szRemoteAddr, inet_ntoa(PeerName.sin_addr), sizeof(m_szRemoteAddr));
#else
inet_ntop(AF_INET, &PeerName.sin_addr, m_szRemoteAddr, sizeof(m_szRemoteAddr));
#endif
}
m_szRemoteAddr[sizeof(m_szRemoteAddr)-1] = '\0';
return m_szRemoteAddr;
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,6 +32,9 @@
#include "Thread.h"
#endif
#endif
#ifndef DISABLE_TLS
#include "TLS.h"
#endif
class Connection
{
@@ -43,22 +46,22 @@ public:
csListening,
csCancelled
};
protected:
char* m_szHost;
int m_iPort;
SOCKET m_iSocket;
bool m_bTLS;
char* m_szCipher;
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
int m_iTimeout;
bool m_bSuppressErrors;
bool m_bAutoClose;
char m_szRemoteAddr[20];
#ifndef DISABLE_TLS
void* m_pTLS;
static bool bTLSLibInitialized;
TLSSocket* m_pTLSSocket;
bool m_bTLSError;
#endif
#ifndef HAVE_GETADDRINFO
@@ -67,18 +70,14 @@ protected:
#endif
#endif
Connection(SOCKET iSocket, bool bTLS);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
bool DoConnect();
bool DoDisconnect();
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
#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();
@@ -86,31 +85,32 @@ protected:
public:
Connection(const char* szHost, int iPort, bool bTLS);
Connection(SOCKET iSocket, bool bAutoClose);
virtual ~Connection();
static void Init();
static void Final();
bool Connect();
bool Disconnect();
int Bind();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
virtual bool Connect();
virtual bool Disconnect();
bool Bind();
bool Send(const char* pBuffer, int iSize);
bool Recv(char* pBuffer, int iSize);
int TryRecv(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
void ReadBuffer(char** pBuffer, int *iBufLen);
int WriteLine(const char* pBuffer);
SOCKET Accept();
Connection* Accept();
void Cancel();
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
bool GetTLS() { return m_bTLS; }
SOCKET GetSocket() { return m_iSocket; }
const char* GetCipher() { return m_szCipher; }
void SetCipher(const char* szCipher);
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
void SetSuppressErrors(bool bSuppressErrors);
bool GetSuppressErrors() { return m_bSuppressErrors; }
const char* GetRemoteAddr();
#ifndef DISABLE_TLS
bool StartTLS();
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
#endif
};

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -77,12 +77,12 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
if (!outfile)
{
error("Could not create file %s", fileName);
error("Error saving diskstate: Could not create file %s", fileName);
perror(fileName);
return false;
}
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 16);
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 25);
// save nzb-infos
SaveNZBList(pDownloadQueue, outfile);
@@ -129,14 +129,14 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
if (!infile)
{
error("Could not open file %s", fileName);
error("Error reading diskstate: could not open file %s", fileName);
return false;
}
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 16)
if (iFormatVersion < 3 || iFormatVersion > 25)
{
error("Could not load diskstate due to file version mismatch");
fclose(infile);
@@ -152,7 +152,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
if (iFormatVersion >= 7)
{
// load post-queue
if (!LoadPostQueue(pDownloadQueue, infile)) goto error;
if (!LoadPostQueue(pDownloadQueue, infile, iFormatVersion)) goto error;
}
else if (iFormatVersion < 7 && g_pOptions->GetReloadPostQueue())
{
@@ -198,14 +198,15 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
for (NZBInfoList::iterator it = pDownloadQueue->GetNZBInfoList()->begin(); it != pDownloadQueue->GetNZBInfoList()->end(); it++)
{
NZBInfo* pNZBInfo = *it;
fprintf(outfile, "%i\n", pNZBInfo->GetID());
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
fprintf(outfile, "%s\n", pNZBInfo->GetQueuedFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetName());
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
fprintf(outfile, "%i\n", pNZBInfo->GetPostProcess() ? 1 : 0);
fprintf(outfile, "%i\n", (int)pNZBInfo->GetParStatus());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetScriptStatus());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetPostProcess());
fprintf(outfile, "%i,%i,%i,%i\n", (int)pNZBInfo->GetParStatus(), (int)pNZBInfo->GetUnpackStatus(), (int)pNZBInfo->GetMoveStatus(), (int)pNZBInfo->GetRenameStatus());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetUnpackCleanedUpDisk());
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
fprintf(outfile, "%i\n", pNZBInfo->GetParkedFileCount());
@@ -239,6 +240,13 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue());
}
fprintf(outfile, "%i\n", pNZBInfo->GetScriptStatuses()->size());
for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++)
{
ScriptStatus* pScriptStatus = *it;
fprintf(outfile, "%i,%s\n", pScriptStatus->GetStatus(), pScriptStatus->GetName());
}
NZBInfo::Messages* pMessages = pNZBInfo->LockMessages();
fprintf(outfile, "%i\n", pMessages->size());
for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++)
@@ -265,6 +273,13 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
pNZBInfo->AddReference();
pDownloadQueue->GetNZBInfoList()->Add(pNZBInfo);
if (iFormatVersion >= 24)
{
int iID;
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
pNZBInfo->SetID(iID);
}
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetFilename(buf);
@@ -298,23 +313,61 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
int iPostProcess;
if (fscanf(infile, "%i\n", &iPostProcess) != 1) goto error;
pNZBInfo->SetPostProcess(iPostProcess == 1);
pNZBInfo->SetPostProcess((bool)iPostProcess);
}
if (iFormatVersion >= 8)
if (iFormatVersion >= 8 && iFormatVersion < 18)
{
int iParStatus;
if (fscanf(infile, "%i\n", &iParStatus) != 1) goto error;
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
}
if (iFormatVersion >= 9)
if (iFormatVersion >= 9 && iFormatVersion < 18)
{
int iScriptStatus;
if (fscanf(infile, "%i\n", &iScriptStatus) != 1) goto error;
pNZBInfo->SetScriptStatus((NZBInfo::EScriptStatus)iScriptStatus);
if (iScriptStatus > 1) iScriptStatus--;
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
}
if (iFormatVersion >= 18)
{
int iParStatus, iUnpackStatus, iScriptStatus, iMoveStatus = 0, iRenameStatus = 0;
if (iFormatVersion >= 23)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iMoveStatus, &iRenameStatus) != 4) goto error;
}
else if (iFormatVersion >= 21)
{
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus, &iMoveStatus, &iRenameStatus) != 5) goto error;
}
else if (iFormatVersion >= 20)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus, &iMoveStatus) != 4) goto error;
}
else
{
if (fscanf(infile, "%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus) != 3) goto error;
}
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
pNZBInfo->SetUnpackStatus((NZBInfo::EUnpackStatus)iUnpackStatus);
pNZBInfo->SetMoveStatus((NZBInfo::EMoveStatus)iMoveStatus);
pNZBInfo->SetRenameStatus((NZBInfo::ERenameStatus)iRenameStatus);
if (iFormatVersion < 23)
{
if (iScriptStatus > 1) iScriptStatus--;
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
}
}
if (iFormatVersion >= 19)
{
int iUnpackCleanedUpDisk;
if (fscanf(infile, "%i\n", &iUnpackCleanedUpDisk) != 1) goto error;
pNZBInfo->SetUnpackCleanedUpDisk((bool)iUnpackCleanedUpDisk);
}
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
pNZBInfo->SetFileCount(iFileCount);
@@ -370,6 +423,26 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
}
}
if (iFormatVersion >= 23)
{
int iScriptCount;
if (fscanf(infile, "%i\n", &iScriptCount) != 1) goto error;
for (int i = 0; i < iScriptCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
char* szScriptName = strchr(buf, ',');
if (szScriptName)
{
szScriptName++;
int iStatus = atoi(buf);
if (iStatus > 1 && iFormatVersion < 25) iStatus--;
pNZBInfo->GetScriptStatuses()->Add(szScriptName, (ScriptStatus::EStatus)iStatus);
}
}
}
if (iFormatVersion >= 11)
{
int iLogCount;
@@ -409,8 +482,8 @@ void DiskState::SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQue
if (!pFileInfo->GetDeleted())
{
int iNZBIndex = FindNZBInfoIndex(pDownloadQueue, pFileInfo->GetNZBInfo());
fprintf(outfile, "%i,%i,%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused(),
(int)pFileInfo->GetTime(), pFileInfo->GetPriority());
fprintf(outfile, "%i,%i,%i,%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused(),
(int)pFileInfo->GetTime(), pFileInfo->GetPriority(), (int)pFileInfo->GetExtraPriority());
}
}
}
@@ -425,8 +498,12 @@ bool DiskState::LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQue
{
unsigned int id, iNZBIndex, paused;
unsigned int iTime = 0;
int iPriority = 0;
if (iFormatVersion >= 14)
int iPriority = 0, iExtraPriority = 0;
if (iFormatVersion >= 17)
{
if (fscanf(infile, "%i,%i,%i,%i,%i,%i\n", &id, &iNZBIndex, &paused, &iTime, &iPriority, &iExtraPriority) != 6) goto error;
}
else if (iFormatVersion >= 14)
{
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &id, &iNZBIndex, &paused, &iTime, &iPriority) != 5) goto error;
}
@@ -451,12 +528,12 @@ bool DiskState::LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQue
pFileInfo->SetPaused(paused);
pFileInfo->SetTime(iTime);
pFileInfo->SetPriority(iPriority);
pFileInfo->SetExtraPriority(iExtraPriority != 0);
pFileInfo->SetNZBInfo(pDownloadQueue->GetNZBInfoList()->at(iNZBIndex - 1));
pFileQueue->push_back(pFileInfo);
}
else
{
warn("Could not load diskstate for file %s", fileName);
delete pFileInfo;
}
}
@@ -484,7 +561,7 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
if (!outfile)
{
error("Could not create file %s", szFilename);
error("Error saving diskstate: could not create file %s", szFilename);
return false;
}
@@ -529,7 +606,7 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool
if (!infile)
{
error("Could not open file %s", szFilename);
error("Error reading diskstate: could not open file %s", szFilename);
return false;
}
@@ -598,14 +675,12 @@ void DiskState::SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile)
{
PostInfo* pPostInfo = *it;
int iNZBIndex = FindNZBInfoIndex(pDownloadQueue, pPostInfo->GetNZBInfo());
fprintf(outfile, "%i,%i,%i,%i\n", iNZBIndex, (int)pPostInfo->GetParCheck(),
(int)pPostInfo->GetParStatus(), (int)pPostInfo->GetStage());
fprintf(outfile, "%i,%i\n", iNZBIndex, (int)pPostInfo->GetStage());
fprintf(outfile, "%s\n", pPostInfo->GetInfoName());
fprintf(outfile, "%s\n", pPostInfo->GetParFilename());
}
}
bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile)
bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion)
{
debug("Loading post-queue from disk");
@@ -618,15 +693,27 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile)
for (int i = 0; i < size; i++)
{
PostInfo* pPostInfo = NULL;
unsigned int iNZBIndex, iParCheck, iParStatus, iStage;
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iParCheck, &iParStatus, &iStage) != 4) goto error;
unsigned int iNZBIndex, iStage, iDummy;
if (iFormatVersion < 19)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
}
else if (iFormatVersion < 22)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
}
else
{
if (fscanf(infile, "%i,%i\n", &iNZBIndex, &iStage) != 2) goto error;
}
if (iFormatVersion < 18 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
if (iFormatVersion < 21 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
if (iFormatVersion < 20 && iStage > (int)PostInfo::ptUnpacking) iStage++;
if (!bSkipPostQueue)
{
pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pDownloadQueue->GetNZBInfoList()->at(iNZBIndex - 1));
pPostInfo->SetParCheck(iParCheck);
pPostInfo->SetParStatus((PostInfo::EParStatus)iParStatus);
pPostInfo->SetStage((PostInfo::EStage)iStage);
}
@@ -634,9 +721,11 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile)
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (!bSkipPostQueue) pPostInfo->SetInfoName(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (!bSkipPostQueue) pPostInfo->SetParFilename(buf);
if (iFormatVersion < 22)
{
// ParFilename, ignore
if (!fgets(buf, sizeof(buf), infile)) goto error;
}
if (!bSkipPostQueue)
{
@@ -672,7 +761,7 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
if (!infile)
{
error("Could not open file %s", fileName);
error("Error reading diskstate: could not open file %s", fileName);
return false;
}
@@ -729,9 +818,8 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
pNZBInfo->SetDestDir(buf);
}
// ParFilename, ignore
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetParFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
@@ -771,11 +859,11 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
}
}
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParCheck(iIntValue);
int iParCheck;
if (fscanf(infile, "%i\n", &iParCheck) != 1) goto error; // ParCheck
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParStatus((PostInfo::EParStatus)iIntValue);
pNZBInfo->SetParStatus(iParCheck ? (NZBInfo::EParStatus)iIntValue : NZBInfo::psSkipped);
if (iFormatVersion < 7)
{
@@ -866,6 +954,7 @@ error:
void DiskState::SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile)
{
fprintf(outfile, "%i\n", pUrlInfo->GetID());
fprintf(outfile, "%i,%i\n", (int)pUrlInfo->GetStatus(), pUrlInfo->GetPriority());
fprintf(outfile, "%i,%i\n", (int)pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused());
fprintf(outfile, "%s\n", pUrlInfo->GetURL());
@@ -877,6 +966,13 @@ bool DiskState::LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion)
{
char buf[10240];
if (iFormatVersion >= 24)
{
int iID;
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
pUrlInfo->SetID(iID);
}
int iStatus, iPriority;
if (fscanf(infile, "%i,%i\n", &iStatus, &iPriority) != 2) goto error;
if (pUrlInfo) pUrlInfo->SetStatus((UrlInfo::EStatus)iStatus);
@@ -917,6 +1013,7 @@ void DiskState::SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile)
{
HistoryInfo* pHistoryInfo = *it;
fprintf(outfile, "%i\n", pHistoryInfo->GetID());
fprintf(outfile, "%i\n", (int)pHistoryInfo->GetKind());
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
@@ -942,6 +1039,13 @@ bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
for (int i = 0; i < size; i++)
{
HistoryInfo* pHistoryInfo = NULL;
int iID = 0;
if (iFormatVersion >= 24)
{
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
}
HistoryInfo::EKind eKind = HistoryInfo::hkNZBInfo;
if (iFormatVersion >= 15)
@@ -965,6 +1069,11 @@ bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
pHistoryInfo = new HistoryInfo(pUrlInfo);
}
if (iFormatVersion >= 24)
{
pHistoryInfo->SetID(iID);
}
int iTime;
if (fscanf(infile, "%i\n", &iTime) != 1) goto error;
pHistoryInfo->SetTime((time_t)iTime);
@@ -995,128 +1104,37 @@ int DiskState::FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo
}
/*
* Delete all files from Queue.
* Returns true if successful, false if not
* Deletes whole download queue including history.
*/
bool DiskState::DiscardDownloadQueue()
void DiskState::DiscardDownloadQueue()
{
debug("Discarding queue");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
szFullFilename[1024-1] = '\0';
remove(szFullFilename);
FILE* infile = fopen(fileName, "rb");
if (!infile)
DirBrowser dir(g_pOptions->GetQueueDir());
while (const char* filename = dir.Next())
{
error("Could not open file %s", fileName);
return false;
}
bool res = false;
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (3 <= iFormatVersion && iFormatVersion <= 16)
{
// skip nzb-infos
int size = 0;
char buf[1024];
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
// delete all files whose names have only characters '0'..'9'
bool bOnlyNums = true;
for (const char* p = filename; *p != '\0'; p++)
{
if (!fgets(buf, sizeof(buf), infile)) break; // filename
if (!fgets(buf, sizeof(buf), infile)) break; // destdir
if (iFormatVersion >= 5)
if (!('0' <= *p && *p <= '9'))
{
if (!fgets(buf, sizeof(buf), infile)) break; // localfile
}
if (iFormatVersion >= 13)
{
if (!fgets(buf, sizeof(buf), infile)) break; // name
}
if (iFormatVersion >= 4)
{
if (!fgets(buf, sizeof(buf), infile)) break; // category
if (!fgets(buf, sizeof(buf), infile)) break; // postprocess
}
if (iFormatVersion >= 8)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ParStatus
}
if (iFormatVersion >= 9)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ScriptStatus
}
if (!fgets(buf, sizeof(buf), infile)) break; // file count
if (iFormatVersion >= 10)
{
if (!fgets(buf, sizeof(buf), infile)) break; // parked file count
}
if (!fgets(buf, sizeof(buf), infile)) break; // file size
if (iFormatVersion >= 4)
{
// completed files
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) break;
for (int i = 0; i < iFileCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break; // filename
}
}
if (iFormatVersion >= 6)
{
// postprocess-parameters
int iParameterCount;
if (fscanf(infile, "%i\n", &iParameterCount) != 1) break;
for (int i = 0; i < iParameterCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break;
}
}
if (iFormatVersion >= 11)
{
// log-messages
int iLogCount;
if (fscanf(infile, "%i\n", &iLogCount) != 1) break;
for (int i = 0; i < iLogCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break;
}
bOnlyNums = false;
break;
}
}
// file-infos
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
if (bOnlyNums)
{
int id;
char tr[100];
if (fscanf(infile, "%i,%s\n", &id, &tr) == 2)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
fileName[1024-1] = '\0';
remove(fileName);
}
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), filename);
szFullFilename[1024-1] = '\0';
remove(szFullFilename);
}
res = true;
}
else
{
error("Could not discard diskstate due to file version mismatch");
res = false;
}
fclose(infile);
if (res)
{
remove(fileName);
}
return res;
}
bool DiskState::DownloadQueueExists()

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +39,7 @@ private:
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 LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
@@ -56,7 +56,7 @@ public:
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
bool DiscardDownloadQueue();
void DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,10 +34,10 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <map>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include <map>
#include "nzbget.h"
#include "DownloadInfo.h"
@@ -117,6 +117,59 @@ void NZBParameterList::SetParameter(const char* szName, const char* szValue)
}
ScriptStatus::ScriptStatus(const char* szName, EStatus eStatus)
{
m_szName = strdup(szName);
m_eStatus = eStatus;
}
ScriptStatus::~ScriptStatus()
{
if (m_szName)
{
free(m_szName);
}
}
ScriptStatusList::~ScriptStatusList()
{
Clear();
}
void ScriptStatusList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void ScriptStatusList::Add(const char* szScriptName, ScriptStatus::EStatus eStatus)
{
push_back(new ScriptStatus(szScriptName, eStatus));
}
ScriptStatus::EStatus ScriptStatusList::CalcTotalStatus()
{
ScriptStatus::EStatus eStatus = ScriptStatus::srNone;
for (iterator it = begin(); it != end(); it++)
{
ScriptStatus* pScriptStatus = *it;
// Failure-Status overrides Success-Status
if ((pScriptStatus->GetStatus() == ScriptStatus::srSuccess && eStatus == ScriptStatus::srNone) ||
(pScriptStatus->GetStatus() == ScriptStatus::srFailure))
{
eStatus = pScriptStatus->GetStatus();
}
}
return eStatus;
}
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
@@ -130,11 +183,15 @@ NZBInfo::NZBInfo()
m_lSize = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_eParStatus = prNone;
m_eScriptStatus = srNone;
m_eRenameStatus = rsNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eCleanupStatus = csNone;
m_eMoveStatus = msNone;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
m_bUnpackCleanedUpDisk = false;
m_szQueuedFilename = strdup("");
m_Owner = NULL;
m_Messages.clear();
@@ -202,6 +259,15 @@ void NZBInfo::Release()
}
}
void NZBInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
}
}
void NZBInfo::ClearCompletedFiles()
{
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
@@ -287,42 +353,55 @@ void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int i
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);
}
char szDestDir[1024];
if (g_pOptions->GetAppendNZBDir())
if (strlen(g_pOptions->GetInterDir()) == 0)
{
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, GetName());
}
else
{
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), GetName());
}
szBuffer[1024-1] = '\0';
BuildFinalDirName(szDestDir, 1024);
}
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()
snprintf(szDestDir, 1024, "%s%s", g_pOptions->GetInterDir(), GetName());
szDestDir[1024-1] = '\0';
}
SetDestDir(szBuffer);
SetDestDir(szDestDir);
}
void NZBInfo::BuildFinalDirName(char* szFinalDirBuf, int iBufSize)
{
char szBuffer[1024];
bool bUseCategory = m_szCategory && m_szCategory[0] != '\0';
snprintf(szFinalDirBuf, iBufSize, "%s", g_pOptions->GetDestDir());
szFinalDirBuf[iBufSize-1] = '\0';
if (bUseCategory)
{
Options::Category *pCategory = g_pOptions->FindCategory(m_szCategory);
if (pCategory && pCategory->GetDestDir() && pCategory->GetDestDir()[0] != '\0')
{
snprintf(szFinalDirBuf, iBufSize, "%s", pCategory->GetDestDir());
szFinalDirBuf[iBufSize-1] = '\0';
bUseCategory = false;
}
}
if (g_pOptions->GetAppendCategoryDir() && bUseCategory)
{
char szCategoryDir[1024];
strncpy(szCategoryDir, m_szCategory, 1024);
szCategoryDir[1024 - 1] = '\0';
Util::MakeValidFilename(szCategoryDir, '_', true);
snprintf(szBuffer, 1024, "%s%s%c", szFinalDirBuf, szCategoryDir, PATH_SEPARATOR);
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
snprintf(szBuffer, 1024, "%s%s", szFinalDirBuf, GetName());
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
void NZBInfo::SetParameter(const char* szName, const char* szValue)
@@ -348,9 +427,8 @@ void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szT
tTime = time(NULL);
}
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_Messages.push_back(pMessage);
m_mutexLog.Unlock();
}
@@ -440,6 +518,8 @@ FileInfo::FileInfo()
m_Groups.clear();
m_szSubject = NULL;
m_szFilename = NULL;
m_szOutputFilename = NULL;
m_pMutexOutputFile = NULL;
m_bFilenameConfirmed = false;
m_lSize = 0;
m_lRemainingSize = 0;
@@ -450,7 +530,9 @@ FileInfo::FileInfo()
m_bOutputInitialized = false;
m_pNZBInfo = NULL;
m_iPriority = 0;
m_bExtraPriority = false;
m_iActiveDownloads = 0;
m_bAutoDeleted = false;
m_iIDGen++;
m_iID = m_iIDGen;
}
@@ -467,6 +549,14 @@ FileInfo::~ FileInfo()
{
free(m_szFilename);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
if (m_pMutexOutputFile)
{
delete m_pMutexOutputFile;
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
@@ -491,9 +581,9 @@ void FileInfo::ClearArticles()
m_Articles.clear();
}
void FileInfo::SetID(int s)
void FileInfo::SetID(int iID)
{
m_iID = s;
m_iID = iID;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
@@ -531,12 +621,36 @@ void FileInfo::MakeValidFilename()
void FileInfo::LockOutputFile()
{
m_mutexOutputFile.Lock();
m_pMutexOutputFile->Lock();
}
void FileInfo::UnlockOutputFile()
{
m_mutexOutputFile.Unlock();
m_pMutexOutputFile->Unlock();
}
void FileInfo::SetOutputFilename(const char* szOutputFilename)
{
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
m_szOutputFilename = strdup(szOutputFilename);
}
void FileInfo::SetActiveDownloads(int iActiveDownloads)
{
m_iActiveDownloads = iActiveDownloads;
if (m_iActiveDownloads > 0 && !m_pMutexOutputFile)
{
m_pMutexOutputFile = new Mutex();
}
else if (m_iActiveDownloads == 0 && m_pMutexOutputFile)
{
delete m_pMutexOutputFile;
m_pMutexOutputFile = NULL;
}
}
bool FileInfo::IsDupe(const char* szFilename)
@@ -587,21 +701,18 @@ 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_bRequestParCheck = false;
m_bRequestParRename = false;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
m_tStartTime = 0;
m_tStageTime = 0;
m_eStage = ptQueued;
m_pScriptThread = NULL;
m_pPostThread = NULL;
m_Messages.clear();
m_iIDMessageGen = 0;
m_iIDGen++;
@@ -612,10 +723,6 @@ PostInfo::~ PostInfo()
{
debug("Destroying PostInfo");
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
@@ -647,11 +754,6 @@ void PostInfo::SetNZBInfo(NZBInfo* pNZBInfo)
m_pNZBInfo->AddReference();
}
void PostInfo::SetParFilename(const char* szParFilename)
{
m_szParFilename = strdup(szParFilename);
}
void PostInfo::SetInfoName(const char* szInfoName)
{
m_szInfoName = strdup(szInfoName);
@@ -679,9 +781,8 @@ void PostInfo::UnlockMessages()
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
@@ -802,9 +903,9 @@ void UrlInfo::SetURL(const char* szURL)
m_szURL = strdup(szURL);
}
void UrlInfo::SetID(int s)
void UrlInfo::SetID(int iID)
{
m_iID = s;
m_iID = iID;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
@@ -884,9 +985,9 @@ HistoryInfo::~HistoryInfo()
}
}
void HistoryInfo::SetID(int s)
void HistoryInfo::SetID(int iID)
{
m_iID = s;
m_iID = iID;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -91,9 +91,12 @@ private:
bool m_bFilenameConfirmed;
int m_iCompleted;
bool m_bOutputInitialized;
Mutex m_mutexOutputFile;
char* m_szOutputFilename;
Mutex* m_pMutexOutputFile;
int m_iPriority;
bool m_bExtraPriority;
int m_iActiveDownloads;
bool m_bAutoDeleted;
static int m_iIDGen;
@@ -101,7 +104,7 @@ public:
FileInfo();
~FileInfo();
int GetID() { return m_iID; }
void SetID(int s);
void SetID(int iID);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
@@ -128,13 +131,19 @@ public:
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* szOutputFilename);
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
bool GetExtraPriority() { return m_bExtraPriority; }
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads) { m_iActiveDownloads = iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads);
bool GetAutoDeleted() { return m_bAutoDeleted; }
void SetAutoDeleted(bool bAutoDeleted) { m_bAutoDeleted = bAutoDeleted; }
};
typedef std::deque<FileInfo*> FileQueue;
@@ -203,25 +212,83 @@ public:
void SetParameter(const char* szName, const char* szValue);
};
class ScriptStatus
{
public:
enum EStatus
{
srNone,
srFailure,
srSuccess
};
private:
char* m_szName;
EStatus m_eStatus;
friend class ScriptStatusList;
public:
ScriptStatus(const char* szName, EStatus eStatus);
~ScriptStatus();
const char* GetName() { return m_szName; }
EStatus GetStatus() { return m_eStatus; }
};
typedef std::deque<ScriptStatus*> ScriptStatusListBase;
class ScriptStatusList : public ScriptStatusListBase
{
public:
~ScriptStatusList();
void Add(const char* szScriptName, ScriptStatus::EStatus eStatus);
void Clear();
ScriptStatus::EStatus CalcTotalStatus();
};
class NZBInfoList;
class NZBInfo
{
public:
enum EParStatus
enum ERenameStatus
{
prNone,
prFailure,
prRepairPossible,
prSuccess
rsNone,
rsSkipped,
rsFailure,
rsSuccess
};
enum EScriptStatus
enum EParStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
psNone,
psSkipped,
psFailure,
psSuccess,
psRepairPossible,
psManual
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum ECleanupStatus
{
csNone,
csFailure,
csSuccess
};
enum EMoveStatus
{
msNone,
msFailure,
msSuccess
};
typedef std::vector<char*> Files;
@@ -239,14 +306,20 @@ private:
long long m_lSize;
Files m_completedFiles;
bool m_bPostProcess;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EScriptStatus m_eScriptStatus;
EUnpackStatus m_eUnpackStatus;
ECleanupStatus m_eCleanupStatus;
EMoveStatus m_eMoveStatus;
char* m_szQueuedFilename;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bParManual;
bool m_bCleanupDisk;
bool m_bUnpackCleanedUpDisk;
NZBInfoList* m_Owner;
NZBParameterList m_ppParameters;
ScriptStatusList m_scriptStatuses;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
@@ -261,6 +334,7 @@ public:
void AddReference();
void Release();
int GetID() { return m_iID; }
void SetID(int iID);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
@@ -277,14 +351,21 @@ public:
int GetParkedFileCount() { return m_iParkedFileCount; }
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
void BuildDestDirName();
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
void ClearCompletedFiles();
bool GetPostProcess() { return m_bPostProcess; }
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ECleanupStatus GetCleanupStatus() { return m_eCleanupStatus; }
void SetCleanupStatus(ECleanupStatus eCleanupStatus) { m_eCleanupStatus = eCleanupStatus; }
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleted() { return m_bDeleted; }
@@ -293,8 +374,11 @@ public:
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
bool GetCleanupDisk() { return m_bCleanupDisk; }
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
bool GetUnpackCleanedUpDisk() { return m_bUnpackCleanedUpDisk; }
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
ScriptStatusList* GetScriptStatuses() { return &m_scriptStatuses; } // needs locking (for shared objects)
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
@@ -320,53 +404,30 @@ public:
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptRenaming,
ptUnpacking,
ptMoving,
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;
bool m_bRequestParCheck;
bool m_bRequestParRename;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pScriptThread;
Thread* m_pPostThread;
Mutex m_mutexLog;
Messages m_Messages;
@@ -380,8 +441,6 @@ public:
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; }
@@ -400,17 +459,13 @@ public:
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; }
bool GetRequestParCheck() { return m_bRequestParCheck; }
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
bool GetRequestParRename() { return m_bRequestParRename; }
void SetRequestParRename(bool bRequestParRename) { m_bRequestParRename = bRequestParRename; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetScriptThread() { return m_pScriptThread; }
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
Messages* LockMessages();
void UnlockMessages();
};
@@ -449,7 +504,7 @@ public:
UrlInfo();
~UrlInfo();
int GetID() { return m_iID; }
void SetID(int s);
void SetID(int iID);
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
void SetURL(const char* szURL); // needs locking (for shared objects)
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
@@ -493,7 +548,7 @@ public:
HistoryInfo(UrlInfo* pUrlInfo);
~HistoryInfo();
int GetID() { return m_iID; }
void SetID(int s);
void SetID(int iID);
EKind GetKind() { return m_eKind; }
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,8 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
@@ -63,11 +62,11 @@ Frontend::Frontend()
m_iNeededLogEntries = 0;
m_bSummary = false;
m_bFileList = false;
m_fCurrentDownloadSpeed = 0;
m_iCurrentDownloadSpeed = 0;
m_lRemainingSize = 0;
m_bPauseDownload = false;
m_bPauseDownload2 = false;
m_fDownloadLimit = 0;
m_iDownloadLimit = 0;
m_iThreadCount = 0;
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
@@ -96,11 +95,11 @@ bool Frontend::PrepareData()
{
if (m_bSummary)
{
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_iCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
m_bPauseDownload = g_pOptions->GetPauseDownload();
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_iDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
m_iPostJobCount = pPostQueue->size();
@@ -182,6 +181,7 @@ void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
}
else
{
g_pOptions->SetResumeTime(0);
if (bSecondRegister)
{
g_pOptions->SetPauseDownload2(bPause);
@@ -193,15 +193,15 @@ void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
}
}
void Frontend::ServerSetDownloadRate(float fRate)
void Frontend::ServerSetDownloadRate(int iRate)
{
if (IsRemoteMode())
{
RequestSetDownloadRate(fRate);
RequestSetDownloadRate(iRate);
}
else
{
g_pOptions->SetDownloadRate(fRate);
g_pOptions->SetDownloadRate(iRate);
}
}
@@ -235,6 +235,10 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -261,15 +265,15 @@ bool Frontend::RequestMessages()
LogRequest.m_iIDFrom = 0;
}
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
{
return false;
}
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
bool bRead = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (!bRead ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
@@ -280,7 +284,7 @@ bool Frontend::RequestMessages()
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
if (!connection.Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -325,15 +329,15 @@ bool Frontend::RequestFileList()
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
{
return false;
}
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
bool bRead = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (!bRead ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
@@ -344,7 +348,7 @@ bool Frontend::RequestFileList()
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
if (!connection.Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -358,8 +362,8 @@ bool Frontend::RequestFileList()
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_iCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate);
m_iDownloadLimit = ntohl(ListResponse.m_iDownloadLimit);
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
@@ -390,11 +394,11 @@ bool Frontend::RequestPauseUnpause(bool bPause, bool bSecondRegister)
return client.RequestServerPauseUnpause(bPause, bSecondRegister ? eRemotePauseUnpauseActionDownload2 : eRemotePauseUnpauseActionDownload);
}
bool Frontend::RequestSetDownloadRate(float fRate)
bool Frontend::RequestSetDownloadRate(int iRate)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerSetDownloadRate(fRate);
return client.RequestServerSetDownloadRate(iRate);
}
bool Frontend::RequestDumpDebug()

View File

@@ -50,11 +50,11 @@ protected:
int m_iUpdateInterval;
// summary
float m_fCurrentDownloadSpeed;
int m_iCurrentDownloadSpeed;
long long m_lRemainingSize;
bool m_bPauseDownload;
bool m_bPauseDownload2;
float m_fDownloadLimit;
int m_iDownloadLimit;
int m_iThreadCount;
int m_iPostJobCount;
int m_iUpTimeSec;
@@ -72,8 +72,8 @@ protected:
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 ServerSetDownloadRate(int iRate);
bool RequestSetDownloadRate(int iRate);
void ServerDumpDebug();
bool RequestDumpDebug();
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,7 +38,7 @@
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <cstdio>
#include <stdio.h>
#include "nzbget.h"
#include "Options.h"

View File

@@ -1,7 +1,7 @@
#
# This file if part of nzbget
#
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -28,16 +28,16 @@ nzbget_SOURCES = \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h \
PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
@@ -51,7 +51,7 @@ doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf nzbget-postprocess.conf
nzbget.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
@@ -64,16 +64,18 @@ webui_FILES = \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
bin_SCRIPTS = nzbget-postprocess.sh
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuiconfdir = $(datadir)/nzbget/webui
dist_webuiconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Note about "sed":
# We need to make some changes in installed files.
@@ -91,12 +93,15 @@ install-exec-hook:
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration files
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
@@ -105,18 +110,9 @@ install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
fi
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:

View File

@@ -17,7 +17,7 @@
#
# This file if part of nzbget
#
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -62,7 +62,7 @@ host_triplet = @host@
target_triplet = @target@
bin_PROGRAMS = nzbget$(EXEEXT)
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
$(dist_exampleconf_DATA) $(dist_webuiconf_DATA) \
$(dist_exampleconf_DATA) $(nobase_dist_ppscripts_SCRIPTS) \
$(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
@@ -77,10 +77,9 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" \
"$(DESTDIR)$(webuidir)"
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
@@ -89,19 +88,27 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
Options.$(OBJEXT) ParChecker.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
RemoteServer.$(OBJEXT) Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
Options.$(OBJEXT) ParChecker.$(OBJEXT) ParRenamer.$(OBJEXT) \
ParCoordinator.$(OBJEXT) PrePostProcessor.$(OBJEXT) \
QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \
RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \
Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) nzbget.$(OBJEXT)
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) Unpack.$(OBJEXT) \
nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
nobase_dist_ppscriptsSCRIPT_INSTALL = $(install_sh_SCRIPT)
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
SCRIPTS = $(nobase_dist_ppscripts_SCRIPTS) $(sbin_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -116,18 +123,11 @@ CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(nzbget_SOURCES)
DIST_SOURCES = $(nzbget_SOURCES)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
dist_docDATA_INSTALL = $(INSTALL_DATA)
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
dist_webuiconfDATA_INSTALL = $(INSTALL_DATA)
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
$(dist_webuiconf_DATA) $(nobase_dist_webui_DATA)
$(nobase_dist_webui_DATA)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -141,16 +141,13 @@ DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
distuninstallcheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
ADDSRCS = @ADDSRCS@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
@@ -228,6 +225,8 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
openssl_CFLAGS = @openssl_CFLAGS@
openssl_LIBS = @openssl_LIBS@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
@@ -247,16 +246,16 @@ nzbget_SOURCES = \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h \
PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
@@ -270,7 +269,7 @@ doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf nzbget-postprocess.conf
nzbget.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
@@ -283,17 +282,19 @@ webui_FILES = \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
bin_SCRIPTS = nzbget-postprocess.sh
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuiconfdir = $(datadir)/nzbget/webui
dist_webuiconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
@@ -381,24 +382,30 @@ clean-binPROGRAMS:
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
@rm -f nzbget$(EXEEXT)
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
install-binSCRIPTS: $(bin_SCRIPTS)
install-nobase_dist_ppscriptsSCRIPTS: $(nobase_dist_ppscripts_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
@list='$(bin_SCRIPTS)'; for p in $$list; do \
test -z "$(ppscriptsdir)" || $(mkdir_p) "$(DESTDIR)$(ppscriptsdir)"
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " $(nobase_dist_ppscriptsSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
$(nobase_dist_ppscriptsSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ppscriptsdir)/$$f"; \
else :; fi; \
done
uninstall-binSCRIPTS:
uninstall-nobase_dist_ppscriptsSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(bin_SCRIPTS)'; for p in $$list; do \
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
rm -f "$(DESTDIR)$(bindir)/$$f"; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " rm -f '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
rm -f "$(DESTDIR)$(ppscriptsdir)/$$f"; \
done
install-sbinSCRIPTS: $(sbin_SCRIPTS)
@$(NORMAL_INSTALL)
@@ -443,6 +450,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PrePostProcessor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@
@@ -454,6 +463,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Unpack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@
@@ -510,23 +520,6 @@ uninstall-dist_exampleconfDATA:
echo " rm -f '$(DESTDIR)$(exampleconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(exampleconfdir)/$$f"; \
done
install-dist_webuiconfDATA: $(dist_webuiconf_DATA)
@$(NORMAL_INSTALL)
test -z "$(webuiconfdir)" || $(mkdir_p) "$(DESTDIR)$(webuiconfdir)"
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_webuiconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuiconfdir)/$$f'"; \
$(dist_webuiconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuiconfdir)/$$f"; \
done
uninstall-dist_webuiconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(webuiconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(webuiconfdir)/$$f"; \
done
install-nobase_dist_webuiDATA: $(nobase_dist_webui_DATA)
@$(NORMAL_INSTALL)
test -z "$(webuidir)" || $(mkdir_p) "$(DESTDIR)$(webuidir)"
@@ -598,7 +591,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
$(mkdir_p) $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
@@ -729,7 +722,7 @@ check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" "$(DESTDIR)$(webuidir)"; do \
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
@@ -778,12 +771,12 @@ info: info-am
info-am:
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
install-dist_webuiconfDATA install-nobase_dist_webuiDATA
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-exec-am: install-binPROGRAMS install-binSCRIPTS \
install-sbinSCRIPTS
install-exec-am: install-binPROGRAMS install-sbinSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
@@ -812,9 +805,9 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
uninstall-dist_webuiconfDATA uninstall-info-am \
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-info-am \
uninstall-nobase_dist_ppscriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
@@ -823,19 +816,19 @@ uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
distclean distclean-compile distclean-generic distclean-hdr \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-binSCRIPTS install-data \
install-data-am install-data-hook install-dist_docDATA \
install-dist_exampleconfDATA install-dist_webuiconfDATA \
install-exec install-exec-am install-exec-hook install-info \
install-info-am install-man install-nobase_dist_webuiDATA \
install-sbinSCRIPTS install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
ps ps-am tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-binSCRIPTS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-dist_webuiconfDATA \
uninstall-info-am uninstall-nobase_dist_webuiDATA \
uninstall-sbinSCRIPTS
install-binPROGRAMS install-data install-data-am \
install-data-hook install-dist_docDATA \
install-dist_exampleconfDATA install-exec install-exec-am \
install-exec-hook install-info install-info-am install-man \
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA install-sbinSCRIPTS \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
uninstall-info-am uninstall-nobase_dist_ppscriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
# Note about "sed":
@@ -854,12 +847,15 @@ install-exec-hook:
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration files
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
@@ -868,18 +864,9 @@ install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
fi
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,7 +27,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6212; // = "nzb-XX" (protocol version)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6217; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -81,6 +81,7 @@ enum eRemoteEditAction
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionFileSetPriority, // set priority for files
eRemoteEditActionFileReorder, // (not supported)
eRemoteEditActionFileSplit, // split - create new group from selected 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
@@ -100,7 +101,8 @@ enum eRemoteEditAction
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
eRemoteEditActionHistoryProcess, // move history-item back to download queue and start postprocessing
eRemoteEditActionHistorySetParameter // set post-process parameter for history-item
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
@@ -126,7 +128,8 @@ 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
char m_szUsername[NZBREQUESTPASSWORDSIZE]; // User name
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password
};
// The basic SNZBResposneBase struct, used in all responses
@@ -422,12 +425,10 @@ struct SNZBPostQueueResponseEntry
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

View File

@@ -613,10 +613,10 @@ void NCursesFrontend::PrintStatus()
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
@@ -624,9 +624,9 @@ void NCursesFrontend::PrintStatus()
}
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
}
else
{
@@ -646,7 +646,7 @@ void NCursesFrontend::PrintStatus()
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s, Avg. %.*f KB/s",
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
m_bPauseDownload || m_bPauseDownload2 ?
@@ -1065,15 +1065,15 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
char szTime[100];
szTime[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (pGroupInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
{
snprintf(szTime, 100, "[paused]");
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pGroupInfo->GetRemainingSize());
}
else if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
else if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(lUnpausedRemainingSize / (fCurrentDownloadSpeed * 1024));
long long remain_sec = (long long)(lUnpausedRemainingSize / iCurrentDownloadSpeed);
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
@@ -1448,7 +1448,7 @@ void NCursesFrontend::UpdateInput(int initialKey)
// Enter
else if (iKey == 10 || iKey == 13)
{
ServerSetDownloadRate((float)m_iInputValue);
ServerSetDownloadRate(m_iInputValue * 1024);
m_eInputMode = eNormal;
return;
}

View File

@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#include "nzbget.h"
#include "Log.h"
@@ -50,6 +50,7 @@ NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
SetCipher(pNewsServer->GetCipher());
}
NNTPConnection::~NNTPConnection()
@@ -84,17 +85,14 @@ const char* NNTPConnection::Request(const char* req)
{
debug("%s requested authorization", GetHost());
//authentication required!
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
@@ -102,13 +100,16 @@ const char* NNTPConnection::Request(const char* req)
bool NNTPConnection::Authenticate()
{
if (!(m_pNewsServer)->GetUser() ||
!(m_pNewsServer)->GetPassword())
if (!m_pNewsServer->GetUser() || strlen(m_pNewsServer->GetUser()) == 0 ||
!m_pNewsServer->GetPassword() || strlen(m_pNewsServer->GetPassword()) == 0)
{
return true;
error("Server%i (%s) requested authorization but username/password are not set in settings", m_pNewsServer->GetID(), m_pNewsServer->GetHost());
m_bAuthError = true;
return false;
}
return AuthInfoUser();
m_bAuthError = !AuthInfoUser(0);
return !m_bAuthError;
}
bool NNTPConnection::AuthInfoUser(int iRecur)
@@ -127,7 +128,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
return false;
}
@@ -149,7 +150,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
}
return false;
}
@@ -170,7 +171,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
return false;
}
else if (!strncmp(answer, "2", 1))
@@ -187,7 +188,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
}
return false;
}
@@ -206,10 +207,6 @@ const char* NNTPConnection::JoinGroup(const char* grp)
tmp[1024-1] = '\0';
const char* answer = Request(tmp);
if (m_bAuthError)
{
return answer;
}
if (answer && !strncmp(answer, "2", 1))
{
@@ -229,26 +226,40 @@ const char* NNTPConnection::JoinGroup(const char* grp)
return answer;
}
bool NNTPConnection::DoConnect()
bool NNTPConnection::Connect()
{
debug("Opening connection to %s", GetHost());
bool res = Connection::DoConnect();
if (!res)
if (m_eStatus == csConnected)
{
return res;
return true;
}
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!Connection::Connect())
{
return false;
}
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Connection to %s failed: Connection closed by remote host.", GetHost(), true, 0);
ReportErrorAnswer("Connection to server%i (%s) failed: Connection closed by remote host", NULL);
Disconnect();
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
ReportErrorAnswer("Connection to server%i (%s) failed (Answer: %s)", answer);
Disconnect();
return false;
}
if ((m_pNewsServer->GetUser() && strlen(m_pNewsServer->GetUser()) > 0 &&
m_pNewsServer->GetPassword() && strlen(m_pNewsServer->GetPassword()) > 0) &&
!Authenticate())
{
return false;
}
@@ -257,7 +268,7 @@ bool NNTPConnection::DoConnect()
return true;
}
bool NNTPConnection::DoDisconnect()
bool NNTPConnection::Disconnect()
{
if (m_eStatus == csConnected)
{
@@ -268,13 +279,13 @@ bool NNTPConnection::DoDisconnect()
m_szActiveGroup = NULL;
}
}
return Connection::DoDisconnect();
return Connection::Disconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, GetHost(), szAnswer);
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetID(), m_pNewsServer->GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);

View File

@@ -33,26 +33,26 @@
class NNTPConnection : public Connection
{
private:
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
virtual bool DoConnect();
virtual bool DoDisconnect();
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
bool Authenticate();
bool AuthInfoUser(int iRecur);
bool AuthInfoPass(int iRecur);
public:
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
virtual bool Connect();
virtual bool Disconnect();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -121,16 +121,6 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
// make Article-List big enough
@@ -357,7 +347,7 @@ void NZBFile::ProcessFilenames()
}
#ifdef WIN32
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
{
CoInitialize(NULL);
@@ -374,21 +364,15 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const c
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
VARIANT_BOOL success;
if (bFromBuffer)
{
success = doc->loadXML(szBuffer);
}
else
{
// 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);
}
// 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);
VARIANT_BOOL success = doc->load(v);
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
@@ -482,11 +466,14 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
int partNumber = atoi(number);
int lsize = atoi(bytes);
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
if (partNumber > 0)
{
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
if (lsize > 0)
{
@@ -501,7 +488,7 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
#else
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
{
NZBFile* pFile = new NZBFile(szFileName, szCategory);
@@ -512,18 +499,10 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const c
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
int ret = 0;
pFile->m_bIgnoreNextError = false;
if (bFromBuffer)
{
ret = xmlSAXUserParseMemory(&SAX_handler, pFile, szBuffer, iSize);
}
else
{
ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
}
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (ret == 0)
{
pFile->ProcessFilenames();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -66,12 +66,10 @@ private:
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
#endif
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* szCategory, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
static NZBFile* Create(const char* szFileName, const char* szCategory);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }

View File

@@ -38,29 +38,23 @@
#include "nzbget.h"
#include "NewsServer.h"
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel)
NewsServer::NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
{
m_iID = iID;
m_szHost = NULL;
m_iPort = iPort;
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = iLevel;
m_iGroup = iGroup;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
if (szHost)
{
m_szHost = strdup(szHost);
}
if (szUser)
{
m_szUser = strdup(szUser);
}
if (szPass)
{
m_szPassword = strdup(szPass);
}
m_szHost = szHost ? strdup(szHost) : NULL;
m_szUser = szUser ? strdup(szUser) : NULL;
m_szPassword = szPass ? strdup(szPass) : NULL;
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
NewsServer::~NewsServer()
@@ -77,4 +71,8 @@ NewsServer::~NewsServer()
{
free(m_szPassword);
}
if (m_szCipher)
{
free(m_szCipher);
}
}

View File

@@ -30,6 +30,8 @@
class NewsServer
{
private:
int m_iID;
int m_iGroup;
char* m_szHost;
int m_iPort;
char* m_szUser;
@@ -38,18 +40,24 @@ private:
int m_iLevel;
bool m_bJoinGroup;
bool m_bTLS;
char* m_szCipher;
public:
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
~NewsServer();
int GetID() { return m_iID; }
int GetGroup() { return m_iGroup; }
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
void SetLevel(int iLevel) { m_iLevel = iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
};
#endif

View File

File diff suppressed because it is too large Load Diff

199
Options.h
View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,7 +28,11 @@
#define OPTIONS_H
#include <vector>
#include <list>
#include <time.h>
#include "Thread.h"
#include "Util.h"
class Options
{
@@ -76,11 +80,17 @@ public:
omColored,
omNCurses
};
enum ELoadPars
enum EParCheck
{
lpNone,
lpOne,
lpAll
pcAuto,
pcForce,
pcManual
};
enum EParScan
{
psLimited,
psFull,
psAuto
};
enum EScriptLogKind
{
@@ -99,12 +109,6 @@ public:
mmRegEx
};
enum EDomain
{
dmServer = 1,
dmPostProcess
};
class OptEntry
{
private:
@@ -138,22 +142,100 @@ public:
OptEntry* FindOption(const char* szName);
};
class ConfigTemplate
{
private:
char* m_szName;
char* m_szDisplayName;
char* m_szTemplate;
friend class Options;
public:
ConfigTemplate(const char* szName, const char* szDisplayName, const char* szTemplate);
~ConfigTemplate();
const char* GetName() { return m_szName; }
const char* GetDisplayName() { return m_szDisplayName; }
const char* GetTemplate() { return m_szTemplate; }
};
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
class ConfigTemplates: public ConfigTemplatesBase
{
public:
~ConfigTemplates();
};
typedef std::vector<char*> NameList;
class Category
{
private:
char* m_szName;
char* m_szDestDir;
char* m_szDefScript;
public:
Category(const char* szName, const char* szDestDir, const char* szDefScript);
~Category();
const char* GetName() { return m_szName; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetDefScript() { return m_szDefScript; }
};
typedef std::vector<Category*> CategoriesBase;
class Categories: public CategoriesBase
{
public:
~Categories();
Category* FindCategory(const char* szName);
};
class Script
{
private:
char* m_szName;
char* m_szLocation;
char* m_szDisplayName;
public:
Script(const char* szName, const char* szLocation);
~Script();
const char* GetName() { return m_szName; }
const char* GetLocation() { return m_szLocation; }
void SetDisplayName(const char* szDisplayName);
const char* GetDisplayName() { return m_szDisplayName; }
};
typedef std::list<Script*> ScriptListBase;
class ScriptList: public ScriptListBase
{
public:
~ScriptList();
Script* Find(const char* szName);
};
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
Categories m_Categories;
// Options
bool m_bConfigErrors;
int m_iConfigLine;
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szInterDir;
char* m_szTempDir;
char* m_szQueueDir;
char* m_szNzbDir;
char* m_szWebDir;
char* m_szConfigTemplate;
char* m_szScriptDir;
EMessageTarget m_eInfoTarget;
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
@@ -164,19 +246,22 @@ private:
bool m_bResetLog;
int m_iConnectionTimeout;
int m_iTerminateTimeout;
bool m_bAppendNZBDir;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
bool m_bRenameBroken;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bDupeCheck;
char* m_szControlIP;
char* m_szControlUsername;
char* m_szControlPassword;
int m_szControlPort;
int m_iControlPort;
bool m_bSecureControl;
int m_iSecurePort;
char* m_szSecureCert;
char* m_szSecureKey;
char* m_szLockFile;
char* m_szDaemonUserName;
char* m_szDaemonUsername;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadUrlQueue;
@@ -185,11 +270,11 @@ private:
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
ELoadPars m_eLoadPars;
bool m_bParCheck;
EParCheck m_eParCheck;
bool m_bParRepair;
char* m_szPostProcess;
char* m_szPostConfigFilename;
EParScan m_eParScan;
char* m_szDefScript;
char* m_szScriptOrder;
char* m_szNZBProcess;
char* m_szNZBAddedProcess;
bool m_bStrictParName;
@@ -200,26 +285,28 @@ private:
bool m_bCursesTime;
bool m_bCursesGroup;
bool m_bCrcCheck;
bool m_bRetryOnCrcError;
int m_iThreadLimit;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EScriptLogKind m_eProcessLogKind;
bool m_bAllowReProcess;
bool m_bTLS;
bool m_bDumpCore;
bool m_bParPauseQueue;
bool m_bPostPauseQueue;
bool m_bScriptPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
int m_iParTimeLimit;
int m_iKeepHistory;
bool m_bAccurateRate;
bool m_bUnpack;
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
// Parsed command-line parameters
bool m_bServerMode;
@@ -240,7 +327,7 @@ private:
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
@@ -250,16 +337,17 @@ private:
bool m_bPauseDownload2;
bool m_bPausePostProcess;
bool m_bPauseScan;
float m_fDownloadRate;
int m_iDownloadRate;
EClientOperation m_eClientOperation;
time_t m_tResumeTime;
void InitDefault();
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitPostConfig();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitCategories();
void InitScheduler();
void CheckOptions();
void PrintUsage(char* com);
@@ -279,24 +367,34 @@ private:
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
void ConfigError(const char* msg, ...);
void ConvertOldOptionName(char *szOption, int iBufLen);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *szOptionName);
void ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue, int iValueBufLen);
static bool CompareScripts(Script* pScript1, Script* pScript2);
void LoadScriptDir(ScriptList* pScriptList, const char* szDirectory, bool bIsSubDir);
void BuildScriptDisplayNames(ScriptList* pScriptList);
public:
Options(int argc, char* argv[]);
~Options();
bool LoadConfig(EDomain eDomain, OptEntries* pOptEntries);
bool SaveConfig(EDomain eDomain, OptEntries* pOptEntries);
bool LoadConfig(OptEntries* pOptEntries);
bool SaveConfig(OptEntries* pOptEntries);
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
void LoadScriptList(ScriptList* pScriptList);
// Options
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetConfigFilename() { return m_szConfigFilename; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetInterDir() { return m_szInterDir; }
const char* GetTempDir() { return m_szTempDir; }
const char* GetQueueDir() { return m_szQueueDir; }
const char* GetNzbDir() { return m_szNzbDir; }
const char* GetWebDir() { return m_szWebDir; }
const char* GetConfigTemplate() { return m_szConfigTemplate; }
const char* GetScriptDir() { return m_szScriptDir; }
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
bool GetResetLog() const { return m_bResetLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
@@ -307,19 +405,22 @@ public:
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
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; }
const char* GetControlIP() { return m_szControlIP; }
const char* GetControlUsername() { return m_szControlUsername; }
const char* GetControlPassword() { return m_szControlPassword; }
int GetControlPort() { return m_szControlPort; }
int GetControlPort() { return m_iControlPort; }
bool GetSecureControl() { return m_bSecureControl; }
int GetSecurePort() { return m_iSecurePort; }
const char* GetSecureCert() { return m_szSecureCert; }
const char* GetSecureKey() { return m_szSecureKey; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUserName() { return m_szDaemonUserName; }
const char* GetDaemonUsername() { return m_szDaemonUsername; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
@@ -328,11 +429,11 @@ public:
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
const char* GetLogFile() { return m_szLogFile; }
ELoadPars GetLoadPars() { return m_eLoadPars; }
bool GetParCheck() { return m_bParCheck; }
EParCheck GetParCheck() { return m_eParCheck; }
bool GetParRepair() { return m_bParRepair; }
const char* GetPostProcess() { return m_szPostProcess; }
const char* GetPostConfigFilename() { return m_szPostConfigFilename; }
EParScan GetParScan() { return m_eParScan; }
const char* GetScriptOrder() { return m_szScriptOrder; }
const char* GetDefScript() { return m_szDefScript; }
const char* GetNZBProcess() { return m_szNZBProcess; }
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
bool GetStrictParName() { return m_bStrictParName; }
@@ -342,26 +443,30 @@ public:
bool GetCursesTime() { return m_bCursesTime; }
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
int GetThreadLimit() { return m_iThreadLimit; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
int GetDiskSpace() { return m_iDiskSpace; }
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
bool GetTLS() { return m_bTLS; }
bool GetDumpCore() { return m_bDumpCore; }
bool GetParPauseQueue() { return m_bParPauseQueue; }
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
bool GetScriptPauseQueue() { return m_bScriptPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
int GetParTimeLimit() { return m_iParTimeLimit; }
int GetKeepHistory() { return m_iKeepHistory; }
bool GetAccurateRate() { return m_bAccurateRate; }
bool GetUnpack() { return m_bUnpack; }
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
Category* FindCategory(const char* szName) { return m_Categories.FindCategory(szName); }
// Parsed command-line parameters
bool GetServerMode() { return m_bServerMode; }
@@ -382,7 +487,7 @@ public:
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
float GetSetRate() { return m_fSetRate; }
int GetSetRate() { return m_iSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
@@ -396,8 +501,10 @@ public:
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; }
void SetDownloadRate(int iRate) { m_iDownloadRate = iRate; }
int GetDownloadRate() const { return m_iDownloadRate; }
void SetResumeTime(time_t tResumeTime) { m_tResumeTime = tResumeTime; }
time_t GetResumeTime() const { return m_tResumeTime; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,8 +35,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
@@ -45,9 +45,11 @@
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ParChecker.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
@@ -67,15 +69,96 @@ const char* Par2CmdLineErrStr[] = { "OK",
class Repairer : public Par2Repairer
{
private:
CommandLine commandLine;
public:
Result PreProcess(const char *szParFilename);
Result Process(bool dorepair);
friend class ParChecker;
};
Result Repairer::PreProcess(const char *szParFilename)
{
#ifdef HAVE_PAR2_BUGFIXES_V2
// Ensure linking against the patched version of libpar2
BugfixesPatchVersion2();
#endif
if (g_pOptions->GetParScan() == Options::psFull)
{
char szWildcardParam[1024];
strncpy(szWildcardParam, szParFilename, 1024);
szWildcardParam[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szWildcardParam);
if (szBasename != szWildcardParam && strlen(szBasename) > 0)
{
szBasename[0] = '*';
szBasename[1] = '\0';
}
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename, szWildcardParam };
if (!commandLine.Parse(6, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
else
{
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
return Par2Repairer::PreProcess(commandLine);
}
Result Repairer::Process(bool dorepair)
{
return Par2Repairer::Process(commandLine, dorepair);
}
class MissingFilesComparator
{
private:
const char* m_szBaseParFilename;
public:
MissingFilesComparator(const char* szBaseParFilename) : m_szBaseParFilename(szBaseParFilename) {}
bool operator()(CommandLine::ExtraFile* pFirst, CommandLine::ExtraFile* pSecond) const;
};
/*
* Files with the same name as in par-file (and a differnt extension) are
* placed at the top of the list to be scanned first.
*/
bool MissingFilesComparator::operator()(CommandLine::ExtraFile* pFile1, CommandLine::ExtraFile* pFile2) const
{
char name1[1024];
strncpy(name1, Util::BaseFileName(pFile1->FileName().c_str()), 1024);
name1[1024-1] = '\0';
if (char* ext = strrchr(name1, '.')) *ext = '\0'; // trim extension
char name2[1024];
strncpy(name2, Util::BaseFileName(pFile2->FileName().c_str()), 1024);
name2[1024-1] = '\0';
if (char* ext = strrchr(name2, '.')) *ext = '\0'; // trim extension
return strcmp(name1, m_szBaseParFilename) == 0 && strcmp(name1, name2) != 0;
}
ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psUndefined;
m_eStatus = psFailed;
m_szDestDir = NULL;
m_szNZBName = NULL;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
@@ -86,16 +169,19 @@ ParChecker::ParChecker()
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
m_eStage = ptLoadingPars;
m_QueuedParFiles.clear();
}
ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
if (m_szParFilename)
if (m_szDestDir)
{
free(m_szParFilename);
free(m_szDestDir);
}
if (m_szNZBName)
{
free(m_szNZBName);
}
if (m_szInfoName)
{
@@ -107,20 +193,42 @@ ParChecker::~ParChecker()
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
Cleanup();
}
void ParChecker::Cleanup()
{
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end() ;it++)
{
free(*it);
}
m_ProcessedFiles.clear();
m_sourceFiles.clear();
}
void ParChecker::SetParFilename(const char * szParFilename)
void ParChecker::SetDestDir(const char * szDestDir)
{
if (m_szParFilename)
if (m_szDestDir)
{
free(m_szParFilename);
free(m_szDestDir);
}
m_szParFilename = strdup(szParFilename);
m_szDestDir = strdup(szDestDir);
}
void ParChecker::SetNZBName(const char * szNZBName)
{
if (m_szNZBName)
{
free(m_szNZBName);
}
m_szNZBName = strdup(szNZBName);
}
void ParChecker::SetInfoName(const char * szInfoName)
@@ -132,34 +240,121 @@ void ParChecker::SetInfoName(const char * szInfoName)
m_szInfoName = strdup(szInfoName);
}
void ParChecker::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParChecker::Run()
{
m_bRepairNotNeeded = false;
ParCoordinator::FileList fileList;
if (!ParCoordinator::FindMainPars(m_szDestDir, &fileList))
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Could not find any par-files", m_szNZBName);
m_eStatus = psFailed;
Completed();
return;
}
m_eStatus = psRepairNotNeeded;
m_bCancelled = false;
for (ParCoordinator::FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
char* szParFilename = *it;
debug("Found par: %s", szParFilename);
if (!IsStopped() && !m_bCancelled)
{
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
char szInfoName[1024];
int iBaseLen = 0;
ParCoordinator::ParseParFilename(szParFilename, &iBaseLen, NULL);
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
strncpy(szInfoName, szParFilename, maxlen);
szInfoName[maxlen] = '\0';
char szParInfoName[1024];
snprintf(szParInfoName, 1024, "%s%c%s", m_szNZBName, (int)PATH_SEPARATOR, szInfoName);
szParInfoName[1024-1] = '\0';
SetInfoName(szParInfoName);
EStatus eStatus = RunParCheck(szFullParFilename);
// accumulate total status, the worst status has priority
if (m_eStatus > eStatus)
{
m_eStatus = eStatus;
}
if (g_pOptions->GetCreateBrokenLog())
{
WriteBrokenLog(eStatus);
}
}
free(szParFilename);
}
Completed();
}
void ParChecker::WriteBrokenLog(EStatus eStatus)
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_szDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (eStatus != psRepairNotNeeded || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (eStatus == psFailed)
{
if (m_bCancelled)
{
fprintf(file, "Repair cancelled for %s\n", m_szInfoName);
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
}
else if (eStatus == psRepairPossible)
{
fprintf(file, "Repair possible for %s\n", m_szInfoName);
}
else if (eStatus == psRepaired)
{
fprintf(file, "Successfully repaired %s\n", m_szInfoName);
}
else if (eStatus == psRepairNotNeeded)
{
fprintf(file, "Repair not needed for %s\n", m_szInfoName);
}
fclose(file);
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
}
ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
{
Cleanup();
m_szParFilename = szParFilename;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
EStatus eStatus = psFailed;
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
PrintMessage(Message::mkInfo, "Verifying %s", m_szInfoName);
debug("par: %s", m_szParFilename);
CommandLine commandLine;
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);
SetStatus(psFailed);
return;
}
Result res;
Repairer* pRepairer = new Repairer();
@@ -175,16 +370,24 @@ void ParChecker::Run()
m_iStageProgress = 0;
UpdateProgress();
res = pRepairer->PreProcess(commandLine);
res = pRepairer->PreProcess(m_szParFilename);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
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);
if (res == eInvalidCommandLineArguments)
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
}
else
{
PrintMessage(Message::mkError, "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");
}
delete pRepairer;
return;
Cleanup();
return psFailed;
}
char BufReason[1024];
@@ -196,13 +399,19 @@ void ParChecker::Run()
m_szErrMsg = NULL;
m_eStage = ptVerifyingSources;
res = pRepairer->Process(commandLine, false);
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && pRepairer->missingfilecount > 0 && g_pOptions->GetParScan() == Options::psAuto && AddMissingFiles())
{
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
@@ -212,7 +421,7 @@ void ParChecker::Run()
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
PrintMessage(Message::mkInfo, "Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
@@ -267,29 +476,33 @@ void ParChecker::Run()
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
SetStatus(psFailed);
delete pRepairer;
return;
Cleanup();
return psFailed;
}
eStatus = psFailed;
if (res == eSuccess)
{
info("Repair not needed for %s", m_szInfoName);
m_bRepairNotNeeded = true;
PrintMessage(Message::mkInfo, "Repair not needed for %s", m_szInfoName);
eStatus = psRepairNotNeeded;
}
else if (res == eRepairPossible)
{
eStatus = psRepairPossible;
if (g_pOptions->GetParRepair())
{
info("Repairing %s", m_szInfoName);
PrintMessage(Message::mkInfo, "Repairing %s", m_szInfoName);
SaveSourceList();
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
@@ -299,62 +512,69 @@ void ParChecker::Run()
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
UpdateProgress();
res = pRepairer->Process(commandLine, true);
res = pRepairer->Process(true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
info("Successfully repaired %s", m_szInfoName);
PrintMessage(Message::mkInfo, "Successfully repaired %s", m_szInfoName);
eStatus = psRepaired;
DeleteLeftovers();
}
}
else
{
info("Repair possible for %s", m_szInfoName);
res = eSuccess;
PrintMessage(Message::mkInfo, "Repair possible for %s", m_szInfoName);
}
}
if (m_bCancelled)
{
warn("Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
SetStatus(psFailed);
if (m_eStage >= ptRepairing)
{
PrintMessage(Message::mkWarning, "Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
eStatus = psRepairPossible;
}
else
{
PrintMessage(Message::mkWarning, "Par-check cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("par-check cancelled");
eStatus = psFailed;
}
}
else if (res == eSuccess)
{
SetStatus(psFinished);
}
else
else if (eStatus == psFailed)
{
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
{
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
}
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
SetStatus(psFailed);
PrintMessage(Message::mkError, "Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
delete pRepairer;
Cleanup();
return eStatus;
}
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
QueuedParFiles moreFiles;
FileList moreFiles;
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
for (FileList::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
{
char* szParFilename = *it;
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
PrintMessage(Message::mkInfo, "File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
PrintMessage(Message::mkInfo, "Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
@@ -381,11 +601,11 @@ bool ParChecker::CheckSplittedFragments()
{
bool bFragmentsAdded = false;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
if (AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
@@ -408,7 +628,7 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
szBasename[-1] = '\0';
int iBaseLen = strlen(szBasename);
list<CommandLine::ExtraFile> extrafiles;
std::list<CommandLine::ExtraFile> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
@@ -438,7 +658,7 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
if (!extrafiles.empty())
{
m_iExtraFiles = extrafiles.size();
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
m_bVerifyingExtraFiles = false;
@@ -447,6 +667,93 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
return bFragmentsAdded;
}
bool ParChecker::AddMissingFiles()
{
PrintMessage(Message::mkInfo, "Performing extra par-scan for %s", m_szInfoName);
char szDirectory[1024];
strncpy(szDirectory, m_szParFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
std::list<CommandLine::ExtraFile*> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && strcmp(filename, "_brokenlog.txt"))
{
bool bAlreadyScanned = false;
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end(); it++)
{
const char* szProcessedFilename = *it;
if (!strcasecmp(Util::BaseFileName(szProcessedFilename), filename))
{
bAlreadyScanned = true;
break;
}
}
if (!bAlreadyScanned)
{
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
extrafiles.push_back(new CommandLine::ExtraFile(fullfilename, Util::FileSize(fullfilename)));
}
}
}
// Sort the list
char* szBaseParFilename = strdup(Util::BaseFileName(m_szParFilename));
if (char* ext = strrchr(szBaseParFilename, '.')) *ext = '\0'; // trim extension
extrafiles.sort(MissingFilesComparator(szBaseParFilename));
free(szBaseParFilename);
// Scan files
bool bFilesAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
std::list<CommandLine::ExtraFile> extrafiles1;
// adding files one by one until all missing files are found
while (!IsStopped() && !m_bCancelled && extrafiles.size() > 0 && ((Repairer*)m_pRepairer)->missingfilecount > 0)
{
CommandLine::ExtraFile* pExtraFile = extrafiles.front();
extrafiles.pop_front();
extrafiles1.clear();
extrafiles1.push_back(*pExtraFile);
bFilesAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles1) || bFilesAdded;
((Repairer*)m_pRepairer)->UpdateVerificationResults();
delete pExtraFile;
}
m_bVerifyingExtraFiles = false;
// free any remaining objects
for (std::list<CommandLine::ExtraFile*>::iterator it = extrafiles.begin(); it != extrafiles.end() ;it++)
{
delete *it;
}
}
return bFilesAdded;
}
void ParChecker::signal_filename(std::string str)
{
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
@@ -456,7 +763,12 @@ void ParChecker::signal_filename(std::string str)
m_eStage = ptVerifyingRepaired;
}
info("%s %s", szStageMessage[m_eStage], str.c_str());
PrintMessage(Message::mkInfo, "%s %s", szStageMessage[m_eStage], str.c_str());
if (m_eStage == ptLoadingPars || m_eStage == ptVerifyingSources)
{
m_ProcessedFiles.push_back(strdup(str.c_str()));
}
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
m_szProgressLabel[1024-1] = '\0';
@@ -521,7 +833,7 @@ void ParChecker::signal_done(std::string str, int available, int total)
{
bool bFileExists = true;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
@@ -535,11 +847,11 @@ void ParChecker::signal_done(std::string str, int available, int total)
if (bFileExists)
{
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
PrintMessage(Message::mkWarning, "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);
PrintMessage(Message::mkWarning, "File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
@@ -551,8 +863,60 @@ void ParChecker::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.");
PrintMessage(Message::mkError, "Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details).");
#endif
}
void ParChecker::SaveSourceList()
{
// Buliding a list of DiskFile-objects, marked as source-files
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile* sourcefile = (Par2RepairerSourceFile*)*it;
vector<DataBlock>::iterator it2 = sourcefile->SourceBlocks();
for (int i = 0; i < (int)sourcefile->BlockCount(); i++, it2++)
{
DataBlock block = *it2;
DiskFile* pSourceFile = block.GetDiskFile();
if (pSourceFile &&
std::find(m_sourceFiles.begin(), m_sourceFiles.end(), pSourceFile) == m_sourceFiles.end())
{
m_sourceFiles.push_back(pSourceFile);
}
}
}
}
void ParChecker::DeleteLeftovers()
{
// After repairing check if all DiskFile-objects saved by "SaveSourceList()" have
// corresponding target-files. If not - the source file was replaced. In this case
// the DiskFile-object points to the renamed bak-file, which we can delete.
for (SourceList::iterator it = m_sourceFiles.begin(); it != m_sourceFiles.end(); it++)
{
DiskFile* pSourceFile = (DiskFile*)*it;
bool bFound = false;
for (std::vector<Par2RepairerSourceFile*>::iterator it2 = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it2 != ((Repairer*)m_pRepairer)->sourcefiles.end(); it2++)
{
Par2RepairerSourceFile* sourcefile = *it2;
if (sourcefile->GetTargetFile() == pSourceFile)
{
bFound = true;
break;
}
}
if (!bFound)
{
PrintMessage(Message::mkInfo, "Deleting file %s", Util::BaseFileName(pSourceFile->FileName().c_str()));
remove(pSourceFile->FileName().c_str());
}
}
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,19 +29,20 @@
#ifndef DISABLE_PARCHECK
#include <deque>
#include <string>
#include "Thread.h"
#include "Observer.h"
#include "Log.h"
class ParChecker : public Thread, public Subject
class ParChecker : public Thread
{
public:
enum EStatus
{
psUndefined,
psWorking,
psFailed,
psFinished
psRepairPossible,
psRepaired,
psRepairNotNeeded
};
enum EStage
@@ -52,19 +53,22 @@ public:
ptVerifyingRepaired,
};
typedef std::deque<char*> QueuedParFiles;
typedef std::deque<char*> FileList;
typedef std::deque<void*> SourceList;
private:
char* m_szInfoName;
char* m_szParFilename;
char* m_szDestDir;
char* m_szNZBName;
const char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
char* m_szErrMsg;
bool m_bRepairNotNeeded;
QueuedParFiles m_QueuedParFiles;
FileList m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
bool m_bQueuedParFilesChanged;
FileList m_ProcessedFiles;
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
@@ -73,10 +77,17 @@ private:
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
SourceList m_sourceFiles;
EStatus RunParCheck(const char* szParFilename);
void WriteBrokenLog(EStatus eStatus);
void Cleanup();
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
bool AddMissingFiles();
void SaveSourceList();
void DeleteLeftovers();
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
@@ -89,6 +100,8 @@ protected:
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
@@ -98,14 +111,12 @@ public:
ParChecker();
virtual ~ParChecker();
virtual void Run();
void SetDestDir(const char* szDestDir);
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
void SetNZBName(const char* szNZBName);
EStatus GetStatus() { return m_eStatus; }
const char* GetErrMsg() { return m_szErrMsg; }
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
void Cancel();

726
ParCoordinator.cpp Normal file
View File

@@ -0,0 +1,726 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nzbget.h"
#include "ParCoordinator.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "QueueCoordinator.h"
#include "DiskState.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#ifndef DISABLE_PARCHECK
bool ParCoordinator::PostParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
return m_pOwner->RequestMorePars(m_pPostInfo->GetNZBInfo(), GetParFilename(), iBlockNeeded, pBlockFound);
}
void ParCoordinator::PostParChecker::UpdateProgress()
{
m_pOwner->UpdateParCheckProgress();
}
void ParCoordinator::PostParChecker::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_pOwner->UpdateParRenameProgress();
}
void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
#endif
ParCoordinator::ParCoordinator()
{
debug("Creating ParCoordinator");
#ifndef DISABLE_PARCHECK
m_bStopped = false;
m_ParChecker.m_pOwner = this;
m_ParRenamer.m_pOwner = this;
#endif
}
ParCoordinator::~ParCoordinator()
{
debug("Destroying ParCoordinator");
}
#ifndef DISABLE_PARCHECK
void ParCoordinator::Stop()
{
debug("Stopping ParCoordinator");
m_bStopped = true;
if (m_ParChecker.IsRunning())
{
m_ParChecker.Stop();
int iMSecWait = 5000;
while (m_ParChecker.IsRunning() && iMSecWait > 0)
{
usleep(50 * 1000);
iMSecWait -= 50;
}
if (m_ParChecker.IsRunning())
{
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
m_ParChecker.Kill();
}
}
}
#endif
void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("ParCoordinator: Pausing pars");
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
QueueEditor::eaGroupPauseExtraPars, 0, NULL);
break;
}
}
}
bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
{
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
const char* filename2 = *it;
exists = SameParCollection(filename, filename2);
if (exists)
{
break;
}
}
if (!exists)
{
pFileList->push_back(strdup(filename));
}
}
}
return pFileList && !pFileList->empty();
}
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
{
int iBaseLen1 = 0, iBaseLen2 = 0;
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
iBaseLen1 == iBaseLen2 &&
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
}
bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
#ifndef DISABLE_PARCHECK
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
{
m_eCurrentJob = jkParCheck;
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParChecker.SetNZBName(pPostInfo->GetNZBInfo()->GetName());
m_ParChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", pPostInfo->GetInfoName());
pPostInfo->SetWorking(true);
m_ParChecker.Start();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
{
m_eCurrentJob = jkParRename;
m_ParRenamer.SetPostInfo(pPostInfo);
m_ParRenamer.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
m_ParRenamer.Start();
}
bool ParCoordinator::Cancel()
{
if (m_eCurrentJob == jkParCheck)
{
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName());
m_ParChecker.Cancel();
return true;
}
#else
warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName());
#endif
}
else if (m_eCurrentJob == jkParRename)
{
if (!m_ParRenamer.GetCancelled())
{
debug("Cancelling par-rename for %s", m_ParRenamer.GetInfoName());
m_ParRenamer.Cancel();
return true;
}
}
return false;
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
bool ParCoordinator::AddPar(FileInfo* pFileInfo, bool bDeleted)
{
bool bSameCollection = m_ParChecker.IsRunning() &&
pFileInfo->GetNZBInfo() == m_ParChecker.GetPostInfo()->GetNZBInfo() &&
SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(m_ParChecker.GetParFilename()));
if (bSameCollection && !bDeleted)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
szFullFilename[1024-1] = '\0';
m_ParChecker.AddParFile(szFullFilename);
if (g_pOptions->GetParPauseQueue())
{
PauseDownload();
}
}
else
{
m_ParChecker.QueueChanged();
}
return bSameCollection;
}
void ParCoordinator::ParCheckCompleted()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
// Update ParStatus (accumulate result)
if ((m_ParChecker.GetStatus() == ParChecker::psRepaired ||
m_ParChecker.GetStatus() == ParChecker::psRepairNotNeeded) &&
pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
else if (m_ParChecker.GetStatus() == ParChecker::psRepairPossible &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
else
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
Blocks blocks;
blocks.clear();
int iBlockFound = 0;
int iCurBlockFound = 0;
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, true, &iCurBlockFound);
iBlockFound += iCurBlockFound;
if (iBlockFound < iBlockNeeded)
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound < iBlockNeeded && !g_pOptions->GetStrictParName())
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, false, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound >= iBlockNeeded)
{
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
// starting from the file with max block count.
// if par-collection was built exponentially and all par-files present,
// this step selects par-files with exact number of blocks we need.
while (iBlockNeeded > 0)
{
BlockInfo* pBestBlockInfo = NULL;
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
{
pBestBlockInfo = pBlockInfo;
}
}
if (pBestBlockInfo)
{
if (pBestBlockInfo->m_pFileInfo->GetPaused())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
pBestBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
blocks.remove(pBestBlockInfo);
delete pBestBlockInfo;
}
else
{
break;
}
}
// 2. then unpause other files
// this step only needed if the par-collection was built not exponentially
// or not all par-files present (or some of them were corrupted)
// this step is not optimal, but we hope, that the first step will work good
// in most cases and we will not need the second step often
while (iBlockNeeded > 0)
{
BlockInfo* pBlockInfo = blocks.front();
if (pBlockInfo->m_pFileInfo->GetPaused())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
pBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
iBlockNeeded -= pBlockInfo->m_iBlockCount;
}
}
g_pQueueCoordinator->UnlockQueue();
if (pBlockFound)
{
*pBlockFound = iBlockFound;
}
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
delete *it;
}
blocks.clear();
bool bOK = iBlockNeeded <= 0;
if (bOK && g_pOptions->GetParPauseQueue())
{
UnpauseDownload();
}
return bOK;
}
void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound)
{
*pBlockFound = 0;
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* szBaseParFilename = Util::BaseFileName(szParFilename);
char szMainBaseFilename[1024];
int iMainBaseLen = 0;
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
{
// should not happen
error("Internal error: could not parse filename %s", szBaseParFilename);
return;
}
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
szMainBaseFilename[maxlen] = '\0';
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (pFileInfo->GetNZBInfo() == pNZBInfo &&
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
bool bUseFile = true;
if (bExactParName)
{
bUseFile = SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
}
else if (bStrictParName)
{
// the pFileInfo->GetFilename() may be not confirmed and may contain
// additional texts if Subject could not be parsed correctly
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
char szCandidateFileName[1024];
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
bUseFile = strstr(szLoFileName, szCandidateFileName);
}
}
bool bAlreadyAdded = false;
// check if file is not in the list already
if (bUseFile)
{
for (Blocks::iterator it = pBlocks->begin(); it != pBlocks->end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_pFileInfo == pFileInfo)
{
bAlreadyAdded = true;
break;
}
}
}
// if it is a par2-file with blocks and it was from the same NZB-request
// and it belongs to the same file collection (same base name),
// then OK, we can use it
if (bUseFile && !bAlreadyAdded)
{
BlockInfo* pBlockInfo = new BlockInfo();
pBlockInfo->m_pFileInfo = pFileInfo;
pBlockInfo->m_iBlockCount = iBlocks;
pBlocks->push_back(pBlockInfo);
*pBlockFound += iBlocks;
}
}
}
}
void ParCoordinator::UpdateParCheckProgress()
{
g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
if (m_ParChecker.GetFileProgress() == 0)
{
pPostInfo->SetProgressLabel(m_ParChecker.GetProgressLabel());
}
pPostInfo->SetFileProgress(m_ParChecker.GetFileProgress());
pPostInfo->SetStageProgress(m_ParChecker.GetStageProgress());
PostInfo::EStage StageKind[] = { PostInfo::ptLoadingPars, PostInfo::ptVerifyingSources, PostInfo::ptRepairing, PostInfo::ptVerifyingRepaired };
PostInfo::EStage eStage = StageKind[m_ParChecker.GetStage()];
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != eStage)
{
pPostInfo->SetStage(eStage);
pPostInfo->SetStageTime(tCurrent);
}
bool bParCancel = false;
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
if ((g_pOptions->GetParTimeLimit() > 0) &&
m_ParChecker.GetStage() == ParChecker::ptRepairing &&
((g_pOptions->GetParTimeLimit() > 5 && tCurrent - pPostInfo->GetStageTime() > 5 * 60) ||
(g_pOptions->GetParTimeLimit() <= 5 && tCurrent - pPostInfo->GetStageTime() > 1 * 60)))
{
// first five (or one) minutes elapsed, now can check the estimated time
int iEstimatedRepairTime = (int)((tCurrent - pPostInfo->GetStartTime()) * 1000 /
(pPostInfo->GetStageProgress() > 0 ? pPostInfo->GetStageProgress() : 1));
if (iEstimatedRepairTime > g_pOptions->GetParTimeLimit() * 60)
{
debug("Estimated repair time %i seconds", iEstimatedRepairTime);
m_ParChecker.PrintMessage(Message::mkWarning, "Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
bParCancel = true;
}
}
}
#endif
if (bParCancel)
{
m_ParChecker.Cancel();
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
{
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = pPostInfo->GetStageTime();
time_t tStartTime = pPostInfo->GetStartTime();
time_t tWaitTime = time(NULL);
// wait until Post-processor is unpaused
while (g_pOptions->GetPausePostProcess() && !m_bStopped)
{
usleep(100 * 1000);
// update time stamps
time_t tDelta = time(NULL) - tWaitTime;
if (tStageTime > 0)
{
pPostInfo->SetStageTime(tStageTime + tDelta);
}
if (tStartTime > 0)
{
pPostInfo->SetStartTime(tStartTime + tDelta);
}
}
}
}
void ParCoordinator::ParRenameCompleted()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->GetNZBInfo()->SetRenameStatus(m_ParRenamer.GetStatus() == ParRenamer::psSuccess ? NZBInfo::rsSuccess : NZBInfo::rsFailure);
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
void ParCoordinator::UpdateParRenameProgress()
{
g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->SetProgressLabel(m_ParRenamer.GetProgressLabel());
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != PostInfo::ptRenaming)
{
pPostInfo->SetStage(PostInfo::ptRenaming);
pPostInfo->SetStageTime(tCurrent);
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
void ParCoordinator::PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
pPostInfo->AppendMessage(eKind, szText);
switch (eKind)
{
case Message::mkDetail:
detail("%s", szText);
break;
case Message::mkInfo:
info("%s", szText);
break;
case Message::mkWarning:
warn("%s", szText);
break;
case Message::mkError:
error("%s", szText);
break;
case Message::mkDebug:
debug("%s", szText);
break;
}
}
#endif

130
ParCoordinator.h Normal file
View File

@@ -0,0 +1,130 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARCOORDINATOR_H
#define PARCOORDINATOR_H
#include <list>
#include <deque>
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#include "ParRenamer.h"
#endif
class ParCoordinator
{
private:
#ifndef DISABLE_PARCHECK
class PostParChecker: public ParChecker
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParCheckCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
class PostParRenamer: public ParRenamer
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParRenameCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
enum EJobKind
{
jkParCheck,
jkParRename
};
private:
PostParChecker m_ParChecker;
bool m_bStopped;
PostParRenamer m_ParRenamer;
EJobKind m_eCurrentJob;
protected:
virtual bool PauseDownload() = 0;
virtual bool UnpauseDownload() = 0;
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void ParCheckCompleted();
void ParRenameCompleted();
void CheckPauseState(PostInfo* pPostInfo);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...);
#endif
public:
typedef std::deque<char*> FileList;
public:
ParCoordinator();
virtual ~ParCoordinator();
static bool FindMainPars(const char* szPath, FileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void StartParCheckJob(PostInfo* pPostInfo);
void StartParRenameJob(PostInfo* pPostInfo);
void Stop();
bool Cancel();
#endif
};
#endif

315
ParRenamer.cpp Normal file
View File

@@ -0,0 +1,315 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#ifndef DISABLE_PARCHECK
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#include <md5.h>
#else
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#include <libpar2/md5.h>
#endif
#include "nzbget.h"
#include "ParRenamer.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
class ParRenamerRepairer : public Par2Repairer
{
public:
friend class ParRenamer;
};
ParRenamer::FileHash::FileHash(const char* szFilename, const char* szHash)
{
m_szFilename = strdup(szFilename);
m_szHash = strdup(szHash);
}
ParRenamer::FileHash::~FileHash()
{
free(m_szFilename);
free(m_szHash);
}
ParRenamer::ParRenamer()
{
debug("Creating ParRenamer");
m_eStatus = psFailed;
m_szDestDir = NULL;
m_szInfoName = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_iStageProgress = 0;
m_bCancelled = false;
}
ParRenamer::~ParRenamer()
{
debug("Destroying ParRenamer");
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
free(m_szProgressLabel);
Cleanup();
}
void ParRenamer::Cleanup()
{
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
delete *it;
}
m_fileHashList.clear();
}
void ParRenamer::SetDestDir(const char * szDestDir)
{
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void ParRenamer::SetInfoName(const char * szInfoName)
{
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
void ParRenamer::Cancel()
{
m_bCancelled = true;
}
void ParRenamer::Run()
{
Cleanup();
m_bCancelled = false;
m_iRenamedCount = 0;
m_eStatus = psFailed;
snprintf(m_szProgressLabel, 1024, "Checking renamed files for %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = 0;
UpdateProgress();
LoadParFiles();
CheckFiles();
if (m_bCancelled)
{
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", m_szInfoName);
}
else if (m_iRenamedCount > 0)
{
PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
m_eStatus = psSuccess;
}
else
{
PrintMessage(Message::mkInfo, "No renamed files found for %s", m_szInfoName);
}
Cleanup();
Completed();
}
void ParRenamer::LoadParFiles()
{
ParCoordinator::FileList parFileList;
ParCoordinator::FindMainPars(m_szDestDir, &parFileList);
for (ParCoordinator::FileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
{
char* szParFilename = *it;
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
LoadParFile(szFullParFilename);
free(*it);
}
}
void ParRenamer::LoadParFile(const char* szParFilename)
{
ParRenamerRepairer* pRepairer = new ParRenamerRepairer();
if (!pRepairer->LoadPacketsFromFile(szParFilename))
{
PrintMessage(Message::mkWarning, "Could not load par2-file %s", szParFilename);
delete pRepairer;
return;
}
for (map<MD5Hash, Par2RepairerSourceFile*>::iterator it = pRepairer->sourcefilemap.begin(); it != pRepairer->sourcefilemap.end(); it++)
{
if (m_bCancelled)
{
break;
}
Par2RepairerSourceFile* sourceFile = (*it).second;
m_fileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
sourceFile->GetDescriptionPacket()->Hash16k().print().c_str()));
}
delete pRepairer;
}
void ParRenamer::CheckFiles()
{
int iFileCount = 0;
DirBrowser dir2(m_szDestDir);
while (const char* filename = dir2.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
iFileCount++;
}
}
int iCurFile = 0;
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = iCurFile * 1000 / iFileCount;
UpdateProgress();
iCurFile++;
CheckFile(szFullFilename);
}
}
}
void ParRenamer::CheckFile(const char* szFilename)
{
debug("Computing hash for %s", szFilename);
const int iBlockSize = 16*1024;
FILE* pFile = fopen(szFilename, "rb");
if (!pFile)
{
PrintMessage(Message::mkError, "Could not open file %s", szFilename);
return;
}
// load first 16K of the file into buffer
void* pBuffer = malloc(iBlockSize);
int iReadBytes = fread(pBuffer, 1, iBlockSize, pFile);
int iError = ferror(pFile);
if (iReadBytes != iBlockSize && iError)
{
PrintMessage(Message::mkError, "Could not read file %s", szFilename);
return;
}
fclose(pFile);
MD5Hash hash16k;
MD5Context context;
context.Update(pBuffer, iReadBytes);
context.Final(hash16k);
free(pBuffer);
debug("file: %s; hash16k: %s", Util::BaseFileName(szFilename), hash16k.print().c_str());
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
FileHash* pFileHash = *it;
if (!strcmp(pFileHash->GetHash(), hash16k.print().c_str()))
{
debug("Found correct filename: %s", pFileHash->GetFilename());
char szDstFilename[1024];
snprintf(szDstFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
szDstFilename[1024-1] = '\0';
if (!Util::FileExists(szDstFilename))
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
if (Util::MoveFile(szFilename, szDstFilename))
{
m_iRenamedCount++;
}
else
{
PrintMessage(Message::mkError, "Could not rename %s to %s", szFilename, szDstFilename);
}
}
break;
}
}
}
#endif

98
ParRenamer.h Normal file
View File

@@ -0,0 +1,98 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARRENAMER_H
#define PARRENAMER_H
#ifndef DISABLE_PARCHECK
#include <deque>
#include "Thread.h"
#include "Log.h"
class ParRenamer : public Thread
{
public:
enum EStatus
{
psFailed,
psSuccess
};
class FileHash
{
private:
char* m_szFilename;
char* m_szHash;
public:
FileHash(const char* szFilename, const char* szHash);
~FileHash();
const char* GetFilename() { return m_szFilename; }
const char* GetHash() { return m_szHash; }
};
typedef std::deque<FileHash*> FileHashList;
private:
char* m_szInfoName;
char* m_szDestDir;
EStatus m_eStatus;
char* m_szProgressLabel;
int m_iStageProgress;
bool m_bCancelled;
FileHashList m_fileHashList;
int m_iRenamedCount;
void Cleanup();
void LoadParFiles();
void LoadParFile(const char* szParFilename);
void CheckFiles();
void CheckFile(const char* szFilename);
protected:
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParRenamer();
virtual ~ParRenamer();
virtual void Run();
void SetDestDir(const char* szDestDir);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
void Cancel();
bool GetCancelled() { return m_bCancelled; }
};
#endif
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -31,11 +31,7 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "Scanner.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#endif
#include "ParCoordinator.h"
class PrePostProcessor : public Thread
{
@@ -48,103 +44,68 @@ public:
eaPostDelete,
eaHistoryDelete,
eaHistoryReturn,
eaHistoryProcess
eaHistoryProcess,
eaHistorySetParameter
};
private:
typedef std::deque<char*> FileList;
class QueueCoordinatorObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
PrePostProcessor* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->QueueCoordinatorUpdate(Caller, Aspect); }
};
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
class PostParCoordinator: public ParCoordinator
{
private:
PrePostProcessor* m_Owner;
PostInfo* m_pPostInfo;
PrePostProcessor* m_pOwner;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
virtual bool PauseDownload() { return m_pOwner->PauseDownload(); }
virtual bool UnpauseDownload() { return m_pOwner->UnpauseDownload(); }
friend class PrePostProcessor;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
#endif
private:
PostParCoordinator m_ParCoordinator;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
Scanner m_Scanner;
const char* m_szPauseReason;
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
bool bIgnorePausedPars, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
void ApplySchedulerState();
void CheckScheduledResume();
void UpdatePauseState(bool bNeedPause, const char* szReason);
bool PauseDownload();
bool UnpauseDownload();
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool 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);
bool HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo);
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
void Cleanup();
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void CheckHistory();
void DeletePostThread(PostInfo* pPostInfo);
#ifndef DISABLE_PARCHECK
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
void ParCheckerUpdate(Subject* Caller, void* Aspect);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void UpdateParProgress();
void StartParJob(PostInfo* pPostInfo);
#endif
public:
PrePostProcessor();
virtual ~PrePostProcessor();
@@ -152,8 +113,7 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
void ScanNZBDir(bool bSyncMode);
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
@@ -117,6 +117,17 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Unlock();
// compute maximum number of allowed download threads
m_iDownloadsLimit = 2; // two extra threads for completing files (when connections are not needed)
for (ServerPool::Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() == 0)
{
m_iDownloadsLimit += pNewsServer->GetMaxConnections();
}
}
m_tStartServer = time(NULL);
m_tLastCheck = m_tStartServer;
bool bWasStandBy = true;
@@ -127,27 +138,33 @@ void QueueCoordinator::Run()
{
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
NNTPConnection* pConnection = g_pServerPool->GetConnection(0);
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, NULL, NULL);
if (pConnection)
{
// start download for next article
FileInfo* pFileInfo;
ArticleInfo* pArticleInfo;
bool bFreeConnection = false;
m_mutexDownloadQueue.Lock();
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
if (bHasMoreArticles && !IsStopped() && (int)m_ActiveDownloads.size() < m_iDownloadsLimit)
{
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
bArticeDownloadsRunning = true;
}
else
{
g_pServerPool->FreeConnection(pConnection, false);
bFreeConnection = true;
}
m_mutexDownloadQueue.Unlock();
if (bFreeConnection)
{
g_pServerPool->FreeConnection(pConnection, false);
}
}
}
else
@@ -297,25 +314,26 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
/*
* NOTE: see note to "AddSpeedReading"
*/
float QueueCoordinator::CalcCurrentDownloadSpeed()
int QueueCoordinator::CalcCurrentDownloadSpeed()
{
if (m_bStandBy)
{
return 0;
}
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
if (iTimeDiff == 0)
{
return 0;
}
float fSpeed = m_iSpeedTotalBytes / 1024.0f / iTimeDiff;
return fSpeed;
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
if (iTimeDiff == 0)
{
return 0;
}
return m_iSpeedTotalBytes / iTimeDiff;
}
void QueueCoordinator::AddSpeedReading(int iBytes)
{
int iNowSlot = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
time_t tCurTime = time(NULL);
int iNowSlot = (int)tCurTime / SPEEDMETER_SLOTSIZE;
if (g_pOptions->GetAccurateRate())
{
@@ -326,33 +344,45 @@ void QueueCoordinator::AddSpeedReading(int iBytes)
#endif
}
while (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
{
//record bytes in next slot
m_iSpeedBytesIndex++;
if (m_iSpeedBytesIndex >= SPEEDMETER_SLOTS)
{
m_iSpeedBytesIndex = 0;
}
//Adjust counters with outging information.
m_iSpeedTotalBytes -= m_iSpeedBytes[m_iSpeedBytesIndex];
while (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
{
//record bytes in next slot
m_iSpeedBytesIndex++;
if (m_iSpeedBytesIndex >= SPEEDMETER_SLOTS)
{
m_iSpeedBytesIndex = 0;
}
//Adjust counters with outgoing information.
m_iSpeedTotalBytes -= m_iSpeedBytes[m_iSpeedBytesIndex];
//Note we should really use the start time of the next slot
//but its easier to just use the outgoing slot time. This
//will result in a small error.
m_iSpeedStartTime = m_iSpeedTime[m_iSpeedBytesIndex];
//Note we should really use the start time of the next slot
//but its easier to just use the outgoing slot time. This
//will result in a small error.
m_iSpeedStartTime = m_iSpeedTime[m_iSpeedBytesIndex];
//Now reset.
m_iSpeedBytes[m_iSpeedBytesIndex] = 0;
m_iSpeedTime[m_iSpeedBytesIndex] = iNowSlot;
}
//Now reset.
m_iSpeedBytes[m_iSpeedBytesIndex] = 0;
m_iSpeedTime[m_iSpeedBytesIndex] = iNowSlot;
}
if (m_iSpeedTotalBytes == 0)
{
m_iSpeedStartTime = iNowSlot;
}
m_iSpeedBytes[m_iSpeedBytesIndex] += iBytes;
m_iSpeedTotalBytes += iBytes;
// Once per second recalculate summary field "m_iSpeedTotalBytes" to recover from possible synchronisation errors
if (tCurTime > m_tSpeedCorrection)
{
int iSpeedTotalBytes = 0;
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
{
iSpeedTotalBytes += m_iSpeedBytes[i];
}
m_iSpeedTotalBytes = iSpeedTotalBytes;
m_tSpeedCorrection = tCurTime;
}
if (m_iSpeedTotalBytes == 0)
{
m_iSpeedStartTime = iNowSlot;
}
m_iSpeedBytes[m_iSpeedBytesIndex] += iBytes;
m_iSpeedTotalBytes += iBytes;
m_iAllBytes += iBytes;
if (g_pOptions->GetAccurateRate())
@@ -367,14 +397,16 @@ void QueueCoordinator::AddSpeedReading(int iBytes)
void QueueCoordinator::ResetSpeedStat()
{
m_iSpeedStartTime = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
time_t tCurTime = time(NULL);
m_iSpeedStartTime = (int)tCurTime / SPEEDMETER_SLOTSIZE;
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
{
m_iSpeedBytes[i] = 0;
m_iSpeedTime[i] = m_iSpeedStartTime;
m_iSpeedTime[i] = m_iSpeedStartTime;
}
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
m_iSpeedTotalBytes = 0;
m_tSpeedCorrection = tCurTime;
}
long long QueueCoordinator::CalcRemainingSize()
@@ -446,6 +478,9 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
// if the file doesn't have any articles left for download, we store that fact and search again,
// ignoring all files which were previously marked as not having any articles.
// special case: if the file has ExtraPriority-flag set, it has the highest priority and the
// Paused-flag is ignored.
//debug("QueueCoordinator::GetNextArticle()");
bool bOK = false;
@@ -466,7 +501,10 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
FileInfo* pFileInfo1 = *it;
if ((!pCheckedFiles || !pCheckedFiles[iNum]) &&
!pFileInfo1->GetPaused() && !pFileInfo1->GetDeleted() &&
(!pFileInfo || (pFileInfo1->GetPriority() > pFileInfo->GetPriority())))
(!pFileInfo ||
(pFileInfo1->GetExtraPriority() == pFileInfo->GetExtraPriority() &&
pFileInfo1->GetPriority() > pFileInfo->GetPriority()) ||
(pFileInfo1->GetExtraPriority() > pFileInfo->GetExtraPriority())))
{
pFileInfo = pFileInfo1;
iFileNum = iNum;
@@ -527,7 +565,11 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
pArticleDownloader->SetFileInfo(pFileInfo);
pArticleDownloader->SetArticleInfo(pArticleInfo);
pArticleDownloader->SetConnection(pConnection);
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
char szInfoName[1024];
snprintf(szInfoName, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
szInfoName[1024-1] = '\0';
pArticleDownloader->SetInfoName(szInfoName);
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() + 1);
@@ -536,31 +578,6 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
pArticleDownloader->Start();
}
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
char name[1024];
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
name[1024-1] = '\0';
pArticleInfo->SetResultFilename(name);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", name);
tmpname[1024-1] = '\0';
pArticleDownloader->SetTempFilename(tmpname);
snprintf(name, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
name[1024-1] = '\0';
pArticleDownloader->SetInfoName(name);
if (g_pOptions->GetDirectWrite())
{
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
pArticleDownloader->SetOutputFilename(name);
}
}
DownloadQueue* QueueCoordinator::LockQueue()
{
m_mutexDownloadQueue.Lock();
@@ -627,6 +644,7 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
fileCompleted = false;
pFileInfo->SetAutoDeleted(true);
DeleteQueueEntry(pFileInfo);
}
}
@@ -732,12 +750,9 @@ void QueueCoordinator::DiscardDiskFile(FileInfo* pFileInfo)
}
}
if (g_pOptions->GetDirectWrite())
if (g_pOptions->GetDirectWrite() && pFileInfo->GetOutputFilename())
{
char name[1024];
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
remove(name);
remove(pFileInfo->GetOutputFilename());
}
}
@@ -774,7 +789,7 @@ void QueueCoordinator::LogDebugInfo()
debug(" SpeedMeter");
debug(" ----------");
float fSpeed = CalcCurrentDownloadSpeed();
float fSpeed = (float)(CalcCurrentDownloadSpeed() / 1024.0);
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
debug(" Speed: %f", fSpeed);
debug(" SpeedStartTime: %i", m_iSpeedStartTime);
@@ -807,8 +822,7 @@ void QueueCoordinator::LogDebugInfo()
void QueueCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
if (g_pOptions->GetTerminateTimeout() == 0 && g_pOptions->GetConnectionTimeout() == 0)
{
return;
}
@@ -819,7 +833,15 @@ void QueueCoordinator::ResetHangingDownloads()
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
ArticleDownloader* pArticleDownloader = *it;
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetConnectionTimeout() + 1 &&
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
{
error("Cancelling hanging download %s", pArticleDownloader->GetInfoName());
pArticleDownloader->Stop();
}
if (tm - pArticleDownloader->GetLastUpdateTime() > g_pOptions->GetTerminateTimeout() &&
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
{
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
@@ -904,7 +926,7 @@ void QueueCoordinator::AdjustStartTime()
if (tDiff > 60 || tDiff < 0)
{
m_tStartServer += tDiff + 1; // "1" because the method is called once per second
if (m_tStartDownload != 0)
if (m_tStartDownload != 0 && !m_bStandBy)
{
m_tStartDownload += tDiff + 1;
}
@@ -1009,3 +1031,76 @@ bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZB
return true;
}
/*
* Creates new nzb-item out of existing files from other nzb-items.
* If any of file-items is being downloaded the command fail.
* For each file-item an event "eaFileDeleted" is fired.
*
* NOTE: DownloadQueue must be locked prior to call of this function
*/
bool QueueCoordinator::SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo)
{
if (pFileList->empty())
{
return false;
}
NZBInfo* pSrcNZBInfo = NULL;
for (FileQueue::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetActiveDownloads() > 0 || pFileInfo->GetCompleted() > 0)
{
error("Could not split %s. File is already (partially) downloaded", pFileInfo->GetFilename());
return false;
}
if (pFileInfo->GetNZBInfo()->GetPostProcess())
{
error("Could not split %s. File in post-process-stage", pFileInfo->GetFilename());
return false;
}
if (!pSrcNZBInfo)
{
pSrcNZBInfo = pFileInfo->GetNZBInfo();
}
}
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->AddReference();
m_DownloadQueue.GetNZBInfoList()->Add(pNZBInfo);
pNZBInfo->SetFilename(pSrcNZBInfo->GetFilename());
pNZBInfo->SetName(szName);
pNZBInfo->SetCategory(pSrcNZBInfo->GetCategory());
pNZBInfo->BuildDestDirName();
pNZBInfo->SetQueuedFilename(pSrcNZBInfo->GetQueuedFilename());
for (NZBParameterList::iterator it = pSrcNZBInfo->GetParameters()->begin(); it != pSrcNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
pNZBInfo->SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
for (FileQueue::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
FileInfo* pFileInfo = *it;
Aspect aspect = { eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
pFileInfo->SetNZBInfo(pNZBInfo);
pSrcNZBInfo->SetFileCount(pSrcNZBInfo->GetFileCount() - 1);
pSrcNZBInfo->SetSize(pSrcNZBInfo->GetSize() - pFileInfo->GetSize());
pNZBInfo->SetFileCount(pNZBInfo->GetFileCount() + 1);
pNZBInfo->SetSize(pNZBInfo->GetSize() + pFileInfo->GetSize());
}
pNZBInfo->Release();
*pNewNZBInfo = pNZBInfo;
return true;
}

View File

@@ -63,6 +63,7 @@ private:
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
int m_iDownloadsLimit;
// statistics
static const int SPEEDMETER_SLOTS = 30;
@@ -71,6 +72,7 @@ private:
int m_iSpeedTotalBytes;
int m_iSpeedTime[SPEEDMETER_SLOTS];
int m_iSpeedStartTime;
time_t m_tSpeedCorrection;
#ifdef HAVE_SPINLOCK
SpinLock m_spinlockSpeed;
#else
@@ -88,7 +90,6 @@ private:
bool GetNextArticle(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);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
@@ -106,7 +107,7 @@ public:
// statistics
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
virtual int CalcCurrentDownloadSpeed();
virtual void AddSpeedReading(int iBytes);
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
@@ -120,6 +121,7 @@ public:
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
bool SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo);
void DiscardDiskFile(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,8 +33,8 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include <set>
#ifndef WIN32
@@ -227,7 +227,11 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
}
else if (eAction == eaGroupMerge)
{
MergeGroups(pDownloadQueue, &cItemList);
return MergeGroups(pDownloadQueue, &cItemList);
}
else if (eAction == eaFileSplit)
{
return SplitGroup(pDownloadQueue, &cItemList, szText);
}
else if (eAction == eaFileReorder)
{
@@ -290,6 +294,7 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
case eaFileSplit:
// remove compiler warning "enumeration not handled in switch"
break;
}
@@ -543,8 +548,8 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom,
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder,
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause,
eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder, eaFileSplit,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
@@ -834,13 +839,15 @@ bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInf
return false;
}
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return;
return false;
}
bool bOK = true;
EditItem* pDestItem = pItemList->front();
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
@@ -849,15 +856,45 @@ void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList
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());
if (g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo()))
{
bOK = false;
}
}
delete pItem;
}
// align group
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
delete pDestItem;
return bOK;
}
bool QueueEditor::SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName)
{
if (pItemList->size() == 0)
{
return false;
}
FileQueue* pFileList = new FileQueue();
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
{
EditItem* pItem = *it;
pFileList->push_back(pItem->m_pFileInfo);
delete pItem;
}
NZBInfo* pNewNZBInfo = NULL;
bool bOK = g_pQueueCoordinator->SplitQueueEntries(pFileList, szName, &pNewNZBInfo);
if (bOK)
{
AlignGroup(pDownloadQueue, pNewNZBInfo);
}
delete pFileList;
return bOK;
}
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ public:
eaFilePauseExtraPars,
eaFileSetPriority,
eaFileReorder,
eaFileSplit,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
@@ -96,7 +97,8 @@ private:
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
bool MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
bool SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName);
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);

26
README
View File

@@ -442,26 +442,15 @@ Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-processing
script, defined in configuration file. See example configuration file for
the description of parameters passed to the script (option "PostProcess").
scripts, defined in configuration file.
An example script for unraring of downloaded files is provided in file
"nzbget-postprocess.sh" installed into "<prefix>/bin". The script requires
configuration file "nzbget-postprocess.conf". If you have installed the
program with "make install" this file is copied to "<prefix>/etc",
where the post-processing script finds it automatically. If you install
the program manually from a binary archive you have to copy the file
from "<prefix>/share/nzbget" to the directory where you have put the
nzbget configuration file ("nzbget.conf").
Example post-processing scripts are provided in directory "ppscripts".
Set the option "PostProcess" in "nzbget.conf" to point to the post-
processing script.
To use the scripts copy them into your local directory and set options
<ScriptDir>, <DefScript> and <ScriptOrder>.
Additional usage instructions are included in "nzbget-postprocess.sh",
please open the file in a text editor to read them.
NOTE: The post-processing script "nzbget-postprocess.sh" is for
POSIX systems and will not work on Windows.
For information on writing your own post-processing scripts please
visit NZBGet web site.
Web-interface
-------------
@@ -505,9 +494,6 @@ Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
Since then the program has been completely rewritten.
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers
(marlam@marlam.de).
=====================================
9. Copyright
=====================================

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#ifdef WIN32
#include <windows.h>
#else
@@ -46,6 +46,7 @@
#include "nzbget.h"
#include "RemoteClient.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
@@ -125,6 +126,10 @@ void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest,
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -137,35 +142,21 @@ bool RemoteClient::ReceiveBoolResponse()
SNZBDownloadResponse BoolResponse;
memset(&BoolResponse, 0, sizeof(BoolResponse));
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
if (iResponseLen != sizeof(BoolResponse) ||
bool bRead = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
if (!bRead ||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
char* buf = (char*)malloc(iTextLen);
iResponseLen = m_pConnection->Recv(buf, iTextLen);
if (iResponseLen != iTextLen)
bRead = m_pConnection->Recv(buf, iTextLen);
if (!bRead)
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
free(buf);
return false;
}
@@ -197,7 +188,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
DownloadRequest.m_bAddPaused = htonl(bAddPaused);
DownloadRequest.m_iPriority = htonl(iPriority);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
DownloadRequest.m_iTrailingDataLength = htonl(iLength - 1);
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
@@ -208,7 +199,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
DownloadRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
if (!m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)))
{
perror("m_pConnection->Send");
OK = false;
@@ -332,7 +323,7 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
ListRequest.m_szPattern[NZBREQUESTFILENAMESIZE-1] = '\0';
}
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
if (!m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -342,19 +333,12 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
bool bRead = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
if (!bRead ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
@@ -362,7 +346,7 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
if (!m_pConnection->Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -700,7 +684,7 @@ bool RemoteClient::RequestServerLog(int iLines)
LogRequest.m_iLines = htonl(iLines);
LogRequest.m_iIDFrom = 0;
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
if (!m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -710,19 +694,12 @@ bool RemoteClient::RequestServerLog(int iLines)
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
bool bRead = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
if (!bRead ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
@@ -730,7 +707,7 @@ bool RemoteClient::RequestServerLog(int iLines)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
if (!m_pConnection->Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -793,7 +770,7 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAct
PauseUnpauseRequest.m_bPause = htonl(bPause);
PauseUnpauseRequest.m_iAction = htonl(iAction);
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
if (!m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)))
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -806,15 +783,15 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAct
return OK;
}
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
bool RemoteClient::RequestServerSetDownloadRate(int iRate)
{
if (!InitConnection()) return false;
SNZBSetDownloadRateRequest SetDownloadRateRequest;
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
SetDownloadRateRequest.m_iDownloadRate = htonl(iRate);
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
if (!m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)))
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -834,7 +811,7 @@ bool RemoteClient::RequestServerDumpDebug()
SNZBDumpDebugRequest DumpDebugInfo;
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
if (!m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)))
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -919,7 +896,7 @@ bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset
}
bool OK = false;
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
if (!m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)))
{
perror("m_pConnection->Send");
}
@@ -943,7 +920,7 @@ bool RemoteClient::RequestServerShutdown()
SNZBShutdownRequest ShutdownRequest;
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest));
if (OK)
{
OK = ReceiveBoolResponse();
@@ -964,7 +941,7 @@ bool RemoteClient::RequestServerReload()
SNZBReloadRequest ReloadRequest;
InitMessageBase(&ReloadRequest.m_MessageBase, eRemoteRequestReload, sizeof(ReloadRequest));
bool OK = m_pConnection->Send((char*)(&ReloadRequest), sizeof(ReloadRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&ReloadRequest), sizeof(ReloadRequest));
if (OK)
{
OK = ReceiveBoolResponse();
@@ -985,7 +962,7 @@ bool RemoteClient::RequestServerVersion()
SNZBVersionRequest VersionRequest;
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest));
if (OK)
{
OK = ReceiveBoolResponse();
@@ -1006,7 +983,7 @@ bool RemoteClient::RequestPostQueue()
SNZBPostQueueRequest PostQueueRequest;
InitMessageBase(&PostQueueRequest.m_MessageBase, eRemoteRequestPostQueue, sizeof(PostQueueRequest));
if (m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)) < 0)
if (!m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -1016,19 +993,12 @@ bool RemoteClient::RequestPostQueue()
// Now listen for the returned list
SNZBPostQueueResponse PostQueueResponse;
int iResponseLen = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
if (iResponseLen != sizeof(PostQueueResponse) ||
bool bRead = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
if (!bRead ||
(int)ntohl(PostQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(PostQueueResponse.m_MessageBase.m_iStructSize) != sizeof(PostQueueResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
@@ -1036,7 +1006,7 @@ bool RemoteClient::RequestPostQueue()
if (ntohl(PostQueueResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(PostQueueResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
if (!m_pConnection->Recv(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -1061,22 +1031,21 @@ bool RemoteClient::RequestPostQueue()
int iStageProgress = ntohl(pPostQueueAnswer->m_iStageProgress);
static const int EXECUTING_SCRIPT = 5;
char szCompleted[100];
szCompleted[0] = '\0';
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != (int)PostInfo::ptExecutingScript)
{
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
}
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Unpacking", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
ntohl(pPostQueueAnswer->m_iInfoNameLen) + ntohl(pPostQueueAnswer->m_iDestDirLen) +
ntohl(pPostQueueAnswer->m_iProgressLabelLen);
}
free(pBuf);
@@ -1097,7 +1066,7 @@ bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
int iLength = strlen(szText) + 1;
WriteLogRequest.m_iTrailingDataLength = htonl(iLength);
if (m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)) < 0)
if (!m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -1118,7 +1087,7 @@ bool RemoteClient::RequestScan(bool bSyncMode)
ScanRequest.m_bSyncMode = htonl(bSyncMode);
bool OK = m_pConnection->Send((char*)(&ScanRequest), sizeof(ScanRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&ScanRequest), sizeof(ScanRequest));
if (OK)
{
OK = ReceiveBoolResponse();
@@ -1139,7 +1108,7 @@ bool RemoteClient::RequestHistory()
SNZBHistoryRequest HistoryRequest;
InitMessageBase(&HistoryRequest.m_MessageBase, eRemoteRequestHistory, sizeof(HistoryRequest));
if (m_pConnection->Send((char*)(&HistoryRequest), sizeof(HistoryRequest)) < 0)
if (!m_pConnection->Send((char*)(&HistoryRequest), sizeof(HistoryRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -1149,19 +1118,12 @@ bool RemoteClient::RequestHistory()
// Now listen for the returned list
SNZBHistoryResponse HistoryResponse;
int iResponseLen = m_pConnection->Recv((char*) &HistoryResponse, sizeof(HistoryResponse));
if (iResponseLen != sizeof(HistoryResponse) ||
bool bRead = m_pConnection->Recv((char*) &HistoryResponse, sizeof(HistoryResponse));
if (!bRead ||
(int)ntohl(HistoryResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(HistoryResponse.m_MessageBase.m_iStructSize) != sizeof(HistoryResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
@@ -1169,7 +1131,7 @@ bool RemoteClient::RequestHistory()
if (ntohl(HistoryResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(HistoryResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(HistoryResponse.m_iTrailingDataLength)))
if (!m_pConnection->Recv(pBuf, ntohl(HistoryResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -1202,7 +1164,7 @@ bool RemoteClient::RequestHistory()
char szSize[20];
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
const char* szParStatusText[] = { "", ", Par failed", ", Par possible", ", Par successful" };
const char* szParStatusText[] = { "", "", ", Par failed", ", Par successful", ", Repair possible", ", Repair needed" };
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
printf("[%i] %s (%i files, %s%s%s)\n", ntohl(pListAnswer->m_iID), szNicename,
@@ -1257,7 +1219,7 @@ bool RemoteClient::RequestServerDownloadUrl(const char* szURL, const char* szNZB
}
DownloadUrlRequest.m_szNZBFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
bool OK = m_pConnection->Send((char*)(&DownloadUrlRequest), sizeof(DownloadUrlRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&DownloadUrlRequest), sizeof(DownloadUrlRequest));
if (OK)
{
OK = ReceiveBoolResponse();
@@ -1278,7 +1240,7 @@ bool RemoteClient::RequestUrlQueue()
SNZBUrlQueueRequest UrlQueueRequest;
InitMessageBase(&UrlQueueRequest.m_MessageBase, eRemoteRequestUrlQueue, sizeof(UrlQueueRequest));
if (m_pConnection->Send((char*)(&UrlQueueRequest), sizeof(UrlQueueRequest)) < 0)
if (!m_pConnection->Send((char*)(&UrlQueueRequest), sizeof(UrlQueueRequest)))
{
perror("m_pConnection->Send");
return false;
@@ -1288,19 +1250,12 @@ bool RemoteClient::RequestUrlQueue()
// Now listen for the returned list
SNZBUrlQueueResponse UrlQueueResponse;
int iResponseLen = m_pConnection->Recv((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
if (iResponseLen != sizeof(UrlQueueResponse) ||
bool bRead = m_pConnection->Recv((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
if (!bRead ||
(int)ntohl(UrlQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(UrlQueueResponse.m_MessageBase.m_iStructSize) != sizeof(UrlQueueResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
@@ -1308,7 +1263,7 @@ bool RemoteClient::RequestUrlQueue()
if (ntohl(UrlQueueResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(UrlQueueResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(UrlQueueResponse.m_iTrailingDataLength)))
if (!m_pConnection->Recv(pBuf, ntohl(UrlQueueResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;

View File

@@ -63,7 +63,7 @@ public:
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerSetDownloadRate(int iRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -47,16 +47,18 @@
#include "WebServer.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
//*****************************************************************
// RemoteServer
RemoteServer::RemoteServer()
RemoteServer::RemoteServer(bool bTLS)
{
debug("Creating RemoteServer");
m_bTLS = bTLS;
m_pConnection = NULL;
}
@@ -74,25 +76,44 @@ void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
#ifndef DISABLE_TLS
if (m_bTLS)
{
if (strlen(g_pOptions->GetSecureCert()) == 0 || !Util::FileExists(g_pOptions->GetSecureCert()))
{
error("Could not initialize TLS, secure certificate is not configured or the cert-file was not found. Check option <SecureCert>");
return;
}
if (strlen(g_pOptions->GetSecureKey()) == 0 || !Util::FileExists(g_pOptions->GetSecureKey()))
{
error("Could not initialize TLS, secure key is not configured or the key-file was not found. Check option <SecureKey>");
return;
}
}
#endif
while (!IsStopped())
{
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
m_pConnection = new Connection(g_pOptions->GetControlIP(),
m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(),
m_bTLS);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind() == 0;
bBind = m_pConnection->Bind();
}
// Accept connections and store the "new" socket value
SOCKET iSocket = INVALID_SOCKET;
// Accept connections and store the new Connection
Connection* pAcceptedConnection = NULL;
if (bBind)
{
iSocket = m_pConnection->Accept();
pAcceptedConnection = m_pConnection->Accept();
}
if (!bBind || iSocket == INVALID_SOCKET)
if (!bBind || pAcceptedConnection == NULL)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
@@ -107,9 +128,13 @@ void RemoteServer::Run()
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetSocket(iSocket);
commandThread->SetConnection(pAcceptedConnection);
#ifndef DISABLE_TLS
commandThread->SetTLS(m_bTLS);
#endif
commandThread->Start();
}
if (m_pConnection)
{
m_pConnection->Disconnect();
@@ -134,32 +159,32 @@ void RemoteServer::Stop()
//*****************************************************************
// RequestProcessor
RequestProcessor::~RequestProcessor()
{
m_pConnection->Disconnect();
delete m_pConnection;
}
void RequestProcessor::Run()
{
// 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)
m_pConnection->SetSuppressErrors(true);
#ifndef DISABLE_TLS
if (m_bTLS && !m_pConnection->StartTLS(false, g_pOptions->GetSecureCert(), g_pOptions->GetSecureKey()))
{
debug("Could not establish secure connection to web-client: Start TLS failed");
return;
}
#endif
// 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)
// Read the first 4 bytes to determine request type
int iSignature = 0;
if (!m_pConnection->Recv((char*)&iSignature, 4))
{
#ifdef WIN32
ip = inet_ntoa(PeerName.sin_addr);
#else
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
#endif
debug("Could not read request signature, request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
@@ -167,9 +192,7 @@ void RequestProcessor::Run()
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.SetConnection(m_pConnection);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) ||
@@ -177,9 +200,8 @@ void RequestProcessor::Run()
!strncmp((char*)&iSignature, "OPTI", 4))
{
// HTTP request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
if (m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
char* szUrl = szBuffer;
@@ -201,8 +223,7 @@ void RequestProcessor::Run()
debug("url: %s", szUrl);
WebProcessor processor;
processor.SetConnection(&con);
processor.SetClientIP(ip);
processor.SetConnection(m_pConnection);
processor.SetUrl(szUrl);
processor.SetHttpMethod(eHttpMethod);
processor.Execute();
@@ -210,15 +231,8 @@ void RequestProcessor::Run()
}
}
if (!bOK && iBytesReceived > 0)
if (!bOK)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), ip);
warn("Non-nzbget request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
}
if (!bOK && iBytesReceived == 0)
{
debug("empty request received on port %i from %s", g_pOptions->GetControlPort(), ip);
}
closesocket(m_iSocket);
}

View File

@@ -1,8 +1,8 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,10 +33,11 @@
class RemoteServer : public Thread
{
private:
bool m_bTLS;
Connection* m_pConnection;
public:
RemoteServer();
RemoteServer(bool bTLS);
~RemoteServer();
virtual void Run();
virtual void Stop();
@@ -45,11 +46,14 @@ public:
class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
bool m_bTLS;
Connection* m_pConnection;
public:
~RequestProcessor();
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
void SetTLS(bool bTLS) { m_bTLS = bTLS; }
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,14 +33,13 @@
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "Scanner.h"
@@ -67,6 +66,41 @@ Scanner::FileData::~FileData()
free(m_szFilename);
}
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
{
m_szFilename = strdup(szFilename);
m_szNZBName = strdup(szNZBName);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iPriority = iPriority;
m_bAddTop = bAddTop;
m_bAddPaused = bAddPaused;
if (pParameters)
{
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pNZBParameter = *it;
m_Parameters.SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
}
}
Scanner::QueueData::~QueueData()
{
free(m_szFilename);
free(m_szNZBName);
free(m_szCategory);
for (NZBParameterList::iterator it = m_Parameters.begin(); it != m_Parameters.end(); it++)
{
delete *it;
}
m_Parameters.clear();
}
Scanner::Scanner()
{
debug("Creating Scanner");
@@ -75,7 +109,6 @@ Scanner::Scanner()
m_bScanning = false;
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
m_iPass = 0;
m_iStepMSec = 0;
const char* szNZBScript = g_pOptions->GetNZBProcess();
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
@@ -90,13 +123,26 @@ Scanner::~Scanner()
delete *it;
}
m_FileList.clear();
ClearQueueList();
}
void Scanner::ClearQueueList()
{
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
delete *it;
}
m_QueueList.clear();
}
void Scanner::Check()
{
if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan ||
m_mutexScan.Lock();
if (m_bRequestedNZBDirScan ||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000))
{
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
bool bCheckStat = !m_bRequestedNZBDirScan;
@@ -105,7 +151,7 @@ void Scanner::Check()
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
if (!bCheckStat && m_bNZBScript)
{
// if immediate scan requesten, we need second scan to process files extracted by NzbProcess-script
// if immediate scan requested, we need second scan to process files extracted by NzbProcess-script
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
}
m_bScanning = false;
@@ -116,7 +162,7 @@ void Scanner::Check()
// - 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())
if (g_pOptions->GetNzbDirInterval() > 0 && g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
{
int iMaxPass = m_bNZBScript ? 3 : 1;
if (m_iPass < iMaxPass)
@@ -133,7 +179,10 @@ void Scanner::Check()
DropOldFiles();
}
m_iNZBDirInterval += m_iStepMSec;
m_iNZBDirInterval += 200;
ClearQueueList();
m_mutexScan.Unlock();
}
/**
@@ -278,16 +327,34 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
return;
}
char* szNZBName = strdup("");
char* szNZBCategory = strdup(szCategory);
NZBParameterList* pParameterList = new NZBParameterList;
NZBParameterList* pParameters = new NZBParameterList();
int iPriority = 0;
bool bAddTop = false;
bool bAddPaused = false;
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
QueueData* pQueueData = *it;
if (Util::SameFilename(pQueueData->GetFilename(), szFullFilename))
{
free(szNZBName);
szNZBName = strdup(pQueueData->GetNZBName());
free(szNZBCategory);
szNZBCategory = strdup(pQueueData->GetCategory());
iPriority = pQueueData->GetPriority();
bAddTop = pQueueData->GetAddTop();
bAddPaused = pQueueData->GetAddPaused();
}
}
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
{
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
&szNZBCategory, &iPriority, pParameterList);
&szNZBName, &szNZBCategory, &iPriority, pParameters, &bAddTop, &bAddPaused);
bExists = Util::FileExists(szFullFilename);
if (bExists && strcasecmp(szExtension, ".nzb"))
{
@@ -295,7 +362,8 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
if (!bRenameOK)
{
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
}
}
}
@@ -306,35 +374,38 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
if (bRenameOK)
{
AddFileToQueue(szRenamedName, szNZBCategory, iPriority, pParameterList);
AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
}
else
{
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, szRenamedName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
AddFileToQueue(szFullFilename, szNZBCategory, iPriority, pParameterList);
AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
}
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
delete *it;
}
pParameterList->clear();
delete pParameterList;
pParameters->clear();
delete pParameters;
free(szNZBName);
free(szNZBCategory);
}
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList)
void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
{
const char* szBasename = Util::BaseFileName(szFilename);
info("Collection %s found", szBasename);
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
NZBFile* pNZBFile = NZBFile::Create(szFilename, szCategory);
if (!pNZBFile)
{
error("Could not add collection %s to queue", szBasename);
@@ -344,14 +415,22 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int
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);
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
}
if (pNZBFile && bRenameOK)
{
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
if (szNZBName && strlen(szNZBName) > 0)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
pNZBFile->GetNZBInfo()->SetFilename(szNZBName);
pNZBFile->GetNZBInfo()->BuildDestDirName();
}
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pParameter = *it;
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
@@ -361,9 +440,10 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
pFileInfo->SetPaused(bAddPaused);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
info("Collection %s added to queue", szBasename);
}
@@ -375,13 +455,91 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int
void Scanner::ScanNZBDir(bool bSyncMode)
{
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
// but it's not critical here.
m_mutexScan.Lock();
m_bScanning = true;
m_bRequestedNZBDirScan = true;
m_mutexScan.Unlock();
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
{
usleep(100 * 1000);
}
}
bool Scanner::AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode)
{
char szTempFileName[1024];
if (szFileName)
{
strncpy(szTempFileName, szFileName, 1024);
szTempFileName[1024-1] = '\0';
}
else
{
int iNum = 1;
while (iNum == 1 || Util::FileExists(szTempFileName))
{
snprintf(szTempFileName, 1024, "%snzb-%i.tmp", g_pOptions->GetTempDir(), iNum);
szTempFileName[1024-1] = '\0';
iNum++;
}
if (!Util::SaveBufferIntoFile(szTempFileName, szBuffer, iBufSize))
{
error("Could not create file %s", szTempFileName);
return false;
}
}
// move file into NzbDir, make sure the file name is unique
char szValidNZBName[1024];
strncpy(szValidNZBName, Util::BaseFileName(szNZBName), 1024);
szValidNZBName[1024-1] = '\0';
Util::MakeValidFilename(szValidNZBName, '_', false);
char szScanFileName[1024];
snprintf(szScanFileName, 1024, "%s%s", g_pOptions->GetNzbDir(), szValidNZBName);
char *szExt = strrchr(szValidNZBName, '.');
if (szExt)
{
*szExt = '\0';
szExt++;
}
int iNum = 2;
while (Util::FileExists(szScanFileName))
{
if (szExt)
{
snprintf(szScanFileName, 1024, "%s%s_%i.%s", g_pOptions->GetNzbDir(), szValidNZBName, iNum, szExt);
}
else
{
snprintf(szScanFileName, 1024, "%s%s_%i", g_pOptions->GetNzbDir(), szValidNZBName, iNum);
}
szScanFileName[1024-1] = '\0';
iNum++;
}
if (!Util::MoveFile(szTempFileName, szScanFileName))
{
char szSysErrStr[256];
error("Could not move file %s to %s: %s", szTempFileName, szScanFileName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
remove(szTempFileName);
return false;
}
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szCategory, iPriority, pParameters, bAddTop, bAddPaused);
m_mutexScan.Lock();
m_QueueList.push_back(pQueueData);
m_mutexScan.Unlock();
ScanNZBDir(bSyncMode);
return true;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,6 +29,7 @@
#include <deque>
#include <time.h>
#include "DownloadInfo.h"
#include "Thread.h"
class Scanner
{
@@ -52,26 +53,57 @@ private:
typedef std::deque<FileData*> FileList;
class QueueData
{
private:
char* m_szFilename;
char* m_szNZBName;
char* m_szCategory;
int m_iPriority;
NZBParameterList m_Parameters;
bool m_bAddTop;
bool m_bAddPaused;
public:
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
~QueueData();
const char* GetFilename() { return m_szFilename; }
const char* GetNZBName() { return m_szNZBName; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
NZBParameterList* GetParameters() { return &m_Parameters; }
bool GetAddTop() { return m_bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
};
typedef std::deque<QueueData*> QueueList;
bool m_bRequestedNZBDirScan;
int m_iNZBDirInterval;
bool m_bNZBScript;
int m_iPass;
int m_iStepMSec;
FileList m_FileList;
QueueList m_QueueList;
bool m_bScanning;
Mutex m_mutexScan;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
void AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList);
void AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
void DropOldFiles();
void ClearQueueList();
public:
Scanner();
~Scanner();
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
void ScanNZBDir(bool bSyncMode);
void Check();
bool AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddPaused, bool bAddTop,
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "nzbget.h"
#include "Scheduler.h"

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,18 +43,21 @@
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include "nzbget.h"
#include "ScriptController.h"
#include "Log.h"
#include "Util.h"
// System global variable holding environments variables
extern char** environ;
extern Options* g_pOptions;
extern char* (*g_szEnvironmentVariables)[];
extern DownloadQueueHolder* g_pDownloadQueueHolder;
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
static const int POSTPROCESS_PARCHECK_ALL = 92;
static const int POSTPROCESS_PARCHECK = 92;
static const int POSTPROCESS_SUCCESS = 93;
static const int POSTPROCESS_ERROR = 94;
static const int POSTPROCESS_NONE = 95;
@@ -110,6 +113,11 @@ EnvironmentStrings::EnvironmentStrings()
}
EnvironmentStrings::~EnvironmentStrings()
{
Clear();
}
void EnvironmentStrings::Clear()
{
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
@@ -189,7 +197,7 @@ ScriptController::ScriptController()
m_szArgs = NULL;
m_bFreeArgs = false;
m_szInfoName = NULL;
m_szDefaultKindPrefix = NULL;
m_szLogPrefix = NULL;
m_bTerminated = false;
m_environmentStrings.InitFromCurrentProcess();
}
@@ -206,6 +214,12 @@ ScriptController::~ScriptController()
}
}
void ScriptController::ResetEnv()
{
m_environmentStrings.Clear();
m_environmentStrings.InitFromCurrentProcess();
}
void ScriptController::SetEnvVar(const char* szName, const char* szValue)
{
int iLen = strlen(szName) + strlen(szValue) + 2;
@@ -214,42 +228,139 @@ void ScriptController::SetEnvVar(const char* szName, const char* szValue)
m_environmentStrings.Append(szVar);
}
void ScriptController::PrepareEnvironmentStrings()
/**
* If szStripPrefix is not NULL, only options, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all options are processed; without stripping.
*/
void ScriptController::PrepareEnvOptions(const char* szStripPrefix)
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
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 (szStripPrefix && !strncmp(pOptEntry->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pOptEntry->GetName()) > iPrefixLen)
{
if (*szPtr == '.')
{
*szPtr = '_';
}
*szPtr = toupper(*szPtr);
SetEnvVarSpecial("NZBPO", pOptEntry->GetName() + iPrefixLen, pOptEntry->GetValue());
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBOP", pOptEntry->GetName(), pOptEntry->GetValue());
}
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pOptEntry->GetValue());
}
g_pOptions->UnlockOptEntries();
}
/**
* If szStripPrefix is not NULL, only pp-parameters, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all pp-parameters are processed; without stripping.
*/
void ScriptController::PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix)
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, pParameter->GetValue());
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName(), pParameter->GetValue());
}
}
}
void ScriptController::SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue)
{
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "%s_%s", szPrefix, szName);
szVarname[1024-1] = '\0';
// Original name
SetEnvVar(szVarname, szValue);
char szNormVarname[1024];
strncpy(szNormVarname, szVarname, sizeof(szVarname));
szNormVarname[1024-1] = '\0';
// Replace special characters with "_" and convert to upper case
for (char* szPtr = szNormVarname; *szPtr; szPtr++)
{
if (strchr(".:*!\"$%&/()=`+~#'{}[]@- ", *szPtr)) *szPtr = '_';
*szPtr = toupper(*szPtr);
}
// Another env var with normalized name (replaced special chars and converted to upper case)
if (strcmp(szVarname, szNormVarname))
{
SetEnvVar(szNormVarname, szValue);
}
}
void ScriptController::PrepareArgs()
{
#ifdef WIN32
if (!m_szArgs)
{
// Special support for script languages:
// automatically find the app registered for this extension and run it
const char* szExtension = strrchr(GetScript(), '.');
if (szExtension && strcasecmp(szExtension, ".exe") && strcasecmp(szExtension, ".bat") && strcasecmp(szExtension, ".cmd"))
{
debug("Looking for associated program for %s", szExtension);
char szCommand[512];
int iBufLen = 512-1;
if (Util::RegReadStr(HKEY_CLASSES_ROOT, szExtension, NULL, szCommand, &iBufLen))
{
szCommand[iBufLen] = '\0';
debug("Extension: %s", szCommand);
char szRegPath[512];
snprintf(szRegPath, 512, "%s\\shell\\open\\command", szCommand);
szRegPath[512-1] = '\0';
iBufLen = 512-1;
if (Util::RegReadStr(HKEY_CLASSES_ROOT, szRegPath, NULL, szCommand, &iBufLen))
{
szCommand[iBufLen] = '\0';
debug("Command: %s", szCommand);
DWORD_PTR pArgs[] = { (DWORD_PTR)GetScript(), (DWORD_PTR)0 };
if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szCommand, 0, 0,
m_szCmdLine, sizeof(m_szCmdLine), (va_list*)pArgs))
{
debug("CmdLine: %s", m_szCmdLine);
return;
}
}
}
warn("Could not found associated program for %s. Trying to execute %s directly", szExtension, Util::BaseFileName(GetScript()));
}
}
#endif
if (!m_szArgs)
{
m_szStdArgs[0] = GetScript();
m_szStdArgs[1] = NULL;
SetArgs(m_szStdArgs, false);
}
}
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();
PrepareEnvOptions(NULL);
PrepareArgs();
int iExitCode = 0;
int pipein;
@@ -262,14 +373,24 @@ int ScriptController::Execute()
#ifdef WIN32
// build command line
char szCmdLine[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
char* szCmdLine = NULL;
if (m_szArgs)
{
snprintf(szCmdLine + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
char szCmdLineBuf[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
snprintf(szCmdLineBuf + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
}
szCmdLineBuf[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
szCmdLine = szCmdLineBuf;
}
else
{
szCmdLine = m_szCmdLine;
}
szCmdLine[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
// create pipes to write and read data
HANDLE hReadPipe, hWritePipe;
@@ -299,8 +420,7 @@ int ScriptController::Execute()
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))
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrCode, 0, szErrMsg, 255, NULL))
{
error("Could not start %s: %s", m_szInfoName, szErrMsg);
}
@@ -308,6 +428,10 @@ int ScriptController::Execute()
{
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
}
if (!Util::FileExists(m_szScript))
{
error("Could not find file %s", m_szScript);
}
free(szEnvironmentStrings);
return -1;
}
@@ -370,10 +494,14 @@ int ScriptController::Execute()
fflush(stdout);
#endif
execve(m_szScript, (char* const*)m_szArgs, (char* const*)pEnvironmentStrings);
fprintf(stdout, "[ERROR] Could not start script: %s", strerror(errno));
chdir(m_szWorkingDir);
environ = pEnvironmentStrings;
execvp(m_szScript, (char* const*)m_szArgs);
// NOTE: the text "[ERROR] Could not start " is checked later,
// by changing adjust the dependent code below.
fprintf(stdout, "[ERROR] Could not start %s: %s", m_szScript, strerror(errno));
fflush(stdout);
_exit(-1);
_exit(254);
}
// continue the first instance
@@ -407,9 +535,11 @@ int ScriptController::Execute()
char* buf = (char*)malloc(10240);
debug("Entering pipe-loop");
bool bFirstLine = true;
bool bStartError = false;
while (!feof(readpipe) && !m_bTerminated)
{
if (fgets(buf, 10240, readpipe))
if (ReadLine(buf, 10240, readpipe))
{
#ifdef CHILD_WATCHDOG
if (!bChildConfirmed)
@@ -417,9 +547,15 @@ int ScriptController::Execute()
bChildConfirmed = true;
pWatchDog->Stop();
debug("Child confirmed");
continue;
}
#endif
if (bFirstLine && !strncmp(buf, "[ERROR] Could not start ", 24))
{
bStartError = true;
}
ProcessOutput(buf);
bFirstLine = false;
}
}
debug("Exited pipe-loop");
@@ -458,6 +594,10 @@ int ScriptController::Execute()
if (WIFEXITED(iStatus))
{
iExitCode = WEXITSTATUS(iStatus);
if (iExitCode == 254 && bStartError)
{
iExitCode = -1;
}
}
#endif
@@ -465,11 +605,7 @@ int ScriptController::Execute()
} // while (!bChildConfirmed && !m_bTerminated)
#endif
if (!m_bTerminated)
{
info("Completed %s", m_szInfoName);
debug("Exit code %i", iExitCode);
}
debug("Exit code %i", iExitCode);
return iExitCode;
}
@@ -503,13 +639,18 @@ void ScriptController::Terminate()
debug("Stopped %s", m_szInfoName);
}
bool ScriptController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
return fgets(szBuf, iBufSize, pStream);
}
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)
if (szText[0] == '\0')
{
// skip empty lines
return;
@@ -517,100 +658,170 @@ void ScriptController::ProcessOutput(char* szText)
if (!strncmp(szText, "[INFO] ", 7))
{
AddMessage(Message::mkInfo, false, g_pOptions->GetInfoTarget(), szText + 7);
PrintMessage(Message::mkInfo, szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
AddMessage(Message::mkWarning, false, g_pOptions->GetWarningTarget(), szText + 10);
PrintMessage(Message::mkWarning, szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
AddMessage(Message::mkError, false, g_pOptions->GetErrorTarget(), szText + 8);
PrintMessage(Message::mkError, szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
AddMessage(Message::mkDetail, false, g_pOptions->GetDetailTarget(), szText + 9);
PrintMessage(Message::mkDetail, szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
AddMessage(Message::mkDebug, false, g_pOptions->GetDebugTarget(), szText + 8);
PrintMessage(Message::mkDebug, 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;
}
{
PrintMessage(Message::mkInfo, szText);
}
debug("Processing output received from script - completed");
}
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void ScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
switch (eKind)
{
case Message::mkDetail:
detail("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
detail("%s", szText);
break;
case Message::mkInfo:
info("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
info("%s", szText);
break;
case Message::mkWarning:
warn("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
warn("%s", szText);
break;
case Message::mkError:
error("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
error("%s", szText);
break;
case Message::mkDebug:
debug("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
debug("%s", szText);
break;
}
}
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
void ScriptController::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
char tmp2[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(tmp2, 1024, szFormat, ap);
tmp2[1024-1] = '\0';
va_end(ap);
char tmp3[1024];
if (m_szLogPrefix)
{
snprintf(tmp3, 1024, "%s: %s", m_szLogPrefix, tmp2);
}
else
{
strncpy(tmp3, tmp2, 1024);
}
tmp3[1024-1] = '\0';
AddMessage(eKind, tmp3);
}
void PostScriptController::StartJob(PostInfo* pPostInfo)
{
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);
pPostInfo->SetPostThread(pScriptController);
pScriptController->Start();
}
void PostScriptController::Run()
{
FileList activeList;
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szVarname = pParameter->GetName();
if (strlen(szVarname) > 0 && szVarname[0] != '*' && szVarname[strlen(szVarname)-1] == ':' &&
(!strcasecmp(pParameter->GetValue(), "yes") || !strcasecmp(pParameter->GetValue(), "on") || !strcasecmp(pParameter->GetValue(), "1")))
{
char* szScriptName = strdup(szVarname);
szScriptName[strlen(szScriptName)-1] = '\0'; // remove trailing ':'
activeList.push_back(szScriptName);
}
}
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Clear();
g_pDownloadQueueHolder->UnlockQueue();
Options::ScriptList scriptList;
g_pOptions->LoadScriptList(&scriptList);
for (Options::ScriptList::iterator it = scriptList.begin(); it != scriptList.end(); it++)
{
Options::Script* pScript = *it;
for (FileList::iterator it2 = activeList.begin(); it2 != activeList.end(); it2++)
{
char* szActiveName = *it2;
// if any script has requested par-check, do not execute other scripts
if (Util::SameFilename(pScript->GetName(), szActiveName) && !m_pPostInfo->GetRequestParCheck())
{
ExecuteScript(pScript->GetName(), pScript->GetDisplayName(), pScript->GetLocation());
}
}
}
for (FileList::iterator it = activeList.begin(); it != activeList.end(); it++)
{
free(*it);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation)
{
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", szScriptName, m_pPostInfo->GetInfoName());
SetScript(szLocation);
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script %s for %s", szScriptName, m_pPostInfo->GetInfoName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(szDisplayName);
PrepareParams(szScriptName);
int iExitCode = Execute();
szInfoName[0] = 'P'; // uppercase
SetLogPrefix(NULL);
ScriptStatus::EStatus eStatus = AnalyseExitCode(iExitCode);
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Add(szScriptName, eStatus);
g_pDownloadQueueHolder->UnlockQueue();
}
void PostScriptController::PrepareParams(const char* szScriptName)
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
@@ -619,162 +830,108 @@ void PostScriptController::Run()
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
char szParStatus[10];
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
snprintf(szParStatus, 10, "%i", iParStatus[m_pPostInfo->GetNZBInfo()->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';
int iUnpackStatus[] = { 0, 0, 1, 2 };
char szUnpackStatus[10];
snprintf(szUnpackStatus, 10, "%i", iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
szUnpackStatus[10-1] = '\0';
char szDestDir[1024];
strncpy(szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
szDestDir[1024-1] = '\0';
char szNZBID[10];
snprintf(szNZBID, 10, "%i", m_pPostInfo->GetNZBInfo()->GetID());
szNZBID[10-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);
// Reset
ResetEnv();
SetEnvVar("NZBPP_NZBNAME", szNZBName);
SetEnvVar("NZBPP_NZBID", szNZBID);
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_UNPACKSTATUS", szUnpackStatus);
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());
}
PrepareEnvParameters(m_pPostInfo->GetNZBInfo(), NULL);
char szParamPrefix[1024];
snprintf(szParamPrefix, 1024, "%s:", szScriptName);
szParamPrefix[1024-1] = '\0';
PrepareEnvParameters(m_pPostInfo->GetNZBInfo(), szParamPrefix);
PrepareEnvOptions(szParamPrefix);
g_pDownloadQueueHolder->UnlockQueue();
}
int iResult = Execute();
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int iExitCode)
{
// The ScriptStatus is accumulated for all scripts:
// If any script has failed the status is "failure", etc.
switch (iResult)
switch (iExitCode)
{
case POSTPROCESS_SUCCESS:
info("%s sucessful", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
break;
PrintMessage(Message::mkInfo, "%s successful", GetInfoName());
return ScriptStatus::srSuccess;
case POSTPROCESS_ERROR:
info("%s failed", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
break;
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", GetInfoName());
return ScriptStatus::srFailure;
case POSTPROCESS_NONE:
info("%s skipped", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srNone);
break;
PrintMessage(Message::mkInfo, "%s skipped", GetInfoName());
return ScriptStatus::srNone;
#ifndef DISABLE_PARCHECK
case POSTPROCESS_PARCHECK_ALL:
if (m_pPostInfo->GetParCheck())
case POSTPROCESS_PARCHECK:
if (m_pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped)
{
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);
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", GetInfoName());
return ScriptStatus::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);
PrintMessage(Message::mkInfo, "%s requested par-check/repair", GetInfoName());
m_pPostInfo->SetRequestParCheck(true);
return ScriptStatus::srSuccess;
}
break;
#endif
default:
info("%s terminated with unknown status", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srUnknown);
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", GetInfoName());
return ScriptStatus::srFailure;
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
if (!strncmp(szText, "[HISTORY] ", 10))
{
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
}
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText);
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(eKind, szText);
}
ScriptController::AddMessage(eKind, szText);
m_pPostInfo->AppendMessage(eKind, szText);
}
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = m_pPostInfo->GetStageTime();
@@ -810,16 +967,59 @@ void PostScriptController::Stop()
Terminate();
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename,
const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList)
/**
* DownloadQueue must be locked prior to call of this function.
*/
void PostScriptController::InitParamsForNewNZB(NZBInfo* pNZBInfo)
{
const char* szDefScript = g_pOptions->GetDefScript();
if (pNZBInfo->GetCategory() && strlen(pNZBInfo->GetCategory()) > 0)
{
Options::Category* pCategory = g_pOptions->FindCategory(pNZBInfo->GetCategory());
if (pCategory && pCategory->GetDefScript() && strlen(pCategory->GetDefScript()) > 0)
{
szDefScript = pCategory->GetDefScript();
}
}
if (!szDefScript || strlen(szDefScript) == 0)
{
return;
}
// split szDefScript into tokens and create pp-parameter for each token
char* szDefScript2 = strdup(szDefScript);
char* saveptr;
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
while (szScriptName)
{
szScriptName = Util::Trim(szScriptName);
if (szScriptName[0] != '\0')
{
char szParam[1024];
snprintf(szParam, 1024, "%s:", szScriptName);
szParam[1024-1] = '\0';
pNZBInfo->GetParameters()->SetParameter(szParam, "yes");
}
szScriptName = strtok_r(NULL, ",;", &saveptr);
}
free(szDefScript2);
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused)
{
info("Executing nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
NZBScriptController* pScriptController = new NZBScriptController();
pScriptController->SetScript(szScript);
pScriptController->m_pNZBName = pNZBName;
pScriptController->m_pCategory = pCategory;
pScriptController->m_pParameterList = pParameterList;
pScriptController->m_pParameters = pParameters;
pScriptController->m_iPriority = iPriority;
pScriptController->m_bAddTop = bAddTop;
pScriptController->m_bAddPaused = bAddPaused;
char szInfoName[1024];
snprintf(szInfoName, 1024, "nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
@@ -836,30 +1036,51 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
szDir[iLen-1] = '\0';
}
pScriptController->SetDefaultKindPrefix("NZB-Process: ");
pScriptController->SetDefaultLogKind(g_pOptions->GetProcessLogKind());
char szPriority[20];
snprintf(szPriority, 20, "%i", *iPriority);
szPriority[20-1] = '\0';
const char* szArgs[4];
szArgs[0] = szScript;
szArgs[1] = szDir;
szArgs[2] = szNZBFilename;
szArgs[3] = NULL;
pScriptController->SetArgs(szArgs, false);
char szAddTop[10];
snprintf(szAddTop, 10, "%i", *bAddTop ? 1 : 0);
szAddTop[10-1] = '\0';
char szAddPaused[10];
snprintf(szAddPaused, 10, "%i", *bAddPaused ? 1 : 0);
szAddPaused[10-1] = '\0';
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
pScriptController->SetLogPrefix(szLogPrefix);
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->SetEnvVar("NZBNP_NZBNAME", strlen(*pNZBName) > 0 ? *pNZBName : Util::BaseFileName(szNZBFilename));
pScriptController->SetEnvVar("NZBNP_CATEGORY", *pCategory);
pScriptController->SetEnvVar("NZBNP_PRIORITY", szPriority);
pScriptController->SetEnvVar("NZBNP_TOP", szAddTop);
pScriptController->SetEnvVar("NZBNP_PAUSED", szAddPaused);
pScriptController->Execute();
delete pScriptController;
}
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
void NZBScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
szText = szText + m_iPrefixLen;
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strncmp(szText + 6, "CATEGORY=", 9))
if (!strncmp(szText + 6, "NZBNAME=", 8))
{
free(*m_pNZBName);
*m_pNZBName = strdup(szText + 6 + 8);
}
else if (!strncmp(szText + 6, "CATEGORY=", 9))
{
free(*m_pCategory);
*m_pCategory = strdup(szText + 6 + 9);
@@ -871,7 +1092,7 @@ void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Op
if (szValue)
{
*szValue = '\0';
m_pParameterList->SetParameter(szParam, szValue + 1);
m_pParameters->SetParameter(szParam, szValue + 1);
}
else
{
@@ -883,6 +1104,14 @@ void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Op
{
*m_iPriority = atoi(szText + 6 + 9);
}
else if (!strncmp(szText + 6, "TOP=", 4))
{
*m_bAddTop = atoi(szText + 6 + 4) != 0;
}
else if (!strncmp(szText + 6, "PAUSED=", 7))
{
*m_bAddPaused = atoi(szText + 6 + 7) != 0;
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
@@ -890,7 +1119,7 @@ void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Op
}
else
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
ScriptController::AddMessage(eKind, szText);
}
}
@@ -899,6 +1128,8 @@ void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInf
NZBAddedScriptController* pScriptController = new NZBAddedScriptController();
pScriptController->SetScript(szScript);
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
pScriptController->SetEnvVar("NZBNA_NZBNAME", pNZBInfo->GetName());
// "NZBNA_NAME" is not correct but kept for compatibility with older versions where this name was used by mistake
pScriptController->SetEnvVar("NZBNA_NAME", pNZBInfo->GetName());
pScriptController->SetEnvVar("NZBNA_FILENAME", pNZBInfo->GetFilename());
pScriptController->SetEnvVar("NZBNA_CATEGORY", pNZBInfo->GetCategory());
@@ -927,14 +1158,7 @@ void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInf
snprintf(buf, 100, "%i", iMaxPriority);
pScriptController->SetEnvVar("NZBNA_PRIORITY", buf);
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
szVarname[1024-1] = '\0';
pScriptController->SetEnvVar(szVarname, pParameter->GetValue());
}
pScriptController->PrepareEnvParameters(pNZBInfo, NULL);
pScriptController->SetAutoDestroy(true);
@@ -950,13 +1174,11 @@ void NZBAddedScriptController::Run()
info("Executing %s", szInfoName);
SetDefaultKindPrefix("NZB-Added Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[2];
szArgs[0] = GetScript();
szArgs[1] = NULL;
SetArgs(szArgs, false);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
Execute();
@@ -989,8 +1211,11 @@ void SchedulerScriptController::Run()
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Scheduled Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
Execute();
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@ private:
public:
EnvironmentStrings();
~EnvironmentStrings();
void Clear();
void InitFromCurrentProcess();
void Append(char* szString);
#ifdef WIN32
@@ -59,22 +60,28 @@ private:
const char* m_szWorkingDir;
const char** m_szArgs;
bool m_bFreeArgs;
const char* m_szStdArgs[2];
const char* m_szInfoName;
const char* m_szDefaultKindPrefix;
const char* m_szLogPrefix;
EnvironmentStrings m_environmentStrings;
Options::EScriptLogKind m_eDefaultLogKind;
bool m_bTerminated;
#ifdef WIN32
HANDLE m_hProcess;
char m_szCmdLine[2048];
#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);
void ProcessOutput(char* szText);
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void AddMessage(Message::EKind eKind, const char* szText);
bool GetTerminated() { return m_bTerminated; }
void ResetEnv();
void PrepareEnvOptions(const char* szStripPrefix);
void PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix);
void PrepareArgs();
public:
ScriptController();
@@ -88,43 +95,54 @@ public:
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 SetLogPrefix(const char* szLogPrefix) { m_szLogPrefix = szLogPrefix; }
void SetEnvVar(const char* szName, const char* szValue);
void SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue);
};
class PostScriptController : public Thread, ScriptController
class PostScriptController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
char m_szNZBName[1024];
void ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation);
void PrepareParams(const char* szScriptName);
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
typedef std::deque<char*> FileList;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
static void StartJob(PostInfo* pPostInfo);
static void InitParamsForNewNZB(NZBInfo* pNZBInfo);
};
class NZBScriptController : public ScriptController
{
private:
char** m_pNZBName;
char** m_pCategory;
int* m_iPriority;
NZBParameterList* m_pParameterList;
NZBParameterList* m_pParameters;
bool* m_bAddTop;
bool* m_bAddPaused;
int m_iPrefixLen;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters,
bool* bAddTop, bool* bAddPaused);
};
class NZBAddedScriptController : public Thread, ScriptController
class NZBAddedScriptController : public Thread, public ScriptController
{
private:
char* m_szNZBName;
@@ -134,7 +152,7 @@ public:
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
};
class SchedulerScriptController : public Thread, ScriptController
class SchedulerScriptController : public Thread, public ScriptController
{
public:
virtual void Run();

View File

@@ -38,6 +38,7 @@
#include <unistd.h>
#include <errno.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ServerPool.h"
@@ -82,70 +83,119 @@ void ServerPool::AddServer(NewsServer* pNewsServer)
{
debug("Adding server to ServerPool");
m_Servers.push_back(pNewsServer);
if (pNewsServer->GetMaxConnections() > 0)
{
m_Servers.push_back(pNewsServer);
}
else
{
delete pNewsServer;
}
}
void ServerPool::NormalizeLevels()
{
if (m_Servers.empty())
{
return;
}
std::sort(m_Servers.begin(), m_Servers.end(), CompareServers);
NewsServer* pNewsServer = m_Servers.front();
m_iMaxLevel = 0;
int iCurLevel = pNewsServer->GetLevel();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() != iCurLevel)
{
m_iMaxLevel++;
}
pNewsServer->SetLevel(m_iMaxLevel);
}
}
bool ServerPool::CompareServers(NewsServer* pServer1, NewsServer* pServer2)
{
return pServer1->GetLevel() < pServer2->GetLevel();
}
void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");
m_iMaxLevel = 0;
NormalizeLevels();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (m_iMaxLevel < pNewsServer->GetLevel())
{
m_iMaxLevel = pNewsServer->GetLevel();
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
if ((int)m_Levels.size() <= pNewsServer->GetLevel())
{
m_Levels.push_back(0);
}
m_Levels[pNewsServer->GetLevel()] += pNewsServer->GetMaxConnections();
}
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
if (m_Levels.empty())
{
int iMaxConnectionsForLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (iLevel == pNewsServer->GetLevel())
{
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
}
}
m_Levels.push_back(iMaxConnectionsForLevel);
warn("No news servers defined, download is not possible");
}
}
NNTPConnection* ServerPool::GetConnection(int iLevel)
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
{
PooledConnection* pConnection = NULL;
m_mutexConnections.Lock();
if (m_Levels[iLevel] > 0)
if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0)
{
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
PooledConnection* pCandidateConnection = *it;
NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer();
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetLevel() == iLevel &&
(!pWantServer || pCandidateServer == pWantServer ||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
{
// free connection found, take it!
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
// free connection found, check if it's not from the server which should be ignored
bool bUseConnection = true;
if (pIgnoreServers && !pWantServer)
{
for (Servers::iterator it = pIgnoreServers->begin(); it != pIgnoreServers->end(); it++)
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetLevel() == pCandidateServer->GetLevel()))
{
bUseConnection = false;
break;
}
}
}
if (bUseConnection)
{
pConnection = pCandidateConnection;
pConnection->SetInUse(true);
break;
}
}
}
m_Levels[iLevel]--;
if (!pConnection)
if (pConnection)
{
error("ServerPool: internal error, no free connection found, but there should be one");
m_Levels[iLevel]--;
}
}
@@ -208,7 +258,7 @@ void ServerPool::LogDebugInfo()
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
debug(" %s: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetHost(), (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
m_mutexConnections.Unlock();

View File

@@ -36,6 +36,9 @@
class ServerPool
{
public:
typedef std::vector<NewsServer*> Servers;
private:
class PooledConnection : public NNTPConnection
{
@@ -50,7 +53,6 @@ private:
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
};
typedef std::vector<NewsServer*> Servers;
typedef std::vector<int> Levels;
typedef std::vector<PooledConnection*> Connections;
@@ -61,6 +63,9 @@ private:
Mutex m_mutexConnections;
int m_iTimeout;
void NormalizeLevels();
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
public:
ServerPool();
~ServerPool();
@@ -68,7 +73,8 @@ public:
void AddServer(NewsServer* pNewsServer);
void InitConnections();
int GetMaxLevel() { return m_iMaxLevel; }
NNTPConnection* GetConnection(int iLevel);
Servers* GetServers() { return &m_Servers; } // Only for read access (no lockings)
NNTPConnection* GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();

1828
TLS.cpp
View File

File diff suppressed because it is too large Load Diff

204
TLS.h
View File

@@ -1,13 +1,7 @@
/*
* 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 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,180 +27,36 @@
#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
class TLSSocket
{
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;
private:
bool m_bIsClient;
char* m_szCertFile;
char* m_szKeyFile;
char* m_szCipher;
SOCKET m_iSocket;
bool m_bSuppressErrors;
int m_iRetCode;
bool m_bInitialized;
bool m_bConnected;
/*
* 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;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TLS.h
void* m_pContext;
void* m_pSession;
/*
* 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);
void ReportError(const char* szErrMsg);
/*
* 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);
public:
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
~TLSSocket();
static void Init();
static void Final();
bool Start();
void Close();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
};
#endif
#endif

888
Unpack.cpp Normal file
View File

@@ -0,0 +1,888 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Unpack.h"
#include "Log.h"
#include "Util.h"
#include "ParCoordinator.h"
extern Options* g_pOptions;
extern DownloadQueueHolder* g_pDownloadQueueHolder;
void UnpackController::FileList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
free(*it);
}
clear();
}
bool UnpackController::FileList::Exists(const char* szFilename)
{
for (iterator it = begin(); it != end(); it++)
{
char* szFilename1 = *it;
if (!strcmp(szFilename1, szFilename))
{
return true;
}
}
return false;
}
UnpackController::~UnpackController()
{
m_archiveFiles.Clear();
}
void UnpackController::StartJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
pUnpackController->m_pPostInfo = pPostInfo;
pUnpackController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pUnpackController);
pUnpackController->Start();
}
void UnpackController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
strncpy(m_szName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
m_szName[1024-1] = '\0';
m_bCleanedUpDisk = false;
bool bUnpack = true;
m_szPassword[0] = '\0';
m_szFinalDir[0] = '\0';
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
if (!strcasecmp(pParameter->GetName(), "*Unpack:") && !strcasecmp(pParameter->GetValue(), "no"))
{
bUnpack = false;
}
if (!strcasecmp(pParameter->GetName(), "*Unpack:Password"))
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
}
}
g_pDownloadQueueHolder->UnlockQueue();
snprintf(m_szInfoName, 1024, "unpack for %s", m_szName);
m_szInfoName[1024-1] = '\0';
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
CheckStateFiles();
#ifndef DISABLE_PARCHECK
if (bUnpack && m_bHasBrokenFiles && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
{
PrintMessage(Message::mkInfo, "%s has broken files", m_szName);
RequestParCheck(false);
m_pPostInfo->SetWorking(false);
return;
}
#endif
if (bUnpack)
{
bool bScanNonStdFiles = m_pPostInfo->GetNZBInfo()->GetRenameStatus() > NZBInfo::rsSkipped ||
m_pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
!m_bHasParFiles;
CheckArchiveFiles(bScanNonStdFiles);
}
if (bUnpack && (m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetWorkingDir(m_szDestDir);
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
CreateUnpackDir();
m_bUnpackOK = true;
m_bUnpackStartError = false;
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
{
ExecuteUnrar();
}
if (m_bHasSevenZipFiles && m_bUnpackOK)
{
ExecuteSevenZip(false);
}
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
{
ExecuteSevenZip(true);
}
Completed();
}
else
{
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
{
RequestParCheck(m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped);
}
else
#endif
{
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
m_pPostInfo->SetWorking(false);
}
void UnpackController::ExecuteUnrar()
{
// Format:
// unrar x -y -p- -o+ *.rar ./_unpack
char szPasswordParam[1024];
const char* szArgs[8];
szArgs[0] = g_pOptions->GetUnrarCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o+";
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
szArgs[6] = m_szUnpackDir;
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
SetLogPrefix("Unrar");
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "Unrar error code: %i", iExitCode);
}
}
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
{
// Format:
// 7z x -y -p- -o./_unpack *.7z
// OR
// 7z x -y -p- -o./_unpack *.7z.001
char szPasswordParam[1024];
const char* szArgs[7];
szArgs[0] = g_pOptions->GetSevenZipCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
szArgs[4] = szUnpackDirParam;
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
szArgs[6] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetSevenZipCmd());
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
PrintMessage(Message::mkInfo, "Executing 7-Zip");
SetLogPrefix("7-Zip");
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "7-Zip error code: %i", iExitCode);
}
}
void UnpackController::Completed()
{
bool bCleanupSuccess = Cleanup();
if (m_bUnpackOK && bCleanupSuccess)
{
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && !m_bUnpackStartError && !GetTerminated() && m_bHasParFiles)
{
RequestParCheck(false);
}
else
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck(bool bRename)
{
PrintMessage(Message::mkInfo, "%s requested %s", m_szInfoNameUp, bRename ? "par-rename": "par-check/repair");
if (bRename)
{
m_pPostInfo->SetRequestParRename(true);
}
else
{
m_pPostInfo->SetRequestParCheck(true);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
void UnpackController::CheckStateFiles()
{
char szBrokenLog[1024];
snprintf(szBrokenLog, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_brokenlog.txt");
szBrokenLog[1024-1] = '\0';
m_bHasBrokenFiles = Util::FileExists(szBrokenLog);
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
}
void UnpackController::CreateUnpackDir()
{
m_bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (m_bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
}
else
{
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
}
m_szUnpackDir[1024-1] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szUnpackDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
}
}
void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
{
m_bHasRarFiles = false;
m_bHasNonStdRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExRar.Match(filename))
{
m_bHasRarFiles = true;
}
else if (regExSevenZip.Match(filename))
{
m_bHasSevenZipFiles = true;
}
else if (regExSevenZipMulti.Match(filename))
{
m_bHasSevenZipMultiFiles = true;
}
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles && !regExRarMultiSeq.Match(filename))
{
// Check if file has RAR signature
char rarSignature[] = {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00};
char fileSignature[7];
FILE* infile;
infile = fopen(szFullFilename, "rb");
if (infile)
{
int cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
fclose(infile);
if (cnt == sizeof(fileSignature) && !strcmp(rarSignature, fileSignature))
{
m_bHasNonStdRarFiles = true;
}
}
}
}
}
}
bool UnpackController::Cleanup()
{
// By success:
// - move unpacked files to destination dir;
// - remove _unpack-dir;
// - delete archive-files.
// By failure:
// - remove _unpack-dir.
bool bOK = true;
FileList extractedFiles;
if (m_bUnpackOK)
{
// moving files back
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szFinalDir[0] != '\0' ? m_szFinalDir : m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// silently overwrite existing files
remove(szDstFile);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s", szSrcFile, szDstFile);
bOK = false;
}
extractedFiles.push_back(strdup(filename));
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szUnpackDir))
{
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
}
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
{
PrintMessage(Message::mkInfo, "Deleting archive files");
// Delete rar-files (only files which were used by unrar)
for (FileList::iterator it = m_archiveFiles.begin(); it != m_archiveFiles.end(); it++)
{
char* szFilename = *it;
if (m_bInterDir || !extractedFiles.Exists(szFilename))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFilename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", szFilename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
// Unfortunately 7-Zip doesn't print the processed archive-files to the output.
// Therefore we don't know for sure which files were extracted.
// We just delete all 7z-files in the directory.
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename)
&& regExSevenZip.Match(filename) && (m_bInterDir || !extractedFiles.Exists(filename)))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
m_bCleanedUpDisk = true;
}
extractedFiles.Clear();
return bOK;
}
/**
* Unrar prints progress information into the same line using backspace control character.
* In order to print progress continuously we analyze the output after every char
* and update post-job progress information.
*/
bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
bool bPrinted = false;
int i = 0;
for (; i < iBufSize - 1; i++)
{
int ch = fgetc(pStream);
szBuf[i] = ch;
szBuf[i+1] = '\0';
if (ch == EOF)
{
break;
}
if (ch == '\n')
{
i++;
break;
}
char* szBackspace = strrchr(szBuf, '\b');
if (szBackspace)
{
if (!bPrinted)
{
char tmp[1024];
strncpy(tmp, szBuf, 1024);
tmp[1024-1] = '\0';
char* szTmpPercent = strrchr(tmp, '\b');
if (szTmpPercent)
{
*szTmpPercent = '\0';
}
if (strncmp(szBuf, "...", 3))
{
ProcessOutput(tmp);
}
bPrinted = true;
}
if (strchr(szBackspace, '%'))
{
int iPercent = atoi(szBackspace + 1);
m_pPostInfo->SetStageProgress(iPercent * 10);
}
}
}
szBuf[i] = '\0';
if (bPrinted)
{
szBuf[0] = '\0';
}
return i > 0;
}
void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
{
char szMsgText[1024];
strncpy(szMsgText, szText, 1024);
szMsgText[1024-1] = '\0';
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting ", 19) &&
!strncmp(szText + 19, m_szUnpackDir, strlen(m_szUnpackDir)))
{
snprintf(szMsgText, 1024, "Unrar: Extracting %s", szText + 19 + strlen(m_szUnpackDir) + 1);
szMsgText[1024-1] = '\0';
}
ScriptController::AddMessage(eKind, szMsgText);
m_pPostInfo->AppendMessage(eKind, szMsgText);
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: UNRAR ", 6) &&
strstr(szMsgText, " Copyright ") && strstr(szMsgText, " Alexander Roshal"))
{
// reset start time for a case if user uses unpack-script to do some things
// (like sending Wake-On-Lan message) before executing unrar
m_pPostInfo->SetStageTime(time(NULL));
}
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: Extracting ", 18))
{
SetProgressLabel(szMsgText + 7);
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting from ", 23))
{
const char *szFilename = szText + 23;
debug("Filename: %s", szFilename);
m_archiveFiles.push_back(strdup(szFilename));
SetProgressLabel(szText + 7);
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: All OK", 13)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "7-Zip: Everything is Ok", 23)))
{
m_bAllOKMessageReceived = true;
}
}
void UnpackController::Stop()
{
debug("Stopping unpack");
Thread::Stop();
Terminate();
}
void UnpackController::SetProgressLabel(const char* szProgressLabel)
{
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->SetProgressLabel(szProgressLabel);
g_pDownloadQueueHolder->UnlockQueue();
}
void MoveController::StartJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
pMoveController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pMoveController);
pMoveController->Start();
}
void MoveController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
m_szDestDir[1024-1] = '\0';
g_pDownloadQueueHolder->UnlockQueue();
info("Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
szInfoName[0] = 'M'; // uppercase
if (bOK)
{
info("%s successful", szInfoName);
// save new dest dir
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
g_pDownloadQueueHolder->UnlockQueue();
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool MoveController::MoveFiles()
{
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
bool bOK = true;
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// prevent overwriting of existing files
int dupcount = 0;
while (Util::FileExists(szDstFile))
{
dupcount++;
snprintf(szDstFile, 1024, "%s%c%s_duplicate%d", m_szDestDir, PATH_SEPARATOR, filename, dupcount);
szDstFile[1024-1] = '\0';
}
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s! Errcode: %i", szSrcFile, szDstFile, errno);
bOK = false;
}
}
}
Util::RemoveDirectory(m_szInterDir);
return bOK;
}
void CleanupController::StartJob(PostInfo* pPostInfo)
{
CleanupController* pCleanupController = new CleanupController();
pCleanupController->m_pPostInfo = pPostInfo;
pCleanupController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pCleanupController);
pCleanupController->Start();
}
void CleanupController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
}
else
{
m_szFinalDir[0] = '\0';
}
g_pDownloadQueueHolder->UnlockQueue();
info("Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
if (bOK && m_szFinalDir[0] != '\0')
{
bool bDeleted2 = false;
bOK = Cleanup(m_szFinalDir, &bDeleted2);
bDeleted = bDeleted || bDeleted2;
}
szInfoName[0] = 'C'; // uppercase
if (bOK && bDeleted)
{
info("%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
info("Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
{
*bDeleted = false;
bool bOK = true;
ExtList extList;
// split ExtCleanupDisk into tokens and create a list
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
char* saveptr;
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
while (szExt)
{
extList.push_back(szExt);
szExt = strtok_r(NULL, ",; ", &saveptr);
}
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
// check file extension
int iFilenameLen = strlen(filename);
bool bDeleteIt = false;
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
{
const char* szExt = *it;
int iExtLen = strlen(szExt);
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, filename + iFilenameLen - iExtLen))
{
bDeleteIt = true;
break;
}
}
if (bDeleteIt)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s! Errcode: %i", szFullFilename, errno);
bOK = false;
}
*bDeleted = true;
}
}
free(szExtCleanupDisk);
return bOK;
}

129
Unpack.h Normal file
View File

@@ -0,0 +1,129 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef UNPACK_H
#define UNPACK_H
#include <deque>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "ScriptController.h"
class UnpackController : public Thread, public ScriptController
{
private:
enum EUnpacker
{
upUnrar,
upSevenZip
};
typedef std::deque<char*> FileListBase;
class FileList : public FileListBase
{
public:
void Clear();
bool Exists(const char* szFilename);
};
private:
PostInfo* m_pPostInfo;
char m_szName[1024];
char m_szInfoName[1024];
char m_szInfoNameUp[1024];
char m_szDestDir[1024];
char m_szFinalDir[1024];
char m_szUnpackDir[1024];
char m_szPassword[1024];
bool m_bInterDir;
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasParFiles;
bool m_bHasBrokenFiles;
bool m_bHasRarFiles;
bool m_bHasNonStdRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bCleanedUpDisk;
EUnpacker m_eUnpacker;
FileList m_archiveFiles;
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
virtual void AddMessage(Message::EKind eKind, const char* szText);
void ExecuteUnrar();
void ExecuteSevenZip(bool bMultiVolumes);
void Completed();
void CreateUnpackDir();
bool Cleanup();
void CheckStateFiles();
void CheckArchiveFiles(bool bScanNonStdFiles);
void SetProgressLabel(const char* szProgressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck(bool bRename);
#endif
public:
virtual ~UnpackController();
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);
};
class MoveController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szInterDir[1024];
char m_szDestDir[1024];
bool MoveFiles();
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
class CleanupController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szDestDir[1024];
char m_szFinalDir[1024];
bool Cleanup(const char* szDestDir, bool *bDeleted);
typedef std::deque<char*> ExtList;
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
@@ -49,10 +49,13 @@
#include "Util.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
extern QueueCoordinator* g_pQueueCoordinator;
extern Scanner* g_pScanner;
UrlDownloader::UrlDownloader() : WebDownloader()
{
@@ -142,7 +145,7 @@ void UrlCoordinator::Run()
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
if (bHasMoreUrls && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
if (bHasMoreUrls && !IsStopped())
{
StartUrlDownload(pUrlInfo);
}
@@ -418,38 +421,8 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
{
info("Queue downloaded collection %s", szOriginalFilename);
NZBFile* pNZBFile = NZBFile::CreateFromFile(szTempFilename, pUrlInfo->GetCategory());
if (pNZBFile)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
pNZBFile->GetNZBInfo()->SetFilename(pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename);
if (strlen(pUrlInfo->GetCategory()) > 0)
{
pNZBFile->GetNZBInfo()->SetCategory(pUrlInfo->GetCategory());
}
else if (szOriginalCategory)
{
pNZBFile->GetNZBInfo()->SetCategory(szOriginalCategory);
}
pNZBFile->GetNZBInfo()->BuildDestDirName();
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(pUrlInfo->GetPriority());
pFileInfo->SetPaused(pUrlInfo->GetAddPaused());
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pUrlInfo->GetAddTop());
delete pNZBFile;
info("Collection %s added to queue", szOriginalFilename);
}
else
{
error("Could not add downloaded collection %s to queue", szOriginalFilename);
}
}
g_pScanner->AddExternalFile(
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename,
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : szOriginalCategory,
pUrlInfo->GetPriority(), NULL, pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(), szTempFilename, NULL, 0, false);
}

290
Util.cpp
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -140,15 +140,7 @@ int getopt(int argc, char *argv[], char *optstring)
DirBrowser::DirBrowser(const char* szPath)
{
char szMask[MAX_PATH + 1];
int len = strlen(szPath);
if (szPath[len] == '\\' || szPath[len] == '/')
{
snprintf(szMask, MAX_PATH + 1, "%s*.*", szPath);
}
else
{
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
}
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
szMask[MAX_PATH] = '\0';
m_hFile = _findfirst(szMask, &m_FindData);
m_bFirst = true;
@@ -211,6 +203,36 @@ const char* DirBrowser::Next()
#endif
StringBuilder::StringBuilder()
{
m_szBuffer = NULL;
m_iBufferSize = 0;
m_iUsedSize = 0;
}
StringBuilder::~StringBuilder()
{
if (m_szBuffer)
{
free(m_szBuffer);
}
}
void StringBuilder::Append(const char* szStr)
{
int iPartLen = strlen(szStr);
if (m_iUsedSize + iPartLen + 1 > m_iBufferSize)
{
m_iBufferSize += iPartLen + 10240;
m_szBuffer = (char*)realloc(m_szBuffer, m_iBufferSize);
}
strcpy(m_szBuffer + m_iUsedSize, szStr);
m_iUsedSize += iPartLen;
m_szBuffer[m_iUsedSize] = '\0';
}
char Util::VersionRevisionBuf[40];
char* Util::BaseFileName(const char* filename)
@@ -245,9 +267,13 @@ void Util::NormalizePathSeparators(char* szPath)
}
}
bool Util::ForceDirectories(const char* szPath)
bool Util::ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize)
{
char* szNormPath = strdup(szPath);
*szErrBuf = '\0';
char szSysErrStr[256];
char szNormPath[1024];
strncpy(szNormPath, szPath, 1024);
szNormPath[1024-1] = '\0';
NormalizePathSeparators(szNormPath);
int iLen = strlen(szNormPath);
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
@@ -260,15 +286,30 @@ bool Util::ForceDirectories(const char* szPath)
}
struct stat buffer;
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
bool bOK = !stat(szNormPath, &buffer);
if (!bOK && errno != ENOENT)
{
snprintf(szErrBuf, iBufSize, "could not read information for directory %s: errno %i, %s", szNormPath, errno, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (bOK && !S_ISDIR(buffer.st_mode))
{
snprintf(szErrBuf, iBufSize, "path %s is not a directory", szNormPath);
szErrBuf[iBufSize-1] = 0;
return false;
}
if (!bOK
#ifdef WIN32
&& strlen(szNormPath) > 2
#endif
)
{
char* szParentPath = strdup(szNormPath);
bOK = true;
char szParentPath[1024];
strncpy(szParentPath, szNormPath, 1024);
szParentPath[1024-1] = '\0';
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
if (p)
{
@@ -282,20 +323,35 @@ bool Util::ForceDirectories(const char* szPath)
{
*p = '\0';
}
if (strlen(szParentPath) != strlen(szPath))
if (strlen(szParentPath) != strlen(szPath) && !ForceDirectories(szParentPath, szErrBuf, iBufSize))
{
bOK = ForceDirectories(szParentPath);
return false;
}
}
if (bOK)
if (mkdir(szNormPath, S_DIRMODE) != 0 && errno != EEXIST)
{
mkdir(szNormPath, S_DIRMODE);
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
snprintf(szErrBuf, iBufSize, "could not create directory %s: %s", szNormPath, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (stat(szNormPath, &buffer) != 0)
{
snprintf(szErrBuf, iBufSize, "could not read information for directory %s: %s", szNormPath, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (!S_ISDIR(buffer.st_mode))
{
snprintf(szErrBuf, iBufSize, "path %s is not a directory", szNormPath);
szErrBuf[iBufSize-1] = 0;
return false;
}
free(szParentPath);
}
free(szNormPath);
return bOK;
return true;
}
bool Util::GetCurrentDirectory(char* szBuffer, int iBufSize)
@@ -361,6 +417,20 @@ bool Util::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBuff
return true;
}
bool Util::SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen)
{
FILE* pFile = fopen(szFileName, "wb");
if (!pFile)
{
return false;
}
int iWrittenBytes = fwrite(szBuffer, 1, iBufLen, pFile);
fclose(pFile);
return iWrittenBytes = iBufLen;
}
bool Util::CreateSparseFile(const char* szFilename, int iSize)
{
bool bOK = false;
@@ -599,19 +669,50 @@ bool Util::CreateDirectory(const char* szDirFilename)
return DirectoryExists(szDirFilename);
}
bool Util::RemoveDirectory(const char* szDirFilename)
{
#ifdef WIN32
return ::RemoveDirectory(szDirFilename);
#else
return remove(szDirFilename) == 0;
#endif
}
bool Util::DeleteDirectoryWithContent(const char* szDirFilename)
{
bool bOK = true;
DirBrowser dir(szDirFilename);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDirFilename, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
if (Util::DirectoryExists(szFullFilename))
{
bOK &= DeleteDirectoryWithContent(szFullFilename);
}
else
{
bOK &= remove(szFullFilename) == 0;
}
}
}
return bOK && RemoveDirectory(szDirFilename);
}
long long Util::FileSize(const char* szFilename)
{
#ifdef WIN32
struct _stat32i64 buffer;
_stat32i64(szFilename, &buffer);
#else
#ifdef HAVE_STAT64
struct stat64 buffer;
stat64(szFilename, &buffer);
#else
struct stat buffer;
stat(szFilename, &buffer);
#endif
#endif
return buffer.st_size;
}
@@ -757,6 +858,23 @@ void Util::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
szBuffer[iBufLen - 1] = '\0';
}
bool Util::SameFilename(const char* szFilename1, const char* szFilename2)
{
#ifdef WIN32
return strcasecmp(szFilename1, szFilename2) == 0;
#else
return strcmp(szFilename1, szFilename2) == 0;
#endif
}
char* Util::GetLastErrorMessage(char* szBuffer, int iBufLen)
{
szBuffer[0] = '\0';
strerror_r(errno, szBuffer, iBufLen);
szBuffer[iBufLen-1] = '\0';
return szBuffer;
}
void Util::InitVersionRevision()
{
#ifndef WIN32
@@ -855,7 +973,7 @@ void Util::TrimRight(char* szStr)
{
int iLen = strlen(szStr);
char ch = szStr[iLen-1];
while (*szStr && (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'))
while (iLen > 0 && (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'))
{
szStr[iLen-1] = 0;
iLen--;
@@ -863,6 +981,34 @@ void Util::TrimRight(char* szStr)
}
}
char* Util::Trim(char* szStr)
{
TrimRight(szStr);
char ch = *szStr;
while (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
{
szStr++;
ch = *szStr;
}
return szStr;
}
#ifdef WIN32
bool Util::RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen)
{
HKEY hSubKey;
if (!RegOpenKeyEx(hKey, szKeyName, 0, KEY_READ, &hSubKey))
{
DWORD iRetBytes = *iBufLen;
LONG iRet = RegQueryValueEx(hSubKey, szValueName, NULL, NULL, (LPBYTE)szBuffer, &iRetBytes);
*iBufLen = iRetBytes;
RegCloseKey(hSubKey);
return iRet == 0;
}
return false;
}
#endif
unsigned int WebUtil::DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer)
{
@@ -924,9 +1070,9 @@ char* WebUtil::XmlEncode(const char* raw)
iReqSize += 6;
break;
default:
if (ch >= 0x80)
if (ch < 0x20 || ch >= 0x80)
{
iReqSize += 6;
iReqSize += 10;
break;
}
}
@@ -964,10 +1110,51 @@ char* WebUtil::XmlEncode(const char* raw)
output += 6;
break;
default:
if (ch >= 0x80)
if (ch < 0x20 || ch > 0x80)
{
sprintf(output, "&#%i;", ch);
output += 6;
unsigned int cp = ch;
// decode utf8
if ((cp >> 5) == 0x6 && (p[1] & 0xc0) == 0x80)
{
// 2 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 6) & 0x7ff) + (ch & 0x3f);
}
else if ((cp >> 4) == 0xe && (p[1] & 0xc0) == 0x80)
{
// 3 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 12) & 0xffff) + ((ch << 6) & 0xfff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
else if ((cp >> 3) == 0x1e && (p[1] & 0xc0) == 0x80)
{
// 4 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 18) & 0x1fffff) + ((ch << 12) & 0x3ffff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += (ch << 6) & 0xfff;
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
// accept only valid XML 1.0 characters
if (cp == 0x9 || cp == 0xA || cp == 0xD ||
(0x20 <= cp && cp <= 0xD7FF) ||
(0xE000 <= cp && cp <= 0xFFFD) ||
(0x10000 <= cp && cp <= 0x10FFFF))
{
sprintf(output, "&#x%06x;", cp);
output += 10;
}
else
{
// replace invalid characters with dots
sprintf(output, ".");
output += 1;
}
}
else
{
@@ -1109,7 +1296,7 @@ char* WebUtil::JsonEncode(const char* raw)
iReqSize++;
break;
default:
if (ch >= 0x80)
if (ch < 0x20 || ch >= 0x80)
{
iReqSize += 6;
break;
@@ -1161,9 +1348,38 @@ char* WebUtil::JsonEncode(const char* raw)
output += 2;
break;
default:
if (ch >= 0x80)
if (ch < 0x20 || ch > 0x80)
{
sprintf(output, "\\u%04x", ch);
unsigned int cp = ch;
// decode utf8
if ((cp >> 5) == 0x6 && (p[1] & 0xc0) == 0x80)
{
// 2 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 6) & 0x7ff) + (ch & 0x3f);
}
else if ((cp >> 4) == 0xe && (p[1] & 0xc0) == 0x80)
{
// 3 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 12) & 0xffff) + ((ch << 6) & 0xfff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
else if ((cp >> 3) == 0x1e && (p[1] & 0xc0) == 0x80)
{
// 4 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 18) & 0x1fffff) + ((ch << 12) & 0x3ffff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += (ch << 6) & 0xfff;
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
// we support only Unicode range U+0000-U+FFFF
sprintf(output, "\\u%04x", cp <= 0xFFFF ? cp : '.');
output += 6;
}
else

28
Util.h
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -57,6 +57,19 @@ public:
const char* Next();
};
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 Util
{
public:
@@ -64,6 +77,7 @@ public:
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen);
static bool CreateSparseFile(const char* szFilename, int iSize);
static bool TruncateFile(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
@@ -71,7 +85,9 @@ public:
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 bool RemoveDirectory(const char* szDirFilename);
static bool DeleteDirectoryWithContent(const char* szDirFilename);
static bool ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize);
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
static bool SetCurrentDirectory(const char* szDirFilename);
static long long FileSize(const char* szFilename);
@@ -83,6 +99,8 @@ public:
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
static bool SameFilename(const char* szFilename1, const char* szFilename2);
static char* GetLastErrorMessage(char* szBuffer, int iBufLen);
/*
* Split command line int arguments.
@@ -110,6 +128,12 @@ public:
static void TrimRight(char* szStr);
static char* Trim(char* szStr);
#ifdef WIN32
static bool RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen);
#endif
/*
* 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").

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
@@ -390,7 +390,7 @@ WebDownloader::EStatus WebDownloader::DownloadBody()
m_pConnection->ReadBuffer(&szBuffer, &iLen);
if (iLen == 0)
{
iLen = m_pConnection->Recv(szLineBuf, LineBufSize);
iLen = m_pConnection->TryRecv(szLineBuf, LineBufSize);
szBuffer = szLineBuf;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
@@ -59,7 +59,6 @@ static const int MAX_UNCOMPRESSED_SIZE = 500;
WebProcessor::WebProcessor()
{
m_pConnection = NULL;
m_szClientIP = NULL;
m_szRequest = NULL;
m_szUrl = NULL;
m_szOrigin = NULL;
@@ -94,7 +93,6 @@ void WebProcessor::Execute()
// reading http header
char szBuffer[1024];
bool bBody = false;
int iContentLen = 0;
while (char* p = m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
@@ -109,7 +107,7 @@ void WebProcessor::Execute()
char* szAuthInfo64 = p + 21;
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
{
error("invalid-request: auth-info too big");
error("Invalid-request: auth-info too big");
return;
}
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
@@ -124,7 +122,6 @@ void WebProcessor::Execute()
}
if (*p == '\0')
{
bBody = true;
break;
}
}
@@ -134,7 +131,7 @@ void WebProcessor::Execute()
if (m_eHttpMethod == hmPost && iContentLen <= 0)
{
error("invalid-request: content length is 0");
error("Invalid-request: content length is 0");
return;
}
@@ -188,20 +185,25 @@ void WebProcessor::Execute()
debug("Final URL=%s", m_szUrl);
if (strlen(szAuthInfo) == 0)
if (strlen(g_pOptions->GetControlPassword()) > 0)
{
SendAuthResponse();
return;
}
if (strlen(szAuthInfo) == 0)
{
SendAuthResponse();
return;
}
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if (strcmp(szAuthInfo, "nzbget") || strcmp(pw, g_pOptions->GetControlPassword()))
{
warn("request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_szClientIP);
SendAuthResponse();
return;
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(szAuthInfo, g_pOptions->GetControlUsername())) ||
strcmp(pw, g_pOptions->GetControlPassword()))
{
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr(), szAuthInfo, pw);
SendAuthResponse();
return;
}
}
if (m_eHttpMethod == hmPost)
@@ -210,16 +212,15 @@ void WebProcessor::Execute()
m_szRequest = (char*)malloc(iContentLen + 1);
m_szRequest[iContentLen] = '\0';
if (!m_pConnection->RecvAll(m_szRequest, iContentLen))
if (!m_pConnection->Recv(m_szRequest, iContentLen))
{
free(m_szRequest);
error("invalid-request: could not read data");
error("Invalid-request: could not read data");
return;
}
debug("Request=%s", m_szRequest);
}
debug("request received from %s", m_szClientIP);
debug("request received from %s", m_pConnection->GetRemoteAddr());
Dispatch();
}
@@ -236,7 +237,6 @@ void WebProcessor::Dispatch()
{
XmlRpcProcessor processor;
processor.SetRequest(m_szRequest);
processor.SetClientIP(m_szClientIP);
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
processor.SetUrl(m_szUrl);
processor.Execute();

View File

@@ -40,7 +40,6 @@ public:
private:
Connection* m_pConnection;
const char* m_szClientIP;
char* m_szRequest;
char* m_szUrl;
EHttpMethod m_eHttpMethod;
@@ -63,7 +62,6 @@ public:
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetUrl(const char* szUrl);
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -33,8 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#include <stdio.h>
#include <stdarg.h>
#ifndef WIN32
#include <unistd.h>
@@ -48,52 +47,23 @@
#include "UrlCoordinator.h"
#include "QueueEditor.h"
#include "PrePostProcessor.h"
#include "Scanner.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern UrlCoordinator* g_pUrlCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Scanner* g_pScanner;
extern void ExitProc();
extern void Reload();
//*****************************************************************
// StringBuilder
StringBuilder::StringBuilder()
{
m_szBuffer = NULL;
m_iBufferSize = 0;
m_iUsedSize = 0;
}
StringBuilder::~StringBuilder()
{
if (m_szBuffer)
{
free(m_szBuffer);
}
}
void StringBuilder::Append(const char* szStr)
{
int iPartLen = strlen(szStr);
if (m_iUsedSize + iPartLen + 1 > m_iBufferSize)
{
m_iBufferSize += iPartLen + 10240;
m_szBuffer = (char*)realloc(m_szBuffer, m_iBufferSize);
}
strcpy(m_szBuffer + m_iUsedSize, szStr);
m_iUsedSize += iPartLen;
m_szBuffer[m_iUsedSize] = '\0';
}
//*****************************************************************
// XmlRpcProcessor
XmlRpcProcessor::XmlRpcProcessor()
{
m_szClientIP = NULL;
m_szRequest = NULL;
m_eProtocol = rpUndefined;
m_eHttpMethod = hmPost;
@@ -416,6 +386,10 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paScan);
}
else if (!strcasecmp(szMethodName, "scheduleresume"))
{
command = new ScheduleResumeXmlCommand();
}
else if (!strcasecmp(szMethodName, "history"))
{
command = new HistoryXmlCommand();
@@ -440,6 +414,10 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
command = new SaveConfigXmlCommand();
}
else if (!strcasecmp(szMethodName, "configtemplates"))
{
command = new ConfigTemplatesXmlCommand();
}
else
{
command = new ErrorXmlCommand(1, "Invalid procedure");
@@ -781,6 +759,8 @@ void PauseUnpauseXmlCommand::Execute()
bool bOK = true;
g_pOptions->SetResumeTime(0);
switch (m_eEPauseAction)
{
case paDownload:
@@ -806,6 +786,28 @@ void PauseUnpauseXmlCommand::Execute()
BuildBoolResponse(bOK);
}
// bool scheduleresume(int Seconds)
void ScheduleResumeXmlCommand::Execute()
{
if (!CheckSafeMethod())
{
return;
}
int iSeconds = 0;
if (!NextParamAsInt(&iSeconds) || iSeconds < 0)
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
time_t tCurTime = time(NULL);
g_pOptions->SetResumeTime(tCurTime + iSeconds);
BuildBoolResponse(true);
}
void ShutdownXmlCommand::Execute()
{
if (!CheckSafeMethod())
@@ -861,7 +863,7 @@ void SetDownloadRateXmlCommand::Execute()
return;
}
g_pOptions->SetDownloadRate((float)iRate);
g_pOptions->SetDownloadRate(iRate * 1024);
BuildBoolResponse(true);
}
@@ -869,11 +871,11 @@ void StatusXmlCommand::Execute()
{
const char* XML_RESPONSE_STATUS_BODY =
"<struct>\n"
"<member><name>RemainingSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>DownloadedSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>DownloadedSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>DownloadedSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>DownloadedSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>DownloadedSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>DownloadRate</name><value><i4>%i</i4></value></member>\n"
"<member><name>AverageDownloadRate</name><value><i4>%i</i4></value></member>\n"
@@ -890,18 +892,20 @@ void StatusXmlCommand::Execute()
"<member><name>ServerStandBy</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>PostPaused</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>ScanPaused</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>FreeDiskSpaceLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>FreeDiskSpaceHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>FreeDiskSpaceLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>FreeDiskSpaceHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>FreeDiskSpaceMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>ServerTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>ResumeTime</name><value><i4>%i</i4></value></member>\n"
"</struct>\n";
const char* JSON_RESPONSE_STATUS_BODY =
"{\n"
"\"RemainingSizeLo\" : %i,\n"
"\"RemainingSizeHi\" : %i,\n"
"\"RemainingSizeLo\" : %u,\n"
"\"RemainingSizeHi\" : %u,\n"
"\"RemainingSizeMB\" : %i,\n"
"\"DownloadedSizeLo\" : %i,\n"
"\"DownloadedSizeHi\" : %i,\n"
"\"DownloadedSizeLo\" : %u,\n"
"\"DownloadedSizeHi\" : %u,\n"
"\"DownloadedSizeMB\" : %i,\n"
"\"DownloadRate\" : %i,\n"
"\"AverageDownloadRate\" : %i,\n"
@@ -918,17 +922,19 @@ void StatusXmlCommand::Execute()
"\"ServerStandBy\" : %s,\n"
"\"PostPaused\" : %s,\n"
"\"ScanPaused\" : %s,\n"
"\"FreeDiskSpaceLo\" : %i,\n"
"\"FreeDiskSpaceHi\" : %i,\n"
"\"FreeDiskSpaceMB\" : %i\n"
"\"FreeDiskSpaceLo\" : %u,\n"
"\"FreeDiskSpaceHi\" : %u,\n"
"\"FreeDiskSpaceMB\" : %i,\n"
"\"ServerTime\" : %i,\n"
"\"ResumeTime\" : %i\n"
"}\n";
unsigned long iRemainingSizeHi, iRemainingSizeLo;
int iDownloadRate = (int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024);
int iDownloadRate = (int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed());
long long iRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
Util::SplitInt64(iRemainingSize, &iRemainingSizeHi, &iRemainingSizeLo);
int iRemainingMBytes = (int)(iRemainingSize / 1024 / 1024);
int iDownloadLimit = (int)(g_pOptions->GetDownloadRate() * 1024);
int iDownloadLimit = (int)(g_pOptions->GetDownloadRate());
bool bDownloadPaused = g_pOptions->GetPauseDownload();
bool bDownload2Paused = g_pOptions->GetPauseDownload2();
bool bPostPaused = g_pOptions->GetPausePostProcess();
@@ -950,6 +956,8 @@ void StatusXmlCommand::Execute()
long long iFreeDiskSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
Util::SplitInt64(iFreeDiskSpace, &iFreeDiskSpaceHi, &iFreeDiskSpaceLo);
int iFreeDiskSpaceMB = (int)(iFreeDiskSpace / 1024 / 1024);
int iServerTime = time(NULL);
int iResumeTime = g_pOptions->GetResumeTime();
char szContent[2048];
snprintf(szContent, 2048, IsJson() ? JSON_RESPONSE_STATUS_BODY : XML_RESPONSE_STATUS_BODY,
@@ -958,7 +966,7 @@ void StatusXmlCommand::Execute()
iPostJobCount, iPostJobCount, iUrlCount, iUpTimeSec, iDownloadTimeSec,
BoolToStr(bDownloadPaused), BoolToStr(bDownloadPaused), BoolToStr(bDownload2Paused),
BoolToStr(bServerStandBy), BoolToStr(bPostPaused), BoolToStr(bScanPaused),
iFreeDiskSpaceLo, iFreeDiskSpaceHi, iFreeDiskSpaceMB);
iFreeDiskSpaceLo, iFreeDiskSpaceHi, iFreeDiskSpaceMB, iServerTime, iResumeTime);
szContent[2048-1] = '\0';
AppendResponse(szContent);
@@ -1074,10 +1082,10 @@ void ListFilesXmlCommand::Execute()
const char* XML_LIST_ITEM =
"<value><struct>\n"
"<member><name>ID</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>PostTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>FilenameConfirmed</name><value><boolean>%s</boolean></value></member>\n"
"<member><name>Paused</name><value><boolean>%s</boolean></value></member>\n"
@@ -1096,10 +1104,10 @@ void ListFilesXmlCommand::Execute()
const char* JSON_LIST_ITEM =
"{\n"
"\"ID\" : %i,\n"
"\"FileSizeLo\" : %i,\n"
"\"FileSizeHi\" : %i,\n"
"\"RemainingSizeLo\" : %i,\n"
"\"RemainingSizeHi\" : %i,\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
"\"RemainingSizeLo\" : %u,\n"
"\"RemainingSizeHi\" : %u,\n"
"\"PostTime\" : %i,\n"
"\"FilenameConfirmed\" : %s,\n"
"\"Paused\" : %s,\n"
@@ -1172,14 +1180,14 @@ void ListGroupsXmlCommand::Execute()
"<value><struct>\n"
"<member><name>FirstID</name><value><i4>%i</i4></value></member>\n"
"<member><name>LastID</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>RemainingSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>PausedSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>PausedSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>PausedSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>PausedSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>PausedSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileCount</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingFileCount</name><value><i4>%i</i4></value></member>\n"
@@ -1205,14 +1213,14 @@ void ListGroupsXmlCommand::Execute()
"{\n"
"\"FirstID\" : %i,\n"
"\"LastID\" : %i,\n"
"\"FileSizeLo\" : %i,\n"
"\"FileSizeHi\" : %i,\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
"\"FileSizeMB\" : %i,\n"
"\"RemainingSizeLo\" : %i,\n"
"\"RemainingSizeHi\" : %i,\n"
"\"RemainingSizeLo\" : %u,\n"
"\"RemainingSizeHi\" : %u,\n"
"\"RemainingSizeMB\" : %i,\n"
"\"PausedSizeLo\" : %i,\n"
"\"PausedSizeHi\" : %i,\n"
"\"PausedSizeLo\" : %u,\n"
"\"PausedSizeHi\" : %u,\n"
"\"PausedSizeMB\" : %i,\n"
"\"FileCount\" : %i,\n"
"\"RemainingFileCount\" : %i,\n"
@@ -1346,6 +1354,7 @@ EditCommandEntry EditCommandNameMap[] = {
{ QueueEditor::eaFilePauseExtraPars, "FilePauseExtraPars" },
{ QueueEditor::eaFileSetPriority, "FileSetPriority" },
{ QueueEditor::eaFileReorder, "FileReorder" },
{ QueueEditor::eaFileSplit, "FileSplit" },
{ QueueEditor::eaGroupMoveOffset, "GroupMoveOffset" },
{ QueueEditor::eaGroupMoveTop, "GroupMoveTop" },
{ QueueEditor::eaGroupMoveBottom, "GroupMoveBottom" },
@@ -1366,6 +1375,7 @@ EditCommandEntry EditCommandNameMap[] = {
{ PrePostProcessor::eaHistoryDelete, "HistoryDelete" },
{ PrePostProcessor::eaHistoryReturn, "HistoryReturn" },
{ PrePostProcessor::eaHistoryProcess, "HistoryProcess" },
{ PrePostProcessor::eaHistorySetParameter, "HistorySetParameter" },
{ 0, NULL }
};
@@ -1432,7 +1442,7 @@ void EditQueueXmlCommand::Execute()
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset, szEditText);
}
BuildBoolResponse(bOK);
@@ -1494,26 +1504,10 @@ void DownloadXmlCommand::Execute()
szFileContent[iLen] = '\0';
//debug("FileContent=%s", szFileContent);
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(szFileName, szCategory, szFileContent, iLen + 1);
bool bOK = g_pScanner->AddExternalFile(szFileName, szCategory, iPriority, NULL, bAddTop,
false, NULL, szFileContent, iLen, true);
if (pNZBFile)
{
info("Request: Queue collection %s", szFileName);
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
delete pNZBFile;
BuildBoolResponse(true);
}
else
{
BuildBoolResponse(false);
}
BuildBoolResponse(bOK);
}
void PostQueueXmlCommand::Execute()
@@ -1531,7 +1525,7 @@ void PostQueueXmlCommand::Execute()
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use "NZBName" instead
"<member><name>NZBFilename</name><value><string>%s</string></value></member>\n"
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>ParFilename</name><value><string>%s</string></value></member>\n"
"<member><name>ParFilename</name><value><string>%s</string></value></member>\n" // deprecated, always empty
"<member><name>InfoName</name><value><string>%s</string></value></member>\n"
"<member><name>Stage</name><value><string>%s</string></value></member>\n"
"<member><name>ProgressLabel</name><value><string>%s</string></value></member>\n"
@@ -1553,7 +1547,7 @@ void PostQueueXmlCommand::Execute()
"\"NZBNicename\" : \"%s\",\n" // deprecated, use "NZBName" instead
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
"\"ParFilename\" : \"%s\",\n"
"\"ParFilename\" : \"%s\",\n" // deprecated, always empty
"\"InfoName\" : \"%s\",\n"
"\"Stage\" : \"%s\",\n"
"\"ProgressLabel\" : \"%s\",\n"
@@ -1596,18 +1590,17 @@ void PostQueueXmlCommand::Execute()
{
PostInfo* pPostInfo = *it;
const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "EXECUTING_SCRIPT", "FINISHED" };
const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "RENAMING", "UNPACKING", "MOVING", "EXECUTING_SCRIPT", "FINISHED" };
char* xmlNZBNicename = EncodeStr(pPostInfo->GetNZBInfo()->GetName());
char* xmlNZBFilename = EncodeStr(pPostInfo->GetNZBInfo()->GetFilename());
char* xmlDestDir = EncodeStr(pPostInfo->GetNZBInfo()->GetDestDir());
char* xmlParFilename = EncodeStr(pPostInfo->GetParFilename());
char* xmlInfoName = EncodeStr(pPostInfo->GetInfoName());
char* xmlProgressLabel = EncodeStr(pPostInfo->GetProgressLabel());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_POSTQUEUE_ITEM_START : XML_POSTQUEUE_ITEM_START,
pPostInfo->GetID(), pPostInfo->GetNZBInfo()->GetID(), xmlNZBNicename,
xmlNZBNicename, xmlNZBFilename, xmlDestDir, xmlParFilename,
xmlNZBNicename, xmlNZBFilename, xmlDestDir, "",
xmlInfoName, szPostStageName[pPostInfo->GetStage()], xmlProgressLabel,
pPostInfo->GetFileProgress(), pPostInfo->GetStageProgress(),
pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0,
@@ -1617,7 +1610,6 @@ void PostQueueXmlCommand::Execute()
free(xmlNZBNicename);
free(xmlNZBFilename);
free(xmlDestDir);
free(xmlParFilename);
free(xmlInfoName);
free(xmlProgressLabel);
@@ -1632,12 +1624,11 @@ void PostQueueXmlCommand::Execute()
PostInfo::Messages* pMessages = pPostInfo->LockMessages();
if (!pMessages->empty())
{
int iStart = pMessages->size();
if (iNrEntries > (int)pMessages->size())
{
iNrEntries = pMessages->size();
}
iStart = pMessages->size() - iNrEntries;
int iStart = pMessages->size() - iNrEntries;
int index = 0;
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
@@ -1734,7 +1725,7 @@ void ScanXmlCommand::Execute()
// optional parameter "SyncMode"
NextParamAsBool(&bSyncMode);
g_pPrePostProcessor->ScanNZBDir(bSyncMode);
g_pScanner->ScanNZBDir(bSyncMode);
BuildBoolResponse(true);
}
@@ -1745,7 +1736,7 @@ void HistoryXmlCommand::Execute()
const char* XML_HISTORY_ITEM_START =
"<value><struct>\n"
"<member><name>ID</name><value><i4>%i</i4></value></member>\n"
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n" // deprecated, use ID instead
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n"
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use Name instead
@@ -1753,9 +1744,11 @@ void HistoryXmlCommand::Execute()
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>Category</name><value><string>%s</string></value></member>\n"
"<member><name>ParStatus</name><value><string>%s</string></value></member>\n"
"<member><name>UnpackStatus</name><value><string>%s</string></value></member>\n"
"<member><name>MoveStatus</name><value><string>%s</string></value></member>\n"
"<member><name>ScriptStatus</name><value><string>%s</string></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileSizeLo</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeHi</name><value><i4>%u</i4></value></member>\n"
"<member><name>FileSizeMB</name><value><i4>%i</i4></value></member>\n"
"<member><name>FileCount</name><value><i4>%i</i4></value></member>\n"
"<member><name>RemainingFileCount</name><value><i4>%i</i4></value></member>\n"
@@ -1764,28 +1757,34 @@ void HistoryXmlCommand::Execute()
"<member><name>UrlStatus</name><value><string>%s</string></value></member>\n"
"<member><name>Parameters</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_LOG_START =
const char* XML_HISTORY_ITEM_SCRIPT_START =
"</data></array></value></member>\n"
"<member><name>ScriptStatuses</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_LOG_START =
"</data></array></value></member>\n"
"<member><name>Log</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_END =
const char* XML_HISTORY_ITEM_END =
"</data></array></value></member>\n"
"</struct></value>\n";
const char* JSON_HISTORY_ITEM_START =
"{\n"
"\"ID\" : %i,\n"
"\"NZBID\" : %i,\n" // deprecated, use ID instead
"\"NZBID\" : %i,\n"
"\"Kind\" : \"%s\",\n"
"\"Name\" : \"%s\",\n" // deprecated, use Name instead
"\"Name\" : \"%s\",\n"
"\"NZBNicename\" : \"%s\",\n" // deprecated, use Name instead
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
"\"Category\" : \"%s\",\n"
"\"ParStatus\" : \"%s\",\n"
"\"UnpackStatus\" : \"%s\",\n"
"\"MoveStatus\" : \"%s\",\n"
"\"ScriptStatus\" : \"%s\",\n"
"\"FileSizeLo\" : %i,\n"
"\"FileSizeHi\" : %i,\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
"\"FileSizeMB\" : %i,\n"
"\"FileCount\" : %i,\n"
"\"RemainingFileCount\" : %i,\n"
@@ -1794,7 +1793,11 @@ void HistoryXmlCommand::Execute()
"\"UrlStatus\" : \"%s\",\n"
"\"Parameters\" : [\n";
const char* JSON_HISTORY_ITEM_LOG_START =
const char* JSON_HISTORY_ITEM_SCRIPT_START =
"],\n"
"\"ScriptStatuses\" : [\n";
const char* JSON_HISTORY_ITEM_LOG_START =
"],\n"
"\"Log\" : [\n";
@@ -1814,7 +1817,19 @@ void HistoryXmlCommand::Execute()
"\"Value\" : \"%s\"\n"
"}";
const char* XML_LOG_ITEM =
const char* XML_SCRIPT_ITEM =
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>Status</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_SCRIPT_ITEM =
"{\n"
"\"Name\" : \"%s\",\n"
"\"Status\" : \"%s\"\n"
"}";
const char* XML_LOG_ITEM =
"<value><struct>\n"
"<member><name>ID</name><value><i4>%i</i4></value></member>\n"
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
@@ -1830,8 +1845,10 @@ void HistoryXmlCommand::Execute()
"\"Text\" : \"%s\"\n"
"}";
const char* szParStatusName[] = { "NONE", "FAILURE", "REPAIR_POSSIBLE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "UNKNOWN", "FAILURE", "SUCCESS" };
const char* szParStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "REPAIR_POSSIBLE", "MANUAL" };
const char* szUnpackStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS" };
const char* szMoveStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szUrlStatusName[] = { "UNKNOWN", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN" };
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
@@ -1865,10 +1882,12 @@ void HistoryXmlCommand::Execute()
xmlCategory = EncodeStr(pNZBInfo->GetCategory());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
pHistoryInfo->GetID(), pHistoryInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename,
xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()],
szScriptStatusName[pNZBInfo->GetScriptStatus()], iFileSizeLo, iFileSizeHi, iFileSizeMB,
pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", "");
pHistoryInfo->GetID(), pNZBInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename,
xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()],
szUnpackStatusName[pNZBInfo->GetUnpackStatus()], szMoveStatusName[pNZBInfo->GetMoveStatus()],
szScriptStatusName[pNZBInfo->GetScriptStatuses()->CalcTotalStatus()],
iFileSizeLo, iFileSizeHi, iFileSizeMB, pNZBInfo->GetFileCount(),
pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", "");
free(xmlDestDir);
}
@@ -1881,8 +1900,8 @@ void HistoryXmlCommand::Execute()
char* xmlURL = EncodeStr(pUrlInfo->GetURL());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
pHistoryInfo->GetID(), pHistoryInfo->GetID(), "URL", xmlNicename, xmlNicename, xmlNZBFilename,
"", xmlCategory, "", "", 0, 0, 0, 0, 0, pHistoryInfo->GetTime(), xmlURL,
pHistoryInfo->GetID(), 0, "URL", xmlNicename, xmlNicename, xmlNZBFilename,
"", xmlCategory, "", "", "", "", 0, 0, 0, 0, 0, pHistoryInfo->GetTime(), xmlURL,
szUrlStatusName[pUrlInfo->GetStatus()]);
free(xmlURL);
@@ -1925,6 +1944,33 @@ void HistoryXmlCommand::Execute()
}
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_SCRIPT_START : XML_HISTORY_ITEM_SCRIPT_START);
if (pNZBInfo)
{
// Script statuses
int iScriptIndex = 0;
for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++)
{
ScriptStatus* pScriptStatus = *it;
char* xmlName = EncodeStr(pScriptStatus->GetName());
char* xmlStatus = EncodeStr(szScriptStatusName[pScriptStatus->GetStatus()]);
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_SCRIPT_ITEM : XML_SCRIPT_ITEM, xmlName, xmlStatus);
szItemBuf[szItemBufSize-1] = '\0';
free(xmlName);
free(xmlStatus);
if (IsJson() && iScriptIndex++ > 0)
{
AppendResponse(",\n");
}
AppendResponse(szItemBuf);
}
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_LOG_START : XML_HISTORY_ITEM_LOG_START);
if (pNZBInfo)
@@ -2019,7 +2065,7 @@ void DownloadUrlXmlCommand::Execute()
char szNicename[1024];
pUrlInfo->GetName(szNicename, sizeof(szNicename));
info("Request: Queue %s", szNicename);
info("Queue %s", szNicename);
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, bAddTop);
@@ -2147,7 +2193,7 @@ void ConfigXmlCommand::Execute()
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}
// struct[] loadconfig(string domain)
// struct[] loadconfig()
void LoadConfigXmlCommand::Execute()
{
const char* XML_CONFIG_ITEM =
@@ -2162,36 +2208,10 @@ void LoadConfigXmlCommand::Execute()
"\"Value\" : \"%s\"\n"
"}";
char* szDomain;
if (!NextParamAsStr(&szDomain))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
const char* szConfigFile = NULL;
Options::EDomain eDomain;
if (!strcasecmp(szDomain, "SERVER"))
{
eDomain = Options::dmServer;
szConfigFile = g_pOptions->GetConfigFilename();
}
else if (!strcasecmp(szDomain, "POST"))
{
eDomain = Options::dmPostProcess;
szConfigFile = g_pOptions->GetPostConfigFilename();
}
else
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
Options::OptEntries* pOptEntries = new Options::OptEntries();
if (!g_pOptions->LoadConfig(eDomain, pOptEntries))
if (!g_pOptions->LoadConfig(pOptEntries))
{
BuildErrorResponse(3, "Could not read configuration file %s", szConfigFile);
BuildErrorResponse(3, "Could not read configuration file");
delete pOptEntries;
return;
}
@@ -2229,40 +2249,9 @@ void LoadConfigXmlCommand::Execute()
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}
// bool saveconfig(string domain, struct[] data)
// bool saveconfig(struct[] data)
void SaveConfigXmlCommand::Execute()
{
char* szDomain;
if (!NextParamAsStr(&szDomain))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
const char* szConfigFile = NULL;
Options::EDomain eDomain;
if (!strcasecmp(szDomain, "SERVER"))
{
eDomain = Options::dmServer;
szConfigFile = g_pOptions->GetConfigFilename();
}
else if (!strcasecmp(szDomain, "POST"))
{
eDomain = Options::dmPostProcess;
szConfigFile = g_pOptions->GetPostConfigFilename();
if (!szConfigFile)
{
BuildErrorResponse(3, "Post-processing script configuration file is not defined");
return;
}
}
else
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
Options::OptEntries* pOptEntries = new Options::OptEntries();
char* szName;
@@ -2276,9 +2265,71 @@ void SaveConfigXmlCommand::Execute()
}
// save to config file
bool bOK = g_pOptions->SaveConfig(eDomain, pOptEntries);
bool bOK = g_pOptions->SaveConfig(pOptEntries);
delete pOptEntries;
BuildBoolResponse(bOK);
}
// struct[] configtemplates()
void ConfigTemplatesXmlCommand::Execute()
{
const char* XML_CONFIG_ITEM =
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>DisplayName</name><value><string>%s</string></value></member>\n"
"<member><name>Template</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_CONFIG_ITEM =
"{\n"
"\"Name\" : \"%s\",\n"
"\"DisplayName\" : \"%s\",\n"
"\"Template\" : \"%s\"\n"
"}";
Options::ConfigTemplates* pConfigTemplates = new Options::ConfigTemplates();
if (!g_pOptions->LoadConfigTemplates(pConfigTemplates))
{
BuildErrorResponse(3, "Could not read configuration templates");
delete pConfigTemplates;
return;
}
AppendResponse(IsJson() ? "[\n" : "<array><data>\n");
int index = 0;
for (Options::ConfigTemplates::iterator it = pConfigTemplates->begin(); it != pConfigTemplates->end(); it++)
{
Options::ConfigTemplate* pConfigTemplate = *it;
char* xmlName = EncodeStr(pConfigTemplate->GetName());
char* xmlDisplayName = EncodeStr(pConfigTemplate->GetDisplayName());
char* xmlTemplate = EncodeStr(pConfigTemplate->GetTemplate());
int szItemBufSize = strlen(xmlName) + strlen(xmlTemplate) + 1024;
char* szItemBuf = (char*)malloc(szItemBufSize);
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlDisplayName, xmlTemplate);
szItemBuf[szItemBufSize-1] = '\0';
free(xmlName);
free(xmlDisplayName);
free(xmlTemplate);
if (IsJson() && index++ > 0)
{
AppendResponse(",\n");
}
AppendResponse(szItemBuf);
free(szItemBuf);
}
delete pConfigTemplates;
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}

View File

@@ -27,19 +27,7 @@
#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; }
};
#include "Util.h"
class XmlCommand;
@@ -61,7 +49,6 @@ public:
};
private:
const char* m_szClientIP;
char* m_szRequest;
const char* m_szContentType;
ERpcProtocol m_eProtocol;
@@ -80,7 +67,6 @@ public:
void Execute();
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUrl(const char* szUrl);
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
void SetRequest(char* szRequest) { m_szRequest = szRequest; }
const char* GetResponse() { return m_cResponse.GetBuffer(); }
const char* GetContentType() { return m_szContentType; }
@@ -154,6 +140,12 @@ public:
virtual void Execute();
};
class ScheduleResumeXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ShutdownXmlCommand: public XmlCommand
{
public:
@@ -278,7 +270,12 @@ class SaveConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
void Save(const char *szFilename);
};
class ConfigTemplatesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
#endif

View File

@@ -67,6 +67,9 @@
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
#undef HAVE_OPENSSL
/* Define to 1 if libpar2 has recent bugfixes-patch (version 2) */
#undef HAVE_PAR2_BUGFIXES_V2
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
@@ -76,9 +79,6 @@
/* Define to 1 if spinlocks are supported */
#undef HAVE_SPINLOCK
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -135,3 +135,9 @@
/* Version number of package */
#undef VERSION
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES

725
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 9.0.
# Generated by GNU Autoconf 2.61 for nzbget 11.0.
#
# Report bugs to <hugbug@users.sourceforge.net>.
#
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='nzbget'
PACKAGE_TARNAME='nzbget'
PACKAGE_VERSION='9.0'
PACKAGE_STRING='nzbget 9.0'
PACKAGE_VERSION='11.0'
PACKAGE_STRING='nzbget 11.0'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="nzbget.cpp"
@@ -711,9 +711,8 @@ libxml2_CFLAGS
libxml2_LIBS
libsigc_CFLAGS
libsigc_LIBS
CFLAGS
AR
ADDSRCS
openssl_CFLAGS
openssl_LIBS
LIBOBJS
LTLIBOBJS'
ac_subst_files=''
@@ -731,7 +730,9 @@ PKG_CONFIG
libxml2_CFLAGS
libxml2_LIBS
libsigc_CFLAGS
libsigc_LIBS'
libsigc_LIBS
openssl_CFLAGS
openssl_LIBS'
# Initialize some variables set by options.
@@ -1234,7 +1235,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures nzbget 9.0 to adapt to many kinds of systems.
\`configure' configures nzbget 11.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1305,7 +1306,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 9.0:";;
short | recursive ) echo "Configuration of nzbget 11.0:";;
esac
cat <<\_ACEOF
@@ -1314,17 +1315,21 @@ Optional Features:
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors
--disable-largefile omit support for large files
--disable-curses do not use curses (removes dependency from
curses-library)
--disable-parcheck do not include par-check/-repair-support (removes
dependency from libpar2- and libsigc-libraries)
--disable-libpar2-bugfixes-check
do not check if libpar2 has recent bugfixes-patch
applied
--disable-tls do not use TLS/SSL (removes dependency from
TLS/SSL-libraries)
--disable-gzip disable gzip-compression/decompression (removes
dependency from zlib-library)
--disable-sigchld-handler
do not use sigchld-handler (the disabling is
recommended for BSD)
do not use sigchld-handler (the disabling may be
neccessary on 32-Bit BSD)
--enable-debug enable debugging
Optional Packages:
@@ -1379,6 +1384,10 @@ Some influential environment variables:
C compiler flags for libsigc, overriding pkg-config
libsigc_LIBS
linker flags for libsigc, overriding pkg-config
openssl_CFLAGS
C compiler flags for openssl, overriding pkg-config
openssl_LIBS
linker flags for openssl, overriding pkg-config
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1444,7 +1453,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 9.0
nzbget configure 11.0
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1458,7 +1467,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by nzbget $as_me 9.0, which was
It was created by nzbget $as_me 11.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2254,7 +2263,7 @@ fi
# Define the identity of the package.
PACKAGE=nzbget
VERSION=9.0
VERSION=11.0
cat >>confdefs.h <<_ACEOF
@@ -4713,64 +4722,55 @@ fi
{ echo "$as_me:$LINENO: checking for stat64" >&5
echo $ECHO_N "checking for stat64... $ECHO_C" >&6; }
if test "${ac_cv_func_stat64+set}" = set; then
# Check whether --enable-largefile was given.
if test "${enable_largefile+set}" = set; then
enableval=$enable_largefile;
fi
if test "$enable_largefile" != no; then
{ echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_largefile_CC+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
ac_cv_sys_largefile_CC=no
if test "$GCC" != yes; then
ac_save_CC=$CC
while :; do
# IRIX 6.2 and later do not support large files by default,
# so use the C compiler's -n32 option if that helps.
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Define stat64 to an innocuous variant, in case <limits.h> declares stat64.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define stat64 innocuous_stat64
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char stat64 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef stat64
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char stat64 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_stat64 || defined __stub___stat64
choke me
#endif
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
return stat64 ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
@@ -4779,27 +4779,297 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_func_stat64=yes
} && test -s conftest.$ac_objext; then
break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_func_stat64=no
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
fi
{ echo "$as_me:$LINENO: result: $ac_cv_func_stat64" >&5
echo "${ECHO_T}$ac_cv_func_stat64" >&6; }
if test $ac_cv_func_stat64 = yes; then
rm -f core conftest.err conftest.$ac_objext
CC="$CC -n32"
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_largefile_CC=' -n32'; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
cat >>confdefs.h <<\_ACEOF
#define HAVE_STAT64 1
fi
rm -f core conftest.err conftest.$ac_objext
break
done
CC=$ac_save_CC
rm -f conftest.$ac_ext
fi
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6; }
if test "$ac_cv_sys_largefile_CC" != no; then
CC=$CC$ac_cv_sys_largefile_CC
fi
{ echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_file_offset_bits+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
while :; do
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_file_offset_bits=no; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_file_offset_bits=64; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_file_offset_bits=unknown
break
done
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6; }
case $ac_cv_sys_file_offset_bits in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
_ACEOF
;;
esac
rm -f conftest*
if test $ac_cv_sys_file_offset_bits = unknown; then
{ echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_large_files+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
while :; do
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_large_files=no; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define _LARGE_FILES 1
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_large_files=1; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_large_files=unknown
break
done
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
echo "${ECHO_T}$ac_cv_sys_large_files" >&6; }
case $ac_cv_sys_large_files in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _LARGE_FILES $ac_cv_sys_large_files
_ACEOF
;;
esac
rm -f conftest*
fi
fi
@@ -5677,7 +5947,6 @@ _ACEOF
# Check whether --with-libxml2_includes was given.
if test "${with_libxml2_includes+set}" = set; then
withval=$with_libxml2_includes; CPPFLAGS="${CPPFLAGS} -I${withval}"
CFLAGS="${CFLAGS} -I${withval}"
INCVAL="yes"
else
INCVAL="no"
@@ -5922,9 +6191,8 @@ else
libxml2_LIBS=$pkg_cv_libxml2_LIBS
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
LDFLAGS="${LDFLAGS} $libxml2_LIBS"
LIBS="${LIBS} $libxml2_LIBS"
CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"
CFLAGS="${CFLAGS} $libxml2_CFLAGS"
fi
fi
if test "${ac_cv_header_libxml_tree_h+set}" = set; then
@@ -6176,7 +6444,6 @@ if test "${with_libcurses_includes+set}" = set; then
fi
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
# Check whether --with-libcurses_libraries was given.
if test "${with_libcurses_libraries+set}" = set; then
@@ -6859,7 +7126,7 @@ else
libsigc_LIBS=$pkg_cv_libsigc_LIBS
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
LDFLAGS="${LDFLAGS} $libsigc_LIBS"
LIBS="${LIBS} $libsigc_LIBS"
CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"
fi
fi
@@ -7357,6 +7624,77 @@ echo "${ECHO_T}no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ echo "$as_me:$LINENO: checking whether libpar2 has recent bugfixes-patch (version 2)" >&5
echo $ECHO_N "checking whether libpar2 has recent bugfixes-patch (version 2)... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } };
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
PAR2PATCHV2=yes
cat >>confdefs.h <<\_ACEOF
#define HAVE_PAR2_BUGFIXES_V2 1
_ACEOF
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ echo "$as_me:$LINENO: result: no" >&5
echo "${ECHO_T}no" >&6; }
PAR2PATCHV2=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
if test "$PAR2PATCHV2" = "no" ; then
# Check whether --enable-libpar2-bugfixes-check was given.
if test "${enable_libpar2_bugfixes_check+set}" = set; then
enableval=$enable_libpar2_bugfixes_check; PAR2PATCHCHECK=$enableval
else
PAR2PATCHCHECK=yes
fi
if test "$PAR2PATCHCHECK" = "yes"; then
{ { echo "$as_me:$LINENO: error: Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!" >&5
echo "$as_me: error: Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!" >&2;}
{ (exit 1); exit 1; }; }
fi
fi
else
cat >>confdefs.h <<\_ACEOF
@@ -7400,7 +7738,6 @@ if test "${with_libgnutls_includes+set}" = set; then
fi
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
# Check whether --with-libgnutls_libraries was given.
if test "${with_libgnutls_libraries+set}" = set; then
@@ -7739,23 +8076,138 @@ _ACEOF
fi
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
# Check whether --with-openssl_includes was given.
if test "${with_openssl_includes+set}" = set; then
withval=$with_openssl_includes; INCVAL="$withval"
withval=$with_openssl_includes; CPPFLAGS="${CPPFLAGS} -I${withval}"
INCVAL="yes"
else
INCVAL="no"
fi
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
# Check whether --with-openssl_libraries was given.
if test "${with_openssl_libraries+set}" = set; then
withval=$with_openssl_libraries; LIBVAL="$withval"
withval=$with_openssl_libraries; LDFLAGS="${LDFLAGS} -L${withval}"
LIBVAL="yes"
else
LIBVAL="no"
fi
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
pkg_failed=no
{ echo "$as_me:$LINENO: checking for openssl" >&5
echo $ECHO_N "checking for openssl... $ECHO_C" >&6; }
if test -n "$PKG_CONFIG"; then
if test -n "$openssl_CFLAGS"; then
pkg_cv_openssl_CFLAGS="$openssl_CFLAGS"
else
if test -n "$PKG_CONFIG" && \
{ (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"openssl\"") >&5
($PKG_CONFIG --exists --print-errors "openssl") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_openssl_CFLAGS=`$PKG_CONFIG --cflags "openssl" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test -n "$PKG_CONFIG"; then
if test -n "$openssl_LIBS"; then
pkg_cv_openssl_LIBS="$openssl_LIBS"
else
if test -n "$PKG_CONFIG" && \
{ (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"openssl\"") >&5
($PKG_CONFIG --exists --print-errors "openssl") 2>&5
ac_status=$?
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); }; then
pkg_cv_openssl_LIBS=`$PKG_CONFIG --libs "openssl" 2>/dev/null`
else
pkg_failed=yes
fi
fi
else
pkg_failed=untried
fi
if test $pkg_failed = yes; then
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
_pkg_short_errors_supported=yes
else
_pkg_short_errors_supported=no
fi
if test $_pkg_short_errors_supported = yes; then
openssl_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "openssl"`
else
openssl_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "openssl"`
fi
# Put the nasty error message in config.log where it belongs
echo "$openssl_PKG_ERRORS" >&5
{ { echo "$as_me:$LINENO: error: Package requirements (openssl) were not met:
$openssl_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables openssl_CFLAGS
and openssl_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
" >&5
echo "$as_me: error: Package requirements (openssl) were not met:
$openssl_PKG_ERRORS
Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.
Alternatively, you may set the environment variables openssl_CFLAGS
and openssl_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
" >&2;}
{ (exit 1); exit 1; }; }
elif test $pkg_failed = untried; then
{ { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
Alternatively, you may set the environment variables openssl_CFLAGS
and openssl_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See \`config.log' for more details." >&5
echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
Alternatively, you may set the environment variables openssl_CFLAGS
and openssl_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
See \`config.log' for more details." >&2;}
{ (exit 1); exit 1; }; }
else
openssl_CFLAGS=$pkg_cv_openssl_CFLAGS
openssl_LIBS=$pkg_cv_openssl_LIBS
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
LIBS="${LIBS} $openssl_LIBS"
CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"
fi
fi
if test "${ac_cv_header_openssl_ssl_h+set}" = set; then
{ echo "$as_me:$LINENO: checking for openssl/ssl.h" >&5
@@ -7901,7 +8353,87 @@ echo "$as_me: error: Couldn't find OpenSSL headers (ssl.h)" >&2;}
{ (exit 1); exit 1; }; }
fi
if test "$FOUND" = "yes"; then
{ echo "$as_me:$LINENO: checking for library containing SSL_library_init" >&5
{ echo "$as_me:$LINENO: checking for library containing CRYPTO_set_locking_callback" >&5
echo $ECHO_N "checking for library containing CRYPTO_set_locking_callback... $ECHO_C" >&6; }
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_func_search_save_LIBS=$LIBS
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char CRYPTO_set_locking_callback ();
int
main ()
{
return CRYPTO_set_locking_callback ();
;
return 0;
}
_ACEOF
for ac_lib in '' crypto; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_search_CRYPTO_set_locking_callback=$ac_res
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
break
fi
done
if test "${ac_cv_search_CRYPTO_set_locking_callback+set}" = set; then
:
else
ac_cv_search_CRYPTO_set_locking_callback=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_CRYPTO_set_locking_callback" >&5
echo "${ECHO_T}$ac_cv_search_CRYPTO_set_locking_callback" >&6; }
ac_res=$ac_cv_search_CRYPTO_set_locking_callback
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
{ echo "$as_me:$LINENO: checking for library containing SSL_library_init" >&5
echo $ECHO_N "checking for library containing SSL_library_init... $ECHO_C" >&6; }
if test "${ac_cv_search_SSL_library_init+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
@@ -7982,6 +8514,10 @@ ac_res=$ac_cv_search_SSL_library_init
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
FOUND=yes
else
FOUND=no
fi
else
FOUND=no
fi
@@ -8043,7 +8579,6 @@ if test "${with_zlib_includes+set}" = set; then
fi
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
# Check whether --with-zlib_libraries was given.
if test "${with_zlib_libraries+set}" = set; then
@@ -8293,17 +8828,9 @@ echo $ECHO_N "checking whether to use an empty SIGCHLD handler... $ECHO_C" >&6;
if test "${enable_sigchld_handler+set}" = set; then
enableval=$enable_sigchld_handler; SIGCHLDHANDLER=$enableval
else
SIGCHLDHANDLER=auto
SIGCHLDHANDLER=yes
fi
if test "$SIGCHLDHANDLER" = "auto"; then
SIGCHLDHANDLER=yes
case "$target" in
*bsd*)
SIGCHLDHANDLER=no
;;
esac
fi
{ echo "$as_me:$LINENO: result: $SIGCHLDHANDLER" >&5
echo "${ECHO_T}$SIGCHLDHANDLER" >&6; }
if test "$SIGCHLDHANDLER" = "yes"; then
@@ -8598,15 +9125,6 @@ rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
fi
ac_config_files="$ac_config_files Makefile"
cat >confcache <<\_ACEOF
@@ -9019,7 +9537,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by nzbget $as_me 9.0, which was
This file was extended by nzbget $as_me 11.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -9072,7 +9590,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 9.0
nzbget config.status 11.0
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -9346,7 +9864,7 @@ libxml2_CFLAGS!$libxml2_CFLAGS$ac_delim
libxml2_LIBS!$libxml2_LIBS$ac_delim
libsigc_CFLAGS!$libsigc_CFLAGS$ac_delim
libsigc_LIBS!$libsigc_LIBS$ac_delim
CFLAGS!$CFLAGS$ac_delim
openssl_CFLAGS!$openssl_CFLAGS$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then
@@ -9388,13 +9906,12 @@ _ACEOF
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
cat >conf$$subs.sed <<_ACEOF
AR!$AR$ac_delim
ADDSRCS!$ADDSRCS$ac_delim
openssl_LIBS!$openssl_LIBS$ac_delim
LIBOBJS!$LIBOBJS$ac_delim
LTLIBOBJS!$LTLIBOBJS$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 4; then
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 3; then
break
elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5

View File

@@ -2,9 +2,9 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 9.0, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 11.0, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 9.0)
AM_INIT_AUTOMAKE(nzbget, 11.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -56,10 +56,9 @@ AC_CHECK_FUNC(getopt_long,
dnl
dnl stat64
dnl use 64-Bits for file sizes
dnl
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],)
AC_SYS_LARGEFILE
dnl
@@ -187,7 +186,6 @@ dnl
AC_ARG_WITH(libxml2_includes,
[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,
@@ -197,9 +195,8 @@ AC_ARG_WITH(libxml2_libraries,
[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"])
[LIBS="${LIBS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"])
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files not found"))
@@ -223,7 +220,6 @@ if test "$USECURSES" = "yes"; then
[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,
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
[LIBVAL="$withval"])
@@ -283,7 +279,7 @@ if test "$ENABLEPARCHECK" = "yes"; then
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
[LDFLAGS="${LDFLAGS} $libsigc_LIBS"]
[LIBS="${LIBS} $libsigc_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
fi
@@ -338,6 +334,32 @@ if test "$ENABLEPARCHECK" = "yes"; then
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
AC_MSG_RESULT([[no]]))
dnl
dnl check if libpar2 has recent bugfixes-patch
dnl
AC_MSG_CHECKING(whether libpar2 has recent bugfixes-patch (version 2))
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } }; ],
[],
AC_MSG_RESULT([[yes]])
PAR2PATCHV2=yes
AC_DEFINE([HAVE_PAR2_BUGFIXES_V2], 1, [Define to 1 if libpar2 has recent bugfixes-patch (version 2)]),
AC_MSG_RESULT([[no]])
PAR2PATCHV2=no)
if test "$PAR2PATCHV2" = "no" ; then
AC_ARG_ENABLE(libpar2-bugfixes-check,
[AS_HELP_STRING([--disable-libpar2-bugfixes-check], [do not check if libpar2 has recent bugfixes-patch applied])],
[ PAR2PATCHCHECK=$enableval ],
[ PAR2PATCHCHECK=yes] )
if test "$PAR2PATCHCHECK" = "yes"; then
AC_ERROR([Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!])
fi
fi
else
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
fi
@@ -367,12 +389,11 @@ if test "$USETLS" = "yes"; then
[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,
@@ -397,18 +418,22 @@ if test "$USETLS" = "yes"; then
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}"
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(openssl, openssl,
[LIBS="${LIBS} $openssl_LIBS"]
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
fi
AC_CHECK_HEADER(openssl/ssl.h,
FOUND=yes
TLSHEADERS=yes,
@@ -417,8 +442,10 @@ if test "$USETLS" = "yes"; 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,
AC_SEARCH_LIBS([CRYPTO_set_locking_callback], [crypto],
AC_SEARCH_LIBS([SSL_library_init], [ssl],
FOUND=yes,
FOUND=no),
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL library])
@@ -458,7 +485,6 @@ if test "$USEZLIB" = "yes"; then
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(zlib_libraries,
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
[LIBVAL="$withval"])
@@ -476,22 +502,14 @@ fi
dnl
dnl Some Linux systems require an empty signal handler for SIGCHLD
dnl in order for exit codes to be correctly delivered to parent process.
dnl Some BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to check the target and disable the handler on BSD but keep it enabled on other systems.
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to install the handler.
dnl
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
AC_ARG_ENABLE(sigchld-handler,
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling is recommended for BSD)])],
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
[SIGCHLDHANDLER=$enableval],
[SIGCHLDHANDLER=auto])
if test "$SIGCHLDHANDLER" = "auto"; then
SIGCHLDHANDLER=yes
case "$target" in
*bsd*)
SIGCHLDHANDLER=no
;;
esac
fi
[SIGCHLDHANDLER=yes])
AC_MSG_RESULT($SIGCHLDHANDLER)
if test "$SIGCHLDHANDLER" = "yes"; then
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
@@ -594,15 +612,5 @@ dnl
fi
dnl Substitute flags.
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(TAR)
AC_SUBST(AR)
AC_SUBST(ADDSRCS)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

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

View File

@@ -1,7 +1,9 @@
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 @@
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2012-12-03 10:48:13.000000000 +0100
@@ -50,10 +50,12 @@
outputbuffer = 0;
noiselevel = CommandLine::nlNormal;
headers = new ParHeaders;
alreadyloaded = false;
@@ -10,7 +12,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
}
Par2Repairer::~Par2Repairer(void)
@@ -406,6 +408,10 @@
{
delete [] (u8*)inputbuffer;
@@ -404,10 +406,14 @@
{
cout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
progress = offset;
sig_progress.emit(newfraction);
@@ -21,7 +27,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
}
}
@@ -584,6 +590,11 @@
// Attempt to read the next packet header
PACKET_HEADER header;
@@ -582,10 +588,15 @@
if (noiselevel > CommandLine::nlQuiet)
cout << "No new packets found" << endl;
delete diskfile;
}
@@ -33,7 +43,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
return true;
}
@@ -833,9 +844,17 @@
// Finish loading a recovery packet
bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header)
@@ -831,26 +842,42 @@
// Load packets from each file that was found
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
@@ -51,7 +65,10 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
}
{
@@ -846,9 +865,17 @@
string wildcard = name.empty() ? "*.PAR2" : name + ".*.PAR2";
list<string> *files = DiskFile::FindFiles(path, wildcard);
// Load packets from each file that was found
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
@@ -69,7 +86,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
}
return true;
@@ -866,9 +893,18 @@
}
@@ -864,13 +891,22 @@
// If the filename contains ".par2" anywhere
if (string::npos != filename.find(".par2") ||
string::npos != filename.find(".PAR2"))
{
LoadPacketsFromFile(filename);
@@ -88,7 +109,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
return true;
}
@@ -1210,6 +1246,11 @@
// Check that the packets are consistent and discard any that are not
bool Par2Repairer::CheckPacketConsistency(void)
@@ -1208,10 +1244,15 @@
// Start verifying the files
sf = sortedfiles.begin();
while (sf != sortedfiles.end())
{
@@ -100,7 +125,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
// Do we have a source file
Par2RepairerSourceFile *sourcefile = *sf;
@@ -1562,6 +1603,10 @@
// What filename does the file use
string filename = sourcefile->TargetFileName();
@@ -1560,10 +1601,14 @@
if (oldfraction != newfraction)
{
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -111,7 +140,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
}
}
@@ -1651,6 +1696,11 @@
// If we fail to find a match, it might be because it was a duplicate of a block
// that we have already found.
@@ -1649,10 +1694,15 @@
return false;
}
}
}
@@ -123,7 +156,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
// Get the Full and 16k hash values of the file
filechecksummer.GetFileHashes(hashfull, hash16k);
@@ -2291,10 +2341,19 @@
// Did we make any matches at all
if (count > 0)
@@ -2289,14 +2339,23 @@
if (oldfraction != newfraction)
{
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -143,7 +180,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
++inputblock;
++inputindex;
}
@@ -2348,9 +2407,18 @@
}
else
@@ -2346,13 +2405,22 @@
if (oldfraction != newfraction)
{
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -162,7 +203,11 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
++copyblock;
++inputblock;
}
@@ -2362,6 +2430,11 @@
}
@@ -2360,10 +2428,15 @@
if (lastopenfile != NULL)
{
lastopenfile->Close();
}
@@ -174,10 +219,14 @@ diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.c
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 @@
// For each output block that has been recomputed
vector<DataBlock*>::iterator outputblock = outputblocks.begin();
diff -aud -U 5 ../libpar2-0.2-with-bugfixes-patch/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2012-12-03 10:48:13.000000000 +0100
@@ -186,8 +186,9 @@
u64 progress; // How much data has been processed.
u64 totaldata; // Total amount of data to be processed.
u64 totalsize; // Total data size

View File

@@ -1,66 +0,0 @@
#
# This file if part of nzbget
#
# Template configuration file for postprocessing script "nzbget-postprocess.sh".
# Please refer to "nzbget-postprocess.sh" for usage instructions.
#
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
##############################################################################
### 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=

View File

@@ -1,321 +0,0 @@
#!/bin/sh
#
# This file if part of nzbget
#
# Example postprocessing script for NZBGet
#
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
# Copyright (C) 2008 Otmar Werner
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
####################### 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 "nzbget-postprocess.conf". Put the configuration file
# into the directory where nzbget's configuration file (nzbget.conf) is located.
# Then edit the configuration file in any text editor to adjust the settings.
#
# o You can also edit the script's configuration via web-interface.
#
# o There are few options, which can be ajdusted for each nzb-file individually.
#
# 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="nzbget-postprocess.conf"
# Exit codes
POSTPROCESS_PARCHECK_CURRENT=91
POSTPROCESS_PARCHECK_ALL=92
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_NONE=95
# Check if the script is called from nzbget
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: Exiting 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 [ "$?" -ne 0 ]; 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 [ "$?" -ne 0 ]; 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

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -74,6 +74,7 @@
#include "PrePostProcessor.h"
#include "ParChecker.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "Util.h"
#ifdef WIN32
#include "NTService.h"
@@ -106,12 +107,14 @@ ServerPool* g_pServerPool = NULL;
QueueCoordinator* g_pQueueCoordinator = NULL;
UrlCoordinator* g_pUrlCoordinator = NULL;
RemoteServer* g_pRemoteServer = NULL;
RemoteServer* g_pRemoteSecureServer = NULL;
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
Scheduler* g_pScheduler = NULL;
Scanner* g_pScanner = NULL;
int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
@@ -222,16 +225,19 @@ void Run(bool bReload)
g_pLog->InitOptions();
if (g_pOptions->GetDaemonMode() && !bReload)
if (g_pOptions->GetDaemonMode())
{
#ifdef WIN32
info("nzbget %s service-mode", Util::VersionRevision());
#else
Daemonize();
if (!bReload)
{
Daemonize();
}
info("nzbget %s daemon-mode", Util::VersionRevision());
#endif
}
else if (g_pOptions->GetServerMode() && !bReload)
else if (g_pOptions->GetServerMode())
{
info("nzbget %s server-mode", Util::VersionRevision());
}
@@ -293,13 +299,20 @@ void Run(bool bReload)
// Setup the network-server
if (g_pOptions->GetServerMode())
{
g_pRemoteServer = new RemoteServer();
g_pRemoteServer = new RemoteServer(false);
g_pRemoteServer->Start();
if (g_pOptions->GetSecureControl())
{
g_pRemoteSecureServer = new RemoteServer(true);
g_pRemoteSecureServer->Start();
}
}
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pScanner = new Scanner();
g_pPrePostProcessor = new PrePostProcessor();
}
@@ -334,7 +347,7 @@ void Run(bool bReload)
// Standalone-mode
if (!g_pOptions->GetServerMode())
{
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
NZBFile* pNZBFile = NZBFile::Create(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
if (!pNZBFile)
{
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
@@ -405,6 +418,24 @@ void Run(bool bReload)
debug("RemoteServer stopped");
}
if (g_pRemoteSecureServer)
{
debug("stopping RemoteSecureServer");
g_pRemoteSecureServer->Stop();
int iMaxWaitMSec = 1000;
while (g_pRemoteSecureServer->IsRunning() && iMaxWaitMSec > 0)
{
usleep(100 * 1000);
iMaxWaitMSec -= 100;
}
if (g_pRemoteSecureServer->IsRunning())
{
debug("Killing RemoteSecureServer");
g_pRemoteSecureServer->Kill();
}
debug("RemoteSecureServer stopped");
}
// Stop Frontend
if (g_pFrontend)
{
@@ -703,12 +734,25 @@ void Cleanup()
}
debug("RemoteServer deleted");
debug("Deleting RemoteSecureServer");
if (g_pRemoteSecureServer)
{
delete g_pRemoteSecureServer;
g_pRemoteSecureServer = NULL;
}
debug("RemoteSecureServer deleted");
debug("Deleting PrePostProcessor");
if (g_pPrePostProcessor)
{
delete g_pPrePostProcessor;
g_pPrePostProcessor = NULL;
}
if (g_pScanner)
{
delete g_pScanner;
g_pScanner = NULL;
}
debug("PrePostProcessor deleted");
debug("Deleting Frontend");
@@ -800,14 +844,14 @@ void Daemonize()
/* Drop user if there is one, and we were run as root */
if ( getuid() == 0 || geteuid() == 0 )
{
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
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);
initgroups( g_pOptions->GetDaemonUsername(),pw->pw_gid);
/* Finally, set uid. */
setuid(pw->pw_uid);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,6 +37,8 @@
#define fdopen _fdopen
#define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep)
#define localtime_r(time, tm) localtime_s(tm, time)
#define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr)
#define strerror_r(errnum, buffer, size) strerror_s(buffer, size, errnum)
#define int32_t __int32
#define mkdir(dir, flags) _mkdir(dir)
#define rmdir _rmdir
@@ -50,6 +52,7 @@
#define usleep(usec) Sleep((usec) / 1000)
#define gettimeofday(tm, ignore) _ftime(tm)
#define socklen_t int
#define SHUT_WR 0x01
#define SHUT_RDWR 0x02
#define PATH_SEPARATOR '\\'
#define ALT_PATH_SEPARATOR '/'

View File

@@ -1,274 +0,0 @@
<?xml version = '1.0'?>
<kdevelop>
<general>
<author>gnu</author>
<email/>
<version>0.3.0</version>
<projectmanagement>KDevAutoProject</projectmanagement>
<primarylanguage>C++</primarylanguage>
<keywords>
<keyword>C++</keyword>
<keyword>Code</keyword>
</keywords>
<ignoreparts>
<part>kdevabbrev</part>
<part>kdevbookmarks</part>
<part>kdevsnippet</part>
<part>kdevctags2</part>
<part>kdevdoxygen</part>
<part>kdevkonsoleview</part>
<part>kdevfilegroups</part>
<part>kdevfilelist</part>
<part>kdevfileview</part>
<part>kdevdistpart</part>
<part>kdevopenwith</part>
<part>kdevregexptest</part>
<part>kdevscripting</part>
<part>kdevfilter</part>
<part>kdevtexttools</part>
</ignoreparts>
<projectdirectory>.</projectdirectory>
<absoluteprojectpath>false</absoluteprojectpath>
<description/>
<projectname>nzbget</projectname>
<defaultencoding/>
<versioncontrol>kdevsubversion</versioncontrol>
</general>
<kdevautoproject>
<general>
<activetarget>src/nzbget</activetarget>
<useconfiguration>default</useconfiguration>
</general>
<run>
<mainprogram>/home/user/nzbget/nzbget</mainprogram>
<terminal>false</terminal>
<directoryradio>executable</directoryradio>
<customdirectory>/</customdirectory>
<programargs/>
<autocompile>true</autocompile>
<envvars/>
<globaldebugarguments></globaldebugarguments>
<globalcwd>/home/user/nzbget</globalcwd>
<useglobalprogram>true</useglobalprogram>
<autoinstall>false</autoinstall>
<autokdesu>false</autokdesu>
</run>
<configurations>
<default>
<envvars/>
<configargs>--enable-debug</configargs>
<builddir/>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevpgf77options</f77compiler>
<ccompilerbinary/>
<cxxcompilerbinary/>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</default>
</configurations>
<make>
<envvars>
<envvar value="1" name="WANT_AUTOCONF_2_5" />
<envvar value="1" name="WANT_AUTOMAKE_1_6" />
</envvars>
<abortonerror>true</abortonerror>
<runmultiplejobs>false</runmultiplejobs>
<numberofjobs>1</numberofjobs>
<dontact>false</dontact>
<makebin/>
<prio>0</prio>
</make>
</kdevautoproject>
<kdevdoctreeview>
<ignoretocs>
<toc>ada</toc>
<toc>ada_bugs_gcc</toc>
<toc>bash</toc>
<toc>bash_bugs</toc>
<toc>clanlib</toc>
<toc>w3c-dom-level2-html</toc>
<toc>fortran_bugs_gcc</toc>
<toc>gnome1</toc>
<toc>gnustep</toc>
<toc>gtk</toc>
<toc>gtk_bugs</toc>
<toc>haskell</toc>
<toc>haskell_bugs_ghc</toc>
<toc>java_bugs_gcc</toc>
<toc>java_bugs_sun</toc>
<toc>kde2book</toc>
<toc>opengl</toc>
<toc>pascal_bugs_fp</toc>
<toc>php</toc>
<toc>php_bugs</toc>
<toc>perl</toc>
<toc>perl_bugs</toc>
<toc>python</toc>
<toc>python_bugs</toc>
<toc>qt-kdev3</toc>
<toc>ruby</toc>
<toc>ruby_bugs</toc>
<toc>sdl</toc>
<toc>w3c-svg</toc>
<toc>sw</toc>
<toc>w3c-uaag10</toc>
<toc>wxwidgets_bugs</toc>
</ignoretocs>
<ignoreqt_xml>
<toc>Guide to the Qt Translation Tools</toc>
<toc>Qt Assistant Manual</toc>
<toc>Qt Designer Manual</toc>
<toc>Qt Reference Documentation</toc>
<toc>qmake User Guide</toc>
</ignoreqt_xml>
<ignoredoxygen>
<toc>KDE Libraries (Doxygen)</toc>
</ignoredoxygen>
</kdevdoctreeview>
<kdevfilecreate>
<filetypes/>
<useglobaltypes>
<type ext="cpp" />
<type ext="h" />
</useglobaltypes>
</kdevfilecreate>
<kdevfileview>
<groups>
<group pattern="*.h" name="Header files" />
<group pattern="*.cpp" name="Source files" />
<hidenonprojectfiles>false</hidenonprojectfiles>
<hidenonlocation>false</hidenonlocation>
</groups>
<tree>
<hidepatterns>*.o,*.lo,CVS</hidepatterns>
<hidenonprojectfiles>false</hidenonprojectfiles>
</tree>
</kdevfileview>
<kdevdocumentation>
<projectdoc>
<docsystem>Doxygen Documentation Collection</docsystem>
<docurl>nzbget.tag</docurl>
<usermanualurl/>
</projectdoc>
</kdevdocumentation>
<substmap>
<APPNAME>nzbget</APPNAME>
<APPNAMELC>nzbget</APPNAMELC>
<APPNAMESC>Nzbget</APPNAMESC>
<APPNAMEUC>NZBGET</APPNAMEUC>
<AUTHOR>gnu</AUTHOR>
<EMAIL/>
<LICENSE>GPL</LICENSE>
<LICENSEFILE>COPYING</LICENSEFILE>
<VERSION>0.3.0</VERSION>
<YEAR>2007</YEAR>
<dest>/home/user/nzbget-0.3.0/nzbget</dest>
</substmap>
<cppsupportpart>
<filetemplates>
<interfacesuffix>.h</interfacesuffix>
<implementationsuffix>.cpp</implementationsuffix>
</filetemplates>
</cppsupportpart>
<kdevcppsupport>
<qt>
<used>false</used>
<version>3</version>
<root></root>
<includestyle>3</includestyle>
<designerintegration>EmbeddedKDevDesigner</designerintegration>
<qmake></qmake>
<designer></designer>
<designerpluginpaths/>
</qt>
<codecompletion>
<includeGlobalFunctions>true</includeGlobalFunctions>
<includeTypes>true</includeTypes>
<includeEnums>true</includeEnums>
<includeTypedefs>false</includeTypedefs>
<automaticCodeCompletion>false</automaticCodeCompletion>
<automaticArgumentsHint>false</automaticArgumentsHint>
<automaticHeaderCompletion>true</automaticHeaderCompletion>
<codeCompletionDelay>250</codeCompletionDelay>
<argumentsHintDelay>400</argumentsHintDelay>
<headerCompletionDelay>250</headerCompletionDelay>
<showOnlyAccessibleItems>false</showOnlyAccessibleItems>
<completionBoxItemOrder>0</completionBoxItemOrder>
<howEvaluationContextMenu>true</howEvaluationContextMenu>
<showCommentWithArgumentHint>false</showCommentWithArgumentHint>
<statusBarTypeEvaluation>false</statusBarTypeEvaluation>
<namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
<processPrimaryTypes>true</processPrimaryTypes>
<processFunctionArguments>false</processFunctionArguments>
<preProcessAllHeaders>false</preProcessAllHeaders>
<parseMissingHeaders>false</parseMissingHeaders>
<resolveIncludePaths>true</resolveIncludePaths>
<alwaysParseInBackground>true</alwaysParseInBackground>
<usePermanentCaching>true</usePermanentCaching>
<alwaysIncludeNamespaces>false</alwaysIncludeNamespaces>
<includePaths>.;</includePaths>
</codecompletion>
<creategettersetter>
<prefixGet/>
<prefixSet>set</prefixSet>
<prefixVariable>m_,_</prefixVariable>
<parameterName>theValue</parameterName>
<inlineGet>true</inlineGet>
<inlineSet>true</inlineSet>
</creategettersetter>
<references/>
<splitheadersource>
<enabled>false</enabled>
<synchronize>true</synchronize>
<orientation>Vertical</orientation>
</splitheadersource>
</kdevcppsupport>
<kdevdebugger>
<general>
<programargs>--standalone /home/user/.nzbget/nzb/t1.nzb</programargs>
<gdbpath/>
<dbgshell/>
<configGdbScript/>
<runShellScript/>
<runGdbScript/>
<breakonloadinglibs>true</breakonloadinglibs>
<separatetty>true</separatetty>
<floatingtoolbar>false</floatingtoolbar>
</general>
<display>
<staticmembers>false</staticmembers>
<demanglenames>true</demanglenames>
<outputradix>10</outputradix>
</display>
</kdevdebugger>
<dist>
<custom>false</custom>
<bzip>false</bzip>
<archname/>
<appname>nzbget</appname>
<version>0.2.4</version>
<release/>
<vendor/>
<licence/>
<summary/>
<group/>
<packager/>
<description/>
<changelog/>
<devpackage>false</devpackage>
<docspackage>false</docspackage>
<appicon>false</appicon>
<arch>0</arch>
<genHTML>false</genHTML>
<useRPM>false</useRPM>
<ftpkde>false</ftpkde>
<appskde>false</appskde>
<url/>
</dist>
</kdevelop>

View File

@@ -391,6 +391,14 @@
RelativePath=".\Options.h"
>
</File>
<File
RelativePath=".\ParCoordinator.cpp"
>
</File>
<File
RelativePath=".\ParCoordinator.h"
>
</File>
<File
RelativePath=".\ParChecker.cpp"
>
@@ -399,6 +407,14 @@
RelativePath=".\ParChecker.h"
>
</File>
<File
RelativePath=".\ParRenamer.cpp"
>
</File>
<File
RelativePath=".\ParRenamer.h"
>
</File>
<File
RelativePath=".\PrePostProcessor.cpp"
>
@@ -487,6 +503,14 @@
RelativePath=".\TLS.h"
>
</File>
<File
RelativePath=".\Unpack.cpp"
>
</File>
<File
RelativePath=".\Unpack.h"
>
</File>
<File
RelativePath=".\UrlCoordinator.cpp"
>

218
ppscripts/EMail.py Executable file
View File

@@ -0,0 +1,218 @@
#!/usr/bin/env python
#
# E-Mail post-processing script for NZBGet
#
# Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Revision$
# $Date$
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Send E-Mail notification.
#
# This script sends E-Mail notification when the job is done.
#
# NOTE: This script requires Python to be installed on your system.
##############################################################################
### OPTIONS ###
# Email address you want this email to be sent from.
#From="NZBGet" <myaccount@gmail.com>
# Email address you want this email to be sent to.
#To=myaccount@gmail.com
# SMTP server host.
#Server=smtp.gmail.com
# SMTP server port (1-65535).
#Port=25
# Secure communication using TLS/SSL (yes, no).
#Encryption=yes
# SMTP server user name, if required.
#Username=myaccount
# SMTP server password, if required.
#Password=mypass
# Append list of files to the message (yes, no).
#
# Add the list of downloaded files (the content of destination directory).
#FileList=yes
# Append broken-log to the message (yes, no).
#
# Add the content of file _brokenlog.txt. This file contains the list of damaged
# files and the result of par-check/repair. For successful downloads the broken-log
# is usually deleted by cleanup-script and therefore is not sent.
#BrokenLog=yes
# Append post-processing log to the message (Always, Never, OnFailure).
#
# Add the post-processing log of active job.
#PostProcessLog=OnFailure
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import os
import sys
import datetime
import smtplib
from email.mime.text import MIMEText
try:
from xmlrpclib import ServerProxy # python 2
except ImportError:
from xmlrpc.client import ServerProxy # python 3
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
# Check if the script is called from nzbget 11.0 or later
if not 'NZBOP_SCRIPTDIR' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (11.0 or later).')
sys.exit(POSTPROCESS_ERROR)
print('[DETAIL] Script successfully started')
sys.stdout.flush()
required_options = ('NZBPO_FROM', 'NZBPO_TO', 'NZBPO_SERVER', 'NZBPO_PORT', 'NZBPO_ENCRYPTION',
'NZBPO_USERNAME', 'NZBPO_PASSWORD', 'NZBPO_FILELIST', 'NZBPO_BROKENLOG', 'NZBPO_POSTPROCESSLOG')
for optname in required_options:
if (not optname in os.environ):
print('[ERROR] Option %s is missing in configuration file. Please check script settings' % optname[6:])
sys.exit(POSTPROCESS_ERROR)
# Check par and unpack status for errors.
success=False
if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_UNPACKSTATUS'] == '1':
subject = 'Failure for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" has failed.' % (os.environ['NZBPP_NZBNAME'])
elif os.environ['NZBPP_PARSTATUS'] == '4':
subject = 'Damaged for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" requires par-repair.' % (os.environ['NZBPP_NZBNAME'])
else:
subject = 'Success for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" has successfully completed.' % (os.environ['NZBPP_NZBNAME'])
success=True
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled.
# 4 = par-check needed but skipped (option ParCheck=manual);
parStatus = { '0': 'skipped', '1': 'failed', '2': 'repaired', '3': 'repairable', '4': 'manual' }
text += '\nPar-Status: %s' % parStatus[os.environ['NZBPP_PARSTATUS']]
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
unpackStatus = { '0': 'skipped', '1': 'failed', '2': 'success' }
text += '\nUnpack-Status: %s' % unpackStatus[os.environ['NZBPP_UNPACKSTATUS']]
# add list of downloaded files
if os.environ['NZBPO_FILELIST'] == 'yes':
text += '\n\nFiles:'
for dirname, dirnames, filenames in os.walk(os.environ['NZBPP_DIRECTORY']):
for filename in filenames:
text += '\n' + os.path.join(dirname, filename)[len(os.environ['NZBPP_DIRECTORY']) + 1:]
# add _brokenlog.txt (if exists)
if os.environ['NZBPO_BROKENLOG'] == 'yes':
brokenlog = '%s/_brokenlog.txt' % os.environ['NZBPP_DIRECTORY']
if os.path.exists(brokenlog):
text += '\n\nBrokenlog:\n' + open(brokenlog, 'r').read().strip()
# add post-processing log
if os.environ['NZBPO_POSTPROCESSLOG'] == 'Always' or \
(os.environ['NZBPO_POSTPROCESSLOG'] == 'OnFailure' and not success):
# To get the post-processing log we connect to NZBGet via XML-RPC
# and call method "postqueue", which returns the list of post-processing job.
# The first item in the list is current job. This item has a field 'Log',
# containing an array of log-entries.
# For more info visit http://nzbget.sourceforge.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
# NZBGet passes all configuration options to post-processing script as
# environment variables.
host = os.environ['NZBOP_CONTROLIP'];
port = os.environ['NZBOP_CONTROLPORT'];
username = os.environ['NZBOP_CONTROLUSERNAME'];
password = os.environ['NZBOP_CONTROLPASSWORD'];
if host == '0.0.0.0': host = '127.0.0.1'
# Build an URL for XML-RPC requests
rpcUrl = 'http://%s:%s@%s:%s/xmlrpc' % (username, password, host, port);
# Create remote server object
server = ServerProxy(rpcUrl)
# Call remote method 'postqueue'. The only parameter tells how many log-entries to return as maximum.
postqueue = server.postqueue(10000)
# Get field 'Log' from the first post-processing job
log = postqueue[0]['Log']
# Now iterate through entries and save them to message text
if len(log) > 0:
text += '\n\nPost-processing log:';
for entry in log:
text += '\n%s\t%s\t%s' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])
# Create message
msg = MIMEText(text)
msg['Subject'] = subject
msg['From'] = os.environ['NZBPO_FROM']
msg['To'] = os.environ['NZBPO_TO']
# Send message
print('[DETAIL] Sending E-Mail')
sys.stdout.flush()
try:
smtp = smtplib.SMTP(os.environ['NZBPO_SERVER'], os.environ['NZBPO_PORT'])
if os.environ['NZBPO_ENCRYPTION'] == 'yes':
smtp.starttls()
if os.environ['NZBPO_USERNAME'] != '' and os.environ['NZBPO_PASSWORD'] != '':
smtp.login(os.environ['NZBPO_USERNAME'], os.environ['NZBPO_PASSWORD'])
smtp.sendmail(os.environ['NZBPO_FROM'], os.environ['NZBPO_TO'], msg.as_string())
smtp.quit()
except Exception as err:
print('[ERROR] %s' % err)
sys.exit(POSTPROCESS_ERROR)
# All OK, returning exit status 'POSTPROCESS_SUCCESS' (int <93>) to let NZBGet know
# that our script has successfully completed.
sys.exit(POSTPROCESS_SUCCESS)

100
ppscripts/Logger.py Executable file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env python
#
# Logger post-processing script for NZBGet
#
# Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Revision$
# $Date$
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Save post-processing log into a file.
#
# This script saves post-processing log of nzb-file into file
# _postprocesslog.txt in the destination directory.
#
# NOTE: This script requires Python to be installed on your system.
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import os
import sys
import datetime
try:
from xmlrpclib import ServerProxy # python 2
except ImportError:
from xmlrpc.client import ServerProxy # python 3
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_NONE=95
POSTPROCESS_ERROR=94
# Check if the script is called from nzbget 11.0 or later
if not 'NZBOP_SCRIPTDIR' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (11.0 or later).')
sys.exit(POSTPROCESS_ERROR)
if not os.path.exists(os.environ['NZBPP_DIRECTORY']):
print('Destination directory doesn\'t exist, exiting')
sys.exit(POSTPROCESS_NONE)
# To get the post-processing log we connect to NZBGet via XML-RPC
# and call method "postqueue", which returns the list of post-processing job.
# The first item in the list is current job. This item has a field 'Log',
# containing an array of log-entries.
# For more info visit http://nzbget.sourceforge.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
# NZBGet passes all configuration options to post-processing script as
# environment variables.
host = os.environ['NZBOP_CONTROLIP'];
port = os.environ['NZBOP_CONTROLPORT'];
username = os.environ['NZBOP_CONTROLUSERNAME'];
password = os.environ['NZBOP_CONTROLPASSWORD'];
if host == '0.0.0.0': host = '127.0.0.1'
# Build an URL for XML-RPC requests
rpcUrl = 'http://%s:%s@%s:%s/xmlrpc' % (username, password, host, port);
# Create remote server object
server = ServerProxy(rpcUrl)
# Call remote method 'postqueue'. The only parameter tells how many log-entries to return as maximum.
postqueue = server.postqueue(10000)
# Get field 'Log' from the first post-processing job
log = postqueue[0]['Log']
# Now iterate through entries and save them to the output file
if len(log) > 0:
f = open('%s/_postprocesslog.txt' % os.environ['NZBPP_DIRECTORY'], 'wb')
for entry in log:
f.write((u'%s\t%s\t%s\n' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])).encode('utf8'))
f.close()
# All OK, returning exit status 'POSTPROCESS_SUCCESS' (int <93>) to let NZBGet know
# that our script has successfully completed.
sys.exit(POSTPROCESS_SUCCESS)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -273,7 +273,10 @@ var Downloads = (new function($)
case 'VERIFYING_SOURCES': group.status = 'checking'; break;
case 'REPAIRING': group.status = 'repairing'; break;
case 'VERIFYING_REPAIRED': group.status = 'verifying'; break;
case 'EXECUTING_SCRIPT': group.status = 'unpacking'; break;
case 'RENAMING': group.status = 'renaming'; break;
case 'MOVING': group.status = 'moving'; break;
case 'UNPACKING': group.status = 'unpacking'; break;
case 'EXECUTING_SCRIPT': group.status = 'processing'; break;
case 'FINISHED': group.status = 'finished'; break;
default: group.status = 'error: ' + group.post.Stage; break;
}
@@ -413,7 +416,14 @@ var Downloads = (new function($)
notification = '#Notif_Downloads_Resumed';
RPC.call('editqueue', ['GroupResume', 0, '', checkedEditIDs], function()
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
if (Options.option('ParCheck') === 'force')
{
editCompleted();
}
else
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
}
});
}
@@ -470,6 +480,9 @@ var Downloads = (new function($)
}
};
Util.show('#DownloadsDeleteConfirmDialog_Cleanup', Options.option('DeleteCleanupDisk') === 'yes');
Util.show('#DownloadsDeleteConfirmDialog_Remain', Options.option('DeleteCleanupDisk') != 'yes');
ConfirmDialog.showModal('DownloadsDeleteConfirmDialog', deleteGroups);
}
@@ -619,7 +632,7 @@ var DownloadsUI = (new function($)
{
if (group.post.StageProgress > 0)
{
return Util.formatTimeHMS(group.post.StageTimeSec / group.post.StageProgress * (1000 - group.post.StageProgress));
return Util.formatTimeLeft(group.post.StageTimeSec / group.post.StageProgress * (1000 - group.post.StageProgress));
}
}
else if (!group.paused && Status.status.DownloadRate > 0)
@@ -633,15 +646,31 @@ var DownloadsUI = (new function($)
this.buildProgressLabel = function(group)
{
var text = '';
if (group.postprocess && !Status.status.PostPaused && group.post.Stage !== 'REPAIRING')
if (group.postprocess && !Status.status.PostPaused)
{
if (group.post.Log && group.post.Log.length > 0)
switch (group.post.Stage)
{
text = group.post.Log[group.post.Log.length-1].Text;
}
else if (group.post.ProgressLabel !== '')
{
text = group.post.ProgressLabel;
case "REPAIRING":
break;
case "LOADING_PARS":
case "VERIFYING_SOURCES":
case "VERIFYING_REPAIRED":
case "UNPACKING":
case "RENAMING":
text = group.post.ProgressLabel;
break;
case "EXECUTING_SCRIPT":
if (group.post.Log && group.post.Log.length > 0)
{
text = group.post.Log[group.post.Log.length-1].Text;
// remove "for <nzb-name>" from label text
text = text.replace(' for ' + group.NZBName, ' ');
}
else
{
text = group.post.ProgressLabel;
}
break;
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,7 +26,9 @@
* In this module:
* 1) Download edit dialog;
* 2) Download multi edit dialog (edit multiple items);
* 3) Download merge dialog.
* 3) Download merge dialog;
* 4) Download split dialog;
* 5) History edit dialog.
*/
/*** DOWNLOAD EDIT DIALOG ************************************************************/
@@ -39,6 +41,7 @@ var DownloadsEditDialog = (new function($)
var $DownloadsEditDialog;
var $DownloadsLogTable;
var $DownloadsFileTable;
var $DownloadsEdit_ParamData;
// State
var curGroup;
@@ -52,6 +55,7 @@ var DownloadsEditDialog = (new function($)
this.init = function()
{
$DownloadsEditDialog = $('#DownloadsEditDialog');
$DownloadsEdit_ParamData = $('#DownloadsEdit_ParamData');
$('#DownloadsEdit_Save').click(saveChanges);
$('#DownloadsEdit_Pause').click(itemPause);
@@ -96,6 +100,7 @@ var DownloadsEditDialog = (new function($)
// cleanup
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
$DownloadsEdit_ParamData.empty();
// resume updates
Refresher.resume();
});
@@ -149,6 +154,7 @@ var DownloadsEditDialog = (new function($)
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
table += '<tr><td>Paused</td><td class="text-right">' + unpausedSize + '</td></tr>';
table += '<tr><td>Unpaused</td><td class="text-right">' + remaining + '</td></tr>';
//table += '<tr><td>Active downloads</td><td class="text-right">' + group.ActiveDownloads + '</td></tr>';
table += '<tr><td>Estimated time</td><td class="text-right">' + estimated + '</td></tr>';
table += '<tr><td>Files (total/remaining/pars)</td><td class="text-center">' + group.FileCount + ' / ' +
group.RemainingFileCount + ' / ' + group.RemainingParCount + '</td></tr>';
@@ -182,13 +188,15 @@ var DownloadsEditDialog = (new function($)
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
var postParamConfig = ParamTab.createPostParamConfig();
Util.show('#DownloadsEdit_NZBNameReadonly', group.postprocess);
Util.show('#DownloadsEdit_CancelPPGroup', group.postprocess);
Util.show('#DownloadsEdit_DeleteGroup', !group.postprocess);
Util.show('#DownloadsEdit_PauseGroup', !group.postprocess);
Util.show('#DownloadsEdit_ResumeGroup', false);
Util.show('#DownloadsEdit_Save', !group.postprocess);
var postParam = Options.postParamConfig && Options.postParamConfig.length > 0;
var postParam = postParamConfig[0].options.length > 0;
var postLog = group.postprocess && group.post.Log.length > 0;
Util.show('#DownloadsEdit_Param', postParam);
Util.show('#DownloadsEdit_Log', postLog);
@@ -216,12 +224,7 @@ var DownloadsEditDialog = (new function($)
if (postParam)
{
postParams = $.extend(true, [], Options.postParamConfig);
Options.mergeValues(postParams, group.Parameters);
var content = Config.buildOptionsContent(postParams[0]);
var configData = $('#DownloadsEdit_ParamData');
configData.empty();
configData.append(content);
postParams = ParamTab.buildPostParamTab($DownloadsEdit_ParamData, postParamConfig, curGroup.Parameters);
}
enableAllButtons();
@@ -394,7 +397,14 @@ var DownloadsEditDialog = (new function($)
notification = '#Notif_Downloads_Resumed';
RPC.call('editqueue', ['GroupResume', 0, '', [curGroup.LastID]], function()
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', [curGroup.LastID]], completed);
if (Options.option('ParCheck') === 'force')
{
completed();
}
else
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', [curGroup.LastID]], completed);
}
});
}
@@ -427,34 +437,9 @@ var DownloadsEditDialog = (new function($)
/*** TAB: POST-PROCESSING PARAMETERS **************************************************/
function prepareParamRequest()
{
var request = [];
for (var i=0; i < postParams.length; i++)
{
var section = postParams[i];
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template && !section.hidden)
{
var oldValue = option.value;
var newValue = Config.getOptionValue(option);
if (oldValue != newValue && !(oldValue === '' && newValue === option.defvalue))
{
var opt = option.name + '=' + newValue;
request.push(opt);
}
}
}
}
return request;
}
function saveParam()
{
var paramList = prepareParamRequest();
var paramList = ParamTab.prepareParamRequest(postParams);
saveNextParam(paramList);
}
@@ -639,6 +624,9 @@ var DownloadsEditDialog = (new function($)
var file = files[i];
file.moved = false;
}
var editIDList = [];
var splitError = false;
for (var i = 0; i < files.length; i++)
{
@@ -652,6 +640,8 @@ var DownloadsEditDialog = (new function($)
if (checkedRows.indexOf(file.ID) > -1)
{
editIDList.push(file.ID);
switch (action)
{
case 'pause':
@@ -704,10 +694,28 @@ var DownloadsEditDialog = (new function($)
i--;
}
break;
case 'split':
if (file.ActiveDownloads > 0 || file.FileSizeLo !== file.RemainingSizeLo)
{
splitError = true;
}
break;
}
}
}
if (action === 'split')
{
if (splitError)
{
Notification.show('#Notif_Downloads_SplitNotPossible');
}
else
{
DownloadsSplitDialog.showModal(curGroup, editIDList);
}
}
filesLoaded(files);
}
@@ -784,6 +792,89 @@ var DownloadsEditDialog = (new function($)
}(jQuery));
/*** PARAM TAB FOR EDIT DIALOGS ************************************************************/
var ParamTab = (new function($)
{
'use strict'
this.buildPostParamTab = function(configData, postParamConfig, parameters)
{
var postParams = $.extend(true, [], postParamConfig);
Options.mergeValues(postParams, parameters);
var content = Config.buildOptionsContent(postParams[0]);
configData.empty();
configData.append(content);
configData.addClass('retain-margin');
var lastClass = '';
var lastDiv = null;
for (var i=0; i < configData.children().length; i++)
{
var div = $(configData.children()[i]);
var divClass = div.attr('class');
if (divClass != lastClass && lastClass != '')
{
lastDiv.addClass('wants-divider');
}
lastDiv = div;
lastClass = divClass;
}
return postParams;
}
this.createPostParamConfig = function()
{
var postParamConfig = Options.postParamConfig;
defineBuiltinParams(postParamConfig);
return postParamConfig;
}
function defineBuiltinParams(postParamConfig)
{
if (Options.option('Unpack') !== 'yes')
{
return;
}
if (postParamConfig.length == 0)
{
postParamConfig.push({category: 'P', postparam: true, options: []});
}
if (!Options.findOption(postParamConfig[0].options, '*Unpack:'))
{
postParamConfig[0].options.unshift({name: '*Unpack:Password', value: '', defvalue: '', select: [], caption: 'Password', sectionId: '_Unpack_', description: 'Unpack-password for encrypted archives.'});
postParamConfig[0].options.unshift({name: '*Unpack:', value: '', defvalue: 'yes', select: ['yes', 'no'], caption: 'Unpack', sectionId: '_Unpack_', description: 'Unpack rar and 7-zip archives.'});
}
}
this.prepareParamRequest = function(postParams)
{
var request = [];
for (var i=0; i < postParams.length; i++)
{
var section = postParams[i];
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template && !section.hidden)
{
var oldValue = option.value;
var newValue = Config.getOptionValue(option);
if (oldValue != newValue && !(oldValue === '' && newValue === option.defvalue))
{
var opt = option.name + '=' + newValue;
request.push(opt);
}
}
}
}
return request;
}
}(jQuery));
/*** DOWNLOAD MULTI EDIT DIALOG ************************************************************/
var DownloadsMultiDialog = (new function($)
@@ -1046,3 +1137,281 @@ var DownloadsMergeDialog = (new function($)
Notification.show('#Notif_Downloads_Merged');
}
}(jQuery));
/*** DOWNLOAD SPLIT DIALOG ************************************************************/
var DownloadsSplitDialog = (new function($)
{
'use strict'
// Controls
var $DownloadsSplitDialog;
// State
var splitEditIDList;
this.init = function()
{
$DownloadsSplitDialog = $('#DownloadsSplitDialog');
$('#DownloadsSplit_Split').click(split);
$DownloadsSplitDialog.on('hidden', function ()
{
Refresher.resume();
});
if (UISettings.setFocus)
{
$DownloadsSplitDialog.on('shown', function ()
{
$('#DownloadsSplit_Merge').focus();
});
}
}
this.showModal = function(group, editIDList)
{
Refresher.pause();
splitEditIDList = editIDList;
var groupName = group.NZBName + ' (' + editIDList[0] + (editIDList.length > 1 ? '-' + editIDList[editIDList.length-1] : '') + ')';
$('#DownloadsSplit_NZBName').attr('value', groupName);
$DownloadsSplitDialog.modal({backdrop: 'static'});
}
function split()
{
var groupName = $('#DownloadsSplit_NZBName').val();
RPC.call('editqueue', ['FileSplit', 0, groupName, splitEditIDList], completed);
}
function completed(result)
{
$('#DownloadsEditDialog').modal('hide');
$DownloadsSplitDialog.modal('hide');
Refresher.update();
Notification.show(result ? '#Notif_Downloads_Splitted' : '#Notif_Downloads_SplitError');
}
}(jQuery));
/*** EDIT HISTORY DIALOG *************************************************************************/
var HistoryEditDialog = (new function()
{
'use strict'
// Controls
var $HistoryEditDialog;
var $HistoryEdit_ParamData;
// State
var curHist;
var notification = null;
var postParams = [];
var lastPage;
var lastFullscreen;
var saveParamCompleted;
this.init = function()
{
$HistoryEditDialog = $('#HistoryEditDialog');
$HistoryEdit_ParamData = $('#HistoryEdit_ParamData');
$('#HistoryEdit_Save').click(saveChanges);
$('#HistoryEdit_Delete').click(itemDelete);
$('#HistoryEdit_Return').click(itemReturn);
$('#HistoryEdit_Reprocess').click(itemReprocess);
$('#HistoryEdit_Param').click(tabClick);
$('#HistoryEdit_Back').click(backClick);
$HistoryEditDialog.on('hidden', function ()
{
$HistoryEdit_ParamData.empty();
// resume updates
Refresher.resume();
});
TabDialog.extend($HistoryEditDialog);
}
this.showModal = function(hist)
{
Refresher.pause();
curHist = hist;
var status;
if (hist.Kind === 'URL')
{
status = HistoryUI.buildStatus(hist.status, '');
}
else
{
status = HistoryUI.buildStatus(hist.ParStatus, 'Par: ') + ' ' +
(Options.option('Unpack') == 'yes' || hist.UnpackStatus != 'NONE' ? HistoryUI.buildStatus(hist.UnpackStatus, 'Unpack: ') + ' ' : '') +
(hist.MoveStatus === "FAILURE" ? HistoryUI.buildStatus(hist.MoveStatus, 'Move: ') + ' ' : "");
for (var i=0; i<hist.ScriptStatuses.length; i++)
{
var scriptStatus = hist.ScriptStatuses[i];
status += HistoryUI.buildStatus(scriptStatus.Status, Options.shortScriptName(scriptStatus.Name) + ': ') + ' ';
}
}
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
if (hist.Kind === 'URL')
{
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + '&nbsp;' + '<span class="label label-info">URL</span>');
}
$('#HistoryEdit_Status').html(status);
$('#HistoryEdit_Category').text(hist.Category !== '' ? hist.Category : '<empty>');
$('#HistoryEdit_Path').text(hist.DestDir);
var size = Util.formatSizeMB(hist.FileSizeMB, hist.FileSizeLo);
var table = '';
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
table += '<tr><td>Files (total/parked)</td><td class="text-right">' + hist.FileCount + '/' + hist.RemainingFileCount + '</td></tr>';
$('#HistoryEdit_Statistics').html(table);
Util.show($('#HistoryEdit_ReturnGroup'), hist.RemainingFileCount > 0 || hist.Kind === 'URL');
Util.show($('#HistoryEdit_PathGroup, #HistoryEdit_StatisticsGroup, #HistoryEdit_ReprocessGroup'), hist.Kind === 'NZB');
var postParamConfig = ParamTab.createPostParamConfig();
var postParam = hist.Kind === 'NZB' && postParamConfig[0].options.length > 0;
Util.show('#HistoryEdit_Param', postParam);
Util.show('#HistoryEdit_Save', postParam);
$('#HistoryEdit_Close').toggleClass('btn-primary', !postParam);
if (postParam)
{
postParams = ParamTab.buildPostParamTab($HistoryEdit_ParamData, postParamConfig, curHist.Parameters);
}
enableAllButtons();
$('#HistoryEdit_GeneralTab').show();
$('#HistoryEdit_ParamTab').hide();
$('#HistoryEdit_Back').hide();
$('#HistoryEdit_BackSpace').show();
$HistoryEditDialog.restoreTab();
notification = null;
$HistoryEditDialog.modal({backdrop: 'static'});
}
function tabClick(e)
{
e.preventDefault();
$('#HistoryEdit_Back').fadeIn(500);
$('#HistoryEdit_BackSpace').hide();
var tab = '#' + $(this).attr('data-tab');
lastPage = $(tab);
lastFullscreen = ($(this).attr('data-fullscreen') === 'true') && !UISettings.miniTheme;
$HistoryEditDialog.switchTab($('#HistoryEdit_GeneralTab'), lastPage,
e.shiftKey || !UISettings.slideAnimation ? 0 : 500,
{fullscreen: lastFullscreen, mini: UISettings.miniTheme});
}
function backClick(e)
{
e.preventDefault();
$('#HistoryEdit_Back').fadeOut(500, function()
{
$('#HistoryEdit_BackSpace').show();
});
$HistoryEditDialog.switchTab(lastPage, $('#HistoryEdit_GeneralTab'),
e.shiftKey || !UISettings.slideAnimation ? 0 : 500,
{fullscreen: lastFullscreen, mini: UISettings.miniTheme, back: true});
}
function disableAllButtons()
{
$('#HistoryEditDialog .modal-footer .btn').attr('disabled', 'disabled');
setTimeout(function()
{
$('#HistoryEdit_Transmit').show();
}, 500);
}
function enableAllButtons()
{
$('#HistoryEditDialog .modal-footer .btn').removeAttr('disabled');
$('#HistoryEdit_Transmit').hide();
}
function itemDelete()
{
disableAllButtons();
notification = '#Notif_History_Deleted';
RPC.call('editqueue', ['HistoryDelete', 0, '', [curHist.ID]], completed);
}
function itemReturn()
{
disableAllButtons();
notification = '#Notif_History_Returned';
RPC.call('editqueue', ['HistoryReturn', 0, '', [curHist.ID]], completed);
}
function itemReprocess()
{
disableAllButtons();
saveParam(function()
{
notification = '#Notif_History_Reproces';
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
});
}
function completed()
{
$HistoryEditDialog.modal('hide');
Refresher.update();
if (notification)
{
Notification.show(notification);
notification = null;
}
}
function saveChanges()
{
disableAllButtons();
notification = null;
saveParam(completed);
}
/*** TAB: POST-PROCESSING PARAMETERS **************************************************/
function saveParam(_saveParamCompleted)
{
saveParamCompleted = _saveParamCompleted;
var paramList = ParamTab.prepareParamRequest(postParams);
saveNextParam(paramList);
}
function saveNextParam(paramList)
{
if (paramList.length > 0)
{
RPC.call('editqueue', ['HistorySetParameter', 0, paramList[0], [curHist.ID]], function()
{
notification = '#Notif_History_Saved';
paramList.shift();
saveNextParam(paramList);
})
}
else
{
saveParamCompleted();
}
}
}(jQuery));

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -192,7 +192,7 @@
pageDots : Util.parseBool(config.pageDots),
curPage : 1,
checkedRows: [],
lastClickedRowID: 0
lastClickedRowID: null
});
}
});
@@ -227,6 +227,11 @@
return $(this).data('fasttable').checkedRows;
},
checkRow : function(id, checked)
{
checkRow($(this).data('fasttable'), id, checked);
},
itemCheckClick : itemCheckClick,
titleCheckClick : titleCheckClick
@@ -624,7 +629,7 @@
var id = row.fasttableID;
var doToggle = true;
if (event.shiftKey && data.lastClickedRowID > 0)
if (event.shiftKey && data.lastClickedRowID != null)
{
var checked = checkedRows.indexOf(id) > -1;
doToggle = !checkRange(data, id, data.lastClickedRowID, !checked);
@@ -656,7 +661,7 @@
}
}
data.lastClickedRowID = 0;
data.lastClickedRowID = null;
checkAll(data, !hasSelectedItems);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,15 +25,15 @@
/*
* In this module:
* 1) History tab;
* 2) History edit dialog.
* 2) Functions for html generation for history, also used from other modules (edit dialog).
*/
/*** HISTORY TAB AND EDIT HISTORY DIALOG **********************************************/
var History = (new function($)
{
'use strict';
// Controls
var $HistoryTable;
var $HistoryTabBadge;
@@ -53,8 +53,6 @@ var History = (new function($)
$HistoryTabBadge = $('#HistoryTabBadge');
$HistoryTabBadgeEmpty = $('#HistoryTabBadgeEmpty');
$HistoryRecordsPerPage = $('#HistoryRecordsPerPage');
historyEditDialog.init();
var recordsPerPage = UISettings.read('HistoryRecordsPerPage', 10);
$HistoryRecordsPerPage.val(recordsPerPage);
@@ -113,19 +111,33 @@ var History = (new function($)
{
if (hist.Kind === 'NZB')
{
switch (hist.ScriptStatus)
if (hist.ParStatus == 'FAILURE' || hist.UnpackStatus == 'FAILURE' || hist.MoveStatus == 'FAILURE' || hist.ScriptStatus == 'FAILURE')
{
case 'SUCCESS': hist.status = 'success'; break;
case 'FAILURE': hist.status = 'failure'; break;
case 'UNKNOWN': hist.status = 'unknown'; break;
case 'NONE':
switch (hist.ParStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
case 'FAILURE': hist.status = 'failure'; break;
case 'NONE': hist.status = 'none'; break;
}
hist.status = 'failure';
}
else if (hist.ParStatus == 'MANUAL')
{
hist.status = 'damaged';
}
else
{
switch (hist.ScriptStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'UNKNOWN': hist.status = 'unknown'; break;
case 'NONE':
switch (hist.UnpackStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'NONE':
switch (hist.ParStatus)
{
case 'SUCCESS': hist.status = 'success'; break;
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
case 'NONE': hist.status = 'unknown'; break;
}
}
}
}
}
else if (hist.Kind === 'URL')
@@ -180,7 +192,7 @@ var History = (new function($)
{
var hist = item.hist;
var status = buildStatus(hist);
var status = HistoryUI.buildStatus(hist.status, '');
var name = '<a href="#" histid="' + hist.ID + '">' + Util.textToHtml(Util.formatNZBName(hist.Name)) + '</a>';
var category = Util.textToHtml(hist.Category);
@@ -222,19 +234,6 @@ var History = (new function($)
}
}
function buildStatus(hist)
{
switch (hist.status)
{
case 'success': return '<span class="label label-status label-success">success</span>';
case 'failure': return '<span class="label label-status label-important">failure</span>';
case 'unknown': return '<span class="label label-status label-info">unknown</span>';
case 'repairable': return '<span class="label label-status label-success">repairable</span>';
case 'none': return '<span class="label label-status">unknown</span>';
default: return '<span class="label label-status label-important">internal error(' + hist.status + ')</span>';
}
}
this.recordsPerPageChange = function()
{
var val = $HistoryRecordsPerPage.val();
@@ -304,127 +303,66 @@ var History = (new function($)
notification = null;
}
}
function editClick()
{
var histid = $(this).attr('histid');
$(this).blur();
historyEditDialog.showModal(histid);
var hist = null;
// find history object
for (var i=0; i<history.length; i++)
{
var gr = history[i];
if (gr.ID == histid)
{
hist = gr;
break;
}
}
if (hist == null)
{
return;
}
HistoryEditDialog.showModal(hist);
}
}(jQuery));
/*** FUNCTIONS FOR HTML GENERATION (also used from other modules) *****************************/
var HistoryUI = (new function($)
{
'use strict';
this.buildStatus = function(status, prefix)
{
switch (status)
{
case 'success':
case 'SUCCESS':
return '<span class="label label-status label-success">' + prefix + 'success</span>';
case 'failure':
case 'FAILURE':
return '<span class="label label-status label-important">' + prefix + 'failure</span>';
case 'unknown':
case 'UNKNOWN':
return '<span class="label label-status label-info">' + prefix + 'unknown</span>';
case 'repairable':
case 'REPAIR_POSSIBLE':
return '<span class="label label-status label-success">' + prefix + 'repairable</span>';
case 'manual':
case 'MANUAL':
case 'damaged':
return '<span class="label label-status label-warning">' + prefix + status + '</span>';
case 'none':
case 'NONE':
return '<span class="label label-status">' + prefix + 'none</span>';
default:
return '<span class="label label-status">' + prefix + status + '</span>';
}
}
/*** EDIT HISTORY DIALOG *************************************************************************/
var historyEditDialog = new function()
{
// Controls
var $HistoryEditDialog;
// State
var curHist;
this.init = function()
{
$HistoryEditDialog = $('#HistoryEditDialog');
$('#HistoryEdit_Delete').click(itemDelete);
$('#HistoryEdit_Return').click(itemReturn);
$('#HistoryEdit_Reprocess').click(itemReprocess);
$HistoryEditDialog.on('hidden', function () {
Refresher.resume();
});
}
this.showModal = function(histid)
{
var hist = null;
// find history object
for (var i=0; i<history.length; i++)
{
var gr = history[i];
if (gr.ID == histid)
{
hist = gr;
break;
}
}
if (hist == null)
{
return;
}
Refresher.pause();
curHist = hist;
var status = buildStatus(hist);
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
if (hist.Kind === 'URL')
{
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + '&nbsp;' + '<span class="label label-info">URL</span>');
}
$('#HistoryEdit_Status').html(status);
$('#HistoryEdit_Category').text(hist.Category !== '' ? hist.Category : '<empty>');
$('#HistoryEdit_Path').text(hist.DestDir);
var size = Util.formatSizeMB(hist.FileSizeMB, hist.FileSizeLo);
var table = '';
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
table += '<tr><td>Files (total/parked)</td><td class="text-right">' + hist.FileCount + '/' + hist.RemainingFileCount + '</td></tr>';
$('#HistoryEdit_Statistics').html(table);
Util.show($('#HistoryEdit_ReturnGroup'), hist.RemainingFileCount > 0 || hist.Kind === 'URL');
Util.show($('#HistoryEdit_PathGroup, #HistoryEdit_StatisticsGroup, #HistoryEdit_ReprocessGroup'), hist.Kind === 'NZB');
enableAllButtons();
$HistoryEditDialog.modal({backdrop: 'static'});
}
function disableAllButtons()
{
$('#HistoryEditDialog .modal-footer .btn').attr('disabled', 'disabled');
setTimeout(function()
{
$('#HistoryEdit_Transmit').show();
}, 500);
}
function enableAllButtons()
{
$('#HistoryEditDialog .modal-footer .btn').removeAttr('disabled');
$('#HistoryEdit_Transmit').hide();
}
function itemDelete()
{
disableAllButtons();
notification = '#Notif_History_Deleted';
RPC.call('editqueue', ['HistoryDelete', 0, '', [curHist.ID]], completed);
}
function itemReturn()
{
disableAllButtons();
notification = '#Notif_History_Returned';
RPC.call('editqueue', ['HistoryReturn', 0, '', [curHist.ID]], completed);
}
function itemReprocess()
{
disableAllButtons();
notification = '#Notif_History_Reproces';
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
}
function completed()
{
$HistoryEditDialog.modal('hide');
editCompleted();
}
}();
}(jQuery));

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -2,7 +2,7 @@
<!--
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -101,6 +101,11 @@
<li class="divider"></li>
<li><a href="#" onclick="Status.pauseClick('download')"><table><tr><td><i class="icon-ok" id="CHSoftPauseDownload"></td><td>Soft-Pause Download</td></tr></table></a></li>
<li class="divider"></li>
<li class="menu-header">Pause for</li>
<li><a href="#" onclick="Status.scheduledPauseClick(30*60)"><table><tr><td></td><td>30 Minutes</td></tr></table></a></li>
<li><a href="#" onclick="Status.scheduledPauseClick(3*60*60)"><table><tr><td></td><td>3 Hours</td></tr></table></a></li>
<li><a href="#" onclick="Status.scheduledPauseDialogClick()"><table><tr><td></td><td>Custom...</td></tr></table></a></li>
<li class="divider"></li>
<li><a data-toggle="modal" href="#PauseHelp"><table><tr><td></td><td>Quick Help</td></tr></table></a></li>
</ul>
</div>
@@ -109,7 +114,7 @@
<!-- SPEED/TIME -->
<div id="InfoBlock">
<div href="#" onclick="Status.limitDialogClick()" title="Current speed (click to set speed limit)"><i class="icon-plane" id="StatusSpeedIcon"></i> <span id="StatusSpeed">--- KB/s</span></div>
<div href="#" onclick="Status.statDialogClick()" title="Remaining time (click for more statistics)"><i class="icon-time"></i> <span id="StatusTime">--h --m</span></div>
<div href="#" onclick="Status.statDialogClick()" title="Remaining time (click for more statistics)"><i class="icon-time" id="StatusTimeIcon"></i> <span id="StatusTime">--h --m</span></div>
</div>
<!-- REFRESH MENU -->
@@ -122,7 +127,7 @@
<li class="menu-header">Automatic refresh</li>
<li data="0.1"><a href="#"><table><tr><td></td><td>0.1</td><td>Second</td></tr></table></a></li>
<li data="0.2"><a href="#"><table><tr><td></td><td>0.2</td><td>Second</td></tr></table></a></li>
<li data="0.5"><a href="#"><table><tr><td></td></i><td>0.5</td><td>Second</td></tr></table></a></li>
<li data="0.5"><a href="#"><table><tr><td></td><td>0.5</td><td>Second</td></tr></table></a></li>
<li data="1"><a href="#"><table><tr><td></td><td>1</td><td>Second</td></tr></table></a></li>
<li data="5"><a href="#"><table><tr><td></td><td>5</td><td>Seconds</td></tr></table></a></li>
<li data="15"><a href="#"><table><tr><td></td><td>15</td><td>Seconds</td></tr></table></a></li>
@@ -176,8 +181,12 @@
<p id="ErrorAlert-text"></p>
</div>
<!-- *** INIT LOADING-ALERT ************************************************************ -->
<!-- *** ERROR BLOCK ****************************************************************** -->
<div id="RefreshError" data-duration="1000" style="display:none" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Communication error, retrying...</strong>
</div>
<!-- *** INIT LOADING-ALERT ************************************************************ -->
<div class="alert alert-info alert-block" id="FirstUpdateInfo">
<h4 class="alert-heading">Loading...</h4>
Please wait.
@@ -395,17 +404,16 @@
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadServerTemplateError">
<h4 class="alert-heading">Loading configuration failed</h4>
Could not load template configuration file <em>nzbget.conf</em>. NZBGet reads the description of options from this file.
Please put the file <em>nzbget.conf</em> which comes with the distribution archive of NZBGet into webui-directory
(option <em>WebDir</em>).
Do not put your actual configuration file but rather the unchanged original file.
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadPostTemplateError">
<h4 class="alert-heading">Loading configuration of post-processing script failed</h4>
Could not load template post-processing configuration file <em class="ConfigLoadPostTemplateErrorFilename"></em>. NZBGet reads the description of options from this file.
Please put the file <em class="ConfigLoadPostTemplateErrorFilename"></em> which comes with the post-processing script into webui-directory
(option <em>WebDir</em>).
Do not put your actual configuration file but rather the unchanged original file.
Could not load template configuration file <em>nzbget.conf</em>. The file contains descriptions of options
and is needed to display settings page. The file comes with NZBGet distribution archive and is usually
automaically installed into the right location. This seems not to be a case with your installation though.<br><br>
<span id="ConfigLoadServerTemplateErrorEmpty">Please put the template configuration file <em>nzbget.conf</em> into the
directory with web-interface files (<em><span id="ConfigLoadServerTemplateErrorWebDir"></span></em>).</span>
<span id="ConfigLoadServerTemplateErrorNotFound">Please edit your configuration file
(<em><span id="ConfigLoadServerTemplateErrorConfigFile"></span></em>) in a text editor
and set the option <em>ConfigTemplate</em> to point to the template configuration file <em>nzbget.conf</em>.</span>
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadError">
<h4 class="alert-heading">Loading configuration failed</h4>
@@ -441,39 +449,35 @@
<div id="ConfigInfo">
<p>
On this page your can review and change settings. When you done with changes click
<em><strong>Save all changes</strong></em>, which will save the changes made on all pages.
It's not neccessary to save the changes on each page individually.
</p>
<p>The configuration consists of two parts:
<ul>
<li>NZBGet settings</li>
<li>Post-processing script settings</li>
</ul>
On this page you can review and change settings. When you done with changes click
<em><strong>Save all changes</strong></em>, which saves the changes made in all sections.
It's not neccessary to save changes in each section individually.
</p>
<h4>NZBGet settings</h4>
<p>
With these settings you configure NZBGet. When you configure NZBGet for the first time you need
When you configure NZBGet for the first time you need
to check at least the option <a class="option" href="#" data-category="S" onclick="Config.scrollToOption(event, this)">MainDir</a> and configure one news server.
Any changes of NZBGet settings require a restart or reload (soft-restart) of NZBGet.
</p>
<h4>Post-processing script settings</h4>
<p>
When NZBGet finishes download
of nzb-file it can start a post-processing script for further processing (unpacking etc.).
You can configure what script must be executed using option <a class="option" href="#" data-category="S" onclick="Config.scrollToOption(event, this)">PostProcess</a>
in section <em><strong>POST-PROCESSING</strong></em>.
</p>
<p>
The <em><strong>POST-PROCESSING SCRIPT SETTINGS</strong></em> are shown on the navigation bar
only if you have setup a post-processing script and if it has a configuration file. For your convenience
NZBGet proivides you with a way to configure the post-processing script options via web-interface.
These settings however do not
have any effect on NZBGet but rather on the post-processing script only. The changes made here are in
effect after saving for the next post-processing job started by NZBGet. A restart of NZBGet is not required.
There are many configuration options affecting performance. If you use
NZBGet on a computer with limited capabilities, such as NAS, media player,
router, etc. you should take your time to configure NZBGet for best
performance - see <a href="http://nzbget.sourceforge.net/Performance_tips">Performance tips</a>.
</p>
<h4>Post-processing scripts settings</h4>
<p>
When NZBGet finishes download of a nzb-file it can execute post-processing scripts for further processing (cleanup, etc.).
To configure scripts use options in section <em><strong>POST-PROCESSING SCRIPTS</strong></em>.
</p>
<p>
If your post-processing scripts define own options they are also shown here and can be configured like NZBGets built-in options.
</p>
<h4>Backup and restore settings</h4>
<p>
This can be done in section <em><strong>SYSTEM</strong></em>.
</p>
</div>
@@ -493,14 +497,16 @@
</div>
</div>
<!--
<div class="control-group config-static config-system">
<label class="control-label nowrap">Backup Settings</label>
<div class="controls">
<button type="button" class="btn" onclick="TODO()">Backup</button>
<button type="button" class="btn" onclick="ConfigBackupRestore.backupSettings()">Backup</button>
<p class="help-block">
<span class="help-option-title">Save settings to local file.</span><br><br>
<span class="help-option-title">Save settings to a local file.</span><br><br>
Export all settings for backup purpose.
<span id="ConfigBackupSafariNote"><br><br>
<span class="label label-warning">NOTE:</span>
This works with all major browsers except Safari (tested with 6.0.4; may work with newer versions).</span>
</p>
</div>
</div>
@@ -508,14 +514,14 @@
<div class="control-group config-static config-system">
<label class="control-label nowrap">Restore Settings</label>
<div class="controls">
<button type="button" class="btn" onclick="TODO()">Restore</button>
<button type="button" class="btn" onclick="ConfigBackupRestore.restoreSettings()">Restore</button>
<input type="file" id="Config_RestoreInput" class="hidden-file-input">
<p class="help-block">
<span class="help-option-title">Load settings from local file.</span><br><br>
Import all settings from a perviously created backup file.
<span class="help-option-title">Load settings from a local file.</span><br><br>
Import settings from a previously created backup file. You can restore the whole configuration file or choose individual sections.
</p>
</div>
</div>
-->
</fieldset>
</div>
@@ -550,8 +556,6 @@
<p>The original project was initially created by Sven Henkel (sidddy@users.sourceforge.net) in 2004 and later developed by Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005. In 2007 the abandoned project was overtaken by Andrey Prygunkov. Since then the program has been completely rewritten.</p>
<p>Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).</p>
<h2>Copyright</h2>
<p>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
@@ -564,7 +568,7 @@
<h2>Contact</h2>
<p>For more info please visit <a href="http://nzbget.sourceforge.net">NZBGet Home Page</a>. Among other things the developers of third-party apps find there complete docs about RPC interface.</p>
<p>Should you need help, have suggestions or want to share your improvements - <a href="http://sourceforge.net/apps/phpbb/nzbget/">NZBGet Forum</a> is a place to do that.</p>
<p>Should you need help, have suggestions or want to share your improvements - <a href="http://nzbget.sourceforge.net/forum">NZBGet Forum</a> is a place to do that.</p>
<p>If you use NZBGet on a media player, router, NAS or other non-PC device the best place to get help is probably forums specialized on your device.</p>
<h1>Information about included libraries</h1>
@@ -631,6 +635,34 @@
</div>
</div>
<!-- *** PAUSE FOR X MINUTES ***************************************************** -->
<div class="modal modal-mini hide" id="ScheduledPauseDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Temporary Pause</h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<fieldset>
<div class="control-group">
<label class="control-label" for="PauseForInputGroup">Pause for</label>
<div class="controls" id="PauseForInputGroup">
<div class="input-append">
<input class="input-mini" id="PauseForInput" size="16" type="text"><span class="add-on">Minutes</span>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div class="dialog-transmit hide" style="position:absolute;margin-left:260px;" id="ScheduledPause_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" onclick="Status.pauseForClick()">Pause</a>
</div>
</div>
<!-- *** QUICK HELP: EXPLAINING PAUSE OPTIONS ************************************************************ -->
<div class="modal hide" id="PauseHelp">
@@ -701,13 +733,13 @@
</table>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="modal-bottom-toolbar">
<button class="btn" data-tab="DownloadsEdit_FileTab" data-fullscreen="true" id="DownloadsEdit_File">Files <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_ParamTab" id="DownloadsEdit_Param" title="Post-processing parameters">PP Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_LogTab" data-fullscreen="true" id="DownloadsEdit_Log" title="Post-processing messages">PP Messages <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_FileTab" data-fullscreen="true" id="DownloadsEdit_File" title="File list">Files <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_ParamTab" id="DownloadsEdit_Param" title="Post-processing parameters">PP-Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="DownloadsEdit_LogTab" data-fullscreen="true" id="DownloadsEdit_Log" title="Post-processing messages">PP-Messages <i class="icon-forward" style="opacity:0.6;"></i></button>
</div>
</div>
</div>
@@ -728,10 +760,12 @@
<button class="btn" onclick="DownloadsEditDialog.editActionClick('pause')" title="Pause selected files"><i class="icon-pause"></i><span class="btn-caption"> Pause</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('resume')" title="Resume selected files"><i class="icon-play"></i><span class="btn-caption"> Resume</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('delete')" title="Delete selected files"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('split')" title="Split selected files into new download"><i class="icon-split"></i><span class="btn-caption"> Split</span></button>
</div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('pause')" title="Pause selected files"><i class="icon-pause"></i><span class="btn-caption"> Pause</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('resume')" title="Resume selected files"><i class="icon-play"></i><span class="btn-caption"> Resume</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('delete')" title="Delete selected files"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('split')" title="Split selected files into new download"><i class="icon-split"></i><span class="btn-caption"> Split</span></button></div>
<!-- MOVE BUTTONS -->
<div class="btn-group phone-hide">
@@ -929,6 +963,40 @@
</div>
</div>
<!-- *** SPLIT DOWNLOAD DIALOG ************************************************************ -->
<div class="modal modal-padded hide" id="DownloadsSplitDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Split Download</h3>
</div>
<div class="modal-body">
<div class="form-vertical">
<fieldset>
<p>New download will be created from selected files. Please choose the name for new download.</p>
<div class="control-group">
<div class="controls" id="DownloadsSplit_NZBNameGroup">
<input type="text" class="input-xxlarge" id="DownloadsSplit_NZBName" />
</div>
</div>
<p class="help-block" style="margin-top:10px;">The new download inherits
all other properties such as category, priority, etc. from the source item.
This action closes the download-edit-dialog. Any changes made in the dialog are not saved.</p>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div class="dialog-transmit hide" style="position:absolute;margin-left:260px;" id="DownloadsSplit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="DownloadsSplit_Split">Split</a>
</div>
</div>
<!-- *** QUICK HELP: Configure Categores ************************************************** -->
<div class="modal modal-mini modal-center hide" id="ConfigureCategoriesHelp">
@@ -946,52 +1014,76 @@
<div class="modal hide" id="HistoryEditDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<a class="back" id="HistoryEdit_Back" href="#" title="Back"><i class="icon-back"></i></a>
<a class="back back-hidden" id="HistoryEdit_BackSpace"><i class="icon-hidden"></i></a>
<h3 id="HistoryEdit_Title"></h3>
</div>
<div class="modal-body">
<div class="form-horizontal">
<fieldset>
<div>
<div class="modal-tab" id="HistoryEdit_GeneralTab">
<div class="form-horizontal">
<fieldset>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Status">Status</label>
<div class="controls" style="margin-top: 4px;">
<span id="HistoryEdit_Status"></span>
</div>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Status">Status</label>
<div class="controls" style="margin-top: 4px;">
<span id="HistoryEdit_Status"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Category">Category</label>
<div class="controls">
<span class="input-xlarge uneditable-input" id="HistoryEdit_Category"></span>
</div>
</div>
<div class="control-group" id="HistoryEdit_PathGroup">
<label class="control-label" for="HistoryEdit_Path">Download Path</label>
<div class="controls">
<span class="uneditable-mulitline-input" id="HistoryEdit_Path"></span>
</div>
</div>
<div class="control-group control-group-last" id="HistoryEdit_StatisticsGroup">
<label class="control-label" for="HistoryEdit_Statistics">Statistics</label>
<div class="controls">
<table class="table table-striped table-bordered data-statistics">
<tbody id="HistoryEdit_Statistics">
</tbody>
</table>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="modal-bottom-toolbar">
<button class="btn" data-tab="HistoryEdit_ParamTab" id="HistoryEdit_Param" title="Post-processing parameters">PP-Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
</div>
</div>
</div>
</fieldset>
</div>
</div>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Category">Category</label>
<div class="controls">
<span class="input-xlarge uneditable-input" id="HistoryEdit_Category"></span>
</div>
<div class="modal-tab hide" id="HistoryEdit_ParamTab">
<div class="form-horizontal">
<fieldset id="HistoryEdit_ParamData">
</fieldset>
</div>
<div class="control-group" id="HistoryEdit_PathGroup">
<label class="control-label" for="HistoryEdit_Path">Download Path</label>
<div class="controls">
<span class="uneditable-mulitline-input" id="HistoryEdit_Path"></span>
<p class="help-block">Post-processing script may have moved files elsewhere.</p>
</div>
</div>
<div class="control-group" style="margin-top: 15px; margin-bottom:0;" id="HistoryEdit_StatisticsGroup">
<label class="control-label" for="HistoryEdit_Statistics">Statistics</label>
<div class="controls">
<table class="table table-striped table-bordered data-statistics">
<tbody id="HistoryEdit_Statistics">
</tbody>
</table>
</div>
</div>
</fieldset>
</div>
</div>
</div>
<div class="modal-footer">
<div class="btn-group pull-left" id="HistoryEdit_DeleteGroup">
<a href="#" class="btn pull-left" id="HistoryEdit_Delete"><i class="icon-trash"></i> Delete</a>
<div class="btn-group dropup pull-left" id="HistoryEdit_DeleteGroup">
<a class="btn dropdown-toggle btn-danger" data-toggle="dropdown"><i class="icon-trash-white"></i> Delete <span class="caret"></span></a>
<ul class="dropdown-menu confirm-menu">
<li class="menu-header">Confirm</li>
<li><a href="#" id="HistoryEdit_Delete">Yes, Delete</a></li>
</ul>
</div>
<div class="btn-group dropup pull-left" id="HistoryEdit_ReprocessGroup">
@@ -1003,15 +1095,16 @@
</div>
<div class="btn-group dropup pull-left" id="HistoryEdit_ReturnGroup">
<a class="btn dropdown-toggle" data-toggle="dropdown">Return to Queue <span class="caret"></span></a>
<a class="btn dropdown-toggle" data-toggle="dropdown">Return <span class="caret"></span></a>
<ul class="dropdown-menu confirm-menu">
<li class="menu-header">Confirm</li>
<li><a href="#" id="HistoryEdit_Return">Yes, Return</a></li>
<li><a href="#" id="HistoryEdit_Return">Yes, Return to Queue</a></li>
</ul>
</div>
<div class="dialog-transmit" style="position:absolute;margin-left:400px;" id="HistoryEdit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
<div class="dialog-transmit" style="position:absolute;margin-left:320px;" id="HistoryEdit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn" data-dismiss="modal" id="HistoryEdit_Close">Close</a>
<a href="#" class="btn btn-primary" id="HistoryEdit_Save">Save changes</a>
</div>
</div>
@@ -1041,8 +1134,13 @@
<p>
Delete selected downloads?
</p>
<p class="confirm-help-block">
Selected items will be removed from the queue. Already downloaded files remain on disk.
<p id="DownloadsDeleteConfirmDialog_Remain" class="confirm-help-block">
Selected items will be removed from queue. Already downloaded files remain on disk (this behavior can be changed via option
<strong><em>DeleteCleanupDisk</em></strong>).
</p>
<p id="DownloadsDeleteConfirmDialog_Cleanup" class="confirm-help-block">
Selected items will be removed from queue. Already downloaded files will be deleted from disk (this behavior can be changed via option
<strong><em>DeleteCleanupDisk</em></strong>).
</p>
</div>
<div id="DownloadsDeleteConfirmDialog_OK">Delete</div>
@@ -1129,7 +1227,7 @@
<label class="control-label" for="URL">Add local files</label>
<div class="controls">
<div style="margin-top:6px; margin-bottom:10px;"><a href="#" class="btn" style="margin-top: -6px;" id="AddDialog_Select">Select files</a> or drop files here.</div>
<input multiple="multiple" type="file" id="AddDialog_Input">
<input multiple="multiple" type="file" id="AddDialog_Input" class="hidden-file-input">
<p class="help-block" id="AddDialog_FilesHelp">You need a modern browser for this to work.</p>
<div class="dialog-add-files hide" id="AddDialog_Files"></div>
</div>
@@ -1205,6 +1303,91 @@
</div>
</div>
<!-- *** CHOOSE SCRIPT DIALOG ****************************************************** -->
<div class="modal modal-padded hide" id="ScriptListDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3 id="ScriptListDialog_Title">Choose scripts</h3>
</div>
<div class="modal-body" style="padding-bottom:0px">
<div class="form-vertical">
<fieldset>
<p id="ScriptListDialog_Instruction">Select scripts for option <strong>DefScript</strong></p>
<div>
<table class="table table-striped table-bordered table-check table-cancheck order-mode datatable" id="ScriptListDialog_ScriptTable" style="margin-bottom:15px;">
<thead><tr><th><div class="check img-check"/></th><th>Name</th></tr></thead>
<tbody></tbody>
</table>
</div>
<p id="ScriptListDialog_OrderInfo" class="hide" style="margin-top:-5px; margin-bottom:10px;">The script execution order is saved globally in the option <em><strong>ScriptOrder</strong></em> and affects all
categories as well as the order of scripts in the edit download dialog.</p>
<div class="hide" id="ScriptListDialog_ScriptTable_pager"></div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="ScriptListDialog_Save">Save</a>
</div>
</div>
<!-- *** RESTORE SETTINGS DIALOG ****************************************************** -->
<div class="modal modal-padded hide" id="RestoreSettingsDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Restore Settings</h3>
</div>
<div class="modal-body" style="padding-bottom:0px">
<div class="form-vertical">
<fieldset>
<p id="RestoreSettingsDialog_Instruction">Select sections to restore. Tip: click on the checkbox of the table header to select all sections.</p>
<div>
<table class="table table-striped table-bordered table-check table-cancheck datatable" id="RestoreSettingsDialog_SectionTable" style="margin-bottom:15px;">
<thead><tr><th><div class="check img-check"/></th><th>Section</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="hide" id="RestoreSettingsDialog_SectionTable_pager"></div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="RestoreSettingsDialog_Restore">Restore</a>
</div>
</div>
<!-- *** SETTINGS RESTORED DIALOG ************************************************** -->
<div class="modal modal-mini modal-center hide" id="SettingsRestoredDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Settings restored</h3>
</div>
<div class="modal-body">
<p>Settings have been loaded from backup file.</p>
<p>You can review the loaded settings and then save them by clicking on <em><strong>Save all changes</strong></em>.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
<!-- *** QUICK HELP: New Configuration Option ************************************************** -->
<div class="modal modal-mini modal-center hide" id="ConfigNewOptionHelp">
@@ -1304,6 +1487,18 @@
<strong>Merged</strong>
</div>
<div id="Notif_Downloads_Splitted" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Splitted</strong>
</div>
<div id="Notif_Downloads_SplitError" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Could not split. Check messages for errors.</strong>
</div>
<div id="Notif_Downloads_SplitNotPossible" data-duration="4000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Cannot split. Some of selected files are already (partially) downloaded.</strong>
</div>
<div id="Notif_Downloads_Select" data-duration="2000" class="alert alert-error alert-center alert-center-small hide">
<strong>Please select records first</strong>
</div>
@@ -1340,6 +1535,10 @@
<strong>Post-Processing</strong>
</div>
<div id="Notif_History_Saved" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Saved</strong>
</div>
<div id="Notif_Config_Unchanged" data-duration="2000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Nothing to save</strong><br>No changes have been made
</div>
@@ -1353,6 +1552,14 @@
Please wait few seconds, then refresh the page.
</div>
<div id="Notif_Config_RestoreSections" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Please select at least one section</strong>
</div>
<div id="Notif_Config_Restoring" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Restoring settings...</strong>
</div>
<div id="Notif_Debug" data-duration="400" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Debug</strong>
</div>

View File

@@ -26,11 +26,10 @@
* In this module:
* 1) Web-interface intialization;
* 2) Web-interface settings;
* 3) RPC queue;
* 4) Refresh handling;
* 5) Window resize handling including automatic theme switching (desktop/phone);
* 6) Confirmation dialog;
* 7) Popup notifications.
* 3) Refresh handling;
* 4) Window resize handling including automatic theme switching (desktop/phone);
* 5) Confirmation dialog;
* 6) Popup notifications.
*/
/*** WEB-INTERFACE SETTINGS (THIS IS NOT NZBGET CONFIG!) ***********************************/
@@ -65,6 +64,10 @@ var UISettings = (new function($)
// The choosen interval is saved in web-browser and then restored.
// The default value sets the interval on first use only.
this.refreshInterval = 1;
// Number of refresh attempts if a communication error occurs.
// If all attempts fail, an error is displayed and the automatic refresh stops.
this.refreshRetries = 4;
// URL for communication with NZBGet via JSON-RPC
this.rpcUrl = './jsonrpc';
@@ -140,7 +143,7 @@ var Frontend = (new function($)
$('#FirstUpdateInfo').show();
UISettings.load();
RPCController.init();
Refresher.init();
initControls();
switchTheme();
@@ -149,26 +152,26 @@ var Frontend = (new function($)
Options.init();
Status.init();
Downloads.init({ updateTabInfo: updateTabInfo });
DownloadsEditDialog.init();
DownloadsMultiDialog.init();
DownloadsMergeDialog.init();
Messages.init({ updateTabInfo: updateTabInfo });
History.init({ updateTabInfo: updateTabInfo });
Upload.init();
Config.init({ updateTabInfo: updateTabInfo });
ConfigBackupRestore.init();
ConfirmDialog.init();
ScriptListDialog.init();
RestoreSettingsDialog.init();
Refresher.init(RPCController.refresh);
DownloadsEditDialog.init();
DownloadsMultiDialog.init();
DownloadsMergeDialog.init();
DownloadsSplitDialog.init();
HistoryEditDialog.init();
$(window).resize(windowResized);
initialized = true;
Refresher.update();
// DEBUG: activate config tab
//$('#DownloadsTab').removeClass('fade').removeClass('in');
//$('#ConfigTabLink').tab('show');
}
function initControls()
@@ -237,8 +240,6 @@ var Frontend = (new function($)
windowResized();
firstLoad = false;
}
Refresher.refreshCompleted();
}
function beforeTabShow(e)
@@ -459,15 +460,23 @@ var Frontend = (new function($)
}(jQuery));
/*** RPC CONTROL *********************************************************/
/*** REFRESH CONTROL *********************************************************/
var RPCController = (new function($)
var Refresher = (new function($)
{
'use strict';
// State
var loadQueue;
var firstLoad = true;
var secondsToUpdate = -1;
var refreshTimer = 0;
var indicatorTimer = 0;
var indicatorFrame=0;
var refreshPaused = 0;
var refreshing = false;
var refreshNeeded = false;
var refreshErrors = 0;
this.init = function()
{
@@ -475,13 +484,17 @@ var RPCController = (new function($)
RPC.connectErrorMessage = 'Cannot establish connection to NZBGet.'
RPC.defaultFailureCallback = rpcFailure;
RPC.next = loadNext;
$('#RefreshMenu li a').click(refreshIntervalClick);
$('#RefreshButton').click(refreshClick);
updateRefreshMenu();
}
this.refresh = function()
function refresh()
{
UISettings.connectionError = false;
$('#ErrorAlert').hide();
Refresher.refreshStarted();
refreshStarted();
loadQueue = new Array(
function() { Options.update(); },
@@ -511,49 +524,44 @@ var RPCController = (new function($)
{
firstLoad = false;
Frontend.loadCompleted();
refreshCompleted();
}
}
function rpcFailure(res)
function rpcFailure(res, result)
{
// If a communication error occurs during status refresh we retry:
// first attempt is made immediately, other attempts are made after defined refresh interval
if (refreshing && !(result && result.error))
{
refreshErrors = refreshErrors + 1;
if (refreshErrors === 1 && refreshErrors <= UISettings.refreshRetries)
{
refresh();
return;
}
else if (refreshErrors <= UISettings.refreshRetries)
{
$('#RefreshError').show();
scheduleNextRefresh();
return;
}
}
Refresher.pause();
UISettings.connectionError = true;
$('#FirstUpdateInfo').hide();
$('#ErrorAlert-text').html(res);
$('#ErrorAlert').show();
$('#RefreshError').hide();
if (Status.status)
{
// stop animations
Status.redraw();
}
};
}(jQuery));
/*** REFRESH CONTROL *********************************************************/
var Refresher = (new function($)
{
'use strict';
// State
var secondsToUpdate = -1;
var refreshTimer = 0;
var indicatorTimer = 0;
var indicatorFrame=0;
var refreshPaused = 0;
var refreshing = false;
var refreshNeeded = false;
var refreshCallback;
this.init = function(refresh)
{
refreshCallback = refresh;
$('#RefreshMenu li a').click(refreshIntervalClick);
$('#RefreshButton').click(refreshClick);
updateRefreshMenu();
}
this.refreshStarted = function()
function refreshStarted()
{
clearTimeout(refreshTimer);
refreshPaused = 0;
@@ -562,9 +570,11 @@ var Refresher = (new function($)
refreshAnimationShow();
}
this.refreshCompleted = function()
function refreshCompleted()
{
refreshing = false;
refreshErrors = 0;
$('#RefreshError').hide();
scheduleNextRefresh();
}
@@ -601,7 +611,8 @@ var Refresher = (new function($)
// force animation restart
indicatorFrame = 0;
}
refreshCallback();
refreshErrors = 0;
refresh();
}
function scheduleNextRefresh()
@@ -625,7 +636,7 @@ var Refresher = (new function($)
secondsToUpdate -= 0.1;
if (secondsToUpdate <= 0)
{
refreshCallback();
refresh();
}
else
{
@@ -663,7 +674,7 @@ var Refresher = (new function($)
'transform': 'rotate(' + degree + 'deg)'
});
if (!refreshing && indicatorFrame === 0 && (UISettings.refreshInterval === 0 || UISettings.refreshInterval > 1 || !UISettings.refreshAnimation) || UISettings.connectionError)
if ((!refreshing && indicatorFrame === 0 && (UISettings.refreshInterval === 0 || UISettings.refreshInterval > 1 || !UISettings.refreshAnimation)) || UISettings.connectionError)
{
indicatorTimer = 0;
}

View File

@@ -1757,7 +1757,7 @@ table .span24 {
*margin-right: .3em;
line-height: 14px;
vertical-align: text-top;
background-image: url("../img/glyphicons-halflings.png");
/* background-image: url("../img/glyphicons-halflings.png"); */
background-position: 14px 14px;
background-repeat: no-repeat;
}
@@ -1768,7 +1768,7 @@ table .span24 {
}
.icon-white {
background-image: url("../img/glyphicons-halflings-white.png");
/* background-image: url("../img/glyphicons-halflings-white.png"); */
}
.icon-glass {

View File

@@ -49,6 +49,7 @@ var Status = (new function($)
var $StatusLeft;
var $StatusSpeed;
var $StatusSpeedIcon;
var $StatusTimeIcon;
var $StatusTime;
var $StatusURLs;
var $PlayBlock;
@@ -59,6 +60,8 @@ var Status = (new function($)
var $CurSpeedLimitBlock;
var $LimitDialog;
var $StatDialog;
var $ScheduledPauseDialog;
var $PauseForInput;
// State
var status;
@@ -85,12 +88,15 @@ var Status = (new function($)
$StatusLeft = $('#StatusLeft');
$StatusSpeed = $('#StatusSpeed');
$StatusSpeedIcon = $('#StatusSpeedIcon');
$StatusTimeIcon = $('#StatusTimeIcon');
$StatusTime = $('#StatusTime');
$StatusURLs = $('#StatusURLs');
$CurSpeedLimit = $('#CurSpeedLimit');
$CurSpeedLimitBlock = $('#CurSpeedLimitBlock');
$LimitDialog = $('#LimitDialog');
$StatDialog = $('#StatDialog');
$ScheduledPauseDialog = $('#ScheduledPauseDialog')
$PauseForInput = $('#PauseForInput');
if (UISettings.setFocus)
{
@@ -98,6 +104,10 @@ var Status = (new function($)
{
$('#SpeedLimitInput').focus();
});
$ScheduledPauseDialog.on('shown', function()
{
$('#PauseForInput').focus();
});
}
$PlayAnimation.hover(function() { $PlayBlock.addClass('hover'); }, function() { $PlayBlock.removeClass('hover'); });
@@ -169,6 +179,11 @@ var Status = (new function($)
'<span class="label label-status label-success">active</span>')) +
'</td></tr>';
if (status.ResumeTime > 0)
{
content += '<tr><td>Autoresume</td><td class="text-right">' + Util.formatTimeHMS(status.ResumeTime - status.ServerTime) + '</td></tr>';
}
content += '</tbody>';
content += '</table>';
@@ -188,7 +203,11 @@ var Status = (new function($)
if (status.ServerStandBy)
{
$StatusSpeed.html('--- KB/s');
if (status.RemainingSizeHi > 0 || status.RemainingSizeLo > 0)
if (status.ResumeTime > 0)
{
$StatusTime.html(Util.formatTimeLeft(status.ResumeTime - status.ServerTime));
}
else if (status.RemainingSizeMB > 0 || status.RemainingSizeLo > 0)
{
if (status.AverageDownloadRate > 0)
{
@@ -217,9 +236,11 @@ var Status = (new function($)
}
}
$StatusSpeedIcon.toggleClass('icon-plane', status.DownloadLimit === 0);
$StatusSpeedIcon.toggleClass('icon-truck', status.DownloadLimit !== 0);
$StatusTime.toggleClass('scheduled-resume', status.ServerStandBy && status.ResumeTime > 0);
$StatusTimeIcon.toggleClass('icon-time', !(status.ServerStandBy && status.ResumeTime > 0));
$StatusTimeIcon.toggleClass('icon-time-orange', status.ServerStandBy && status.ResumeTime > 0);
}
function updatePlayButton()
@@ -374,6 +395,34 @@ var Status = (new function($)
});
}
this.scheduledPauseClick = function(seconds)
{
RPC.call('pausedownload2', [],
function(){RPC.call('pausepost', [],
function(){RPC.call('pausescan', [],
function(){RPC.call('scheduleresume', [seconds], Refresher.update)})})});
}
this.scheduledPauseDialogClick = function()
{
$PauseForInput.val('');
$ScheduledPauseDialog.modal();
}
this.pauseForClick = function()
{
var val = $PauseForInput.val();
var minutes = parseInt(val);
if (isNaN(minutes) || minutes <= 0)
{
return;
}
$ScheduledPauseDialog.modal('hide');
this.scheduledPauseClick(minutes * 60);
}
function modalShow()
{
modalShown = true;

View File

@@ -1,7 +1,7 @@
/*!
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -225,7 +225,7 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
margin-right: 10px;
padding-top: 2px;
cursor: pointer;
width: 85px;
width: 86px;
}
#InfoBlock div {
@@ -254,6 +254,14 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
filter: alpha(opacity=100);
}
#StatusTime.scheduled-resume {
color: #F08929;
}
#StatusTime.scheduled-resume:hover {
color: #FFA15A;
}
.navbar-inner .btn:hover i {
opacity: 1;
filter: alpha(opacity=100);
@@ -542,6 +550,14 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
background-position: -464px -48px;
}
.icon-time-orange {
background-position: -400px -80px;
}
.icon-split {
background-position: -432px -80px;
}
.img-checkmark {
background-position: -432px -16px;
}
@@ -574,6 +590,10 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
text-transform: uppercase;
}
.controls .label-status {
line-height: 22px;
}
/* links in black color */
.datatable a {
color: #000000;
@@ -654,7 +674,8 @@ form {
margin-bottom: 0px;
}
#DownloadsEdit_PostParamData {
#DownloadsEdit_PostParamData,
#HistoryEdit_PostParamData {
padding-bottom: 1px;
}
@@ -771,6 +792,11 @@ tr.checked td {
background-color: #FFFFCC;
}
table.table-hidecheck thead > tr > th:first-child,
table.table-hidecheck tbody > tr > td:first-child {
display: none;
}
.checked .progress {
background-color: #FFFFE1;
}
@@ -880,7 +906,11 @@ tr.checked td {
}
.modal .form-horizontal .control-group:last-child {
margin-bottom: 0;
margin-bottom: 0;
}
.modal .form-horizontal .retain-margin .control-group:last-child {
margin-bottom: 15px;
}
.form-horizontal .control-group-last {
@@ -899,6 +929,11 @@ tr.checked td {
.modal .input-xlarge {
width: 350px;
}
.modal.modal-padded .input-xxlarge {
width: 470px;
}
/* END: override bootstrap styles for modals */
.modal-bottom-toolbar .btn {
@@ -951,7 +986,7 @@ ul.help > li {
}
/* Make "select files" native control invisible */
#AddDialog_Input {
.hidden-file-input {
position: absolute;
left: 0;
top: 0;
@@ -1079,6 +1114,11 @@ ul.help > li {
font-size: 12px;
}
#ConfigNav.nav-list.long-list a {
padding-top: 3px;
padding-bottom: 3px;
}
#ConfigNav.nav-list > .active > a,
#ConfigNav.nav-list > .active > a:hover {
color: #ffffff;
@@ -1116,7 +1156,7 @@ ul.help > li {
margin-right: 15px;
}
#ConfigContent span.help-option-title {
span.help-option-title {
color: #8D1212;
}
@@ -1145,6 +1185,19 @@ ul.help > li {
width: 150px;
}
#ConfigContent table.editor {
width: 97%;
}
#ConfigContent table.editor td:first-child {
width: 100%;
padding-right:15px;
}
#ConfigContent table.editor input {
width: 100%;
}
.ConfigFooter hr {
margin: 6px 0 15px;
}
@@ -1175,6 +1228,8 @@ div.ConfigFooter {
font-weight: normal;
}
#DownloadsEdit_ParamTab div.control-group.wants-divider,
#HistoryEdit_ParamTab div.control-group.wants-divider,
#ConfigContent div.control-group,
#ConfigContent.search div.control-group.multiset {
border-bottom: 1px solid #eeeeee;
@@ -1195,7 +1250,6 @@ div.ConfigFooter {
#ConfigContent .control-label {
width: 170px;
white-space: nowrap;
}
#ConfigContent .form-horizontal .controls {
@@ -1239,11 +1293,37 @@ div.ConfigFooter {
color: #005580;
}
/* table "LIST OF ALL OPTIONS" */
#ConfigContent .datatable td:first-child {
font-weight: bold;
#ScriptListDialog_ScriptTable td:nth-child(2) {
padding-right: 100px;
}
#ScriptListDialog_ScriptTable .btn-row-order-block {
float: right;
width: 100px;
margin-right: -115px;
display: block;
}
#ScriptListDialog_ScriptTable .btn-row-order {
float: none;
width: 20px;
display: none;
}
#ScriptListDialog_ScriptTable tr:hover .btn-row-order {
display: inline-block;
cursor: pointer;
}
#ScriptListDialog_ScriptTable tbody > tr:first-child div.btn-row-order:first-child,
#ScriptListDialog_ScriptTable tbody > tr:last-child div.btn-row-order:last-child,
#ScriptListDialog_ScriptTable tbody > tr:first-child div.btn-row-order:nth-child(2),
#ScriptListDialog_ScriptTable tbody > tr:last-child div.btn-row-order:nth-child(3) {
opacity: 0.4;
}
/*#ScriptListDialog_ScriptTable*/
/****************************************************************************/
/* SMARTPHONE THEME */
@@ -1324,6 +1404,10 @@ body.phone,
height: inherit;
}
.phone .controls .label-status {
line-height: 28px;
}
.phone .menu-header {
font-size: 18px;
line-height: 24px;
@@ -1705,7 +1789,8 @@ body.phone,
text-align: center;
}
.phone .modal-mini .modal-body {
.phone .modal-mini .modal-body,
.phone .modal-padded .modal-body {
padding-left: 15px;
padding-right: 15px;
}
@@ -1793,6 +1878,8 @@ body.phone,
}
.modal .input-xlarge ,
.modal .input-xxlarge,
.modal.modal-padded .input-xxlarge,
.uneditable-mulitline-input {
width: 95%;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -170,11 +170,22 @@ var Upload = (new function($)
function selectFiles()
{
$('#AddDialog_Input').click();
var inp = $('#AddDialog_Input');
// Reset file input control (needed for IE10)
inp.wrap('<form>').closest('form').get(0).reset();
inp.unwrap();
inp.click();
}
function fileSelectHandler(event)
{
if (!event.target.files)
{
alert("Unfortunately your browser doesn't support direct access to local files.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
return;
}
addFiles(event.target.files);
}
@@ -184,8 +195,9 @@ var Upload = (new function($)
for (var i = 0; i<selectedFiles.length; i++)
{
var file = selectedFiles[i];
var filename = file.name.replace(/\.queued$/g, '');
var html = '<table><tr><td width="18px" valign="top"><i class="icon-file" style="vertical-align:top;margin-top:2px;"></i><img class="hide" style="vertical-align:top;margin-top:1px;" src="img/transmit-file.gif" width="16px" height="16px"></td><td>' +
Util.formatNZBName(file.name) + '</td></tr></table>';
Util.formatNZBName(filename) + '</td></tr></table>';
$('#AddDialog_Files').append(html);
files.push(file);
}
@@ -250,10 +262,10 @@ var Upload = (new function($)
}
var testreader = new FileReader();
if (!testreader.readAsBinaryString)
if (!testreader.readAsBinaryString && !testreader.readAsDataURL)
{
$AddDialog.modal('hide');
alert("Unfortunately your browser doesn't support the function \"readAsBinaryString\" of FileReader API.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
alert("Unfortunately your browser doesn't support neither \"readAsBinaryString\" nor \"readAsDataURL\" functions of FileReader API.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
return;
}
}
@@ -304,13 +316,29 @@ var Upload = (new function($)
var reader = new FileReader();
reader.onload = function (event)
{
var base64str = window.btoa(event.target.result);
var base64str;
if (reader.readAsBinaryString)
{
base64str = window.btoa(event.target.result);
}
else
{
base64str = event.target.result.replace(/^data:[^,]+,/, '');
}
var category = $('#AddDialog_Category').val();
var priority = parseInt($('#AddDialog_Priority').val());
RPC.call('append', [file.name, category, priority, false, base64str], fileCompleted, fileFailure);
var filename = file.name.replace(/\.queued$/g, '');
RPC.call('append', [filename, category, priority, false, base64str], fileCompleted, fileFailure);
};
reader.readAsBinaryString(file);
if (reader.readAsBinaryString)
{
reader.readAsBinaryString(file);
}
else
{
reader.readAsDataURL(file);
}
}
function fileCompleted(result)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -172,6 +172,14 @@ var Util = (new function($)
.replace(/>/g, '&gt;');
}
this.textToAttr = function(str)
{
return str.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
this.setMenuMark = function(menu, data)
{
// remove marks from all items
@@ -253,7 +261,7 @@ var TabDialog = (new function($)
var dialog = this;
var sign = options.back ? -1 : 1;
var fullscreen = options.fullscreen && !options.back;
var bodyPadding = 30;
var bodyPadding = 15;
var dialogMargin = options.mini ? 0 : 15;
var dialogBorder = 2;
@@ -284,8 +292,8 @@ var TabDialog = (new function($)
// CONTROL POINT: at this point the destination dialog size is active
// store destination positions and sizes
var newBodyHeight = fullscreen ? windowHeight - header.outerHeight() - footer.outerHeight() - dialogMargin*2 - bodyPadding : body.height();
var newTabWidth = fullscreen ? windowWidth - dialogMargin*2 - dialogBorder - bodyPadding : toTab.width();
var newBodyHeight = fullscreen ? windowHeight - header.outerHeight() - footer.outerHeight() - dialogMargin*2 - bodyPadding*2 : body.height();
var newTabWidth = fullscreen ? windowWidth - dialogMargin*2 - dialogBorder - bodyPadding*2 : toTab.width();
var leftPos = toTab.position().left;
var newDialogPosition = dialog.position();
var newDialogWidth = dialog.width();
@@ -305,9 +313,9 @@ var TabDialog = (new function($)
body.css({position: '', height: oldBodyHeight});
dialog.css('overflow', 'hidden');
fromTab.css({position: 'absolute', left: leftPos, width: oldTabWidth});
toTab.css({position: 'absolute', width: newTabWidth, height: newBodyHeight,
left: sign * ((options.back ? newTabWidth : oldTabWidth) + bodyPadding)});
fromTab.css({position: 'absolute', left: leftPos, width: oldTabWidth, height: oldBodyHeight});
toTab.css({position: 'absolute', width: newTabWidth, height: oldBodyHeight,
left: sign * ((options.back ? newTabWidth : oldTabWidth) + bodyPadding*2)});
fromTab.show();
// animate dialog to destination position and sizes
@@ -348,8 +356,9 @@ var TabDialog = (new function($)
body.animate({height: newBodyHeight}, duration);
}
fromTab.animate({left: sign * -((options.back ? newTabWidth : oldTabWidth) + bodyPadding)}, duration);
toTab.animate({left: leftPos}, duration, function()
fromTab.animate({left: sign * -((options.back ? newTabWidth : oldTabWidth) + bodyPadding*2),
height: newBodyHeight + bodyPadding}, duration);
toTab.animate({left: leftPos, height: newBodyHeight + bodyPadding}, duration, function()
{
fromTab.hide();
fromTab.css({position: '', width: '', height: '', left: ''});
@@ -396,7 +405,7 @@ var RPC = (new function($)
var _this = this;
var request = JSON.stringify({nocache: new Date().getTime(), method: method, params: params});
var xhr = createXMLHttpRequest();
var xhr = new XMLHttpRequest();
xhr.open('post', this.rpcUrl);
@@ -463,40 +472,4 @@ var RPC = (new function($)
};
xhr.send(request);
}
function createXMLHttpRequest()
{
var xmlHttp;
if (window.XMLHttpRequest)
{
xmlHttp = new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
try
{
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e)
{
try
{
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch(e)
{
throw(e);
}
}
}
if (xmlHttp==null)
{
alert("Your browser does not support XMLHTTP.");
throw("Your browser does not support XMLHTTP.");
}
return xmlHttp;
}
}(jQuery));

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,6 +36,8 @@
#ifndef DISABLE_TLS
/* Define to 1 to use GnuTLS library for TLS/SSL-support */
#define HAVE_LIBGNUTLS
/* Define to 1 to use OpenSSL library for TLS/SSL-support */
//#define HAVE_OPENSSL
#endif
/* Define to the name of macro which returns the name of function being
@@ -57,6 +59,9 @@
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#define HAVE_PAR2_CANCEL
/* Define to 1 if libpar2 has bugfixes applied (needs a special patch) */
#define HAVE_PAR2_BUGFIXES_V2
/* Define to 1 if function GetAddrInfo is supported */
#define HAVE_GETADDRINFO
@@ -69,7 +74,7 @@
/* Define to 1 if spinlocks are supported */
#define HAVE_SPINLOCK
#define VERSION "9.0-testing"
#define VERSION "11.0"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE