Compare commits

..

74 Commits
v14.2 ... v15.0

Author SHA1 Message Date
Andrey Prygunkov
36d772e5fe version 15.0 2015-05-20 05:40:07 +00:00
Andrey Prygunkov
91ab53a471 updated version string (preparing to release 15.0) 2015-05-19 19:25:35 +00:00
Andrey Prygunkov
9656d5274c updated ChangeLog 2015-05-19 18:24:48 +00:00
Andrey Prygunkov
01fd805ee6 fixed: crash during fetching of RSS feeds (Linux installer, CPU-architecture "armel" only) 2015-05-16 20:30:30 +00:00
Andrey Prygunkov
846b3b81ff improved installer for Linux: 1) changed defaults directory names for DestDir and InterDir; 2) automatically activating article cache, increasing write buffer and par repair buffer, if the system has enough free RAM; 3) automatically disabling simultaneous download and post-processing if the system has slow CPU 2015-05-13 16:37:41 +00:00
Andrey Prygunkov
5c921c2bff fixed: static builds for Linux may display a warning about wrong libz.so version 2015-05-12 20:51:30 +00:00
Andrey Prygunkov
d3317a48c0 improved build script for unpackers (Linux only) 2015-05-11 20:53:04 +00:00
Andrey Prygunkov
ac8b7610cf added CPU target x86_64 to Linux installer 2015-05-11 20:52:32 +00:00
Andrey Prygunkov
a06edd4c4e fixed: integrity check may not work in the linux installer 2015-05-10 14:04:32 +00:00
Andrey Prygunkov
b9d6c11e63 added linux build scripts into subversion repository and distribution source archive 2015-05-09 21:49:02 +00:00
Andrey Prygunkov
ce9519c9cb added linux installer files to distribution archive 2015-05-09 12:45:59 +00:00
Andrey Prygunkov
dbda657e6e created universal installer for Linux; the installer includes precompiled binaries for many common CPU architectures including x86, ARM, MIPS, PowerPC; CPU architecture is automatically detected and an appropriate binary is installed; configuration file is adjusted and the program can be started immediately; the installer supports automatic updates via web-interface (Settings - SYSTEM - Check for updates) 2015-05-09 12:15:13 +00:00
Andrey Prygunkov
7d2b895bfb updated URLs to fetch version information during automatic updates 2015-05-09 11:57:20 +00:00
Andrey Prygunkov
ad719a2c07 updated README 2015-05-08 21:57:35 +00:00
Andrey Prygunkov
ae31139ffe improved support for update-scripts: 1) all command line parameters used to launch nzbget are passed to the script in env vars NZBUP_CMDLINEX, where X is a parameter number starting with 0; 2) if the path to update-script defined in package-info.json does not start with slash the path is considered being relative to application directory; 3) new env var NZBUP_RUNMODE (DAEMON, SERVER) is passed to the script; 4) fixed: env var NZBUP_PROCESSID had wrong value (ID of the parent process instead of the nzbget process) 2015-05-05 18:56:04 +00:00
Andrey Prygunkov
bea0806a47 addition to r1270: suppressed few warnings when compiled in release mode with clang 2015-05-05 18:46:51 +00:00
Andrey Prygunkov
7394595abb addition to r1271: fixed possible crash on start or reload 2015-05-04 22:57:08 +00:00
Andrey Prygunkov
d2ce6f826a fixed: lowercase hex digits were not correctly parsed in URLs passed to RPC-API method "append" 2015-05-04 21:50:06 +00:00
Andrey Prygunkov
2544ff5902 configuration file nzbget.conf is now also searched in the app-directory on all platforms (for easier installation) 2015-04-29 21:08:54 +00:00
Andrey Prygunkov
113306ac23 disabled unnecessary assert-statements in par2-module when building in release mode 2015-04-27 19:49:21 +00:00
Andrey Prygunkov
e2cedb5594 updated ChangeLog 2015-04-25 19:59:47 +00:00
Andrey Prygunkov
63cf2ec616 fixed: web-interface may fail to load on Firefox mobile 2015-04-25 19:54:47 +00:00
Andrey Prygunkov
c99cc22138 disabled changing of compiler options during configuring in debug mode (--enable-debug); it conflicted with cross-compiling and did not allow to pass extra options via CXXFLAGS; required debug options must be passed via CXXFLAGS now (for example for gcc: CXXFLAGS=-g ./configure --enable-debug) 2015-04-24 21:43:43 +00:00
Andrey Prygunkov
b76f7bd3ba fixed: command "make install" installed README from par2-subdirectory instead of main README 2015-04-22 20:56:06 +00:00
Andrey Prygunkov
d37e9ea1c3 reverted back r1264: a proper toolchain fixes the issue making the trick not neccessary 2015-04-17 21:39:33 +00:00
Andrey Prygunkov
1d008961ab addition to r1250: fixed: connection could not be established on certain systems 2015-04-16 22:22:46 +00:00
Andrey Prygunkov
8a7224d4b5 addition to r1261: improved windows installer to reuse the old installation path if available 2015-04-15 21:09:20 +00:00
Andrey Prygunkov
bd95a7fc01 improved update script: configuring the search path for system commands (tasklist, find, etc.) (Windows only) 2015-04-15 18:48:11 +00:00
Andrey Prygunkov
6a9cdc9e18 fixed: when updating the installation path was not properly set if the update was installed under another user account (Windows only) 2015-04-14 21:39:57 +00:00
Andrey Prygunkov
3a9fbf88bd addition to r1250: better handling of multiple addresses and protocols 2015-04-14 21:06:12 +00:00
Andrey Prygunkov
d9ca826095 improved handling of temporary paths with spaces in the update script for Windows (Windows only) 2015-04-14 20:59:53 +00:00
Andrey Prygunkov
1d36fbba53 addition to r1257: improved support for paths with spaces 2015-04-13 19:41:48 +00:00
Andrey Prygunkov
7d230aeebc improved logging during update (Windows only) 2015-04-12 21:52:25 +00:00
Andrey Prygunkov
4e573c46cb updated ChangeLog 2015-04-11 18:54:41 +00:00
Andrey Prygunkov
0c649c3959 download speed in context menu of menubar icon is now shown in MB/s instead of KB/s (for speeds from 1 MB/s) (Mac OS X only) 2015-04-11 18:53:36 +00:00
Andrey Prygunkov
a14a99b681 added confirmation dialog for command "Download again" in history list 2015-04-11 11:32:49 +00:00
Andrey Prygunkov
aefd87e3e5 addition to r1182: do not block news servers when a download is cancelled/deleted 2015-04-03 11:34:33 +00:00
Andrey Prygunkov
109d56b55e improved connection handling when fetching nzb-files and rss feeds; do not print warning "Content-Length is not submitted by server..." anymore 2015-04-03 09:55:28 +00:00
Andrey Prygunkov
4eed0b38f8 addition to r1250: fixed compiling error on a certain system (added missing include) 2015-04-03 09:44:06 +00:00
Andrey Prygunkov
424ae68621 1) added button "Test Connection" to make a news server connection test from web-interface; 2) improved timeout handling when connecting to news servers which have multiple addresses; 3) improved error handling when communicating with secure servers (do not trying to send quit-command if connection could not be established or was interrupted; this avoids unnecessary timeout) 2015-03-31 19:52:57 +00:00
Andrey Prygunkov
101b2e7bdf removed shell script "nzbgetd" which were used to control nzbget as a service; modern systems manage services in a diffreent way and do not require that old script anymore 2015-03-28 17:21:35 +00:00
Andrey Prygunkov
049dba4b06 small change in text message generated by pp-script "EMail.py" 2015-03-28 13:09:50 +00:00
Andrey Prygunkov
3024d32257 fixed: command "nzbget -L H" may crash if the history contained URL-items with certain status 2015-03-27 22:50:02 +00:00
Andrey Prygunkov
784ed7f21b fixed: action "Split" may not work for bad nzb-files with missing segments; new Field "Progress" returned by RPC-method "listfiles" shows the download progress of the file taking missing articles into account 2015-03-26 22:30:32 +00:00
Andrey Prygunkov
2de44bfd99 new action "Mark as success" on history page and in history details dialog; items marked as success are considered successfully downloaded and processed, which is important for duplicate check; new action "HistoryMarkSuccess" in RPC-method "editqueue"; new subcommand "S" of command "-E H" (command line interface); new status "SUCCESS/MARK" can be returned by RPC-method "history" 2015-03-26 22:28:30 +00:00
Andrey Prygunkov
65e391a4a7 fixed: update log shown during automatic update via web-interface may show duplicate messages or messages may clear out 2015-03-26 22:13:03 +00:00
Andrey Prygunkov
16057247c2 partially reverted back r1233: the program terminates if the lock-file cannot be created or the lock could not be acquired and an error message is printed to the log-file; the termination prevents unintentional starting of multiple daemon instances (daemon mode, POSIX only) 2015-03-25 20:31:53 +00:00
Andrey Prygunkov
04506c1e1e fixed: automatic update via web-interface may not work (Windows only) 2015-03-24 17:18:22 +00:00
Andrey Prygunkov
7038e2a18e changed defaults for few logging options 2015-03-22 17:48:39 +00:00
Andrey Prygunkov
3429444c0c additon to r1235: improved error reporting when using command "Troubleshooting -> Reset to Factory Defaults" (Windows only) 2015-03-22 17:24:42 +00:00
Andrey Prygunkov
3c46953d47 updated desription of few options 2015-03-22 17:21:18 +00:00
Andrey Prygunkov
6c7a1b5697 improved the quality of speed throttling when a speed limit is active 2015-03-21 16:12:01 +00:00
Andrey Prygunkov
cc35644e24 adjusted defaults for few settings on Windows; in order to make these settings hidden from web ui 2015-03-20 20:12:10 +00:00
Andrey Prygunkov
ccf2b3bc5b added hidden webui setting "rowSelect" to select records by clicking on any part of the row, not just on the check mark; to activate it change the setting "rowSelect" in webui/index.js 2015-03-20 20:02:44 +00:00
Andrey Prygunkov
698a46eb7b added command "Troubleshooting -> Reset to Factory Defaults" to tray menu (Windows only) 2015-03-19 21:58:01 +00:00
Andrey Prygunkov
9e5de62841 fixed: in JSON-RPC the request-id was not transfered back in the response as required by JSON-RPC specification 2015-03-17 21:58:49 +00:00
Andrey Prygunkov
70ac9029c6 no more fatal abort if the lock-file cannot be created (daemon mode only, POSIX only); an error message is printed to log instead 2015-03-16 20:53:30 +00:00
Andrey Prygunkov
b66b989de0 fixed: parsing of RPC-parameters passed via URL were sometimes incorrect 2015-03-16 18:25:37 +00:00
Andrey Prygunkov
9c82b2ea34 when moving files to final destination the hidden files (with names starting with dot) are considered unimportant and no errors are printed if they cannot be moved; such files (.AppleDouble, .DS_Store, etc.) are usually used by services to hold metadata and can be safely ignored 2015-03-13 21:14:52 +00:00
Andrey Prygunkov
d033088113 option sets (such as news-servers, categories, etc.) can now be reordered using news buttons "move up" and "move down" 2015-03-12 20:27:25 +00:00
Andrey Prygunkov
240ccdc65e fixed: par-check in full verification mode (not in quick mode) could not detected damaged files if they were completely empty (0 bytes), which is possible when option "DirectWrite" was not active and all articles of the file were missing 2015-03-10 22:13:06 +00:00
Andrey Prygunkov
975d632007 improved cleanup (option ExtCleanupDisk): now the files are deleted in subdirectories too (recursively) 2015-03-04 20:29:38 +00:00
Andrey Prygunkov
53371344ae improved cleanup: if download was successful with health 100% the cleanup is now performed even if par-check and unpack were not made; previously a successful par-check or unpack were required for cleanup 2015-03-04 20:12:07 +00:00
Andrey Prygunkov
e9356ebe79 added built-in update feature to windows package; accessible via web-interface -> settings -> system -> check for updates 2015-03-02 20:49:05 +00:00
Andrey Prygunkov
bc0c8fc84a files with names starting with ".nfs" are now ignored during cleanup and moving of unpacked files; these files are managed by NAS software 2015-02-27 21:07:26 +00:00
Andrey Prygunkov
6252f2c8c1 updated pp-script "EMail.py": 1) to use the new nzb-log feature; 2) new option "SendMail" allows to choose if the e-mail should be send always or on failure only 2015-02-27 19:20:15 +00:00
Andrey Prygunkov
708f819182 updated pp-script "Logger.py" to use the new nzb-log feature 2015-02-27 19:18:22 +00:00
Andrey Prygunkov
bad4c7ed34 renamed option "CreateBrokenLog" to "BrokenLog"; the old option name is recognized and automatically converted when the configuration is saved in web-interface 2015-02-27 18:43:15 +00:00
Andrey Prygunkov
7e6f8f19eb each nzb now has its own individual log, where messages printed during download or post-processing are saved; the messages can be retrieved later at any time; new button "Log" in the history details dialog; button "Log" in the download details dialog is now active during download too (not only during post-processing); the log contains all nzb-related messages except detail-messages and errors printed during retrieving of articles (they would produce way too many messages and are not that useful anyway); new option "NzbLog" to deactivate per-nzb logging if necessary; per-nzb logs are saved in the queue-directory (option "QueueDir"); new RPC-method "loadlog" returns the previously saved messages for a given nzb-file; new field "MessageCount" is returned by RPC-methods "listgroups" and "history" and indicates if there are any messages saved for the item; parameter "NumberOfLogEntries" of RPC-method "listgroups" and the field "Log" returned by the method are now deprecated, use method "loadlag" instead; field "PostInfoText" returned by RPC-method "listgroups" is now automatically filled with the latest message printed by a pp-script eliminating the need to access deprecated field "Log" 2015-02-26 20:57:38 +00:00
Andrey Prygunkov
a6fd969ead addition to r1217: rewritten descriptions of new options 2015-02-22 22:23:17 +00:00
Andrey Prygunkov
7b5443d680 addition to r1217: options "FeedX.URL" are no longer hidden from the restricted user; indexer API keys (which are part of URLs) can be leaked via other API calls and therefore are not really protected by simple hiding of feed URLs 2015-02-22 22:16:51 +00:00
Andrey Prygunkov
8de723d2aa added a small button near feed name in the feed menu on downloads-page; a click on the button fetches the feed, whereas a click on the feed title shows feed's content (as before) 2015-02-22 22:05:59 +00:00
Andrey Prygunkov
82b252ce2e added restricted user and add-user; restricted user has access to most program functions but cannot see security related options (including usernames and passwords) and cannot save configuration; restricted user can be used with other programs and web-sites; add-user can only add new downloads via RPC-API and can be used with other programs or web-sites 2015-02-20 21:05:51 +00:00
Andrey Prygunkov
a2dfb26b36 improved detection of malformed nzb-files: nzbs which are valid xml-documents but without nzb content are now rejected with an appropriate error message 2015-02-19 19:10:15 +00:00
116 changed files with 7498 additions and 1764 deletions

262
ChangeLog
View File

@@ -1,22 +1,260 @@
nzbget-15.0:
- improved application for Windows:
- added tray icon (near clock);
- left click on icon pauses/resumes download;
- right click opens menu with important functions;
- console window can be shown/hidden via preferences (is hidden by
default);
- new preference to automatically start the program after login;
- new preference to show browser on start;
- new preference to hide tray icon;
- menu commands to show important folders in windows explorer
(destination, etc.);
- on first start the config file is now placed into subdirectory
"NZBGet" inside standard AppData-directory;
- default destination and other directories are now placed in the
AppData\NZBGet-directory instead of programs directory; this allows
to install the program into "program files"-directory since the
program does not write into the programs directory anymore;
- the program exe has an icon now;
- if the exe is started from windows explorer the program starts in
application mode; if the exe is called from command prompt the program
works in console mode;
- added built-in update feature to windows package; accessible via
web-interface -> settings -> system -> check for updates;
- created installer for windows:
- the program is installed into "program files" by default;
- the working directory with all subdirectories is now placed into
"AppData" directory;
- the batch files nzbget-start.bat and nzbget-recovery-mode.bat are not
needed and not installed anymore;
- created installer for Linux:
- included are precompiled binaries for the most common CPU architectures:
x86, ARM, MIPS and PowerPC;
- installer automatically detects CPU architecture of the system and
installs an appropriate executable;
- configuration file is automatically preconfigured for immediate use;
- installation on supported platforms has become as simple as:
download, run installer, start installed nzbget;
- installer supports automatic updates via web-interface -> settings
-> system - check for updates;
- added support for password list file:
- new option "UnpackPassFile" to set the location of the file;
- during unpack the passwords are tried from the file until unpack
succeeds or all passwords were tried;
- implemented different strategies for rar4 and rar5-archives taking
into account the features of formats;
- for rar5-archives a wrong password is reported by unrar unambiguously
and the program can immediately try other passwords from the password
list;
- for rar4-archives and for 7z-archives it is not possible to
differentiate between damaged archive and wrong password; for those
archives if the first unpack attempt (without password) fails the
program executes par-check (preferably quick par-check if enabled via
option "ParQuick) before trying the passwords from the list;
- another optimization is that the password list is tried only when the
first unpack attempt (without password) reports a password error or
decryption errors; this saves unnecessary unpack attempts for damaged
unencrypted archives;
- options "UnrarCmd" and "SevenZipCmd" can include extra switches to pass to
unrar/7-zip:
- this allows for easy passing of additional parameters without creating
of proxy shell scripts;
- improved news server connections handling:
- if a download of an article fails due to connection error the news
server becomes temporary disabled (blocked) for several seconds
(defined by option "RetryInterval");
- the download is then retried on another news server (of the same
level) if available;
- if no other news servers (of the same level) exist the program will
retry the same news server after its block interval expires;
- this increases failure tolerance when multiple news servers are used;
- added on-demand queue sorting:
- one click on column title in web interface sorts the selected or all
items;
- if the items were already sorted in that order they are sorted
backwards; in other words the second click sorts in descending order;
- when sorting selected items they are also grouped together in a case
there were holes between selected items;
- RPC-method "editqueue" has new command "GroupSort", parameter "Text"
must be one of: "name", "priority", "category", "size", "left"; add
character "+" or "-" to explicitly define ascending or descending
order (for example "name-"); if none of these characters
is used the auto-mode is active: the items are sorted in ascending
order first, if nothing changed - they are sorted again in descending
order;
- added restricted user and add-user:
- restricted user has access to most program functions but cannot see
security related options (including usernames and passwords) and
cannot save configuration;
- restricted user can be used with other programs and web-sites;
- add-user can only add new downloads via RPC-API and can be used with
other programs or web-sites;
- added per-nzb logging:
- each nzb now has its own individual log;
- messages printed during download or post-processing are saved;
- the messages can be retrieved later at any time;
- new button "Log" in the history details dialog;
- button "Log" in the download details dialog is now active during
download too (not only during post-processing);
- the log contains all nzb-related messages except detail-messages and
errors printed during retrieving of articles (they would produce way
too many messages and are not that useful anyway);
- new option "NzbLog" to deactivate per-nzb logging if necessary;
- per-nzb logs are saved in the queue-directory (option "QueueDir");
- new RPC-method "loadlog" returns the previously saved messages for a
given nzb-file;
- new field "MessageCount" is returned by RPC-methods "listgroups" and
"history" and indicates if there are any messages saved for the item;
- parameter "NumberOfLogEntries" of RPC-method "listgroups" and the
field "Log" returned by the method are now deprecated, use method
"loadlog" instead;
- field "PostInfoText" returned by RPC-method "listgroups" is now
automatically filled with the latest message printed by a pp-script
eliminating the need to access deprecated field "Log"';
- actions for history items can now be performed for multiple (selected) records:
- post-process again, download again, mark as good, mark as bad;
- extended RPC-API method "editqueue": for history-records of type
"URL" the action "HistoryRedownload" can now be used as synonym to
"HistoryReturn" (makes it easier to redownload multiple items of
different types (URL and NZB) with one API call).
- options "ParIgnoreExt" and "ExtCleanupDisk" can now contain wildcard
characters * and ?;
- added new option "ServerX.Retention" to define server retention time (days);
files older than configured server retention time are not even tried on this
server;
- added support for negative numeric values in rss filter (useful for fields
"dupescore" and "priority");
- added subcommand "HA" to remote command "--list/-L" to list the whole
history including hidden records;
- added optional parameters to remote command "--append/-A" allowing to pass
duplicate key, duplicate mode and duplicate score; removed parameters "F"
and "U" of command "--append/-A", which were used to set mode (file or URL),
which is now detected automatically; the parameters are still supported for
compatibility;
- name and category of history items can now be changed in web-interface;
RPC-API method "editqueue" extended with new actions "HistorySetName" and
"HistorySetCategory";
- improved timeout handling during establishing of connections;
- updated pp-script "EMail.py":
- using the new nzb-log feature;
- new option "SendMail" allows to choose if the e-mail should be send
always or on failure only;
- updated pp-script "Logger.py" to use the new nzb-log feature;
- improved cleanup (option ExtCleanupDisk):
- if download was successful with health 100% the cleanup is now
performed even if par-check and unpack were not made; previously a
successful par-check or unpack were required for cleanup;
- now the files are deleted in subdirectories too (recursively);
- added a small button near feed name in the feed menu on downloads-page; a
click on the button fetches the feed, whereas a click on the feed title
shows feed's content (as before);
- improved detection of malformed nzb-files: nzbs which are valid
xml-documents but without nzb content are now rejected with an appropriate
error message;
- new action "Mark as success" on history page and in history details dialog:
- items marked as success are considered successfully downloaded and
processed, which is important for duplicate check;
- use this command if the download was repaired outside of NZBGet;
- new action "HistoryMarkSuccess" in RPC-method "editqueue";
- new subcommand "S" of command "-E H" (command line interface);
- new status "SUCCESS/MARK" can be returned by RPC-method "history";
- improved support for update-scripts:
- all command line parameters used to launch nzbget are passed to the
script in env vars NZBUP_CMDLINEX, where X is a parameter number
starting with 0;
- if the path to update-script defined in package-info.json does not
start with slash the path is considered being relative to application
directory;
- new env var NZBUP_RUNMODE (DAEMON, SERVER) is passed to the script;
- fixed: env var NZBUP_PROCESSID had wrong value (ID of the parent
process instead of the nzbget process);
- added button "Test Connection" to make a news server connection test from
web-interface;
- renamed option "CreateBrokenLog" to "BrokenLog"; the old option name is
recognized and automatically converted when the configuration is saved in
web-interface;
- improved the quality of speed throttling when a speed limit is active;
- added hidden webui setting "rowSelect" to select records by clicking on any
part of the row, not just on the check mark; to activate it change the
setting "rowSelect" in webui/index.js;
- when moving files to final destination the hidden files (with names starting
with dot) are considered unimportant and no errors are printed if they
cannot be moved; such files (.AppleDouble, .DS_Store, etc.) are usually
used by services to hold metadata and can be safely ignored;
- option sets (such as news-servers, categories, etc.) can now be reordered
using news buttons "move up" and "move down";
- updated info in about dialogs (Windows and Mac OS X);
- updated description of few options;
- changed defaults for few logging options;
- improved timeout handling when connecting to news servers which have
multiple addresses;
- improved error handling when communicating with secure servers (do not
trying to send quit-command if connection could not be established or was
interrupted; this avoids unnecessary timeout);
- improved connection handling when fetching nzb-files and rss feeds; do not
print warning "Content-Length is not submitted by server..." anymore;
- download speed in context menu of menubar icon is now shown in MB/s instead
of KB/s (for speeds from 1 MB/s) (Mac OS X only);
- configuration file nzbget.conf is now also searched in the app-directory on
all platforms (for easier installation);
- removed shell script "nzbgetd" which were used to control nzbget as a
service; modern systems manage services in a diffreent way and do not
require that old script anymore;
- disabled changing of compiler options during configuring in debug mode
(--enable-debug); it conflicted with cross-compiling and did not allow to
pass extra options via CXXFLAGS; required debug options must be passed via
CXXFLAGS now (for example for gcc: CXXFLAGS=-g ./configure --enable-debug);
- disabled unnecessary assert-statements in par2-module when building in
release mode;
- fixed: parsing of RPC-parameters passed via URL were sometimes incorrect;
- fixed: lowercase hex digits were not correctly parsed in URLs passed to
RPC-API method "append";
- fixed: in JSON-RPC the request-id was not transfered back in the response as
required by JSON-RPC specification;
- fixed: par-check in full verification mode (not in quick mode) could not
detect damaged files if they were completely empty (0 bytes), which is
possible when option "DirectWrite" was not active and all articles of the
file were missing;
- fixed possible crash when using remote command "-B dump" to print debug
info;
- fixed: remote command "-L HA" (which prints the history including hidden
records) could crash;
- suppress printing of memory leaks reports when the program terminates
because of wrong command line switches (Windows debug mode only);
- fixed: command "nzbget -L H" may crash if the history contained URL-items
with certain status;
- fixed: action "Split" may not work for bad nzb-files with missing segments;
new Field "Progress" returned by RPC-method "listfiles" shows the download
progress of the file taking missing articles into account;
- if the lock-file cannot be created or the lock could not be acquired an
error message is printed to the log-file;
- fixed: update log shown during automatic update via web-interface may show
duplicate messages or messages may clear out;
- fixed: web-interface may fail to load on Firefox mobile;
- fixed: command "make install" installed README from par2-subdirectory
instead of main README.
nzbget-14.2:
- fixed: the program could crash during download when article cache was
active (more likely on very high download speeds);
- fixed: the program could crash during download when article cache was active
(more likely on very high download speeds);
- fixed: unlike to all other scripts the update-script should not be
automatically terminated when the program quits;
- fixed: XML-RPC method "history" returned invalid xml when used with
parameter "hidden=true" (JSON-RPC was fine).
nzbget-14.1:
- fixed: program could crash during unpack (Posix) or unpack failure
was reported (Windows);
- fixed: program could crash during unpack (Posix) or unpack failure was
reported (Windows);
- fixed: quick par-check could hang on certain nzb-files containing multiple
par-sets (occured only in 64 bit mode);
- fixed: menubar icon was not visible on OSX in dark mode;
- system sleep on idle state is now prevented during download and
post-processing (Mac OSX only);
- fixed: unrar may sometimes fail with message "no files to extract"
(certain Linux systems);
- fixed: menubar icon was not visible on Mac OS X in dark mode; system sleep
on idle state is now prevented during download and post-processing
(Mac OS X only);
- fixed: unrar may sometimes fail with message "no files to extract" (certain
Linux systems);
- fixed false memory leak warning when compiled in debug mode (Windows only);
- fixed: Mac OS X app didn't work on OS X 10.7 Lion.
nzbget-14.0:
- added article cache:
@@ -223,7 +461,7 @@ nzbget-14.0:
- fixed: splitted .cbr-files were not properly joined;
- fixed: inner files (files listed in nzb) bigger than 2GB could not be
downloaded;
- fixed: cleanup may leave some files undeleted (Mac OSX only);
- fixed: cleanup may leave some files undeleted (Mac OS X only);
- fixed: compiler error if configured using parameter "--disable-gzip";
- fixed: one log-message was printed only to global log but not to nzb-item
pp-log;
@@ -677,7 +915,7 @@ nzbget-12.0:
be viewed and changed in download-edit-dialog and
history-edit-dialog via new button "Dupe";
- for full documentation see http://nzbget.net/RSS#Duplicates;
- created NZBGet.app - NZBGet is now a user friendly Mac OSX application
- created NZBGet.app - NZBGet is now a user friendly Mac OS X application
with easy installation and seamless integration into OS UI:
works in background, is controlled from a web-browser, few
important functions are accessible via menubar icon;
@@ -811,7 +1049,7 @@ nzbget-12.0:
DestDir is mounted to a network drive which is not available on program start;
- added special handling for files ".AppleDouble" and ".DS_Store" during
unpack to avoid problems on NAS having support for AFP protocol (used
on Mac OSX);
on Mac OS X);
- history records with failed script status are now shown as "PP-FAILURE"
in history list (instead of just "FAILURE");
- option "DiskSpace" now checks space on "InterDir" in addition to

View File

@@ -183,17 +183,31 @@ AM_CPPFLAGS = \
EXTRA_DIST = \
Makefile.cvs \
nzbgetd \
$(windows_FILES) \
$(osx_FILES)
$(osx_FILES) \
$(linux_FILES)
windows_FILES = \
daemon/windows/NTService.cpp \
daemon/windows/NTService.h \
daemon/windows/win32.h \
daemon/windows/WinConsole.cpp \
daemon/windows/WinConsole.h \
nzbget.sln \
nzbget.vcproj \
nzbget-shell.bat
windows/nzbget-command-shell.bat \
windows/install-update.bat \
windows/README-WINDOWS.txt \
windows/package-info.json \
windows/resources/mainicon.ico \
windows/resources/nzbget.rc \
windows/resources/resource.h \
windows/resources/trayicon_idle.ico \
windows/resources/trayicon_paused.ico \
windows/resources/trayicon_working.ico \
windows/setup/nzbget-setup.nsi \
windows/setup/install.bmp \
windows/setup/uninstall.bmp
osx_FILES = \
osx/App_Prefix.pch \
@@ -226,12 +240,21 @@ osx_FILES = \
osx/Resources/Localizable.strings \
osx/Resources/Welcome.rtf
linux_FILES = \
linux/installer.sh \
linux/install-update.sh \
linux/package-info.json \
linux/build-info.txt \
linux/build-nzbget \
linux/build-unpack
doc_FILES = \
lib/par2/AUTHORS \
lib/par2/README \
AUTHORS \
README \
ChangeLog \
COPYING \
lib/par2/AUTHORS \
lib/par2/README
COPYING
exampleconf_FILES = \
nzbget.conf
@@ -273,7 +296,6 @@ scripts_FILES = \
scripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
@@ -291,13 +313,6 @@ nobase_dist_scripts_SCRIPTS = $(scripts_FILES)
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"

View File

@@ -122,8 +122,8 @@ mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(scriptsdir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"
"$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" \
"$(DESTDIR)$(webuidir)"
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am__nzbget_SOURCES_DIST = daemon/connect/Connection.cpp \
@@ -249,8 +249,7 @@ am__vpath_adj = case $$p in \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
nobase_dist_scriptsSCRIPT_INSTALL = $(install_sh_SCRIPT)
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(nobase_dist_scripts_SCRIPTS) $(sbin_SCRIPTS)
SCRIPTS = $(nobase_dist_scripts_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -453,17 +452,31 @@ AM_CPPFLAGS = \
EXTRA_DIST = \
Makefile.cvs \
nzbgetd \
$(windows_FILES) \
$(osx_FILES)
$(osx_FILES) \
$(linux_FILES)
windows_FILES = \
daemon/windows/NTService.cpp \
daemon/windows/NTService.h \
daemon/windows/win32.h \
daemon/windows/WinConsole.cpp \
daemon/windows/WinConsole.h \
nzbget.sln \
nzbget.vcproj \
nzbget-shell.bat
windows/nzbget-command-shell.bat \
windows/install-update.bat \
windows/README-WINDOWS.txt \
windows/package-info.json \
windows/resources/mainicon.ico \
windows/resources/nzbget.rc \
windows/resources/resource.h \
windows/resources/trayicon_idle.ico \
windows/resources/trayicon_paused.ico \
windows/resources/trayicon_working.ico \
windows/setup/nzbget-setup.nsi \
windows/setup/install.bmp \
windows/setup/uninstall.bmp
osx_FILES = \
osx/App_Prefix.pch \
@@ -496,12 +509,21 @@ osx_FILES = \
osx/Resources/Localizable.strings \
osx/Resources/Welcome.rtf
linux_FILES = \
linux/installer.sh \
linux/install-update.sh \
linux/package-info.json \
linux/build-info.txt \
linux/build-nzbget \
linux/build-unpack
doc_FILES = \
lib/par2/AUTHORS \
lib/par2/README \
AUTHORS \
README \
ChangeLog \
COPYING \
lib/par2/AUTHORS \
lib/par2/README
COPYING
exampleconf_FILES = \
nzbget.conf
@@ -544,7 +566,6 @@ scripts_FILES = \
# Install
sbin_SCRIPTS = nzbgetd
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
@@ -664,25 +685,6 @@ uninstall-nobase_dist_scriptsSCRIPTS:
echo " rm -f '$(DESTDIR)$(scriptsdir)/$$f'"; \
rm -f "$(DESTDIR)$(scriptsdir)/$$f"; \
done
install-sbinSCRIPTS: $(sbin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
$(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \
else :; fi; \
done
uninstall-sbinSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
rm -f "$(DESTDIR)$(sbindir)/$$f"; \
done
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -1846,7 +1848,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/daemon/windows $(distdir)/lib/par2 $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/scripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
$(mkdir_p) $(distdir)/daemon/windows $(distdir)/lib/par2 $(distdir)/linux $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/scripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib $(distdir)/windows $(distdir)/windows/resources $(distdir)/windows/setup
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
@@ -1977,7 +1979,7 @@ check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; do \
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(scriptsdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
@@ -2031,9 +2033,7 @@ install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-exec-am: install-binPROGRAMS install-sbinSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-exec-am: install-binPROGRAMS
install-info: install-info-am
@@ -2063,7 +2063,7 @@ ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-info-am \
uninstall-nobase_dist_scriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
uninstall-nobase_dist_webuiDATA
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
@@ -2074,16 +2074,16 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
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-info install-info-am install-man \
install-nobase_dist_scriptsSCRIPTS \
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_scriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
install-nobase_dist_webuiDATA 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_scriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA
# Note about "sed":
@@ -2095,13 +2095,6 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"

36
README
View File

@@ -44,25 +44,16 @@ depends on command-line parameters passed to the program.
2. Supported OS
=====================================
NZBGet is written in C++ and was initialy developed on Linux.
It was ported to Windows later and tested for compatibility with
several POSIX-OS'es.
It should run at least on:
- Linux Debian 5.0 on x86;
- Linux with uClibc on MIPSEL and ARM;
- OpenBSD 5.0 on x86;
- Mac OS X 10.7 Lion on x64;
- Windows XP SP3 on x86 and Windows 7 on x64.
NZBGet is written in C++ and works on Windows, OS X, Linux and
most POSIX-conform OS'es.
Clients and servers running on different OS'es may communicate with
each other. For example, you can use NZBGet as client on Windows to
control your NZBGet-server running on Linux.
The download-section of NZBGet web-site provides binary files
for Windows. The binary packages for many routers and NAS devices are
also available in OPTWARE repository (http://www.nslu2-linux.org),
but for most POSIX-systems you need to compile the program yourself.
for Windows, OS X and Linux. For most POSIX-systems you need to compile
the program yourself.
If you have downloaded binaries you can just jump to section
"Configuration".
@@ -71,8 +62,8 @@ If you have downloaded binaries you can just jump to section
3. Prerequisites on POSIX
=====================================
NZBGet is developed on a linux-system, but it should run on other
POSIX platforms (see the list of tested platforms above).
NZBGet is developed on a linux-system, but it runs on other
POSIX platforms.
NZBGet absolutely needs the following libraries:
@@ -94,7 +85,7 @@ And the following libraries are optional:
- for gzip support in web-server and web-client (enabled by default):
- zlib (http://www.zlib.net)
All these libraries are included in modern Linux distributions and
All these libraries are included in modern POSIX distributions and
should be available as installable packages. Please note that you also
need the developer packages for these libraries too, they package names
have often suffix "dev" or "devel". On other systems you may need to
@@ -180,8 +171,8 @@ For curses-outputmode you need ncurses or curses on your system.
If you do not have one of them you can download and compile ncurses yourself.
Following configure-parameters may be useful:
--with-libcurses-includes
--with-libcurses-libraries
--with-libcurses-includes=/path/to/curses/includes
--with-libcurses-libraries=/path/to/curses/libraries
If you are not able to use curses or ncurses or do not want them you can
make the program without support for curses using option "--disable-curses":
@@ -200,11 +191,11 @@ the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
Following configure-parameters may be useful:
--with-libtls-includes
--with-libtls-libraries
--with-libtls-includess=/path/to/gnutls/includes
--with-libtls-libraries=/path/to/gnutls/libraries
--with-openssl-includes
--with-openssl-libraries
--with-openssl-includess=/path/to/openssl/includes
--with-openssl-libraries=/path/to/openssl/libraries
If none of these libraries is available you can make the program without
TLS/SSL support using option "--disable-tls":
@@ -253,6 +244,7 @@ The program looks for configuration file in following standard
locations (in this order):
On POSIX systems:
<EXE-DIR>/nzbget.conf
~/.nzbget
/etc/nzbget.conf
/usr/etc/nzbget.conf

View File

@@ -155,6 +155,9 @@
/* Define to 1 if the system has the type `_Bool'. */
#undef HAVE__BOOL
/* Define to 1 to exclude debug-code */
#undef NDEBUG
/* Name of package */
#undef PACKAGE

45
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 14.2.
# Generated by GNU Autoconf 2.61 for nzbget 15.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='14.2'
PACKAGE_STRING='nzbget 14.2'
PACKAGE_VERSION='15.0'
PACKAGE_STRING='nzbget 15.0'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="daemon/main/nzbget.cpp"
@@ -1233,7 +1233,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 14.2 to adapt to many kinds of systems.
\`configure' configures nzbget 15.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1304,7 +1304,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 14.2:";;
short | recursive ) echo "Configuration of nzbget 15.0:";;
esac
cat <<\_ACEOF
@@ -1435,7 +1435,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 14.2
nzbget configure 15.0
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1449,7 +1449,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 14.2, which was
It was created by nzbget $as_me 15.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2245,7 +2245,7 @@ fi
# Define the identity of the package.
PACKAGE=nzbget
VERSION=14.2
VERSION=15.0
cat >>confdefs.h <<_ACEOF
@@ -8243,13 +8243,11 @@ _ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h> /* for off_t */
#include <stdio.h>
#include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0);
;
return 0;
}
@@ -8289,13 +8287,11 @@ cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define _LARGEFILE_SOURCE 1
#include <sys/types.h> /* for off_t */
#include <stdio.h>
#include <stdio.h>
int
main ()
{
int (*fp) (FILE *, off_t, int) = fseeko;
return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
return fseeko (stdin, 0, 0) && (fseeko) (stdin, 0, 0);
;
return 0;
}
@@ -10070,13 +10066,6 @@ _ACEOF
if test "$CC" = "gcc"; then
CXXFLAGS="-g -Wall"
else
CXXFLAGS=""
fi
{ echo "$as_me:$LINENO: checking for macro returning current function name" >&5
echo $ECHO_N "checking for macro returning current function name... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
@@ -10327,6 +10316,12 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
else
cat >>confdefs.h <<\_ACEOF
#define NDEBUG 1
_ACEOF
fi
@@ -10756,7 +10751,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 14.2, which was
This file was extended by nzbget $as_me 15.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -10809,7 +10804,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 14.2
nzbget config.status 15.0
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View File

@@ -1,7 +1,7 @@
#
# This file is part of nzbget
#
# Copyright (C) 2008-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2008-2015 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
@@ -23,9 +23,9 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 14.2, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 15.0, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 14.2)
AM_INIT_AUTOMAKE(nzbget, 15.0)
AC_CONFIG_SRCDIR([daemon/main/nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -524,16 +524,6 @@ dnl
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
dnl
dnl Set debug flags for gcc (if gcc is used)
dnl
if test "$CC" = "gcc"; then
CXXFLAGS="-g -Wall"
else
CXXFLAGS=""
fi
dnl
dnl check for __FUNCTION__ or __func__ macro
dnl
@@ -597,6 +587,8 @@ AC_MSG_CHECKING(for rdynamic linker flag)
dnl
dnl End of debugging code
dnl
else
AC_DEFINE([NDEBUG],1,Define to 1 to exclude debug-code)
fi

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -49,8 +49,12 @@
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#endif
#include <vector>
#include <algorithm>
#include "nzbget.h"
#include "Connection.h"
#include "Log.h"
@@ -62,7 +66,6 @@ Mutex* Connection::m_pMutexGetHostByName = NULL;
#endif
#endif
void Connection::Init()
{
debug("Initializing global connection data");
@@ -128,6 +131,7 @@ Connection::Connection(const char* szHost, int iPort, bool bTLS)
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_iTotalBytesRead = 0;
m_bBroken = false;
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_bTLSError = false;
@@ -254,14 +258,18 @@ bool Connection::Bind()
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error("Could not resolve hostname %s", m_szHost);
ReportError("Could not resolve hostname %s", m_szHost, false, 0);
return false;
}
m_bBroken = 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);
#ifdef WIN32
SetHandleInformation((HANDLE)m_iSocket, HANDLE_FLAG_INHERIT, 0);
#endif
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
@@ -345,6 +353,10 @@ int Connection::WriteLine(const char* pBuffer)
}
int iRes = send(m_iSocket, pBuffer, strlen(pBuffer), 0);
if (iRes <= 0)
{
m_bBroken = true;
}
return iRes;
}
@@ -364,6 +376,7 @@ bool Connection::Send(const char* pBuffer, int iSize)
int iRes = send(m_iSocket, pBuffer + iBytesSent, iSize-iBytesSent, 0);
if (iRes <= 0)
{
m_bBroken = true;
return false;
}
iBytesSent += iRes;
@@ -392,6 +405,7 @@ char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
m_bBroken = true;
break;
}
else if (iBufAvail == 0)
@@ -530,6 +544,7 @@ bool Connection::DoConnect()
debug("Do connecting");
m_iSocket = INVALID_SOCKET;
m_bBroken = false;
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
@@ -548,32 +563,49 @@ bool Connection::DoConnect()
return false;
}
std::vector<SockAddr> triedAddr;
bool bConnected = false;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
bool bLastAddr = !addr->ai_next;
// don't try the same combinations of ai_family, ai_socktype, ai_protocol multiple times
SockAddr sa = { addr->ai_family, addr->ai_socktype, addr->ai_protocol };
if (std::find(triedAddr.begin(), triedAddr.end(), sa) != triedAddr.end())
{
continue;
}
triedAddr.push_back(sa);
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
#ifdef WIN32
SetHandleInformation((HANDLE)m_iSocket, HANDLE_FLAG_INHERIT, 0);
#endif
if (m_iSocket == INVALID_SOCKET)
{
res = connect(m_iSocket , addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
if (bLastAddr)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
}
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
// try another addr/family/protocol
continue;
}
else if (bLastAddr)
if (ConnectWithTimeout(addr->ai_addr, addr->ai_addrlen))
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
// Connection established
bConnected = true;
break;
}
}
if (m_iSocket == INVALID_SOCKET && addr_list)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
}
if (!bConnected && m_iSocket != INVALID_SOCKET)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
@@ -600,8 +632,7 @@ bool Connection::DoConnect()
return false;
}
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
if (!ConnectWithTimeout(&sSocketAddress, sizeof(sSocketAddress)))
{
ReportError("Connection to %s failed", m_szHost, true, 0);
closesocket(m_iSocket);
@@ -610,18 +641,9 @@ bool Connection::DoConnect()
}
#endif
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
#else
struct timeval TimeVal;
TimeVal.tv_sec = m_iTimeout;
TimeVal.tv_usec = 0;
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeVal, sizeof(TimeVal));
#endif
if (err != 0)
if (!InitSocketOpts())
{
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
return false;
}
#ifndef DISABLE_TLS
@@ -634,6 +656,146 @@ bool Connection::DoConnect()
return true;
}
bool Connection::InitSocketOpts()
{
char* optbuf = NULL;
int optsize = 0;
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
optbuf = (char*)&MSecVal;
optsize = sizeof(MSecVal);
#else
struct timeval TimeVal;
TimeVal.tv_sec = m_iTimeout;
TimeVal.tv_usec = 0;
optbuf = (char*)&TimeVal;
optsize = sizeof(TimeVal);
#endif
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, optbuf, optsize);
if (err != 0)
{
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
return false;
}
err = setsockopt(m_iSocket, SOL_SOCKET, SO_SNDTIMEO, optbuf, optsize);
if (err != 0)
{
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
return false;
}
return true;
}
bool Connection::ConnectWithTimeout(void* address, int address_len)
{
int flags = 0, error = 0, ret = 0;
fd_set rset, wset;
socklen_t len = sizeof(error);
struct timeval ts;
ts.tv_sec = m_iTimeout;
ts.tv_usec = 0;
//clear out descriptor sets for select
//add socket to the descriptor sets
FD_ZERO(&rset);
FD_SET(m_iSocket, &rset);
wset = rset; //structure assignment ok
//set socket nonblocking flag
#ifdef WIN32
u_long mode = 1;
if (ioctlsocket(m_iSocket, FIONBIO, &mode) != 0)
{
return false;
}
#else
flags = fcntl(m_iSocket, F_GETFL, 0);
if (flags < 0)
{
return false;
}
if (fcntl(m_iSocket, F_SETFL, flags | O_NONBLOCK) < 0)
{
return false;
}
#endif
//initiate non-blocking connect
ret = connect(m_iSocket, (struct sockaddr*)address, address_len);
if (ret < 0)
{
#ifdef WIN32
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK)
{
return false;
}
#else
if (errno != EINPROGRESS)
{
return false;
}
#endif
}
//connect succeeded right away?
if (ret != 0)
{
ret = select(m_iSocket + 1, &rset, &wset, NULL, m_iTimeout ? &ts : NULL);
//we are waiting for connect to complete now
if (ret < 0)
{
return false;
}
if (ret == 0)
{
//we had a timeout
#ifdef WIN32
WSASetLastError(WSAETIMEDOUT);
#else
errno = ETIMEDOUT;
#endif
return false;
}
if (!(FD_ISSET(m_iSocket, &rset) || FD_ISSET(m_iSocket, &wset)))
{
return false;
}
//we had a positivite return so a descriptor is ready
if (getsockopt(m_iSocket, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0)
{
return false;
}
//check if we had a socket error
if (error)
{
errno = error;
return false;
}
}
//put socket back in blocking mode
#ifdef WIN32
mode = 0;
if (ioctlsocket(m_iSocket, FIONBIO, &mode) != 0)
{
return false;
}
#else
if (fcntl(m_iSocket, F_SETFL, flags) < 0)
{
return false;
}
#endif
return true;
}
bool Connection::DoDisconnect()
{
debug("Do disconnecting");
@@ -686,12 +848,15 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
char szErrPrefix[1024];
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
szErrPrefix[1024-1] = '\0';
char szMessage[1024];
if (PrintErrCode)
{
#ifdef WIN32
int ErrCode = WSAGetLastError();
char szErrMsg[1024];
szErrMsg[0] = '\0';
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrCode, 0, szErrMsg, 1024, NULL);
szErrMsg[1024-1] = '\0';
#else
@@ -713,7 +878,9 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
snprintf(szMessage, sizeof(szMessage), "%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
szMessage[sizeof(szMessage) - 1] = '\0';
PrintError(szMessage);
}
}
else
@@ -724,18 +891,23 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
}
else
{
error(szErrPrefix);
PrintError(szErrPrefix);
}
}
}
void Connection::PrintError(const char* szErrMsg)
{
error("%s", szErrMsg);
}
#ifndef DISABLE_TLS
bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile)
{
debug("Starting TLS");
delete m_pTLSSocket;
m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher);
m_pTLSSocket = new ConTLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher, this);
m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors);
return m_pTLSSocket->Start();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -61,8 +61,31 @@ protected:
bool m_bSuppressErrors;
char m_szRemoteAddr[20];
int m_iTotalBytesRead;
bool m_bBroken;
struct SockAddr
{
int ai_family;
int ai_socktype;
int ai_protocol;
bool operator==(const SockAddr& rhs) const
{ return memcmp(this, &rhs, sizeof(SockAddr)) == 0; }
};
#ifndef DISABLE_TLS
TLSSocket* m_pTLSSocket;
class ConTLSSocket: public TLSSocket
{
private:
Connection* m_pOwner;
protected:
virtual void PrintError(const char* szErrMsg) { m_pOwner->PrintError(szErrMsg); }
public:
ConTLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile,
const char* szKeyFile, const char* szCipher, Connection* pOwner):
TLSSocket(iSocket, bIsClient, szCertFile, szKeyFile, szCipher), m_pOwner(pOwner) {}
};
ConTLSSocket* m_pTLSSocket;
bool m_bTLSError;
#endif
#ifndef HAVE_GETADDRINFO
@@ -73,8 +96,11 @@ protected:
Connection(SOCKET iSocket, bool bTLS);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
virtual void PrintError(const char* szErrMsg);
bool DoConnect();
bool DoDisconnect();
bool InitSocketOpts();
bool ConnectWithTimeout(void* address, int address_len);
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2015 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>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -283,6 +284,8 @@ TLSSocket::~TLSSocket()
void TLSSocket::ReportError(const char* szErrMsg)
{
char szMessage[1024];
#ifdef HAVE_LIBGNUTLS
const char* errstr = gnutls_strerror(m_iRetCode);
if (m_bSuppressErrors)
@@ -291,7 +294,9 @@ void TLSSocket::ReportError(const char* szErrMsg)
}
else
{
error("%s: %s", szErrMsg, errstr);
snprintf(szMessage, sizeof(szMessage), "%s: %s", szErrMsg, errstr);
szMessage[sizeof(szMessage) - 1] = '\0';
PrintError(szMessage);
}
#endif /* HAVE_LIBGNUTLS */
@@ -311,16 +316,23 @@ void TLSSocket::ReportError(const char* szErrMsg)
}
else if (errcode != 0)
{
error("%s: %s", szErrMsg, errstr);
snprintf(szMessage, sizeof(szMessage), "%s: %s", szErrMsg, errstr);
szMessage[sizeof(szMessage) - 1] = '\0';
PrintError(szMessage);
}
else
{
error("%s", szErrMsg);
PrintError(szErrMsg);
}
} while (errcode);
#endif /* HAVE_OPENSSL */
}
void TLSSocket::PrintError(const char* szErrMsg)
{
error("%s", szErrMsg);
}
bool TLSSocket::Start()
{
#ifdef HAVE_LIBGNUTLS

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2015 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
@@ -46,9 +46,12 @@ private:
void ReportError(const char* szErrMsg);
protected:
virtual void PrintError(const char* szErrMsg);
public:
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
~TLSSocket();
virtual ~TLSSocket();
static void Init();
static void Final();
bool Start();

View File

@@ -366,10 +366,6 @@ WebDownloader::EStatus WebDownloader::DownloadHeaders()
// detect body of response
if (*line == '\r' || *line == '\n')
{
if (m_iContentLen == -1 && !m_bGZip)
{
warn("URL %s: Content-Length is not submitted by server, cannot verify whether the file is complete", m_szInfoName);
}
break;
}
@@ -420,10 +416,10 @@ WebDownloader::EStatus WebDownloader::DownloadBody()
szBuffer = szLineBuf;
}
// Have we encountered a timeout?
// Connection closed or timeout?
if (iLen <= 0)
{
if (m_iContentLen == -1 && iWrittenLen > 0)
if (iLen == 0 && m_iContentLen == -1 && iWrittenLen > 0)
{
bEnd = true;
break;

View File

@@ -521,7 +521,7 @@ bool FeedFilter::Term::ParseNumericParam(const char* szParam)
m_bFloat = strchr(szParam, '.');
const char* p;
for (p = szParam; *p && ((*p >= '0' && *p <='9') || *p == '.') ; p++) ;
for (p = szParam; *p && ((*p >= '0' && *p <='9') || *p == '.' || *p == '-') ; p++) ;
if (*p)
{
return false;

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -119,11 +119,7 @@ void Frontend::FreeData()
{
if (IsRemoteMode())
{
for (Log::Messages::iterator it = m_RemoteMessages.begin(); it != m_RemoteMessages.end(); it++)
{
delete *it;
}
m_RemoteMessages.clear();
m_RemoteMessages.Clear();
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
pDownloadQueue->GetQueue()->Clear();
@@ -131,7 +127,7 @@ void Frontend::FreeData()
}
}
Log::Messages* Frontend::LockMessages()
MessageList* Frontend::LockMessages()
{
if (IsRemoteMode())
{

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,7 +36,7 @@
class Frontend : public Thread
{
private:
Log::Messages m_RemoteMessages;
MessageList m_RemoteMessages;
bool RequestMessages();
bool RequestFileList();
@@ -62,7 +62,7 @@ protected:
bool PrepareData();
void FreeData();
Log::Messages* LockMessages();
MessageList* LockMessages();
void UnlockMessages();
DownloadQueue* LockQueue();
void UnlockQueue();

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -79,7 +79,7 @@ void LoggableFrontend::Update()
BeforePrint();
Log::Messages* pMessages = LockMessages();
MessageList* pMessages = LockMessages();
if (!pMessages->empty())
{
Message* pFirstMessage = pMessages->front();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -504,7 +504,7 @@ void NCursesFrontend::PrintMessages()
int iLine = iLineNr + m_iMessagesWinClientHeight - 1;
int iLinesToPrint = m_iMessagesWinClientHeight;
Log::Messages* pMessages = LockMessages();
MessageList* pMessages = LockMessages();
// print messages from bottom
for (int i = (int)pMessages->size() - 1; i >= 0 && iLinesToPrint > 0; i--)

View File

@@ -48,6 +48,9 @@
extern Options* g_pOptions;
extern Maintenance* g_pMaintenance;
extern void ExitProc();
extern int g_iArgumentCount;
extern char* (*g_szArguments)[];
Maintenance::Maintenance()
{
@@ -69,7 +72,7 @@ Maintenance::~Maintenance()
}
}
ClearMessages();
m_Messages.Clear();
free(m_szUpdateScript);
}
@@ -81,16 +84,7 @@ void Maintenance::ResetUpdateController()
m_mutexController.Unlock();
}
void Maintenance::ClearMessages()
{
for (Log::Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
}
Log::Messages* Maintenance::LockMessages()
MessageList* Maintenance::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
@@ -101,7 +95,7 @@ void Maintenance::UnlockMessages()
m_mutexLog.Unlock();
}
void Maintenance::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
void Maintenance::AddMessage(Message::EKind eKind, time_t tTime, const char * szText)
{
if (tTime == 0)
{
@@ -137,7 +131,20 @@ bool Maintenance::StartUpdate(EBranch eBranch)
return false;
}
ClearMessages();
// make absolute path
if (m_szUpdateScript[0] != PATH_SEPARATOR
#ifdef WIN32
&& !(strlen(m_szUpdateScript) > 2 && m_szUpdateScript[1] == ':')
#endif
)
{
char szFilename[MAX_PATH + 100];
snprintf(szFilename, sizeof(szFilename), "%s%c%s", g_pOptions->GetAppDir(), PATH_SEPARATOR, m_szUpdateScript);
free(m_szUpdateScript);
m_szUpdateScript = strdup(szFilename);
}
m_Messages.Clear();
m_UpdateScriptController = new UpdateScriptController();
m_UpdateScriptController->SetScript(m_szUpdateScript);
@@ -243,11 +250,21 @@ void UpdateScriptController::Run()
const char* szBranchName[] = { "STABLE", "TESTING", "DEVEL" };
SetEnvVar("NZBUP_BRANCH", szBranchName[m_eBranch]);
SetEnvVar("NZBUP_RUNMODE", g_pOptions->GetDaemonMode() ? "DAEMON" : "SERVER");
for (int i = 0; i < g_iArgumentCount; i++)
{
char szEnvName[40];
snprintf(szEnvName, 40, "NZBUP_CMDLINE%i", i);
szInfoName[40-1] = '\0';
SetEnvVar(szEnvName, (*g_szArguments)[i]);
}
char szProcessID[20];
#ifdef WIN32
int pid = (int)GetCurrentProcessId();
#else
int pid = (int)getppid();
int pid = (int)getpid();
#endif
snprintf(szProcessID, 20, "%i", pid);
szProcessID[20-1] = '\0';
@@ -269,8 +286,24 @@ void UpdateScriptController::AddMessage(Message::EKind eKind, const char* szText
{
szText = szText + m_iPrefixLen;
g_pMaintenance->AppendMessage(eKind, time(NULL), szText);
ScriptController::AddMessage(eKind, szText);
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strcmp(szText + 6, "QUIT"))
{
Detach();
ExitProc();
}
else
{
error("Invalid command \"%s\" received", szText);
}
}
else
{
g_pMaintenance->AddMessage(eKind, time(NULL), szText);
ScriptController::AddMessage(eKind, szText);
}
}
void UpdateInfoScriptController::ExecuteScript(const char* szScript, char** pUpdateInfo)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-2015 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,7 +35,7 @@ class UpdateScriptController;
class Maintenance
{
private:
Log::Messages m_Messages;
MessageList m_Messages;
Mutex m_mutexLog;
Mutex m_mutexController;
int m_iIDMessageGen;
@@ -54,9 +54,8 @@ public:
Maintenance();
~Maintenance();
void ClearMessages();
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Log::Messages* LockMessages();
void AddMessage(Message::EKind eKind, time_t tTime, const char* szText);
MessageList* LockMessages();
void UnlockMessages();
bool StartUpdate(EBranch eBranch);
void ResetUpdateController();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,11 +35,13 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <set>
#ifdef WIN32
#include <direct.h>
#include <Shlobj.h>
#else
#include <unistd.h>
#include <getopt.h>
@@ -60,6 +62,10 @@ extern ServerPool* g_pServerPool;
extern Scheduler* g_pScheduler;
extern FeedCoordinator* g_pFeedCoordinator;
#ifdef WIN32
extern void SetupFirstStart();
#endif
#ifdef HAVE_GETOPT_LONG
static struct option long_options[] =
{
@@ -77,7 +83,7 @@ static struct option long_options[] =
{"pause", no_argument, 0, 'P'},
{"unpause", no_argument, 0, 'U'},
{"rate", required_argument, 0, 'R'},
{"debug", no_argument, 0, 'B'},
{"system", no_argument, 0, 'B'},
{"log", required_argument, 0, 'G'},
{"top", no_argument, 0, 'T'},
{"edit", required_argument, 0, 'E'},
@@ -120,6 +126,10 @@ static const char* OPTION_CONTROLIP = "ControlIp";
static const char* OPTION_CONTROLPORT = "ControlPort";
static const char* OPTION_CONTROLUSERNAME = "ControlUsername";
static const char* OPTION_CONTROLPASSWORD = "ControlPassword";
static const char* OPTION_RESTRICTEDUSERNAME = "RestrictedUsername";
static const char* OPTION_RESTRICTEDPASSWORD = "RestrictedPassword";
static const char* OPTION_ADDUSERNAME = "AddUsername";
static const char* OPTION_ADDPASSWORD = "AddPassword";
static const char* OPTION_SECURECONTROL = "SecureControl";
static const char* OPTION_SECUREPORT = "SecurePort";
static const char* OPTION_SECURECERT = "SecureCert";
@@ -129,7 +139,8 @@ static const char* OPTION_ARTICLETIMEOUT = "ArticleTimeout";
static const char* OPTION_URLTIMEOUT = "UrlTimeout";
static const char* OPTION_SAVEQUEUE = "SaveQueue";
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
static const char* OPTION_CREATEBROKENLOG = "CreateBrokenLog";
static const char* OPTION_BROKENLOG = "BrokenLog";
static const char* OPTION_NZBLOG = "NzbLog";
static const char* OPTION_DECODE = "Decode";
static const char* OPTION_RETRIES = "Retries";
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
@@ -176,6 +187,7 @@ static const char* OPTION_UNPACK = "Unpack";
static const char* OPTION_UNPACKCLEANUPDISK = "UnpackCleanupDisk";
static const char* OPTION_UNRARCMD = "UnrarCmd";
static const char* OPTION_SEVENZIPCMD = "SevenZipCmd";
static const char* OPTION_UNPACKPASSFILE = "UnpackPassFile";
static const char* OPTION_UNPACKPAUSEQUEUE = "UnpackPauseQueue";
static const char* OPTION_SCRIPTORDER = "ScriptOrder";
static const char* OPTION_POSTSCRIPT = "PostScript";
@@ -272,6 +284,29 @@ void Options::OptEntry::SetValue(const char* szValue)
}
}
bool Options::OptEntry::Restricted()
{
char szLoName[256];
strncpy(szLoName, m_szName, sizeof(szLoName));
szLoName[256-1] = '\0';
for (char* p = szLoName; *p; p++) *p = tolower(*p); // convert string to lowercase
bool bRestricted = !strcasecmp(m_szName, OPTION_CONTROLIP) ||
!strcasecmp(m_szName, OPTION_CONTROLPORT) ||
!strcasecmp(m_szName, OPTION_SECURECONTROL) ||
!strcasecmp(m_szName, OPTION_SECUREPORT) ||
!strcasecmp(m_szName, OPTION_SECURECERT) ||
!strcasecmp(m_szName, OPTION_SECUREKEY) ||
!strcasecmp(m_szName, OPTION_AUTHORIZEDIP) ||
!strcasecmp(m_szName, OPTION_DAEMONUSERNAME) ||
!strcasecmp(m_szName, OPTION_UMASK) ||
strchr(m_szName, ':') || // All extension script options
strstr(szLoName, "username") || // ServerX.Username, ControlUsername, etc.
strstr(szLoName, "password"); // ServerX.Password, ControlPassword, etc.
return bRestricted;
}
Options::OptEntries::~OptEntries()
{
for (iterator it = begin(); it != end(); it++)
@@ -448,7 +483,7 @@ Options::Script* Options::Scripts::Find(const char* szName)
}
Options::Options(int argc, char* argv[])
Options::Options()
{
m_bConfigErrors = false;
m_iConfigLine = 0;
@@ -456,6 +491,7 @@ Options::Options(int argc, char* argv[])
// initialize options with default values
m_bConfigInitialized = false;
m_szConfigFilename = NULL;
m_szAppDir = NULL;
m_szDestDir = NULL;
m_szInterDir = NULL;
m_szTempDir = NULL;
@@ -474,7 +510,8 @@ Options::Options(int argc, char* argv[])
m_bPausePostProcess = false;
m_bPauseScan = false;
m_bTempPauseDownload = false;
m_bCreateBrokenLog = false;
m_bBrokenLog = false;
m_bNzbLog = false;
m_iDownloadRate = 0;
m_iEditQueueAction = 0;
m_pEditQueueIDList = NULL;
@@ -495,6 +532,9 @@ Options::Options(int argc, char* argv[])
m_bRemoteClientMode = false;
m_bPrintOptions = false;
m_bAddTop = false;
m_szAddDupeKey = NULL;
m_iAddDupeScore = 0;
m_iAddDupeMode = 0;
m_bAppendCategoryDir = false;
m_bContinuePartial = false;
m_bSaveQueue = false;
@@ -505,6 +545,10 @@ Options::Options(int argc, char* argv[])
m_szControlIP = NULL;
m_szControlUsername = NULL;
m_szControlPassword = NULL;
m_szRestrictedUsername = NULL;
m_szRestrictedPassword = NULL;
m_szAddUsername = NULL;
m_szAddPassword = NULL;
m_bSecureControl = false;
m_iSecurePort = 0;
m_szSecureCert = NULL;
@@ -547,6 +591,8 @@ Options::Options(int argc, char* argv[])
m_bParCleanupQueue = false;
m_iDiskSpace = 0;
m_bTestBacktrace = false;
m_bWebGet = false;
m_szWebGetFilename = NULL;
m_bTLS = false;
m_bDumpCore = false;
m_bParPauseQueue = false;
@@ -562,6 +608,7 @@ Options::Options(int argc, char* argv[])
m_bUnpackCleanupDisk = false;
m_szUnrarCmd = NULL;
m_szSevenZipCmd = NULL;
m_szUnpackPassFile = NULL;
m_bUnpackPauseQueue = false;
m_szExtCleanupDisk = NULL;
m_szParIgnoreExt = NULL;
@@ -572,22 +619,73 @@ Options::Options(int argc, char* argv[])
m_iPropagationDelay = 0;
m_iArticleCache = 0;
m_iEventInterval = 0;
}
Options::~Options()
{
free(m_szConfigFilename);
free(m_szAppDir);
free(m_szDestDir);
free(m_szInterDir);
free(m_szTempDir);
free(m_szQueueDir);
free(m_szNzbDir);
free(m_szWebDir);
free(m_szConfigTemplate);
free(m_szScriptDir);
free(m_szArgFilename);
free(m_szAddCategory);
free(m_szEditQueueText);
free(m_szLastArg);
free(m_szControlIP);
free(m_szControlUsername);
free(m_szControlPassword);
free(m_szRestrictedUsername);
free(m_szRestrictedPassword);
free(m_szAddUsername);
free(m_szAddPassword);
free(m_szSecureCert);
free(m_szSecureKey);
free(m_szAuthorizedIP);
free(m_szLogFile);
free(m_szLockFile);
free(m_szDaemonUsername);
free(m_szScriptOrder);
free(m_szPostScript);
free(m_szScanScript);
free(m_szQueueScript);
free(m_pEditQueueIDList);
free(m_szAddNZBFilename);
free(m_szAddDupeKey);
free(m_szUnrarCmd);
free(m_szSevenZipCmd);
free(m_szUnpackPassFile);
free(m_szExtCleanupDisk);
free(m_szParIgnoreExt);
free(m_szWebGetFilename);
for (NameList::iterator it = m_EditQueueNameList.begin(); it != m_EditQueueNameList.end(); it++)
{
free(*it);
}
m_EditQueueNameList.clear();
}
void Options::Init(int argc, char* argv[])
{
// Option "ConfigFile" will be initialized later, but we want
// to see it at the top of option list, so we add it first
SetOption(OPTION_CONFIGFILE, "");
char szFilename[MAX_PATH + 1];
#ifdef WIN32
GetModuleFileName(NULL, szFilename, sizeof(szFilename));
#else
Util::ExpandFileName(argv[0], szFilename, sizeof(szFilename));
#endif
Util::GetExeFileName(argv[0], szFilename, sizeof(szFilename));
Util::NormalizePathSeparators(szFilename);
SetOption(OPTION_APPBIN, szFilename);
char* end = strrchr(szFilename, PATH_SEPARATOR);
if (end) *end = '\0';
SetOption(OPTION_APPDIR, szFilename);
m_szAppDir = strdup(szFilename);
SetOption(OPTION_VERSION, Util::VersionRevision());
@@ -652,48 +750,6 @@ Options::Options(int argc, char* argv[])
}
}
Options::~Options()
{
free(m_szConfigFilename);
free(m_szDestDir);
free(m_szInterDir);
free(m_szTempDir);
free(m_szQueueDir);
free(m_szNzbDir);
free(m_szWebDir);
free(m_szConfigTemplate);
free(m_szScriptDir);
free(m_szArgFilename);
free(m_szAddCategory);
free(m_szEditQueueText);
free(m_szLastArg);
free(m_szControlIP);
free(m_szControlUsername);
free(m_szControlPassword);
free(m_szSecureCert);
free(m_szSecureKey);
free(m_szAuthorizedIP);
free(m_szLogFile);
free(m_szLockFile);
free(m_szDaemonUsername);
free(m_szScriptOrder);
free(m_szPostScript);
free(m_szScanScript);
free(m_szQueueScript);
free(m_pEditQueueIDList);
free(m_szAddNZBFilename);
free(m_szUnrarCmd);
free(m_szSevenZipCmd);
free(m_szExtCleanupDisk);
free(m_szParIgnoreExt);
for (NameList::iterator it = m_EditQueueNameList.begin(); it != m_EditQueueNameList.end(); it++)
{
free(*it);
}
m_EditQueueNameList.clear();
}
void Options::Dump()
{
for (OptEntries::iterator it = m_OptEntries.begin(); it != m_OptEntries.end(); it++)
@@ -750,8 +806,12 @@ void Options::InitDefault()
{
#ifdef WIN32
SetOption(OPTION_MAINDIR, "${AppDir}");
SetOption(OPTION_WEBDIR, "${AppDir}\\webui");
SetOption(OPTION_CONFIGTEMPLATE, "${AppDir}\\nzbget.conf.template");
#else
SetOption(OPTION_MAINDIR, "~/downloads");
SetOption(OPTION_WEBDIR, "");
SetOption(OPTION_CONFIGTEMPLATE, "");
#endif
SetOption(OPTION_TEMPDIR, "${MainDir}/tmp");
SetOption(OPTION_DESTDIR, "${MainDir}/dst");
@@ -760,8 +820,6 @@ void Options::InitDefault()
SetOption(OPTION_NZBDIR, "${MainDir}/nzb");
SetOption(OPTION_LOCKFILE, "${MainDir}/nzbget.lock");
SetOption(OPTION_LOGFILE, "${DestDir}/nzbget.log");
SetOption(OPTION_WEBDIR, "");
SetOption(OPTION_CONFIGTEMPLATE, "");
SetOption(OPTION_SCRIPTDIR, "${MainDir}/scripts");
SetOption(OPTION_WRITELOG, "append");
SetOption(OPTION_ROTATELOG, "3");
@@ -772,6 +830,10 @@ void Options::InitDefault()
SetOption(OPTION_CONTROLIP, "0.0.0.0");
SetOption(OPTION_CONTROLUSERNAME, "nzbget");
SetOption(OPTION_CONTROLPASSWORD, "tegbzn6789");
SetOption(OPTION_RESTRICTEDUSERNAME, "");
SetOption(OPTION_RESTRICTEDPASSWORD, "");
SetOption(OPTION_ADDUSERNAME, "");
SetOption(OPTION_ADDPASSWORD, "");
SetOption(OPTION_CONTROLPORT, "6789");
SetOption(OPTION_SECURECONTROL, "no");
SetOption(OPTION_SECUREPORT, "6791");
@@ -782,7 +844,8 @@ void Options::InitDefault()
SetOption(OPTION_URLTIMEOUT, "60");
SetOption(OPTION_SAVEQUEUE, "yes");
SetOption(OPTION_RELOADQUEUE, "yes");
SetOption(OPTION_CREATEBROKENLOG, "yes");
SetOption(OPTION_BROKENLOG, "yes");
SetOption(OPTION_NZBLOG, "yes");
SetOption(OPTION_DECODE, "yes");
SetOption(OPTION_RETRIES, "3");
SetOption(OPTION_RETRYINTERVAL, "10");
@@ -837,6 +900,7 @@ void Options::InitDefault()
SetOption(OPTION_UNRARCMD, "unrar");
SetOption(OPTION_SEVENZIPCMD, "7z");
#endif
SetOption(OPTION_UNPACKPASSFILE, "");
SetOption(OPTION_UNPACKPAUSEQUEUE, "no");
SetOption(OPTION_EXTCLEANUPDISK, "");
SetOption(OPTION_PARIGNOREEXT, "");
@@ -859,33 +923,53 @@ void Options::InitOptFile()
{
// search for config file in default locations
#ifdef WIN32
char szFilename[MAX_PATH + 1];
GetModuleFileName(NULL, szFilename, MAX_PATH + 1);
szFilename[MAX_PATH] = '\0';
Util::NormalizePathSeparators(szFilename);
char* end = strrchr(szFilename, PATH_SEPARATOR);
if (end) end[1] = '\0';
strcat(szFilename, "nzbget.conf");
char szFilename[MAX_PATH + 20];
snprintf(szFilename, sizeof(szFilename), "%s\\nzbget.conf", m_szAppDir);
if (!Util::FileExists(szFilename))
{
char szAppDataPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, szAppDataPath);
snprintf(szFilename, sizeof(szFilename), "%s\\NZBGet\\nzbget.conf", szAppDataPath);
szFilename[sizeof(szFilename)-1] = '\0';
if (!Util::FileExists(szFilename))
{
SetupFirstStart();
}
}
if (Util::FileExists(szFilename))
{
m_szConfigFilename = strdup(szFilename);
}
#else
int p = 0;
while (const char* szFilename = PossibleConfigLocations[p++])
{
// substitute HOME-variable
char szExpandedFilename[1024];
if (Util::ExpandHomePath(szFilename, szExpandedFilename, sizeof(szExpandedFilename)))
{
szFilename = szExpandedFilename;
}
// look in the exe-directory first
char szFilename[1024];
snprintf(szFilename, sizeof(szFilename), "%s/nzbget.conf", m_szAppDir);
szFilename[1024-1] = '\0';
if (Util::FileExists(szFilename))
if (Util::FileExists(szFilename))
{
m_szConfigFilename = strdup(szFilename);
}
else
{
int p = 0;
while (const char* szFilename = PossibleConfigLocations[p++])
{
m_szConfigFilename = strdup(szFilename);
break;
// substitute HOME-variable
char szExpandedFilename[1024];
if (Util::ExpandHomePath(szFilename, szExpandedFilename, sizeof(szExpandedFilename)))
{
szFilename = szExpandedFilename;
}
if (Util::FileExists(szFilename))
{
m_szConfigFilename = strdup(szFilename);
break;
}
}
}
#endif
@@ -1000,6 +1084,10 @@ void Options::InitOptions()
m_szControlIP = strdup(GetOption(OPTION_CONTROLIP));
m_szControlUsername = strdup(GetOption(OPTION_CONTROLUSERNAME));
m_szControlPassword = strdup(GetOption(OPTION_CONTROLPASSWORD));
m_szRestrictedUsername = strdup(GetOption(OPTION_RESTRICTEDUSERNAME));
m_szRestrictedPassword = strdup(GetOption(OPTION_RESTRICTEDPASSWORD));
m_szAddUsername = strdup(GetOption(OPTION_ADDUSERNAME));
m_szAddPassword = strdup(GetOption(OPTION_ADDPASSWORD));
m_szSecureCert = strdup(GetOption(OPTION_SECURECERT));
m_szSecureKey = strdup(GetOption(OPTION_SECUREKEY));
m_szAuthorizedIP = strdup(GetOption(OPTION_AUTHORIZEDIP));
@@ -1008,6 +1096,7 @@ void Options::InitOptions()
m_szLogFile = strdup(GetOption(OPTION_LOGFILE));
m_szUnrarCmd = strdup(GetOption(OPTION_UNRARCMD));
m_szSevenZipCmd = strdup(GetOption(OPTION_SEVENZIPCMD));
m_szUnpackPassFile = strdup(GetOption(OPTION_UNPACKPASSFILE));
m_szExtCleanupDisk = strdup(GetOption(OPTION_EXTCLEANUPDISK));
m_szParIgnoreExt = strdup(GetOption(OPTION_PARIGNOREEXT));
@@ -1045,7 +1134,8 @@ void Options::InitOptions()
CheckDir(&m_szNzbDir, OPTION_NZBDIR, szMainDir, m_iNzbDirInterval == 0, true);
m_bCreateBrokenLog = (bool)ParseEnumValue(OPTION_CREATEBROKENLOG, BoolCount, BoolNames, BoolValues);
m_bBrokenLog = (bool)ParseEnumValue(OPTION_BROKENLOG, BoolCount, BoolNames, BoolValues);
m_bNzbLog = (bool)ParseEnumValue(OPTION_NZBLOG, BoolCount, BoolNames, BoolValues);
m_bAppendCategoryDir = (bool)ParseEnumValue(OPTION_APPENDCATEGORYDIR, BoolCount, BoolNames, BoolValues);
m_bContinuePartial = (bool)ParseEnumValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, BoolValues);
m_bSaveQueue = (bool)ParseEnumValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, BoolValues);
@@ -1250,19 +1340,15 @@ void Options::InitCommandLine(int argc, char* argv[])
m_bDaemonMode = true;
break;
case 'A':
m_eClientOperation = opClientRequestDownload; // default
m_eClientOperation = opClientRequestDownload;
while (true)
{
optind++;
optarg = optind > argc ? NULL : argv[optind-1];
if (optarg && !strcasecmp(optarg, "F"))
if (optarg && (!strcasecmp(optarg, "F") || !strcasecmp(optarg, "U")))
{
m_eClientOperation = opClientRequestDownload;
}
else if (optarg && !strcasecmp(optarg, "U"))
{
m_eClientOperation = opClientRequestDownloadUrl;
// option ignored (but kept for compatibility)
}
else if (optarg && !strcasecmp(optarg, "T"))
{
@@ -1301,6 +1387,51 @@ void Options::InitCommandLine(int argc, char* argv[])
free(m_szAddNZBFilename);
m_szAddNZBFilename = strdup(argv[optind-1]);
}
else if (optarg && !strcasecmp(optarg, "DK"))
{
optind++;
if (optind > argc)
{
abort("FATAL ERROR: Could not parse value of option 'A'\n");
}
free(m_szAddDupeKey);
m_szAddDupeKey = strdup(argv[optind-1]);
}
else if (optarg && !strcasecmp(optarg, "DS"))
{
optind++;
if (optind > argc)
{
abort("FATAL ERROR: Could not parse value of option 'A'\n");
}
m_iAddDupeScore = atoi(argv[optind-1]);
}
else if (optarg && !strcasecmp(optarg, "DM"))
{
optind++;
if (optind > argc)
{
abort("FATAL ERROR: Could not parse value of option 'A'\n");
}
const char* szDupeMode = argv[optind-1];
if (!strcasecmp(szDupeMode, "score"))
{
m_iAddDupeMode = dmScore;
}
else if (!strcasecmp(szDupeMode, "all"))
{
m_iAddDupeMode = dmAll;
}
else if (!strcasecmp(szDupeMode, "force"))
{
m_iAddDupeMode = dmForce;
}
else
{
abort("FATAL ERROR: Could not parse value of option 'A'\n");
}
}
else
{
optind--;
@@ -1336,6 +1467,10 @@ void Options::InitCommandLine(int argc, char* argv[])
{
m_eClientOperation = opClientRequestHistory;
}
else if (!strcasecmp(optarg, "HA"))
{
m_eClientOperation = opClientRequestHistoryAll;
}
else
{
abort("FATAL ERROR: Could not parse value of option 'L'\n");
@@ -1392,6 +1527,17 @@ void Options::InitCommandLine(int argc, char* argv[])
{
m_bTestBacktrace = true;
}
else if (!strcasecmp(optarg, "webget"))
{
m_bWebGet = true;
optind++;
if (optind > argc)
{
abort("FATAL ERROR: Could not parse value of option 'E'\n");
}
optarg = argv[optind-1];
m_szWebGetFilename = strdup(optarg);
}
else
{
abort("FATAL ERROR: Could not parse value of option 'B'\n");
@@ -1495,6 +1641,10 @@ void Options::InitCommandLine(int argc, char* argv[])
{
m_iEditQueueAction = DownloadQueue::eaHistoryMarkGood;
}
else if (!strcasecmp(optarg, "S"))
{
m_iEditQueueAction = DownloadQueue::eaHistoryMarkSuccess;
}
else
{
abort("FATAL ERROR: Could not parse value of option 'E'\n");
@@ -1720,16 +1870,17 @@ void Options::PrintUsage(char* com)
" -V, --serverversion Print server's version and exit\n"
" -Q, --quit Shutdown server\n"
" -O, --reload Reload config and restart all services\n"
" -A, --append [F|U] [<options>] <nzb-file/url> Send file/url to server's\n"
" -A, --append [<options>] <nzb-file/url> Send file/url to server's\n"
" download queue\n"
" F Send file (default)\n"
" U Send url\n"
" <options> are (multiple options must be separated with space):\n"
" T Add file to the top (beginning) of queue\n"
" P Pause added files\n"
" C <name> Assign category to nzb-file\n"
" N <name> Use this name as nzb-filename (only for URLs)\n"
" N <name> Use this name as nzb-filename\n"
" I <priority> Set priority (signed integer)\n"
" DK <dupekey> Set duplicate key (string)\n"
" DS <dupescore> Set duplicate score (signed integer)\n"
" DM (score|all|force) Set duplicate mode\n"
" -C, --connect Attach client to server\n"
" -L, --list [F|FR|G|GR|O|H|S] [RegEx] Request list of items from server\n"
" F List individual files and server status (default)\n"
@@ -1738,6 +1889,7 @@ void Options::PrintUsage(char* com)
" GR Like \"G\" but apply regular expression filter\n"
" O List post-processor-queue\n"
" H List history\n"
" HA List history, all records (incl. hidden)\n"
" S Print only server status\n"
" <RegEx> Regular expression (only with options \"FR\", \"GR\")\n"
" using POSIX Extended Regular Expression Syntax\n"
@@ -1795,6 +1947,7 @@ void Options::PrintUsage(char* com)
" O <name>=<value> Set post-process parameter\n"
" B Mark as bad\n"
" G Mark as good\n"
" S Mark as success\n"
" <IDs> Comma-separated list of file- or group- ids or\n"
" ranges of file-ids, e. g.: 1-5,3,10-22\n"
" <Names> List of names (with options \"FN\" and \"GN\"),\n"
@@ -1821,7 +1974,7 @@ void Options::InitFileArg(int argc, char* argv[])
}
else
{
abort("FATAL ERROR: Nzb-file not specified\n");
abort("FATAL ERROR: Nzb-file or Url not specified\n");
}
}
}
@@ -1847,7 +2000,7 @@ void Options::InitFileArg(int argc, char* argv[])
#ifdef WIN32
m_szArgFilename = strdup(szFileName);
#else
if (szFileName[0] == '/')
if (szFileName[0] == '/' || !strncasecmp(szFileName, "http://", 6) || !strncasecmp(szFileName, "https://", 7))
{
m_szArgFilename = strdup(szFileName);
}
@@ -1865,7 +2018,6 @@ void Options::InitFileArg(int argc, char* argv[])
if (m_bServerMode || m_bRemoteClientMode ||
!(m_eClientOperation == opClientNoOperation ||
m_eClientOperation == opClientRequestDownload ||
m_eClientOperation == opClientRequestDownloadUrl ||
m_eClientOperation == opClientRequestWriteLog))
{
abort("FATAL ERROR: Too many arguments\n");
@@ -2045,8 +2197,11 @@ void Options::InitServers()
sprintf(optname, "Server%i.Connections", n);
const char* nconnections = GetOption(optname);
sprintf(optname, "Server%i.Retention", n);
const char* nretention = GetOption(optname);
bool definition = nactive || nname || nlevel || ngroup || nhost || nport ||
nusername || npassword || nconnections || njoingroup || ntls || ncipher;
nusername || npassword || nconnections || njoingroup || ntls || ncipher || nretention;
bool completed = nhost && nport && nconnections;
if (!definition)
@@ -2057,10 +2212,14 @@ void Options::InitServers()
if (completed)
{
NewsServer* pNewsServer = new NewsServer(n, bActive, nname,
nhost, atoi(nport), nusername, npassword,
bJoinGroup, bTLS, ncipher, atoi((char*)nconnections),
nlevel ? atoi((char*)nlevel) : 0,
ngroup ? atoi((char*)ngroup) : 0);
nhost,
nport ? atoi(nport) : 119,
nusername, npassword,
bJoinGroup, bTLS, ncipher,
nconnections ? atoi(nconnections) : 1,
nretention ? atoi(nretention) : 0,
nlevel ? atoi(nlevel) : 0,
ngroup ? atoi(ngroup) : 0);
g_pServerPool->AddServer(pNewsServer);
}
else
@@ -2072,6 +2231,7 @@ void Options::InitServers()
}
g_pServerPool->SetTimeout(GetArticleTimeout());
g_pServerPool->SetRetryInterval(GetRetryInterval());
}
void Options::InitCategories()
@@ -2577,7 +2737,8 @@ bool Options::ValidateOptionName(const char* optname, const char* optvalue)
!strcasecmp(p, ".port") || !strcasecmp(p, ".username") ||
!strcasecmp(p, ".password") || !strcasecmp(p, ".joingroup") ||
!strcasecmp(p, ".encryption") || !strcasecmp(p, ".connections") ||
!strcasecmp(p, ".cipher") || !strcasecmp(p, ".group")))
!strcasecmp(p, ".cipher") || !strcasecmp(p, ".group") ||
!strcasecmp(p, ".retention")))
{
return true;
}
@@ -2730,6 +2891,11 @@ void Options::ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue,
strncpy(szOption, "ArticleTimeout", iOptionBufLen);
}
if (!strcasecmp(szOption, "CreateBrokenLog"))
{
strncpy(szOption, "BrokenLog", iOptionBufLen);
}
szOption[iOptionBufLen-1] = '\0';
szOption[iValueBufLen-1] = '\0';
}
@@ -2807,6 +2973,11 @@ void Options::CheckOptions()
m_iArticleCache = 1900;
m_iParBuffer = 400;
}
if (!Util::EmptyStr(m_szUnpackPassFile) && !Util::FileExists(m_szUnpackPassFile))
{
ConfigError("Invalid value for option \"UnpackPassFile\": %s. File not found", m_szUnpackPassFile);
}
}
void Options::ParseFileIDList(int argc, char* argv[], int optind)

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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 @@ public:
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory,
opClientRequestDownloadUrl
opClientRequestHistoryAll
};
enum EWriteLog
{
@@ -132,6 +132,7 @@ public:
const char* GetValue() { return m_szValue; }
const char* GetDefValue() { return m_szDefValue; }
int GetLineNo() { return m_iLineNo; }
bool Restricted();
};
typedef std::vector<OptEntry*> OptEntriesBase;
@@ -248,6 +249,7 @@ private:
// Options
bool m_bConfigErrors;
int m_iConfigLine;
char* m_szAppDir;
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szInterDir;
@@ -263,7 +265,8 @@ private:
EMessageTarget m_eDebugTarget;
EMessageTarget m_eDetailTarget;
bool m_bDecode;
bool m_bCreateBrokenLog;
bool m_bBrokenLog;
bool m_bNzbLog;
int m_iArticleTimeout;
int m_iUrlTimeout;
int m_iTerminateTimeout;
@@ -276,6 +279,10 @@ private:
char* m_szControlIP;
char* m_szControlUsername;
char* m_szControlPassword;
char* m_szRestrictedUsername;
char* m_szRestrictedPassword;
char* m_szAddUsername;
char* m_szAddPassword;
int m_iControlPort;
bool m_bSecureControl;
int m_iSecurePort;
@@ -329,6 +336,7 @@ private:
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
char* m_szUnpackPassFile;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
char* m_szParIgnoreExt;
@@ -358,10 +366,15 @@ private:
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
char* m_szAddDupeKey;
int m_iAddDupeScore;
int m_iAddDupeMode;
int m_iSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
bool m_bWebGet;
char* m_szWebGetFilename;
// Current state
bool m_bPauseDownload;
@@ -413,8 +426,9 @@ private:
void LoadScripts(Scripts* pScripts);
public:
Options(int argc, char* argv[]);
Options();
~Options();
void Init(int argc, char* argv[]);
bool LoadConfig(OptEntries* pOptEntries);
bool SaveConfig(OptEntries* pOptEntries);
@@ -426,6 +440,7 @@ public:
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetConfigFilename() { return m_szConfigFilename; }
const char* GetAppDir() { return m_szAppDir; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetInterDir() { return m_szInterDir; }
const char* GetTempDir() { return m_szTempDir; }
@@ -434,7 +449,8 @@ public:
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 GetBrokenLog() const { return m_bBrokenLog; }
bool GetNzbLog() const { return m_bNzbLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
@@ -453,6 +469,10 @@ public:
const char* GetControlIP();
const char* GetControlUsername() { return m_szControlUsername; }
const char* GetControlPassword() { return m_szControlPassword; }
const char* GetRestrictedUsername() { return m_szRestrictedUsername; }
const char* GetRestrictedPassword() { return m_szRestrictedPassword; }
const char* GetAddUsername() { return m_szAddUsername; }
const char* GetAddPassword() { return m_szAddPassword; }
int GetControlPort() { return m_iControlPort; }
bool GetSecureControl() { return m_bSecureControl; }
int GetSecurePort() { return m_iSecurePort; }
@@ -505,6 +525,7 @@ public:
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
const char* GetUnpackPassFile() { return m_szUnpackPassFile; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
const char* GetParIgnoreExt() { return m_szParIgnoreExt; }
@@ -515,6 +536,7 @@ public:
int GetArticleCache() { return m_iArticleCache; }
int GetEventInterval() { return m_iEventInterval; }
Categories* GetCategories() { return &m_Categories; }
Category* FindCategory(const char* szName, bool bSearchAliases) { return m_Categories.FindCategory(szName, bSearchAliases); }
// Parsed command-line parameters
@@ -536,10 +558,15 @@ public:
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
const char* GetAddDupeKey() { return m_szAddDupeKey; }
int GetAddDupeScore() { return m_iAddDupeScore; }
int GetAddDupeMode() { return m_iAddDupeMode; }
int GetSetRate() { return m_iSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
bool GetWebGet() { return m_bWebGet; }
const char* GetWebGetFilename() { return m_szWebGetFilename; }
// Current state
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }

0
daemon/main/StackTrace.cpp Executable file → Normal file
View File

0
daemon/main/StackTrace.h Executable file → Normal file
View File

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -82,6 +82,8 @@
#include "StackTrace.h"
#ifdef WIN32
#include "NTService.h"
#include "WinConsole.h"
#include "WebDownloader.h"
#endif
// Prototypes
@@ -90,6 +92,7 @@ void Run(bool bReload);
void Reload();
void Cleanup();
void ProcessClientRequest();
void ProcessWebGet();
#ifndef WIN32
void Daemonize();
#endif
@@ -120,6 +123,9 @@ int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
bool g_bReloading = true;
#ifdef WIN32
WinConsole* g_pWinConsole = NULL;
#endif
/*
* Main loop
@@ -200,6 +206,11 @@ void Run(bool bReload)
Thread::Init();
}
#ifdef WIN32
g_pWinConsole = new WinConsole();
g_pWinConsole->InitAppMode();
#endif
g_pServerPool = new ServerPool();
g_pScheduler = new Scheduler();
g_pQueueCoordinator = new QueueCoordinator();
@@ -215,7 +226,8 @@ void Run(bool bReload)
g_pQueueScriptCoordinator = new QueueScriptCoordinator();
debug("Reading options");
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
g_pOptions = new Options();
g_pOptions->Init(g_iArgumentCount, *g_szArguments);
#ifndef WIN32
if (g_pOptions->GetUMask() < 01000)
@@ -270,6 +282,12 @@ void Run(bool bReload)
}
#endif
if (g_pOptions->GetWebGet())
{
ProcessWebGet();
return;
}
// client request
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
{
@@ -339,6 +357,9 @@ void Run(bool bReload)
g_pDiskState = new DiskState();
}
#ifdef WIN32
g_pWinConsole->Start();
#endif
g_pQueueCoordinator->Start();
g_pUrlCoordinator->Start();
g_pPrePostProcessor->Start();
@@ -353,6 +374,9 @@ void Run(bool bReload)
g_pUrlCoordinator->IsRunning() ||
g_pPrePostProcessor->IsRunning() ||
g_pFeedCoordinator->IsRunning() ||
#ifdef WIN32
g_pWinConsole->IsRunning() ||
#endif
g_pArticleCache->IsRunning())
{
if (!g_pOptions->GetServerMode() &&
@@ -504,7 +528,9 @@ void ProcessClientRequest()
break;
case Options::opClientRequestDownload:
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
Client->RequestServerDownload(g_pOptions->GetAddNZBFilename(), g_pOptions->GetArgFilename(),
g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority(),
g_pOptions->GetAddDupeKey(), g_pOptions->GetAddDupeMode(), g_pOptions->GetAddDupeScore());
break;
case Options::opClientRequestVersion:
@@ -543,12 +569,9 @@ void ProcessClientRequest()
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionScan);
break;
case Options::opClientRequestHistory:
Client->RequestHistory();
break;
case Options::opClientRequestDownloadUrl:
Client->RequestServerDownloadUrl(g_pOptions->GetLastArg(), g_pOptions->GetAddNZBFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
case Options::opClientRequestHistory:
case Options::opClientRequestHistoryAll:
Client->RequestHistory(g_pOptions->GetClientOperation() == Options::opClientRequestHistoryAll);
break;
case Options::opClientNoOperation:
@@ -558,6 +581,27 @@ void ProcessClientRequest()
delete Client;
}
void ProcessWebGet()
{
WebDownloader downloader;
downloader.SetURL(g_pOptions->GetLastArg());
downloader.SetForce(true);
downloader.SetRetry(false);
downloader.SetOutputFilename(g_pOptions->GetWebGetFilename());
downloader.SetInfoName("WebGet");
int iRedirects = 0;
WebDownloader::EStatus eStatus = WebDownloader::adRedirect;
while (eStatus == WebDownloader::adRedirect && iRedirects < 5)
{
iRedirects++;
eStatus = downloader.Download();
}
bool bOK = eStatus == WebDownloader::adFinished;
exit(bOK ? 0 : 1);
}
void ExitProc()
{
if (!g_bReloading)
@@ -582,6 +626,9 @@ void ExitProc()
g_pPrePostProcessor->Stop();
g_pFeedCoordinator->Stop();
g_pArticleCache->Stop();
#ifdef WIN32
g_pWinConsole->Stop();
#endif
}
}
}
@@ -698,6 +745,11 @@ void Cleanup()
Thread::Final();
}
#ifdef WIN32
delete g_pWinConsole;
g_pWinConsole = NULL;
#endif
debug("Global objects cleaned up");
delete g_pLog;
@@ -707,42 +759,76 @@ void Cleanup()
#ifndef WIN32
void Daemonize()
{
int i, lfp;
char str[10];
if (getppid() == 1) return; /* already a daemon */
i = fork();
if (i < 0) exit(1); /* fork error */
if (i > 0) exit(0); /* parent exits */
int f = fork();
if (f < 0) exit(1); /* fork error */
if (f > 0) exit(0); /* parent exits */
/* child (daemon) continues */
setsid(); /* obtain a new process group */
for (i = getdtablesize();i >= 0;--i) close(i); /* close all descriptors */
i = open("/dev/null", O_RDWR); dup(i); dup(i); /* handle standart I/O */
chdir(g_pOptions->GetDestDir()); /* change running directory */
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0) exit(1); /* can not open */
if (lockf(lfp, F_TLOCK, 0) < 0) exit(0); /* can not lock */
// obtain a new process group
setsid();
// close all descriptors
for (int i = getdtablesize(); i >= 0; --i)
{
close(i);
}
// handle standart I/O
int d = open("/dev/null", O_RDWR);
dup(d);
dup(d);
// change running directory
chdir(g_pOptions->GetDestDir());
// set up lock-file
int lfp = -1;
if (!Util::EmptyStr(g_pOptions->GetLockFile()))
{
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0)
{
error("Starting daemon failed: could not create lock-file %s", g_pOptions->GetLockFile());
exit(1);
}
if (lockf(lfp, F_TLOCK, 0) < 0)
{
error("Starting daemon failed: could not acquire lock on lock-file %s", g_pOptions->GetLockFile());
exit(1);
}
}
/* Drop user if there is one, and we were run as root */
if ( getuid() == 0 || geteuid() == 0 )
if (getuid() == 0 || geteuid() == 0)
{
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUsername());
if (pw)
{
fchown(lfp, pw->pw_uid, pw->pw_gid); /* change owner of lock file */
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
setgid(pw->pw_gid); /* Set primary group. */
/* Try setting aux groups correctly - not critical if this fails. */
initgroups( g_pOptions->GetDaemonUsername(),pw->pw_gid);
/* Finally, set uid. */
// Change owner of lock file
fchown(lfp, pw->pw_uid, pw->pw_gid);
// Set aux groups to null.
setgroups(0, (const gid_t*)0);
// Set primary group.
setgid(pw->pw_gid);
// Try setting aux groups correctly - not critical if this fails.
initgroups(g_pOptions->GetDaemonUsername(), pw->pw_gid);
// Finally, set uid.
setuid(pw->pw_uid);
}
}
/* first instance continues */
sprintf(str, "%d\n", getpid());
write(lfp, str, strlen(str)); /* record pid to lockfile */
signal(SIGCHLD, SIG_IGN); /* ignore child */
signal(SIGTSTP, SIG_IGN); /* ignore tty signals */
// record pid to lockfile
if (lfp > -1)
{
char str[10];
sprintf(str, "%d\n", getpid());
write(lfp, str, strlen(str));
}
// ignore unwanted signals
signal(SIGCHLD, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
}

View File

@@ -70,6 +70,11 @@
#define FOPEN_AB "abN"
#define FOPEN_ABP "ab+N"
#ifdef DEBUG
// redefine "exit" to avoid printing memory leaks report when terminated because of wrong command line switches
#define exit(code) ExitProcess(code)
#endif
#pragma warning(disable:4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data

View File

@@ -158,13 +158,31 @@ void ArticleDownloader::Run()
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
m_szConnectionName[sizeof(m_szConnectionName) - 1] = '\0';
// check server retention
bool bRetentionFailure = m_pConnection->GetNewsServer()->GetRetention() > 0 &&
(time(NULL) - m_pFileInfo->GetTime()) / 86400 > m_pConnection->GetNewsServer()->GetRetention();
if (bRetentionFailure)
{
detail("Article %s @ %s failed: out of server retention (file age: %i, configured retention: %i)",
m_szInfoName, m_szConnectionName,
(time(NULL) - m_pFileInfo->GetTime()) / 86400,
m_pConnection->GetNewsServer()->GetRetention());
Status = adFailed;
FreeConnection(true);
}
if (m_pConnection && !IsStopped())
{
detail("Downloading %s @ %s", m_szInfoName, m_szConnectionName);
}
// test connection
bool bConnected = m_pConnection && m_pConnection->Connect();
if (bConnected && !IsStopped())
{
NewsServer* pNewsServer = m_pConnection->GetNewsServer();
detail("Downloading %s @ %s", m_szInfoName, m_szConnectionName);
// Download article
Status = Download();
if (Status == adFinished || Status == adFailed || Status == adNotFound || Status == adCrcError)
@@ -173,66 +191,45 @@ void ArticleDownloader::Run()
}
}
if (bConnected)
{
if (Status == adConnectError)
{
m_pConnection->Disconnect();
bConnected = false;
Status = adFailed;
}
else
{
// freeing connection allows other threads to start.
// we doing this only if the problem was with article or group.
// if the problem occurs by connecting or authorization we do not
// 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 || Status == adNotFound);
}
}
if (m_pConnection)
{
AddServerData();
}
if (Status == adFinished || Status == adFatalError)
if (!bConnected && m_pConnection)
{
break;
detail("Article %s @ %s failed: could not establish connection", m_szInfoName, m_szConnectionName);
}
pWantServer = NULL;
if (Status == adConnectError)
{
bConnected = false;
Status = adFailed;
}
if (bConnected && Status == adFailed)
{
iRemainedRetries--;
}
if (!bConnected || (Status == adFailed && iRemainedRetries > 0))
if (!bConnected && m_pConnection && !IsStopped())
{
g_pServerPool->BlockServer(pLastServer);
}
pWantServer = NULL;
if (bConnected && Status == adFailed && iRemainedRetries > 0 && !bRetentionFailure)
{
pWantServer = pLastServer;
}
if (pWantServer &&
!(IsStopped() || (g_pOptions->GetPauseDownload() && !bForce) ||
(g_pOptions->GetTempPauseDownload() && !m_pFileInfo->GetExtraPriority()) ||
iServerConfigGeneration != g_pServerPool->GetGeneration()))
else
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
SetStatus(adWaiting);
int msec = 0;
while (!(IsStopped() || (g_pOptions->GetPauseDownload() && !bForce) ||
(g_pOptions->GetTempPauseDownload() && !m_pFileInfo->GetExtraPriority()) ||
iServerConfigGeneration != g_pServerPool->GetGeneration()) &&
msec < g_pOptions->GetRetryInterval() * 1000)
{
usleep(100 * 1000);
msec += 100;
}
SetLastUpdateTimeNow();
SetStatus(adRunning);
FreeConnection(Status == adFinished || Status == adNotFound);
}
if (Status == adFinished || Status == adFatalError)
{
break;
}
if (IsStopped() || (g_pOptions->GetPauseDownload() && !bForce) ||
@@ -243,7 +240,7 @@ void ArticleDownloader::Run()
break;
}
if (!pWantServer)
if (!pWantServer && (bConnected || bRetentionFailure))
{
failedServers.push_back(pLastServer);
@@ -397,7 +394,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
// Throttle the bandwidth
while (!IsStopped() && (g_pOptions->GetDownloadRate() > 0.0f) &&
(g_pStatMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
(g_pStatMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate() ||
g_pStatMeter->CalcMomentaryDownloadSpeed() > g_pOptions->GetDownloadRate()))
{
SetLastUpdateTimeNow();
usleep(10 * 1000);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2014-2015 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
@@ -190,7 +190,9 @@ bool ArticleWriter::Start(Decoder::EFormat eFormat, const char* szFilename, long
m_pOutFile = fopen(szFilename, bDirectWrite ? FOPEN_RBP : FOPEN_WB);
if (!m_pOutFile)
{
error("Could not %s file %s: %s", bDirectWrite ? "open" : "create", szFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not %s file %s: %s", bDirectWrite ? "open" : "create", szFilename,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return false;
}
SetWriteBuffer(m_pOutFile, m_pArticleInfo->GetSize());
@@ -250,7 +252,9 @@ void ArticleWriter::Finish(bool bSuccess)
{
if (!Util::MoveFile(m_szTempFilename, m_szResultFilename))
{
error("Could not rename file %s to %s: %s", m_szTempFilename, m_szResultFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not rename file %s to %s: %s", m_szTempFilename, m_szResultFilename,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
@@ -279,7 +283,9 @@ void ArticleWriter::Finish(bool bSuccess)
// rawmode
if (!Util::MoveFile(m_szTempFilename, m_szResultFilename))
{
error("Could not move file %s to %s: %s", m_szTempFilename, m_szResultFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", m_szTempFilename, m_szResultFilename,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
}
@@ -307,13 +313,15 @@ bool ArticleWriter::CreateOutputFile(long long iSize)
if (!Util::ForceDirectories(szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", szDestDir, szErrBuf);
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create directory %s: %s", szDestDir, szErrBuf);
return false;
}
if (!Util::CreateSparseFile(m_szOutputFilename, iSize))
{
error("Could not create file %s", m_szOutputFilename);
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create file %s", m_szOutputFilename);
return false;
}
@@ -399,7 +407,8 @@ void ArticleWriter::CompleteFileParts()
// Ensure the DstDir is created
if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create directory %s: %s", szNZBDestDir, szErrBuf);
return;
}
@@ -417,7 +426,8 @@ void ArticleWriter::CompleteFileParts()
outfile = fopen(tmpdestfile, FOPEN_WBP);
if (!outfile)
{
error("Could not create file %s: %s", tmpdestfile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create file %s: %s", tmpdestfile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return;
}
}
@@ -426,7 +436,8 @@ void ArticleWriter::CompleteFileParts()
outfile = fopen(m_szOutputFilename, FOPEN_RBP);
if (!outfile)
{
error("Could not open file %s: %s", m_szOutputFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not open file %s: %s", m_szOutputFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return;
}
strncpy(tmpdestfile, m_szOutputFilename, 1024);
@@ -437,7 +448,8 @@ void ArticleWriter::CompleteFileParts()
remove(tmpdestfile);
if (!Util::CreateDirectory(ofn))
{
error("Could not create directory %s: %s", ofn, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create directory %s: %s", ofn, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return;
}
}
@@ -504,7 +516,8 @@ void ArticleWriter::CompleteFileParts()
{
m_pFileInfo->SetFailedArticles(m_pFileInfo->GetFailedArticles() + 1);
m_pFileInfo->SetSuccessArticles(m_pFileInfo->GetSuccessArticles() - 1);
error("Could not find file %s for %s%c%s [%i/%i]",
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not find file %s for %s%c%s [%i/%i]",
pa->GetResultFilename(), szNZBName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(),
pa->GetPartNumber(), (int)m_pFileInfo->GetArticles()->size());
}
@@ -516,7 +529,9 @@ void ArticleWriter::CompleteFileParts()
dstFileName[1024-1] = '\0';
if (!Util::MoveFile(pa->GetResultFilename(), dstFileName))
{
error("Could not move file %s to %s: %s", pa->GetResultFilename(), dstFileName, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", pa->GetResultFilename(), dstFileName,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
@@ -540,7 +555,9 @@ void ArticleWriter::CompleteFileParts()
fclose(outfile);
if (!bDirectWrite && !Util::MoveFile(tmpdestfile, ofn))
{
error("Could not move file %s to %s: %s", tmpdestfile, ofn, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", tmpdestfile, ofn,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
@@ -548,7 +565,9 @@ void ArticleWriter::CompleteFileParts()
{
if (!Util::MoveFile(m_szOutputFilename, ofn))
{
error("Could not move file %s to %s: %s", m_szOutputFilename, ofn, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not move file %s to %s: %s", m_szOutputFilename, ofn,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
// if destination directory was changed delete the old directory (if empty)
@@ -581,14 +600,16 @@ void ArticleWriter::CompleteFileParts()
if (m_pFileInfo->GetMissedArticles() == 0 && m_pFileInfo->GetFailedArticles() == 0)
{
info("Successfully downloaded %s", szInfoFilename);
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkInfo, "Successfully downloaded %s", szInfoFilename);
}
else
{
warn("%i of %i article downloads failed for \"%s\"", m_pFileInfo->GetMissedArticles() + m_pFileInfo->GetFailedArticles(),
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkWarning,
"%i of %i article downloads failed for \"%s\"",
m_pFileInfo->GetMissedArticles() + m_pFileInfo->GetFailedArticles(),
m_pFileInfo->GetTotalArticles(), szInfoFilename);
if (g_pOptions->GetCreateBrokenLog())
if (g_pOptions->GetBrokenLog())
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szNZBDestDir, (int)PATH_SEPARATOR);
@@ -668,7 +689,9 @@ void ArticleWriter::FlushCache()
outfile = fopen(m_pFileInfo->GetOutputFilename(), FOPEN_RBP);
if (!outfile)
{
error("Could not open file %s: %s", m_pFileInfo->GetOutputFilename(), Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not open file %s: %s", m_pFileInfo->GetOutputFilename(),
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
break;
}
bNeedBufFile = true;
@@ -682,7 +705,9 @@ void ArticleWriter::FlushCache()
outfile = fopen(szDestFile, FOPEN_WB);
if (!outfile)
{
error("Could not create file %s: %s", "create", szDestFile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not create file %s: %s", "create", szDestFile,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
break;
}
bNeedBufFile = true;
@@ -713,7 +738,9 @@ void ArticleWriter::FlushCache()
if (!Util::MoveFile(szDestFile, pa->GetResultFilename()))
{
error("Could not rename file %s to %s: %s", szDestFile, pa->GetResultFilename(), Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
m_pFileInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Could not rename file %s to %s: %s", szDestFile, pa->GetResultFilename(),
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
}
@@ -743,7 +770,7 @@ bool ArticleWriter::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestD
char szErrBuf[1024];
if (!Util::ForceDirectories(pNZBInfo->GetDestDir(), szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
pNZBInfo->PrintMessage(Message::mkError, "Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
return false;
}
@@ -770,13 +797,14 @@ bool ArticleWriter::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestD
if (!Util::MoveFile(szOldFileName, szNewFileName))
{
char szErrBuf[256];
error("Could not move file %s to %s: %s", szOldFileName, szNewFileName, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
pNZBInfo->PrintMessage(Message::mkError, "Could not move file %s to %s: %s",
szOldFileName, szNewFileName, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
}
// move brokenlog.txt
if (g_pOptions->GetCreateBrokenLog())
if (g_pOptions->GetBrokenLog())
{
char szOldBrokenLogName[1024];
snprintf(szOldBrokenLogName, 1024, "%s%c_brokenlog.txt", szOldDestDir, (int)PATH_SEPARATOR);
@@ -813,13 +841,13 @@ bool ArticleWriter::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestD
}
else
{
error("Could not open file %s", szOldBrokenLogName);
pNZBInfo->PrintMessage(Message::mkError, "Could not open file %s", szOldBrokenLogName);
}
fclose(outfile);
}
else
{
error("Could not open file %s", szBrokenLogName);
pNZBInfo->PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
else
@@ -828,7 +856,8 @@ bool ArticleWriter::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestD
if (!Util::MoveFile(szOldBrokenLogName, szBrokenLogName))
{
char szErrBuf[256];
error("Could not move file %s to %s: %s", szOldBrokenLogName, szBrokenLogName, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
pNZBInfo->PrintMessage(Message::mkError, "Could not move file %s to %s: %s",
szOldBrokenLogName, szBrokenLogName, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
}
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -99,8 +99,8 @@ bool NNTPConnection::Authenticate()
{
if (strlen(m_pNewsServer->GetUser()) == 0 || strlen(m_pNewsServer->GetPassword()) == 0)
{
error("%c%s (%s) requested authorization but username/password are not set in settings",
toupper(m_pNewsServer->GetName()[0]), m_pNewsServer->GetName() + 1, m_pNewsServer->GetHost());
ReportError("Could not connect to %s: server requested authorization but username/password are not set in settings",
m_pNewsServer->GetHost(), false, 0);
m_bAuthError = true;
return false;
}
@@ -125,7 +125,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportErrorAnswer("Authorization for %s (%s) failed: Connection closed by remote host", NULL);
return false;
}
@@ -147,7 +147,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s (%s) failed: %s", answer);
}
return false;
}
@@ -168,7 +168,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportErrorAnswer("Authorization failed for %s (%s): Connection closed by remote host", NULL);
return false;
}
else if (!strncmp(answer, "2", 1))
@@ -185,7 +185,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s (%s) failed: %s", answer);
}
return false;
}
@@ -237,14 +237,14 @@ bool NNTPConnection::Connect()
if (!answer)
{
ReportErrorAnswer("Connection to server%i (%s) failed: Connection closed by remote host", NULL);
ReportErrorAnswer("Connection to %s (%s) failed: Connection closed by remote host", NULL);
Disconnect();
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Connection to %s (%s) failed: %s", answer);
Disconnect();
return false;
}
@@ -264,7 +264,10 @@ bool NNTPConnection::Disconnect()
{
if (m_eStatus == csConnected)
{
Request("quit\r\n");
if (!m_bBroken)
{
Request("quit\r\n");
}
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
@@ -274,7 +277,7 @@ bool NNTPConnection::Disconnect()
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetID(), m_pNewsServer->GetHost(), szAnswer);
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetName(), m_pNewsServer->GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -53,6 +53,7 @@ public:
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 if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2014 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
@@ -41,7 +41,7 @@
NewsServer::NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS,
const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
const char* szCipher, int iMaxConnections, int iRetention, int iLevel, int iGroup)
{
m_iID = iID;
m_iStateID = 0;
@@ -57,6 +57,8 @@ NewsServer::NewsServer(int iID, bool bActive, const char* szName, const char* sz
m_szUser = strdup(szUser ? szUser : "");
m_szPassword = strdup(szPass ? szPass : "");
m_szCipher = strdup(szCipher ? szCipher : "");
m_iRetention = iRetention;
m_tBlockTime = 0;
if (szName && strlen(szName) > 0)
{

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,6 +28,7 @@
#define NEWSSERVER_H
#include <vector>
#include <time.h>
class NewsServer
{
@@ -47,11 +48,14 @@ private:
bool m_bJoinGroup;
bool m_bTLS;
char* m_szCipher;
int m_iRetention;
time_t m_tBlockTime;
public:
NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
bool bTLS, const char* szCipher, int iMaxConnections, int iRetention,
int iLevel, int iGroup);
~NewsServer();
int GetID() { return m_iID; }
int GetStateID() { return m_iStateID; }
@@ -71,6 +75,9 @@ public:
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
int GetRetention() { return m_iRetention; }
time_t GetBlockTime() { return m_tBlockTime; }
void SetBlockTime(time_t tBlockTime) { m_tBlockTime = tBlockTime; }
};
typedef std::vector<NewsServer*> Servers;

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -58,6 +58,7 @@ ServerPool::ServerPool()
m_iMaxNormLevel = 0;
m_iTimeout = 60;
m_iGeneration = 0;
m_iRetryInterval = 0;
g_pLog->RegisterDebuggable(this);
}
@@ -157,6 +158,7 @@ void ServerPool::InitConnections()
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
{
NewsServer* pNewsServer = *it;
pNewsServer->SetBlockTime(0);
int iNormLevel = pNewsServer->GetNormLevel();
if (pNewsServer->GetNormLevel() > -1)
{
@@ -199,11 +201,15 @@ void ServerPool::InitConnections()
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
{
PooledConnection* pConnection = NULL;
m_mutexConnections.Lock();
time_t tCurTime = time(NULL);
if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0)
{
Connections candidates;
candidates.reserve(m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pCandidateConnection = *it;
@@ -211,7 +217,11 @@ NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, S
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetActive() &&
pCandidateServer->GetNormLevel() == iLevel &&
(!pWantServer || pCandidateServer == pWantServer ||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())) &&
(pCandidateConnection->GetStatus() == Connection::csConnected ||
!pCandidateServer->GetBlockTime() ||
pCandidateServer->GetBlockTime() + m_iRetryInterval <= tCurTime ||
pCandidateServer->GetBlockTime() > tCurTime))
{
// free connection found, check if it's not from the server which should be ignored
bool bUseConnection = true;
@@ -230,15 +240,25 @@ NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, S
}
}
pCandidateServer->SetBlockTime(0);
if (bUseConnection)
{
pConnection = pCandidateConnection;
pConnection->SetInUse(true);
break;
candidates.push_back(pCandidateConnection);
}
}
}
if (!candidates.empty())
{
// Peeking a random free connection. This is better than taking the first
// available connection because provides better distribution across news servers,
// especially when one of servers becomes unavailable or doesn't have requested articles.
int iRandomIndex = rand() % candidates.size();
pConnection = candidates[iRandomIndex];
pConnection->SetInUse(true);
}
if (pConnection)
{
m_Levels[iLevel]--;
@@ -273,12 +293,27 @@ void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
m_mutexConnections.Unlock();
}
void ServerPool::BlockServer(NewsServer* pNewsServer)
{
m_mutexConnections.Lock();
time_t tCurTime = time(NULL);
bool bNewBlock = pNewsServer->GetBlockTime() != tCurTime;
pNewsServer->SetBlockTime(tCurTime);
m_mutexConnections.Unlock();
if (bNewBlock && m_iRetryInterval > 0)
{
warn("Blocking %s (%s) for %i sec", pNewsServer->GetName(), pNewsServer->GetHost(), m_iRetryInterval);
}
}
void ServerPool::CloseUnusedConnections()
{
m_mutexConnections.Lock();
time_t curtime = ::time(NULL);
// close and free all connections of servers which were disabled since the last check
int i = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); )
{
@@ -300,16 +335,6 @@ void ServerPool::CloseUnusedConnections()
bDeleted = true;
}
if (!bDeleted && !pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID());
pConnection->Disconnect();
}
}
if (!bDeleted)
{
it++;
@@ -317,6 +342,50 @@ void ServerPool::CloseUnusedConnections()
}
}
// close all opened connections on levels not having any in-use connections
for (int iLevel = 0; iLevel <= m_iMaxNormLevel; iLevel++)
{
// check if we have in-use connections on the level
bool bHasInUseConnections = false;
int iInactiveTime = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (pConnection->GetNewsServer()->GetNormLevel() == iLevel)
{
if (pConnection->GetInUse())
{
bHasInUseConnections = true;
break;
}
else
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > iInactiveTime)
{
iInactiveTime = tdiff;
}
}
}
}
// if there are no in-use connections on the level and the hold time out has
// expired - close all connections of the level.
if (!bHasInUseConnections && iInactiveTime > CONNECTION_HOLD_SECODNS)
{
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (pConnection->GetNewsServer()->GetNormLevel() == iLevel &&
pConnection->GetStatus() == Connection::csConnected)
{
debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID());
pConnection->Disconnect();
}
}
}
}
m_mutexConnections.Unlock();
}
@@ -336,12 +405,16 @@ void ServerPool::LogDebugInfo()
m_mutexConnections.Lock();
time_t tCurTime = time(NULL);
info(" Servers: %i", m_Servers.size());
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
info(" %i) %s (%s): Level=%i, NormLevel=%i", pNewsServer->GetID(), pNewsServer->GetName(),
pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel());
info(" %i) %s (%s): Level=%i, NormLevel=%i, BlockSec=%i", pNewsServer->GetID(), pNewsServer->GetName(),
pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel(),
pNewsServer->GetBlockTime() && pNewsServer->GetBlockTime() + m_iRetryInterval > tCurTime ?
pNewsServer->GetBlockTime() + m_iRetryInterval - tCurTime : 0);
}
info(" Levels: %i", m_Levels.size());

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2014 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
@@ -61,18 +61,20 @@ private:
int m_iMaxNormLevel;
Mutex m_mutexConnections;
int m_iTimeout;
int m_iRetryInterval;
int m_iGeneration;
void NormalizeLevels();
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
protected:
virtual void LogDebugInfo();
virtual void LogDebugInfo();
public:
ServerPool();
~ServerPool();
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
void SetRetryInterval(int iRetryInterval) { m_iRetryInterval = iRetryInterval; }
void AddServer(NewsServer* pNewsServer);
void InitConnections();
int GetMaxNormLevel() { return m_iMaxNormLevel; }
@@ -82,6 +84,7 @@ public:
void CloseUnusedConnections();
void Changed();
int GetGeneration() { return m_iGeneration; }
void BlockServer(NewsServer* pNewsServer);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2014-2015 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
@@ -40,6 +40,7 @@
#include "Options.h"
#include "ServerPool.h"
#include "DiskState.h"
#include "Util.h"
extern ServerPool* g_pServerPool;
extern Options* g_pOptions;
@@ -171,43 +172,42 @@ void ServerVolume::LogDebugInfo()
{
info(" ---------- ServerVolume");
char szSec[4000];
StringBuilder msg;
szSec[0] = '\0';
for (int i = 0; i < 60; i++)
{
char szNum[20];
snprintf(szNum, 20, "[%i]=%lli ", i, m_BytesPerSeconds[i]);
strncat(szSec, szNum, 4000);
char szNum[30];
snprintf(szNum, 30, "[%i]=%lli ", i, m_BytesPerSeconds[i]);
msg.Append(szNum);
}
info("Secs: %s", szSec);
info("Secs: %s", msg.GetBuffer());
szSec[0] = '\0';
msg.Clear();
for (int i = 0; i < 60; i++)
{
char szNum[20];
snprintf(szNum, 20, "[%i]=%lli ", i, m_BytesPerMinutes[i]);
strncat(szSec, szNum, 4000);
char szNum[30];
snprintf(szNum, 30, "[%i]=%lli ", i, m_BytesPerMinutes[i]);
msg.Append(szNum);
}
info("Mins: %s", szSec);
info("Mins: %s", msg.GetBuffer());
szSec[0] = '\0';
msg.Clear();
for (int i = 0; i < 24; i++)
{
char szNum[20];
snprintf(szNum, 20, "[%i]=%lli ", i, m_BytesPerHours[i]);
strncat(szSec, szNum, 4000);
char szNum[30];
snprintf(szNum, 30, "[%i]=%lli ", i, m_BytesPerHours[i]);
msg.Append(szNum);
}
info("Hours: %s", szSec);
info("Hours: %s", msg.GetBuffer());
szSec[0] = '\0';
msg.Clear();
for (int i = 0; i < (int)m_BytesPerDays.size(); i++)
{
char szNum[20];
snprintf(szNum, 20, "[%i]=%lli ", m_iFirstDay + i, m_BytesPerDays[i]);
strncat(szSec, szNum, 4000);
char szNum[30];
snprintf(szNum, 30, "[%i]=%lli ", m_iFirstDay + i, m_BytesPerDays[i]);
msg.Append(szNum);
}
info("Days: %s", szSec);
info("Days: %s", msg.GetBuffer());
}
StatMeter::StatMeter()
@@ -356,9 +356,7 @@ void StatMeter::CalcTotalStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllB
m_mutexStat.Unlock();
}
/*
* NOTE: see note to "AddSpeedReading"
*/
// Average speed in last 30 seconds
int StatMeter::CalcCurrentDownloadSpeed()
{
if (m_bStandBy)
@@ -375,6 +373,14 @@ int StatMeter::CalcCurrentDownloadSpeed()
return (int)(m_iSpeedTotalBytes / iTimeDiff);
}
// Amount of data downloaded in current second
int StatMeter::CalcMomentaryDownloadSpeed()
{
time_t tCurTime = time(NULL);
int iSpeed = tCurTime == m_tCurSecTime ? m_iCurSecBytes : 0;
return iSpeed;
}
void StatMeter::AddSpeedReading(int iBytes)
{
time_t tCurTime = time(NULL);
@@ -389,6 +395,13 @@ void StatMeter::AddSpeedReading(int iBytes)
#endif
}
if (tCurTime != m_tCurSecTime)
{
m_tCurSecTime = tCurTime;
m_iCurSecBytes = 0;
}
m_iCurSecBytes += iBytes;
while (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
{
//record bytes in next slot
@@ -452,6 +465,8 @@ void StatMeter::ResetSpeedStat()
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
m_tSpeedCorrection = tCurTime;
m_tCurSecTime = 0;
m_iCurSecBytes = 0;
}
void StatMeter::LogDebugInfo()

View File

@@ -93,6 +93,8 @@ private:
int m_iSpeedStartTime;
time_t m_tSpeedCorrection;
int m_iSpeedBytesIndex;
int m_iCurSecBytes;
time_t m_tCurSecTime;
#ifdef HAVE_SPINLOCK
SpinLock m_spinlockSpeed;
#else
@@ -125,6 +127,7 @@ public:
~StatMeter();
void Init();
int CalcCurrentDownloadSpeed();
int CalcMomentaryDownloadSpeed();
void AddSpeedReading(int iBytes);
void AddServerData(int iBytes, int iServerID);
void CalcTotalStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,9 @@ bool Repairer::ScanDataFile(DiskFile *diskfile, Par2RepairerSourceFile* &sourcef
{
sig_done(name, iAvailableBlocks, sourcefile->BlockCount());
sig_progress(1000.0);
matchtype = eFileStatus == ParChecker::fsSuccess ? eFullMatch : ParChecker::fsPartial ? ePartialMatch : eNoMatch;
matchtype = eFileStatus == ParChecker::fsSuccess ? eFullMatch :
eFileStatus == ParChecker::fsPartial ? ePartialMatch : eNoMatch;
m_pOwner->SetParFull(false);
return true;
}
}
@@ -414,6 +416,7 @@ ParChecker::ParChecker()
m_eStage = ptLoadingPars;
m_bParQuick = false;
m_bForceRepair = false;
m_bParFull = false;
}
ParChecker::~ParChecker()
@@ -494,6 +497,7 @@ ParChecker::EStatus ParChecker::RunParCheckAll()
EStatus eAllStatus = psRepairNotNeeded;
m_bCancelled = false;
m_bParFull = true;
for (ParCoordinator::ParFileList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
@@ -527,7 +531,7 @@ ParChecker::EStatus ParChecker::RunParCheckAll()
eAllStatus = eStatus;
}
if (g_pOptions->GetCreateBrokenLog())
if (g_pOptions->GetBrokenLog())
{
WriteBrokenLog(eStatus);
}
@@ -572,6 +576,11 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
if (!m_bParQuick)
{
CheckEmptyFiles();
}
bool bAddedSplittedFragments = false;
if (m_bHasDamagedFiles && !IsStopped() && res == eRepairNotPossible)
{
@@ -957,7 +966,7 @@ bool ParChecker::AddSplittedFragments()
{
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
info("Found %i splitted fragments for %s", (int)extrafiles.size(), m_szInfoName);
PrintMessage(Message::mkInfo, "Found %i splitted fragments for %s", (int)extrafiles.size(), m_szInfoName);
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
((Repairer*)m_pRepairer)->UpdateVerificationResults();
m_bVerifyingExtraFiles = false;
@@ -1027,7 +1036,7 @@ bool ParChecker::AddMissingFiles()
bool bAdded = iWasMissing > (int)((Repairer*)m_pRepairer)->missingfilecount;
if (bAdded)
{
info("Found missing file %s", Util::BaseFileName(pExtraFile->FileName().c_str()));
PrintMessage(Message::mkInfo, "Found missing file %s", Util::BaseFileName(pExtraFile->FileName().c_str()));
RegisterParredFile(Util::BaseFileName(pExtraFile->FileName().c_str()));
}
@@ -1185,6 +1194,39 @@ void ParChecker::signal_done(std::string str, int available, int total)
}
}
/*
* Only if ParQuick isn't enabled:
* For empty damaged files the callback-function "signal_done" isn't called and the flag "m_bHasDamagedFiles"
* therefore isn't set. In this function we expicitly check such files.
*/
void ParChecker::CheckEmptyFiles()
{
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (sourcefile && sourcefile->GetDescriptionPacket())
{
const char* szFilename = sourcefile->GetDescriptionPacket()->FileName().c_str();
if (!IsProcessedFile(szFilename))
{
bool bIgnore = Util::MatchFileExt(szFilename, g_pOptions->GetParIgnoreExt(), ",;") ||
Util::MatchFileExt(szFilename, g_pOptions->GetExtCleanupDisk(), ",;");
m_bHasDamagedFiles |= !bIgnore;
int total = sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0;
PrintMessage(Message::mkWarning, "File %s has %i bad block(s) of total %i block(s)%s",
szFilename, total, total, bIgnore ? ", ignoring" : "");
}
}
else
{
m_bHasDamagedFiles = true;
}
}
}
void ParChecker::Cancel()
{
((Repairer*)m_pRepairer)->cancelled = true;
@@ -1294,7 +1336,7 @@ void ParChecker::DeleteLeftovers()
* download with CRC stored in PAR2-file;
* - for partially downloaded files the CRCs of articles are compared with block-CRCs stored
* in PAR2-file;
* - for completely failed files (not a single successful artice) no verification is needed at all.
* - for completely failed files (not a single successful article) no verification is needed at all.
*
* Limitation of the function:
* This function requires every block in the file to have an unique CRC (across all blocks
@@ -1463,7 +1505,8 @@ bool ParChecker::VerifyPartialDataFile(void* pDiskfile, void* pSourcefile, Segme
FILE* infile = fopen(szFilename, FOPEN_RB);
if (!infile)
{
error("Could not open file %s: %s", szFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
PrintMessage(Message::mkError, "Could not open file %s: %s",
szFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
// For each sequential range of presumably valid blocks:

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -119,6 +119,7 @@ private:
bool m_bHasDamagedFiles;
bool m_bParQuick;
bool m_bForceRepair;
bool m_bParFull;
void Cleanup();
EStatus RunParCheckAll();
@@ -144,6 +145,7 @@ private:
bool SmartCalcFileRangeCrc(FILE* pFile, long long lStart, long long lEnd, SegmentList* pSegments,
unsigned long* pDownloadCrc);
bool DumbCalcFileRangeCrc(FILE* pFile, long long lStart, long long lEnd, unsigned long* pDownloadCrc);
void CheckEmptyFiles();
protected:
/**
@@ -176,6 +178,8 @@ public:
bool GetParQuick() { return m_bParQuick; }
void SetForceRepair(bool bForceRepair) { m_bForceRepair = bForceRepair; }
bool GetForceRepair() { return m_bForceRepair; }
void SetParFull(bool bParFull) { m_bParFull = bParFull; }
bool GetParFull() { return m_bParFull; }
EStatus GetStatus() { return m_eStatus; }
void AddParFile(const char* szParFilename);
void QueueChanged();

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -72,7 +72,7 @@ void ParCoordinator::PostParChecker::PrintMessage(Message::EKind eKind, const ch
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}
void ParCoordinator::PostParChecker::RegisterParredFile(const char* szFilename)
@@ -160,7 +160,7 @@ void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind eKind, const ch
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}
void ParCoordinator::PostParRenamer::RegisterParredFile(const char* szFilename)
@@ -441,6 +441,7 @@ void ParCoordinator::ParCheckCompleted()
pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
pPostInfo->SetParRepaired(m_ParChecker.GetStatus() == ParChecker::psRepaired);
}
else if (m_ParChecker.GetStatus() == ParChecker::psRepairPossible &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
@@ -457,7 +458,7 @@ void ParCoordinator::ParCheckCompleted()
int iParSec = (int)(time(NULL) - m_ParChecker.GetParTime()) - iWaitTime;
pPostInfo->GetNZBInfo()->SetParSec(pPostInfo->GetNZBInfo()->GetParSec() + iParSec);
pPostInfo->GetNZBInfo()->SetParFull(!m_ParChecker.GetParQuick());
pPostInfo->GetNZBInfo()->SetParFull(m_ParChecker.GetParFull());
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
@@ -578,7 +579,7 @@ void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
{
// should not happen
error("Internal error: could not parse filename %s", szBaseParFilename);
pNZBInfo->PrintMessage(Message::mkError, "Internal error: could not parse filename %s", szBaseParFilename);
return;
}
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
@@ -758,7 +759,7 @@ void ParCoordinator::ParRenameCompleted()
if (m_ParRenamer.HasMissedFiles() && pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
PrintMessage(pPostInfo, Message::mkInfo, "Requesting par-check/repair for %s to restore missing files ", m_ParRenamer.GetInfoName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Requesting par-check/repair for %s to restore missing files ", m_ParRenamer.GetInfoName());
pPostInfo->SetRequestParCheck(true);
}
@@ -790,39 +791,4 @@ void ParCoordinator::UpdateParRenameProgress()
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

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -114,7 +114,6 @@ protected:
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:

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-2015 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
@@ -262,7 +262,7 @@ void ParRenamer::LoadParFile(const char* szParFilename)
Par2RepairerSourceFile* sourceFile = (*it).second;
if (!sourceFile || !sourceFile->GetDescriptionPacket())
{
warn("Damaged par2-file detected: %s", szParFilename);
PrintMessage(Message::mkWarning, "Damaged par2-file detected: %s", szParFilename);
continue;
}
m_FileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
@@ -315,11 +315,11 @@ void ParRenamer::CheckMissing()
if (Util::MatchFileExt(pFileHash->GetFilename(), g_pOptions->GetParIgnoreExt(), ",;") ||
Util::MatchFileExt(pFileHash->GetFilename(), g_pOptions->GetExtCleanupDisk(), ",;"))
{
info("File %s is missing, ignoring", pFileHash->GetFilename());
PrintMessage(Message::mkInfo, "File %s is missing, ignoring", pFileHash->GetFilename());
}
else
{
info("File %s is missing", pFileHash->GetFilename());
PrintMessage(Message::mkInfo, "File %s is missing", pFileHash->GetFilename());
m_bHasMissedFiles = true;
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -104,6 +104,14 @@ void PostScriptController::ExecuteScript(Options::Script* pScript)
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", pScript->GetName(), m_pPostInfo->GetNZBInfo()->GetName());
char szProgressLabel[1024];
snprintf(szProgressLabel, 1024, "Executing post-process-script %s", pScript->GetName());
szProgressLabel[1024-1] = '\0';
DownloadQueue::Lock();
m_pPostInfo->SetProgressLabel(szProgressLabel);
DownloadQueue::Unlock();
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
@@ -277,7 +285,8 @@ void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
m_pPostInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
free(szParam);
}
@@ -290,17 +299,16 @@ void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
m_pPostInfo->GetNZBInfo()->PrintMessage(Message::mkError,
"Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
}
else if (!strncmp(szMsgText, "[HISTORY] ", 10))
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szMsgText);
}
else
{
ScriptController::AddMessage(eKind, szText);
m_pPostInfo->AppendMessage(eKind, szText);
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
DownloadQueue::Lock();
m_pPostInfo->SetProgressLabel(szText);
DownloadQueue::Unlock();
}
if (g_pOptions->GetPausePostProcess() && !m_pPostInfo->GetNZBInfo()->GetForcePriority())

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -198,7 +198,8 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
{
// the deleting of nzbs is usually handled via eaFileDeleted-event, but when deleting nzb without
// any files left the eaFileDeleted-event is not fired and we need to process eaNzbDeleted-event instead
info("Collection %s deleted from queue", pQueueAspect->pNZBInfo->GetName());
pQueueAspect->pNZBInfo->PrintMessage(Message::mkInfo,
"Collection %s deleted from queue", pQueueAspect->pNZBInfo->GetName());
NZBDeleted(pQueueAspect->pDownloadQueue, pQueueAspect->pNZBInfo);
}
else if ((pQueueAspect->eAction == DownloadQueue::eaFileCompleted ||
@@ -222,7 +223,8 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
IsNZBFileCompleted(pQueueAspect->pNZBInfo, false, true))) &&
pQueueAspect->pFileInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsHealth)
{
info("Collection %s completely downloaded", pQueueAspect->pNZBInfo->GetName());
pQueueAspect->pNZBInfo->PrintMessage(Message::mkInfo,
"Collection %s completely downloaded", pQueueAspect->pNZBInfo->GetName());
g_pQueueScriptCoordinator->EnqueueScript(pQueueAspect->pNZBInfo, QueueScriptCoordinator::qeNzbDownloaded);
NZBDownloaded(pQueueAspect->pDownloadQueue, pQueueAspect->pNZBInfo);
}
@@ -232,7 +234,8 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
!pQueueAspect->pNZBInfo->GetParCleanup() &&
IsNZBFileCompleted(pQueueAspect->pNZBInfo, false, true))
{
info("Collection %s deleted from queue", pQueueAspect->pNZBInfo->GetName());
pQueueAspect->pNZBInfo->PrintMessage(Message::mkInfo,
"Collection %s deleted from queue", pQueueAspect->pNZBInfo->GetName());
NZBDeleted(pQueueAspect->pDownloadQueue, pQueueAspect->pNZBInfo);
}
}
@@ -269,7 +272,7 @@ void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
{
if (!pNZBInfo->GetPostInfo() && g_pOptions->GetDecode())
{
info("Queueing %s for post-processing", pNZBInfo->GetName());
pNZBInfo->PrintMessage(Message::mkInfo, "Queueing %s for post-processing", pNZBInfo->GetName());
pNZBInfo->EnterPostProcess();
m_iJobCount++;
@@ -449,13 +452,15 @@ void PrePostProcessor::CheckPostQueue()
if (!pPostInfo->GetNZBInfo()->GetFileList()->empty())
{
info("Downloading all remaining files for manual par-check for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"Downloading all remaining files for manual par-check for %s", pPostInfo->GetNZBInfo()->GetName());
pDownloadQueue->EditEntry(pPostInfo->GetNZBInfo()->GetID(), DownloadQueue::eaGroupResume, 0, NULL);
pPostInfo->SetStage(PostInfo::ptFinished);
}
else
{
info("There are no par-files remain for download for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"There are no par-files remain for download for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetStage(PostInfo::ptQueued);
}
}
@@ -564,7 +569,8 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
}
else
{
info("Nothing to par-check for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"Nothing to par-check for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSkipped);
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
@@ -576,7 +582,8 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) < 1000 &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
warn("Skipping par-check for %s due to health %.1f%% below critical %.1f%%", pPostInfo->GetNZBInfo()->GetName(),
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkWarning,
"Skipping par-check for %s due to health %.1f%% below critical %.1f%%", pPostInfo->GetNZBInfo()->GetName(),
pPostInfo->GetNZBInfo()->CalcHealth() / 10.0, pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) / 10.0);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
return;
@@ -585,7 +592,8 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
pPostInfo->GetNZBInfo()->GetFailedSize() - pPostInfo->GetNZBInfo()->GetParFailedSize() > 0 &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
info("Collection %s with health %.1f%% needs par-check",
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"Collection %s with health %.1f%% needs par-check",
pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->CalcHealth() / 10.0);
pPostInfo->SetRequestParCheck(true);
return;
@@ -603,13 +611,18 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
bool bCleanup = !bUnpack &&
pPostInfo->GetNZBInfo()->GetCleanupStatus() == NZBInfo::csNone &&
!Util::EmptyStr(g_pOptions->GetExtCleanupDisk()) &&
((pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usFailure &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usSpace &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usPassword) ||
(pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)) &&
!Util::EmptyStr(g_pOptions->GetExtCleanupDisk());
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure) ||
((pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone ||
pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSkipped) &&
(pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSkipped) &&
pPostInfo->GetNZBInfo()->CalcHealth() == 1000));
bool bMoveInter = !bUnpack &&
pPostInfo->GetNZBInfo()->GetMoveStatus() == NZBInfo::msNone &&
@@ -626,7 +639,8 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
if (bUnpack && bParFailed)
{
warn("Skipping unpack for %s due to %s", pPostInfo->GetNZBInfo()->GetName(),
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkWarning,
"Skipping unpack for %s due to %s", pPostInfo->GetNZBInfo()->GetName(),
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psManual ? "required par-repair" : "par-failure");
pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
bUnpack = false;
@@ -703,7 +717,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
pNZBInfo->CalcCriticalHealth(false) < 1000);
if (g_pOptions->GetParCleanupQueue() && bCanCleanupQueue && !pNZBInfo->GetFileList()->empty())
{
info("Cleaning up download queue for %s", pNZBInfo->GetName());
pNZBInfo->PrintMessage(Message::mkInfo, "Cleaning up download queue for %s", pNZBInfo->GetName());
pNZBInfo->SetParCleanup(true);
pDownloadQueue->EditEntry(pNZBInfo->GetID(), DownloadQueue::eaGroupDelete, 0, NULL);
}
@@ -809,7 +823,8 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* pDownloadQueue, IDList* pI
{
if (pPostInfo->GetWorking())
{
info("Deleting active post-job %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"Deleting active post-job %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetDeleted(true);
#ifndef DISABLE_PARCHECK
if (PostInfo::ptLoadingPars <= pPostInfo->GetStage() && pPostInfo->GetStage() <= PostInfo::ptRenaming)
@@ -834,7 +849,8 @@ bool PrePostProcessor::PostQueueDelete(DownloadQueue* pDownloadQueue, IDList* pI
}
else
{
info("Deleting queued post-job %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->GetNZBInfo()->PrintMessage(Message::mkInfo,
"Deleting queued post-job %s", pPostInfo->GetNZBInfo()->GetName());
JobCompleted(pDownloadQueue, pPostInfo);
bOK = true;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-2015 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
@@ -72,6 +72,28 @@ bool UnpackController::FileList::Exists(const char* szFilename)
return false;
}
UnpackController::ParamList::~ParamList()
{
for (iterator it = begin(); it != end(); it++)
{
free(*it);
}
}
bool UnpackController::ParamList::Exists(const char* szParam)
{
for (iterator it = begin(); it != end(); it++)
{
char* szParam1 = *it;
if (!strcmp(szParam1, szParam))
{
return true;
}
}
return false;
}
void UnpackController::StartJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
@@ -100,6 +122,13 @@ void UnpackController::Run()
m_szPassword[0] = '\0';
m_szFinalDir[0] = '\0';
m_bFinalDirCreated = false;
m_bUnpackOK = true;
m_bUnpackStartError = false;
m_bUnpackSpaceError = false;
m_bUnpackDecryptError = false;
m_bUnpackPasswordError = false;
m_bAutoTerminated = false;
m_bPassListTried = false;
NZBParameter* pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
bool bUnpack = !(pParameter && !strcasecmp(pParameter->GetValue(), "no"));
@@ -134,32 +163,32 @@ void UnpackController::Run()
bool bHasFiles = m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles || m_bHasSplittedFiles;
if (bUnpack && bHasFiles)
if (m_pPostInfo->GetUnpackTried() && !m_pPostInfo->GetParRepaired() &&
(!Util::EmptyStr(m_szPassword) || Util::EmptyStr(g_pOptions->GetUnpackPassFile()) || m_pPostInfo->GetPassListTried()))
{
PrintMessage(Message::mkError, "%s failed: second unpack attempt skipped due to par-check not repaired anything", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus((NZBInfo::EUnpackStatus)m_pPostInfo->GetLastUnpackStatus());
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else if (bUnpack && bHasFiles)
{
m_bUnpackOK = true;
m_bUnpackStartError = false;
m_bUnpackSpaceError = false;
m_bUnpackPasswordError4 = false;
m_bUnpackPasswordError5 = false;
m_bAutoTerminated = false;
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
CreateUnpackDir();
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
{
ExecuteUnrar();
UnpackArchives(upUnrar, false);
}
if (m_bHasSevenZipFiles && m_bUnpackOK)
{
ExecuteSevenZip(false);
UnpackArchives(upSevenZip, false);
}
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
{
ExecuteSevenZip(true);
UnpackArchives(upSevenZip, true);
}
if (m_bHasSplittedFiles && m_bUnpackOK)
@@ -195,36 +224,126 @@ void UnpackController::Run()
m_pPostInfo->SetWorking(false);
}
void UnpackController::ExecuteUnrar()
void UnpackController::UnpackArchives(EUnpacker eUnpacker, bool bMultiVolumes)
{
if (!m_pPostInfo->GetUnpackTried() || m_pPostInfo->GetParRepaired())
{
ExecuteUnpack(eUnpacker, m_szPassword, bMultiVolumes);
if (!m_bUnpackOK && m_bHasParFiles && !m_bUnpackPasswordError &&
m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
// for rar4- or 7z-archives try par-check first, before trying password file
return;
}
}
else
{
m_bUnpackOK = false;
m_bUnpackDecryptError = m_pPostInfo->GetLastUnpackStatus() == (int)NZBInfo::usPassword;
}
if (!m_bUnpackOK && !m_bUnpackStartError && !m_bUnpackSpaceError &&
(m_bUnpackDecryptError || m_bUnpackPasswordError) &&
(!GetTerminated() || m_bAutoTerminated) &&
Util::EmptyStr(m_szPassword) && !Util::EmptyStr(g_pOptions->GetUnpackPassFile()))
{
FILE* infile = fopen(g_pOptions->GetUnpackPassFile(), FOPEN_RB);
if (!infile)
{
PrintMessage(Message::mkError, "Could not open file %s", g_pOptions->GetUnpackPassFile());
return;
}
char szPassword[512];
while (!m_bUnpackOK && !m_bUnpackStartError && !m_bUnpackSpaceError &&
(m_bUnpackDecryptError || m_bUnpackPasswordError) &&
fgets(szPassword, sizeof(szPassword) - 1, infile))
{
// trim trailing <CR> and <LF>
char* szEnd = szPassword + strlen(szPassword) - 1;
while (szEnd >= szPassword && (*szEnd == '\n' || *szEnd == '\r')) *szEnd-- = '\0';
if (!Util::EmptyStr(szPassword))
{
if (IsStopped() && m_bAutoTerminated)
{
ScriptController::Resume();
Thread::Resume();
}
m_bUnpackDecryptError = false;
m_bUnpackPasswordError = false;
m_bAutoTerminated = false;
PrintMessage(Message::mkInfo, "Trying password %s for %s", szPassword, m_szName);
ExecuteUnpack(eUnpacker, szPassword, bMultiVolumes);
}
}
fclose(infile);
m_bPassListTried = !IsStopped() || m_bAutoTerminated;
}
}
void UnpackController::ExecuteUnpack(EUnpacker eUnpacker, const char* szPassword, bool bMultiVolumes)
{
switch (eUnpacker)
{
case upUnrar:
ExecuteUnrar(szPassword);
break;
case upSevenZip:
ExecuteSevenZip(szPassword, bMultiVolumes);
break;
}
}
void UnpackController::ExecuteUnrar(const char* szPassword)
{
// Format:
// unrar x -y -p- -o+ *.rar ./_unpack
// 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)
ParamList params;
if (!PrepareCmdParams(g_pOptions->GetUnrarCmd(), &params, "unrar"))
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szPasswordParam[1024-1] = '\0';
szArgs[3] = szPasswordParam;
return;
}
szArgs[4] = "-o+";
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
if (!params.Exists("x") && !params.Exists("e"))
{
params.push_back(strdup("x"));
}
params.push_back(strdup("-y"));
if (!Util::EmptyStr(szPassword))
{
char szPasswordParam[1024];
snprintf(szPasswordParam, 1024, "-p%s", szPassword);
szPasswordParam[1024-1] = '\0';
params.push_back(strdup(szPasswordParam));
}
else
{
params.push_back(strdup("-p-"));
}
if (!params.Exists("-o+") && !params.Exists("-o-"))
{
params.push_back(strdup("-o+"));
}
params.push_back(strdup(m_bHasNonStdRarFiles ? "*.*" : "*.rar"));
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "%s%c", m_szUnpackDir, PATH_SEPARATOR);
szUnpackDirParam[1024-1] = '\0';
szArgs[6] = szUnpackDirParam;
params.push_back(strdup(szUnpackDirParam));
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
params.push_back(NULL);
SetArgs((const char**)&params.front(), false);
SetScript(params.at(0));
SetLogPrefix("Unrar");
ResetEnv();
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
@@ -237,7 +356,7 @@ void UnpackController::ExecuteUnrar()
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
m_bUnpackSpaceError = iExitCode == 5;
m_bUnpackPasswordError5 |= iExitCode == 11; // only for rar5-archives
m_bUnpackPasswordError |= iExitCode == 11; // only for rar5-archives
if (!m_bUnpackOK && iExitCode > 0)
{
@@ -245,37 +364,49 @@ void UnpackController::ExecuteUnrar()
}
}
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
void UnpackController::ExecuteSevenZip(const char* szPassword, 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)
ParamList params;
if (!PrepareCmdParams(g_pOptions->GetSevenZipCmd(), &params, "7-Zip"))
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
return;
}
if (!params.Exists("x") && !params.Exists("e"))
{
params.push_back(strdup("x"));
}
params.push_back(strdup("-y"));
if (!Util::EmptyStr(szPassword))
{
char szPasswordParam[1024];
snprintf(szPasswordParam, 1024, "-p%s", szPassword);
szPasswordParam[1024-1] = '\0';
szArgs[3] = szPasswordParam;
params.push_back(strdup(szPasswordParam));
}
else
{
params.push_back(strdup("-p-"));
}
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
szUnpackDirParam[1024-1] = '\0';
szArgs[4] = szUnpackDirParam;
params.push_back(strdup(szUnpackDirParam));
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
szArgs[6] = NULL;
SetArgs(szArgs, false);
params.push_back(strdup(bMultiVolumes ? "*.7z.001" : "*.7z"));
SetScript(g_pOptions->GetSevenZipCmd());
params.push_back(NULL);
SetArgs((const char**)&params.front(), false);
SetScript(params.at(0));
ResetEnv();
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
@@ -296,6 +427,32 @@ void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
}
}
bool UnpackController::PrepareCmdParams(const char* szCommand, ParamList* pParams, const char* szInfoName)
{
if (Util::FileExists(szCommand))
{
pParams->push_back(strdup(szCommand));
return true;
}
char** pCmdArgs = NULL;
if (!Util::SplitCommandLine(szCommand, &pCmdArgs))
{
PrintMessage(Message::mkError, "Could not start %s, failed to parse command line: %s", szInfoName, szCommand);
m_bUnpackOK = false;
m_bUnpackStartError = true;
return false;
}
for (char** szArgPtr = pCmdArgs; *szArgPtr; szArgPtr++)
{
pParams->push_back(*szArgPtr);
}
free(pCmdArgs);
return true;
}
void UnpackController::JoinSplittedFiles()
{
SetLogPrefix("Join");
@@ -405,7 +562,7 @@ bool UnpackController::JoinFile(const char* szFragBaseName)
FILE* pOutFile = fopen(szDestFilename, FOPEN_WBP);
if (!pOutFile)
{
error("Could not create file %s: %s", szDestFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
PrintMessage(Message::mkError, "Could not create file %s: %s", szDestFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return false;
}
if (g_pOptions->GetWriteBuffer() > 0)
@@ -457,7 +614,7 @@ bool UnpackController::JoinFile(const char* szFragBaseName)
}
else
{
error("Could not open file %s", szFragFilename);
PrintMessage(Message::mkError, "Could not open file %s", szFragFilename);
bOK = false;
break;
}
@@ -491,11 +648,13 @@ void UnpackController::Completed()
if (!m_bUnpackOK &&
(m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped ||
!m_pPostInfo->GetNZBInfo()->GetParFull()) &&
!m_bUnpackStartError && !m_bUnpackSpaceError &&
(!m_bUnpackPasswordError5 || m_bUnpackPasswordError4) &&
!m_bUnpackStartError && !m_bUnpackSpaceError && !m_bUnpackPasswordError &&
(!GetTerminated() || m_bAutoTerminated) && m_bHasParFiles)
{
RequestParCheck(true);
RequestParCheck(!Util::EmptyStr(m_szPassword) ||
Util::EmptyStr(g_pOptions->GetUnpackPassFile()) || m_bPassListTried ||
!(m_bUnpackDecryptError || m_bUnpackPasswordError) ||
m_pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped);
}
else
#endif
@@ -503,8 +662,7 @@ void UnpackController::Completed()
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(
m_bUnpackSpaceError ? NZBInfo::usSpace :
m_bUnpackPasswordError5 || (m_bUnpackPasswordError4 &&
m_pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess) ? NZBInfo::usPassword :
m_bUnpackPasswordError || m_bUnpackDecryptError ? NZBInfo::usPassword :
NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
@@ -514,10 +672,15 @@ void UnpackController::Completed()
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck(bool bForceRepair)
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", m_szInfoNameUp);
PrintMessage(Message::mkInfo, "%s requested %s", m_szInfoNameUp, bForceRepair ? "par-check with forced repair" : "par-check/repair");
m_pPostInfo->SetRequestParCheck(true);
m_pPostInfo->SetForceRepair(bForceRepair);
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetUnpackTried(true);
m_pPostInfo->SetPassListTried(m_bPassListTried);
m_pPostInfo->SetLastUnpackStatus((int)(m_bUnpackSpaceError ? NZBInfo::usSpace :
m_bUnpackPasswordError || m_bUnpackDecryptError ? NZBInfo::usPassword :
NZBInfo::usFailure));
}
#endif
@@ -541,7 +704,7 @@ void UnpackController::CreateUnpackDir()
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szUnpackDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
PrintMessage(Message::mkError, "Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
}
}
@@ -639,8 +802,7 @@ bool UnpackController::Cleanup()
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
@@ -653,10 +815,13 @@ bool UnpackController::Cleanup()
// silently overwrite existing files
remove(szDstFile);
if (!Util::MoveFile(szSrcFile, szDstFile))
bool bHiddenFile = filename[0] == '.';
if (!Util::MoveFile(szSrcFile, szDstFile) && !bHiddenFile)
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not move file %s to %s: %s", szSrcFile, szDstFile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
PrintMessage(Message::mkError, "Could not move file %s to %s: %s", szSrcFile, szDstFile,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
bOK = false;
}
@@ -787,6 +952,7 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
char szMsgText[1024];
strncpy(szMsgText, szText, 1024);
szMsgText[1024-1] = '\0';
int iLen = strlen(szText);
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
@@ -797,8 +963,7 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
szMsgText[1024-1] = '\0';
}
ScriptController::AddMessage(eKind, szMsgText);
m_pPostInfo->AppendMessage(eKind, szMsgText);
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szMsgText);
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: UNRAR ", 6) &&
strstr(szMsgText, " Copyright ") && strstr(szMsgText, " Alexander Roshal"))
@@ -824,16 +989,21 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
(!strncmp(szText, "Unrar: Checksum error in the encrypted file", 42) ||
!strncmp(szText, "Unrar: CRC failed in the encrypted file", 39)))
{
m_bUnpackPasswordError4 = true;
m_bUnpackDecryptError = true;
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: The specified password is incorrect.'", 43))
{
m_bUnpackPasswordError5 = true;
m_bUnpackPasswordError = true;
}
int iLen = strlen(szText);
if (m_eUnpacker == upUnrar && !IsStopped() && (m_bUnpackPasswordError4 || m_bUnpackPasswordError5 ||
if (m_eUnpacker == upSevenZip &&
(iLen > 18 && !strncmp(szText + iLen - 45, "Data Error in encrypted file. Wrong password?", 45)))
{
m_bUnpackDecryptError = true;
}
if (!IsStopped() && (m_bUnpackDecryptError || m_bUnpackPasswordError ||
strstr(szText, " : packed data CRC failed in volume") ||
strstr(szText, " : packed data checksum error in volume") ||
(iLen > 13 && !strncmp(szText + iLen - 13, " - CRC failed", 13)) ||
@@ -843,8 +1013,7 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
char szMsgText[1024];
snprintf(szMsgText, 1024, "Cancelling %s due to errors", m_szInfoName);
szMsgText[1024-1] = '\0';
ScriptController::AddMessage(Message::mkWarning, szMsgText);
m_pPostInfo->AppendMessage(Message::mkWarning, szMsgText);
m_pPostInfo->GetNZBInfo()->AddMessage(Message::mkWarning, szMsgText);
m_bAutoTerminated = true;
Stop();
}
@@ -904,7 +1073,7 @@ void MoveController::Run()
DownloadQueue::Unlock();
info("Moving completed files for %s", szNZBName);
PrintMessage(Message::mkInfo, "Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
@@ -912,7 +1081,7 @@ void MoveController::Run()
if (bOK)
{
info("%s successful", szInfoName);
PrintMessage(Message::mkInfo, "%s successful", szInfoName);
// save new dest dir
DownloadQueue::Lock();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
@@ -921,7 +1090,7 @@ void MoveController::Run()
}
else
{
error("%s failed", szInfoName);
PrintMessage(Message::mkError, "%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
@@ -934,7 +1103,7 @@ bool MoveController::MoveFiles()
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
PrintMessage(Message::mkError, "Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
@@ -942,8 +1111,7 @@ bool MoveController::MoveFiles()
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
@@ -952,10 +1120,18 @@ bool MoveController::MoveFiles()
char szDstFile[1024];
Util::MakeUniqueFilename(szDstFile, 1024, m_szDestDir, filename);
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
if (!Util::MoveFile(szSrcFile, szDstFile))
bool bHiddenFile = filename[0] == '.';
if (!bHiddenFile)
{
PrintMessage(Message::mkError, "Could not move file %s to %s: %s", szSrcFile, szDstFile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
}
if (!Util::MoveFile(szSrcFile, szDstFile) && !bHiddenFile)
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not move file %s to %s: %s", szSrcFile, szDstFile,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
bOK = false;
}
}
@@ -963,12 +1139,16 @@ bool MoveController::MoveFiles()
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir, szErrBuf, sizeof(szErrBuf)))
{
PrintMessage(Message::mkError, "Could not delete intermediate directory %s: %s", m_szInterDir, szErrBuf);
PrintMessage(Message::mkWarning, "Could not delete intermediate directory %s: %s", m_szInterDir, szErrBuf);
}
return bOK;
}
void MoveController::AddMessage(Message::EKind eKind, const char* szText)
{
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}
void CleanupController::StartJob(PostInfo* pPostInfo)
{
@@ -1012,7 +1192,7 @@ void CleanupController::Run()
DownloadQueue::Unlock();
info("Cleaning up %s", szNZBName);
PrintMessage(Message::mkInfo, "Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
@@ -1028,17 +1208,17 @@ void CleanupController::Run()
if (bOK && bDeleted)
{
info("%s successful", szInfoName);
PrintMessage(Message::mkInfo, "%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
info("Nothing to cleanup for %s", szNZBName);
PrintMessage(Message::mkInfo, "Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
error("%s failed", szInfoName);
PrintMessage(Message::mkError, "%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
@@ -1054,15 +1234,22 @@ bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
bool bIsDir = Util::DirectoryExists(szFullFilename);
if (strcmp(filename, ".") && strcmp(filename, "..") && bIsDir)
{
bOK &= Cleanup(szFullFilename, bDeleted);
}
// check file extension
bool bDeleteIt = Util::MatchFileExt(filename, g_pOptions->GetExtCleanupDisk(), ",;");
bool bDeleteIt = Util::MatchFileExt(filename, g_pOptions->GetExtCleanupDisk(), ",;") && !bIsDir;
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)
{
@@ -1077,3 +1264,8 @@ bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
return bOK;
}
void CleanupController::AddMessage(Message::EKind eKind, const char* szText)
{
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-2015 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,6 +27,7 @@
#define UNPACK_H
#include <deque>
#include <vector>
#include "Log.h"
#include "Thread.h"
@@ -50,6 +51,14 @@ private:
bool Exists(const char* szFilename);
};
typedef std::vector<char*> ParamListBase;
class ParamList : public ParamListBase
{
public:
~ParamList();
bool Exists(const char* szParam);
};
private:
PostInfo* m_pPostInfo;
char m_szName[1024];
@@ -71,19 +80,22 @@ private:
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bUnpackSpaceError;
bool m_bUnpackPasswordError4;
bool m_bUnpackPasswordError5;
bool m_bUnpackDecryptError;
bool m_bUnpackPasswordError;
bool m_bCleanedUpDisk;
bool m_bAutoTerminated;
EUnpacker m_eUnpacker;
bool m_bFinalDirCreated;
FileList m_JoinedFiles;
bool m_bPassListTried;
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 ExecuteUnpack(EUnpacker eUnpacker, const char* szPassword, bool bMultiVolumes);
void ExecuteUnrar(const char* szPassword);
void ExecuteSevenZip(const char* szPassword, bool bMultiVolumes);
void UnpackArchives(EUnpacker eUnpacker, bool bMultiVolumes);
void JoinSplittedFiles();
bool JoinFile(const char* szFragBaseName);
void Completed();
@@ -95,6 +107,7 @@ protected:
void RequestParCheck(bool bForceRepair);
#endif
bool FileHasRarSignature(const char* szFilename);
bool PrepareCmdParams(const char* szCommand, ParamList* pParams, const char* szInfoName);
public:
virtual void Run();
@@ -111,6 +124,9 @@ private:
bool MoveFiles();
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
@@ -125,6 +141,9 @@ private:
bool Cleanup(const char* szDestDir, bool *bDeleted);
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,9 @@
#ifdef WIN32
#include "win32.h"
#else
#include <pthread.h>
#include <unistd.h>
#endif
#include <stdlib.h>
@@ -136,7 +139,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
return false;
}
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 51);
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 53);
// save nzb-infos
SaveNZBQueue(pDownloadQueue, outfile);
@@ -179,7 +182,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue, Servers* pServe
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 51)
if (iFormatVersion < 3 || iFormatVersion > 53)
{
error("Could not load diskstate due to file version mismatch");
fclose(infile);
@@ -361,7 +364,7 @@ void DiskState::SaveNZBInfo(NZBInfo* pNZBInfo, FILE* outfile)
(int)pNZBInfo->GetMarkStatus(), (int)pNZBInfo->GetUrlStatus());
fprintf(outfile, "%i,%i,%i\n", (int)pNZBInfo->GetUnpackCleanedUpDisk(), (int)pNZBInfo->GetHealthPaused(),
(int)pNZBInfo->GetAddUrlPaused());
fprintf(outfile, "%i,%i\n", pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount());
fprintf(outfile, "%i,%i,%i\n", pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount(), pNZBInfo->GetMessageCount());
fprintf(outfile, "%i,%i\n", (int)pNZBInfo->GetMinTime(), (int)pNZBInfo->GetMaxTime());
fprintf(outfile, "%i,%i,%i\n", (int)pNZBInfo->GetParFull(),
pNZBInfo->GetPostInfo() ? (int)pNZBInfo->GetPostInfo()->GetForceParFull() : 0,
@@ -389,9 +392,6 @@ void DiskState::SaveNZBInfo(NZBInfo* pNZBInfo, FILE* outfile)
fprintf(outfile, "%lu,%lu,%i,%i,%i,%i,%i\n", High1, Low1, pNZBInfo->GetDownloadSec(), pNZBInfo->GetPostTotalSec(),
pNZBInfo->GetParSec(), pNZBInfo->GetRepairSec(), pNZBInfo->GetUnpackSec());
char DestDirSlash[1024];
snprintf(DestDirSlash, 1023, "%s%c", pNZBInfo->GetDestDir(), PATH_SEPARATOR);
fprintf(outfile, "%i\n", (int)pNZBInfo->GetCompletedFiles()->size());
for (CompletedFiles::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
{
@@ -416,15 +416,6 @@ void DiskState::SaveNZBInfo(NZBInfo* pNZBInfo, FILE* outfile)
SaveServerStats(pNZBInfo->GetServerStats(), outfile);
NZBInfo::Messages* pMessages = pNZBInfo->LockMessages();
fprintf(outfile, "%i\n", (int)pMessages->size());
for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++)
{
Message* pMessage = *it;
fprintf(outfile, "%i,%i,%s\n", pMessage->GetKind(), (int)pMessage->GetTime(), pMessage->GetText());
}
pNZBInfo->UnlockMessages();
// save file-infos
int iSize = 0;
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
@@ -638,10 +629,19 @@ bool DiskState::LoadNZBInfo(NZBInfo* pNZBInfo, Servers* pServers, FILE* infile,
if (iFormatVersion >= 28)
{
int iFileCount, iParkedFileCount;
if (fscanf(infile, "%i,%i\n", &iFileCount, &iParkedFileCount) != 2) goto error;
int iFileCount, iParkedFileCount, iMessageCount = 0;
if (iFormatVersion >= 52)
{
if (fscanf(infile, "%i,%i,%i\n", &iFileCount, &iParkedFileCount, &iMessageCount) != 3) goto error;
}
else
{
if (fscanf(infile, "%i,%i\n", &iFileCount, &iParkedFileCount) != 2) goto error;
}
pNZBInfo->SetFileCount(iFileCount);
pNZBInfo->SetParkedFileCount(iParkedFileCount);
pNZBInfo->SetMessageCount(iMessageCount);
}
else
{
@@ -852,23 +852,13 @@ bool DiskState::LoadNZBInfo(NZBInfo* pNZBInfo, Servers* pServers, FILE* infile,
pNZBInfo->GetCurrentServerStats()->ListOp(pNZBInfo->GetServerStats(), ServerStatList::soSet);
}
if (iFormatVersion >= 11)
if (iFormatVersion >= 11 && iFormatVersion < 52)
{
int iLogCount;
if (fscanf(infile, "%i\n", &iLogCount) != 1) goto error;
for (int i = 0; i < iLogCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
int iKind, iTime;
sscanf(buf, "%i,%i", &iKind, &iTime);
char* szText = strchr(buf + 2, ',');
if (szText)
{
szText++;
}
pNZBInfo->AppendMessage((Message::EKind)iKind, (time_t)iTime, szText);
}
}
@@ -1372,12 +1362,13 @@ void DiskState::DiscardFiles(NZBInfo* pNZBInfo)
DiscardFile(pFileInfo, true, true, true);
}
char szFilename[1024];
for (CompletedFiles::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
{
CompletedFile* pCompletedFile = *it;
if (pCompletedFile->GetStatus() != CompletedFile::cfSuccess && pCompletedFile->GetID() > 0)
{
char szFilename[1024];
snprintf(szFilename, 1024, "%s%i", g_pOptions->GetQueueDir(), pCompletedFile->GetID());
szFilename[1024-1] = '\0';
remove(szFilename);
@@ -1391,6 +1382,10 @@ void DiskState::DiscardFiles(NZBInfo* pNZBInfo)
remove(szFilename);
}
}
snprintf(szFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), pNZBInfo->GetID());
szFilename[1024-1] = '\0';
remove(szFilename);
}
bool DiskState::LoadPostQueue12(DownloadQueue* pDownloadQueue, NZBList* pNZBList, FILE* infile, int iFormatVersion)
@@ -1973,7 +1968,7 @@ bool DiskState::DownloadQueueExists()
return Util::FileExists(fileName);
}
bool DiskState::DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeletePartialState, bool bDeleteCompletedState)
void DiskState::DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeletePartialState, bool bDeleteCompletedState)
{
char fileName[1024];
@@ -2000,8 +1995,6 @@ bool DiskState::DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeleteP
fileName[1024-1] = '\0';
remove(fileName);
}
return true;
}
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
@@ -2884,3 +2877,123 @@ void DiskState::DeleteCacheFlag()
remove(szFlagFilename);
}
void DiskState::AppendNZBMessage(int iNZBID, Message::EKind eKind, const char* szText)
{
char szLogFilename[1024];
snprintf(szLogFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), iNZBID);
szLogFilename[1024-1] = '\0';
FILE* outfile = fopen(szLogFilename, FOPEN_ABP);
if (!outfile)
{
error("Error saving log: Could not create file %s", szLogFilename);
return;
}
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
char tmp2[1024];
strncpy(tmp2, szText, 1024);
tmp2[1024-1] = '\0';
// replace bad chars
for (char* p = tmp2; *p; p++)
{
char ch = *p;
if (ch == '\n' || ch == '\r' || ch == '\t')
{
*p = ' ';
}
}
time_t tm = time(NULL);
time_t rawtime = tm + g_pOptions->GetTimeCorrection();
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&rawtime, szTime, 50);
#else
ctime_r(&rawtime, szTime);
#endif
szTime[50-1] = '\0';
szTime[strlen(szTime) - 1] = '\0'; // trim LF
fprintf(outfile, "%s\t%u\t%s\t%s%s", szTime, (int)tm, szMessageType[eKind], tmp2, LINE_ENDING);
fclose(outfile);
}
void DiskState::LoadNZBMessages(int iNZBID, MessageList* pMessages)
{
// Important:
// - Other threads may be writing into the log-file at any time;
// - The log-file may also be deleted from another thread;
char szLogFilename[1024];
snprintf(szLogFilename, 1024, "%sn%i.log", g_pOptions->GetQueueDir(), iNZBID);
szLogFilename[1024-1] = '\0';
if (!Util::FileExists(szLogFilename))
{
return;
}
FILE* infile = fopen(szLogFilename, FOPEN_RB);
if (!infile)
{
error("Error reading log: could not open file %s", szLogFilename);
return;
}
int iID = 0;
char szLine[1024];
while (fgets(szLine, sizeof(szLine), infile))
{
Util::TrimRight(szLine);
// time (skip formatted time first)
char* p = strchr(szLine, '\t');
if (!p) goto exit;
int iTime = atoi(p + 1);
// kind
p = strchr(p + 1, '\t');
if (!p) goto exit;
char* szKind = p + 1;
Message::EKind eKind = Message::mkError;
if (!strncmp(szKind, "INFO", 4))
{
eKind = Message::mkInfo;
}
else if (!strncmp(szKind, "WARNING", 7))
{
eKind = Message::mkWarning;
}
else if (!strncmp(szKind, "ERROR", 5))
{
eKind = Message::mkError;
}
else if (!strncmp(szKind, "DETAIL", 6))
{
eKind = Message::mkDetail;
}
else if (!strncmp(szKind, "DEBUG", 5))
{
eKind = Message::mkDebug;
}
// text
p = strchr(p + 1, '\t');
if (!p) goto exit;
char* szText = p + 1;
Message* pMessage = new Message(++iID, eKind, (time_t)iTime, szText);
pMessages->push_back(pMessage);
}
exit:
fclose(infile);
return;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,6 +30,7 @@
#include "FeedInfo.h"
#include "NewsServer.h"
#include "StatMeter.h"
#include "Log.h"
class DiskState
{
@@ -83,7 +84,7 @@ public:
bool LoadFileState(FileInfo* pFileInfo, Servers* pServers, bool bCompleted);
bool LoadArticles(FileInfo* pFileInfo);
void DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeletePartialState, bool bDeleteCompletedState);
void DiscardFile(FileInfo* pFileInfo, bool bDeleteData, bool bDeletePartialState, bool bDeleteCompletedState);
void DiscardFiles(NZBInfo* pNZBInfo);
bool SaveFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
bool LoadFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
@@ -92,6 +93,8 @@ public:
void CleanupTempDir(DownloadQueue* pDownloadQueue);
void WriteCacheFlag();
void DeleteCacheFlag();
void AppendNZBMessage(int iNZBID, Message::EKind eKind, const char* szText);
void LoadNZBMessages(int iNZBID, MessageList* pMessages);
};
#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-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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 <stdarg.h>
#include <ctype.h>
#include <sys/stat.h>
#include <algorithm>
@@ -42,11 +43,13 @@
#include "nzbget.h"
#include "DownloadInfo.h"
#include "ArticleWriter.h"
#include "DiskState.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
extern ArticleCache* g_pArticleCache;
extern DiskState* g_pDiskState;
int FileInfo::m_iIDGen = 0;
int FileInfo::m_iIDMax = 0;
@@ -342,6 +345,8 @@ NZBInfo::NZBInfo() : m_FileList(true)
m_bReprocess = false;
m_tQueueScriptTime = 0;
m_bParFull = false;
m_iMessageCount = 0;
m_iCachedMessageCount = 0;
}
NZBInfo::~NZBInfo()
@@ -360,12 +365,6 @@ NZBInfo::~NZBInfo()
ClearCompletedFiles();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
m_FileList.Clear();
}
@@ -658,27 +657,81 @@ void NZBInfo::UpdateMinMaxTime()
}
}
NZBInfo::Messages* NZBInfo::LockMessages()
MessageList* NZBInfo::LockCachedMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void NZBInfo::UnlockMessages()
void NZBInfo::UnlockCachedMessages()
{
m_mutexLog.Unlock();
}
void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
void NZBInfo::AddMessage(Message::EKind eKind, const char * szText)
{
if (tTime == 0)
switch (eKind)
{
tTime = time(NULL);
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;
}
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode() && g_pOptions->GetNzbLog())
{
g_pDiskState->AppendNZBMessage(m_iID, eKind, szText);
m_iMessageCount++;
}
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
m_iCachedMessageCount = m_Messages.size();
m_mutexLog.Unlock();
}
void NZBInfo::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, szFormat);
vsnprintf(tmp2, 1024, szFormat, ap);
tmp2[1024-1] = '\0';
va_end(ap);
AddMessage(eKind, tmp2);
}
void NZBInfo::ClearMessages()
{
m_mutexLog.Lock();
m_Messages.Clear();
m_iCachedMessageCount = 0;
m_mutexLog.Unlock();
}
@@ -735,6 +788,7 @@ void NZBInfo::LeavePostProcess()
{
delete m_pPostInfo;
m_pPostInfo = NULL;
ClearMessages();
}
void NZBInfo::SetActiveDownloads(int iActiveDownloads)
@@ -759,14 +813,16 @@ void NZBInfo::SetActiveDownloads(int iActiveDownloads)
bool NZBInfo::IsDupeSuccess()
{
bool bFailure =
m_eDeleteStatus != NZBInfo::dsNone ||
m_eMarkStatus != NZBInfo::ksSuccess &&
m_eMarkStatus != NZBInfo::ksGood &&
(m_eDeleteStatus != NZBInfo::dsNone ||
m_eMarkStatus == NZBInfo::ksBad ||
m_eParStatus == NZBInfo::psFailure ||
m_eUnpackStatus == NZBInfo::usFailure ||
m_eUnpackStatus == NZBInfo::usPassword ||
(m_eParStatus == NZBInfo::psSkipped &&
m_eUnpackStatus == NZBInfo::usSkipped &&
CalcHealth() < CalcCriticalHealth(true));
CalcHealth() < CalcCriticalHealth(true)));
return !bFailure;
}
@@ -788,6 +844,10 @@ const char* NZBInfo::MakeTextStatus(bool bIgnoreScriptStatus)
{
szStatus = "SUCCESS/GOOD";
}
else if (m_eMarkStatus == NZBInfo::ksSuccess)
{
szStatus = "SUCCESS/MARK";
}
else if (m_eDeleteStatus == NZBInfo::dsHealth)
{
szStatus = "FAILURE/HEALTH";
@@ -1196,6 +1256,10 @@ PostInfo::PostInfo()
m_bRequestParCheck = false;
m_bForceParFull = false;
m_bForceRepair = false;
m_bParRepaired = false;
m_bUnpackTried = false;
m_bPassListTried = false;
m_eLastUnpackStatus = 0;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
@@ -1203,7 +1267,6 @@ PostInfo::PostInfo()
m_tStageTime = 0;
m_eStage = ptQueued;
m_pPostThread = NULL;
m_iIDMessageGen = 0;
}
PostInfo::~ PostInfo()
@@ -1212,11 +1275,6 @@ PostInfo::~ PostInfo()
free(m_szProgressLabel);
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
for (ParredFiles::iterator it = m_ParredFiles.begin(); it != m_ParredFiles.end(); it++)
{
free(*it);
@@ -1229,32 +1287,6 @@ void PostInfo::SetProgressLabel(const char* szProgressLabel)
m_szProgressLabel = strdup(szProgressLabel);
}
PostInfo::Messages* PostInfo::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void PostInfo::UnlockMessages()
{
m_mutexLog.Unlock();
}
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
{
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
m_mutexLog.Unlock();
}
DupInfo::DupInfo()
{

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -398,7 +398,8 @@ public:
{
ksNone,
ksBad,
ksGood
ksGood,
ksSuccess
};
enum EUrlStatus
@@ -418,8 +419,6 @@ public:
nkUrl
};
typedef std::deque<Message*> Messages;
static const int FORCE_PRIORITY = 900;
friend class DupInfo;
@@ -489,7 +488,7 @@ private:
ServerStatList m_ServerStats;
ServerStatList m_CurrentServerStats;
Mutex m_mutexLog;
Messages m_Messages;
MessageList m_Messages;
int m_iIDMessageGen;
PostInfo* m_pPostInfo;
long long m_lDownloadedSize;
@@ -502,10 +501,14 @@ private:
bool m_bReprocess;
time_t m_tQueueScriptTime;
bool m_bParFull;
int m_iMessageCount;
int m_iCachedMessageCount;
static int m_iIDGen;
static int m_iIDMax;
void ClearMessages();
public:
NZBInfo();
~NZBInfo();
@@ -666,9 +669,13 @@ public:
bool IsDupeSuccess();
const char* MakeTextStatus(bool bIgnoreScriptStatus);
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
void AddMessage(Message::EKind eKind, const char* szText);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
int GetMessageCount() { return m_iMessageCount; }
void SetMessageCount(int iMessageCount) { m_iMessageCount = iMessageCount; }
int GetCachedMessageCount() { return m_iCachedMessageCount; }
MessageList* LockCachedMessages();
void UnlockCachedMessages();
};
typedef std::deque<NZBInfo*> NZBQueueBase;
@@ -703,7 +710,6 @@ public:
ptFinished
};
typedef std::deque<Message*> Messages;
typedef std::vector<char*> ParredFiles;
private:
@@ -713,6 +719,10 @@ private:
bool m_bRequestParCheck;
bool m_bForceParFull;
bool m_bForceRepair;
bool m_bParRepaired;
bool m_bUnpackTried;
bool m_bPassListTried;
int m_eLastUnpackStatus;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
@@ -721,9 +731,6 @@ private:
time_t m_tStageTime;
Thread* m_pPostThread;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
ParredFiles m_ParredFiles;
public:
@@ -753,11 +760,16 @@ public:
void SetForceParFull(bool bForceParFull) { m_bForceParFull = bForceParFull; }
bool GetForceRepair() { return m_bForceRepair; }
void SetForceRepair(bool bForceRepair) { m_bForceRepair = bForceRepair; }
bool GetParRepaired() { return m_bParRepaired; }
void SetParRepaired(bool bParRepaired) { m_bParRepaired = bParRepaired; }
bool GetUnpackTried() { return m_bUnpackTried; }
void SetUnpackTried(bool bUnpackTried) { m_bUnpackTried = bUnpackTried; }
bool GetPassListTried() { return m_bPassListTried; }
void SetPassListTried(bool bPassListTried) { m_bPassListTried = bPassListTried; }
int GetLastUnpackStatus() { return m_eLastUnpackStatus; }
void SetLastUnpackStatus(int eUnpackStatus) { m_eLastUnpackStatus = eUnpackStatus; }
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
void AppendMessage(Message::EKind eKind, const char* szText);
Messages* LockMessages();
void UnlockMessages();
ParredFiles* GetParredFiles() { return &m_ParredFiles; }
};
@@ -897,6 +909,7 @@ public:
eaGroupSetDupeKey, // set duplicate key
eaGroupSetDupeScore, // set duplicate score
eaGroupSetDupeMode, // set duplicate mode
eaGroupSort, // sort groups
eaPostDelete, // cancel post-processing
eaHistoryDelete, // hide history-item
eaHistoryFinalDelete, // delete history-item
@@ -909,7 +922,10 @@ public:
eaHistorySetDupeMode, // set duplicate mode
eaHistorySetDupeBackup, // set duplicate backup flag
eaHistoryMarkBad, // mark history-item as bad (and download other duplicate)
eaHistoryMarkGood // mark history-item as good (and push it into dup-history)
eaHistoryMarkGood, // mark history-item as good (and push it into dup-history)
eaHistoryMarkSuccess, // mark history-item as success (and do nothing more)
eaHistorySetCategory, // set or change category for history-item
eaHistorySetName // set history-item name (rename)
};
enum EMatchMode

View File

@@ -236,7 +236,7 @@ void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pHistoryInfo->GetNZBInfo()->GetName());
info("Collection %s is a duplicate to %s", pNZBInfo->GetName(), pHistoryInfo->GetNZBInfo()->GetName());
return;
}
}
@@ -283,7 +283,7 @@ void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pQueuedNZBInfo->GetName());
info("Collection %s is a duplicate to %s", pNZBInfo->GetName(), pQueuedNZBInfo->GetName());
return;
}
@@ -334,8 +334,7 @@ void DupeCoordinator::ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
(pHistoryInfo->GetNZBInfo()->IsDupeSuccess() ||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood) &&
pHistoryInfo->GetNZBInfo()->IsDupeSuccess() &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
if (!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore)
@@ -412,20 +411,25 @@ void DupeCoordinator::ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
}
}
void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood)
void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, NZBInfo::EMarkStatus eMarkStatus)
{
char szNZBName[1024];
pHistoryInfo->GetName(szNZBName, 1024);
info("Marking %s as %s", szNZBName, (bGood ? "good" : "bad"));
const char* szMarkStatusName[] = { "NONE", "bad", "good", "success" };
info("Marking %s as %s", szNZBName, szMarkStatusName[eMarkStatus]);
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
{
pHistoryInfo->GetNZBInfo()->SetMarkStatus(bGood ? NZBInfo::ksGood : NZBInfo::ksBad);
pHistoryInfo->GetNZBInfo()->SetMarkStatus(eMarkStatus);
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup)
{
pHistoryInfo->GetDupInfo()->SetStatus(bGood ? DupInfo::dsGood : DupInfo::dsBad);
pHistoryInfo->GetDupInfo()->SetStatus(
eMarkStatus == NZBInfo::ksGood ? DupInfo::dsGood :
eMarkStatus == NZBInfo::ksSuccess ? DupInfo::dsSuccess :
DupInfo::dsBad);
}
else
{
@@ -442,13 +446,13 @@ void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pH
return;
}
if (bGood)
if (eMarkStatus == NZBInfo::ksGood)
{
// mark as good
// moving all duplicates from history to dup-history
HistoryCleanup(pDownloadQueue, pHistoryInfo);
}
else
else if (eMarkStatus == NZBInfo::ksBad)
{
// mark as bad
const char* szDupeKey = pHistoryInfo->GetKind() == HistoryInfo::hkNzb ? pHistoryInfo->GetNZBInfo()->GetDupeKey() :

View File

@@ -49,7 +49,7 @@ private:
public:
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood);
void HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, NZBInfo::EMarkStatus eMarkStatus);
EDupeStatus GetDupeStatus(DownloadQueue* pDownloadQueue, const char* szName, const char* szDupeKey);
};

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -228,7 +228,7 @@ void HistoryCoordinator::AddToHistory(DownloadQueue* pDownloadQueue, NZBInfo* pN
pNZBInfo->GetFileList()->Clear();
}
info("Collection %s added to history", pNZBInfo->GetName());
pNZBInfo->PrintMessage(Message::mkInfo, "Collection %s added to history", pNZBInfo->GetName());
}
void HistoryCoordinator::HistoryHide(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex)
@@ -250,6 +250,7 @@ void HistoryCoordinator::HistoryHide(DownloadQueue* pDownloadQueue, HistoryInfo*
pDupInfo->SetStatus(
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood ? DupInfo::dsGood :
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad ? DupInfo::dsBad :
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksSuccess ? DupInfo::dsSuccess :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe ? DupInfo::dsDupe :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsManual ? DupInfo::dsDeleted :
pHistoryInfo->GetNZBInfo()->IsDupeSuccess() ? DupInfo::dsSuccess :
@@ -277,6 +278,8 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
HistoryInfo* pHistoryInfo = *itHistory;
if (pHistoryInfo->GetID() == iID)
{
bOK = true;
switch (eAction)
{
case DownloadQueue::eaHistoryDelete:
@@ -294,7 +297,15 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
break;
case DownloadQueue::eaHistorySetParameter:
HistorySetParameter(pHistoryInfo, szText);
bOK = HistorySetParameter(pHistoryInfo, szText);
break;
case DownloadQueue::eaHistorySetCategory:
bOK = HistorySetCategory(pHistoryInfo, szText);
break;
case DownloadQueue::eaHistorySetName:
bOK = HistorySetName(pHistoryInfo, szText);
break;
case DownloadQueue::eaHistorySetDupeKey:
@@ -305,8 +316,15 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
break;
case DownloadQueue::eaHistoryMarkBad:
g_pDupeCoordinator->HistoryMark(pDownloadQueue, pHistoryInfo, NZBInfo::ksBad);
break;
case DownloadQueue::eaHistoryMarkGood:
g_pDupeCoordinator->HistoryMark(pDownloadQueue, pHistoryInfo, eAction == DownloadQueue::eaHistoryMarkGood);
g_pDupeCoordinator->HistoryMark(pDownloadQueue, pHistoryInfo, NZBInfo::ksGood);
break;
case DownloadQueue::eaHistoryMarkSuccess:
g_pDupeCoordinator->HistoryMark(pDownloadQueue, pHistoryInfo, NZBInfo::ksSuccess);
break;
default:
@@ -314,7 +332,6 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
break;
}
bOK = true;
break;
}
}
@@ -440,7 +457,7 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
pNZBInfo = pHistoryInfo->GetNZBInfo();
pHistoryInfo->DiscardNZBInfo();
pNZBInfo->SetUrlStatus(NZBInfo::lsNone);
pNZBInfo->SetDeleteStatus(NZBInfo::dsNone);
@@ -449,7 +466,7 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
pDownloadQueue->GetHistory()->erase(itHistory);
// the object "pHistoryInfo" is released few lines later, after the call to "NZBDownloaded"
info("%s returned from history back to download queue", szNiceName);
pNZBInfo->PrintMessage(Message::mkInfo, "%s returned from history back to download queue", szNiceName);
if (bReprocess)
{
@@ -464,12 +481,26 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
void HistoryCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory,
HistoryInfo* pHistoryInfo, bool bRestorePauseState)
{
if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
HistoryReturn(pDownloadQueue, itHistory, pHistoryInfo, false);
return;
}
if (pHistoryInfo->GetKind() != HistoryInfo::hkNzb)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
error("Could not return %s from history back to queue: history item has wrong type", szNiceName);
return;
}
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
bool bPaused = bRestorePauseState && pNZBInfo->GetDeletePaused();
if (!Util::FileExists(pNZBInfo->GetQueuedFilename()))
{
error("Could not return collection %s from history back to queue: could not find source nzb-file %s",
error("Could not return %s from history back to queue: could not find source nzb-file %s",
pNZBInfo->GetName(), pNZBInfo->GetQueuedFilename());
return;
}
@@ -477,12 +508,12 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, Histor
NZBFile* pNZBFile = NZBFile::Create(pNZBInfo->GetQueuedFilename(), "");
if (pNZBFile == NULL)
{
error("Could not return collection %s from history back to queue: could not parse nzb-file",
error("Could not return %s from history back to queue: could not parse nzb-file",
pNZBInfo->GetName());
return;
}
info("Returning collection %s from history back to queue", pNZBInfo->GetName());
info("Returning %s from history back to queue", pNZBInfo->GetName());
for (FileList::iterator it = pNZBFile->GetNZBInfo()->GetFileList()->begin(); it != pNZBFile->GetNZBInfo()->GetFileList()->end(); it++)
{
@@ -538,7 +569,7 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, Histor
g_pPrePostProcessor->NZBAdded(pDownloadQueue, pNZBInfo);
}
void HistoryCoordinator::HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText)
bool HistoryCoordinator::HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
@@ -547,7 +578,7 @@ void HistoryCoordinator::HistorySetParameter(HistoryInfo* pHistoryInfo, const ch
if (!(pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl))
{
error("Could not set post-process-parameter for %s: history item has wrong type", szNiceName);
return;
return false;
}
char* szStr = strdup(szText);
@@ -565,6 +596,49 @@ void HistoryCoordinator::HistorySetParameter(HistoryInfo* pHistoryInfo, const ch
}
free(szStr);
return true;
}
bool HistoryCoordinator::HistorySetCategory(HistoryInfo* pHistoryInfo, const char* szText)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Setting category '%s' for '%s'", szText, szNiceName);
if (!(pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl))
{
error("Could not set category for %s: history item has wrong type", szNiceName);
return false;
}
pHistoryInfo->GetNZBInfo()->SetCategory(szText);
return true;
}
bool HistoryCoordinator::HistorySetName(HistoryInfo* pHistoryInfo, const char* szText)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Setting name '%s' for '%s'", szText, szNiceName);
if (Util::EmptyStr(szText))
{
error("Could not rename %s. The new name cannot be empty", szNiceName);
return false;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
pHistoryInfo->GetNZBInfo()->SetName(szText);
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup)
{
pHistoryInfo->GetDupInfo()->SetName(szText);
}
return true;
}
void HistoryCoordinator::HistorySetDupeParam(HistoryInfo* pHistoryInfo, DownloadQueue::EEditAction eAction, const char* szText)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,10 @@ private:
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bFinal);
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bRestorePauseState);
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
bool HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
void HistorySetDupeParam(HistoryInfo* pHistoryInfo, DownloadQueue::EEditAction eAction, const char* szText);
bool HistorySetCategory(HistoryInfo* pHistoryInfo, const char* szText);
bool HistorySetName(HistoryInfo* pHistoryInfo, const char* szText);
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
void SaveQueue(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-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -526,21 +526,27 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
{
_bstr_t r(doc->GetparseError()->reason);
const char* szErrMsg = r;
error("Error parsing nzb-file: %s", szErrMsg);
error("Error parsing nzb-file %s: %s", Util::BaseFileName(szFileName), szErrMsg);
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->ProcessFiles();
}
else
if (!pFile->ParseNZB(doc))
{
delete pFile;
pFile = NULL;
return NULL;
}
if (pFile->GetNZBInfo()->GetFileList()->empty())
{
error("Error parsing nzb-file %s: file has no content", Util::BaseFileName(szFileName));
delete pFile;
return NULL;
}
pFile->ProcessFiles();
return pFile;
}
@@ -654,17 +660,22 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (ret == 0)
if (ret != 0)
{
pFile->ProcessFiles();
}
else
{
error("Failed to parse nzb-file");
error("Error parsing nzb-file %s", Util::BaseFileName(szFileName));
delete pFile;
pFile = NULL;
return NULL;
}
if (pFile->GetNZBInfo()->GetFileList()->empty())
{
error("Error parsing nzb-file %s: file has no content", Util::BaseFileName(szFileName));
delete pFile;
return NULL;
}
pFile->ProcessFiles();
return pFile;
}

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-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -398,7 +398,7 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, NZBInfo* pUrlInfo, b
if (eDeleteStatus == NZBInfo::dsNone)
{
info("Collection %s added to queue", pNZBInfo->GetName());
pNZBInfo->PrintMessage(Message::mkInfo, "Collection %s added to queue", pNZBInfo->GetName());
}
if (eDeleteStatus != NZBInfo::dsManual)
@@ -887,8 +887,10 @@ void QueueCoordinator::CheckHealth(DownloadQueue* pDownloadQueue, FileInfo* pFil
}
else if (g_pOptions->GetHealthCheck() == Options::hcDelete)
{
warn("Cancelling download and deleting %s due to health %.1f%% below critical %.1f%%", pFileInfo->GetNZBInfo()->GetName(),
pFileInfo->GetNZBInfo()->CalcHealth() / 10.0, pFileInfo->GetNZBInfo()->CalcCriticalHealth(true) / 10.0);
pFileInfo->GetNZBInfo()->PrintMessage(Message::mkWarning,
"Cancelling download and deleting %s due to health %.1f%% below critical %.1f%%",
pFileInfo->GetNZBInfo()->GetName(), pFileInfo->GetNZBInfo()->CalcHealth() / 10.0,
pFileInfo->GetNZBInfo()->CalcCriticalHealth(true) / 10.0);
pFileInfo->GetNZBInfo()->SetDeleteStatus(NZBInfo::dsHealth);
pDownloadQueue->EditEntry(pFileInfo->GetNZBInfo()->GetID(), DownloadQueue::eaGroupDelete, 0, NULL);
}
@@ -1141,6 +1143,7 @@ bool QueueCoordinator::MergeQueueEntries(DownloadQueue* pDownloadQueue, NZBInfo*
free(szQueuedFilename);
pDownloadQueue->GetQueue()->Remove(pSrcNZBInfo);
g_pDiskState->DiscardFiles(pSrcNZBInfo);
delete pSrcNZBInfo;
return true;
@@ -1258,6 +1261,7 @@ bool QueueCoordinator::SplitQueueEntries(DownloadQueue* pDownloadQueue, FileList
if (pSrcNZBInfo->GetFileList()->empty())
{
pDownloadQueue->GetQueue()->Remove(pSrcNZBInfo);
g_pDiskState->DiscardFiles(pSrcNZBInfo);
delete pSrcNZBInfo;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,6 +62,206 @@ extern Options* g_pOptions;
const int MAX_ID = 1000000000;
class GroupSorter
{
public:
enum ESortCriteria
{
scName,
scSize,
scRemainingSize,
scAge,
scCategory,
scPriority
};
enum ESortOrder
{
soAscending,
soDescending,
soAuto
};
private:
NZBList* m_pNZBList;
QueueEditor::ItemList* m_pSortItemList;
ESortCriteria m_eSortCriteria;
ESortOrder m_eSortOrder;
void AlignSelectedGroups();
public:
GroupSorter(NZBList* pNZBList, QueueEditor::ItemList* pSortItemList) :
m_pNZBList(pNZBList), m_pSortItemList(pSortItemList) {}
bool Execute(const char* szSort);
bool operator()(NZBInfo* pNZBInfo1, NZBInfo* pNZBInfo2) const;
};
bool GroupSorter::Execute(const char* szSort)
{
if (!strcasecmp(szSort, "name") || !strcasecmp(szSort, "name+") || !strcasecmp(szSort, "name-"))
{
m_eSortCriteria = scName;
}
else if (!strcasecmp(szSort, "size") || !strcasecmp(szSort, "size+") || !strcasecmp(szSort, "size-"))
{
m_eSortCriteria = scSize;
}
else if (!strcasecmp(szSort, "left") || !strcasecmp(szSort, "left+") || !strcasecmp(szSort, "left-"))
{
m_eSortCriteria = scRemainingSize;
}
else if (!strcasecmp(szSort, "age") || !strcasecmp(szSort, "age+") || !strcasecmp(szSort, "age-"))
{
m_eSortCriteria = scAge;
}
else if (!strcasecmp(szSort, "category") || !strcasecmp(szSort, "category+") || !strcasecmp(szSort, "category-"))
{
m_eSortCriteria = scCategory;
}
else if (!strcasecmp(szSort, "priority") || !strcasecmp(szSort, "priority+") || !strcasecmp(szSort, "priority-"))
{
m_eSortCriteria = scPriority;
}
else
{
error("Could not sort groups: incorrect sort order (%s)", szSort);
return false;
}
char lastCh = szSort[strlen(szSort) - 1];
if (lastCh == '+')
{
m_eSortOrder = soAscending;
}
else if (lastCh == '-')
{
m_eSortOrder = soDescending;
}
else
{
m_eSortOrder = soAuto;
}
AlignSelectedGroups();
NZBList tempList = *m_pNZBList;
ESortOrder eOrigSortOrder = m_eSortOrder;
if (m_eSortOrder == soAuto && m_eSortCriteria == scPriority)
{
m_eSortOrder = soDescending;
}
std::sort(m_pNZBList->begin(), m_pNZBList->end(), *this);
if (eOrigSortOrder == soAuto && tempList == *m_pNZBList)
{
m_eSortOrder = m_eSortOrder == soDescending ? soAscending : soDescending;
std::sort(m_pNZBList->begin(), m_pNZBList->end(), *this);
}
tempList.clear(); // prevent destroying of elements
return true;
}
bool GroupSorter::operator()(NZBInfo* pNZBInfo1, NZBInfo* pNZBInfo2) const
{
// if list of ID is empty - sort all items
bool bSortItem1 = m_pSortItemList->empty();
bool bSortItem2 = m_pSortItemList->empty();
for (QueueEditor::ItemList::iterator it = m_pSortItemList->begin(); it != m_pSortItemList->end(); it++)
{
QueueEditor::EditItem* pItem = *it;
bSortItem1 |= pItem->m_pNZBInfo == pNZBInfo1;
bSortItem2 |= pItem->m_pNZBInfo == pNZBInfo2;
}
if (!bSortItem1 || !bSortItem2)
{
return false;
}
bool ret = false;
if (m_eSortOrder == soDescending)
{
std::swap(pNZBInfo1, pNZBInfo2);
}
switch (m_eSortCriteria)
{
case scName:
ret = strcmp(pNZBInfo1->GetName(), pNZBInfo2->GetName()) < 0;
break;
case scSize:
ret = pNZBInfo1->GetSize() < pNZBInfo2->GetSize();
break;
case scRemainingSize:
ret = pNZBInfo1->GetRemainingSize() - pNZBInfo1->GetPausedSize() <
pNZBInfo2->GetRemainingSize() - pNZBInfo2->GetPausedSize();
break;
case scAge:
ret = pNZBInfo1->GetMinTime() > pNZBInfo2->GetMinTime();
break;
case scCategory:
ret = strcmp(pNZBInfo1->GetCategory(), pNZBInfo2->GetCategory()) < 0;
break;
case scPriority:
ret = pNZBInfo1->GetPriority() < pNZBInfo2->GetPriority();
break;
}
return ret;
}
void GroupSorter::AlignSelectedGroups()
{
NZBInfo* pLastNZBInfo = NULL;
unsigned int iLastNum = 0;
unsigned int iNum = 0;
while (iNum < m_pNZBList->size())
{
NZBInfo* pNZBInfo = m_pNZBList->at(iNum);
bool bSelected = false;
for (QueueEditor::ItemList::iterator it = m_pSortItemList->begin(); it != m_pSortItemList->end(); it++)
{
QueueEditor::EditItem* pItem = *it;
if (pItem->m_pNZBInfo == pNZBInfo)
{
bSelected = true;
break;
}
}
if (bSelected)
{
if (pLastNZBInfo && iNum - iLastNum > 1)
{
m_pNZBList->erase(m_pNZBList->begin() + iNum);
m_pNZBList->insert(m_pNZBList->begin() + iLastNum + 1, pNZBInfo);
iLastNum++;
}
else
{
iLastNum = iNum;
}
pLastNZBInfo = pNZBInfo;
}
iNum++;
}
}
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, NZBInfo* pNZBInfo, int iOffset)
{
m_pFileInfo = pFileInfo;
@@ -109,14 +309,9 @@ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
{
if (pFileInfo->GetNZBInfo()->GetDeleting())
{
detail("Deleting file %s from download queue", pFileInfo->GetFilename());
}
else
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
}
pFileInfo->GetNZBInfo()->PrintMessage(
pFileInfo->GetNZBInfo()->GetDeleting() ? Message::mkDetail : Message::mkInfo,
"Deleting file %s from download queue", pFileInfo->GetFilename());
g_pQueueCoordinator->DeleteQueueEntry(m_pDownloadQueue, pFileInfo);
}
@@ -205,7 +400,7 @@ bool QueueEditor::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameL
{
return g_pPrePostProcessor->EditList(pDownloadQueue, pIDList, eAction, iOffset, szText);
}
else if (DownloadQueue::eaHistoryDelete <= eAction && eAction <= DownloadQueue::eaHistoryMarkGood)
else if (DownloadQueue::eaHistoryDelete <= eAction && eAction <= DownloadQueue::eaHistorySetName)
{
return g_pHistoryCoordinator->EditList(pDownloadQueue, pIDList, eAction, iOffset, szText);
}
@@ -251,6 +446,9 @@ bool QueueEditor::InternEditList(ItemList* pItemList,
case DownloadQueue::eaGroupMerge:
return MergeGroups(pItemList);
case DownloadQueue::eaGroupSort:
return SortGroups(pItemList, szText);
case DownloadQueue::eaFileSplit:
return SplitGroup(pItemList, szText);
@@ -984,6 +1182,12 @@ bool QueueEditor::SplitGroup(ItemList* pItemList, const char* szName)
return bOK;
}
bool QueueEditor::SortGroups(ItemList* pItemList, const char* szSort)
{
GroupSorter sorter(m_pDownloadQueue->GetQueue(), pItemList);
return sorter.Execute(szSort);
}
void QueueEditor::ReorderFiles(ItemList* pItemList)
{
if (pItemList->size() == 0)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,7 +32,7 @@
class QueueEditor
{
private:
public:
class EditItem
{
public:
@@ -45,6 +45,7 @@ private:
typedef std::vector<EditItem*> ItemList;
private:
DownloadQueue* m_pDownloadQueue;
private:
@@ -60,6 +61,7 @@ private:
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
bool CanCleanupDisk(NZBInfo* pNZBInfo);
bool MergeGroups(ItemList* pItemList);
bool SortGroups(ItemList* pItemList, const char* szSort);
bool SplitGroup(ItemList* pItemList, const char* szName);
bool DeleteUrl(NZBInfo* pNZBInfo, DownloadQueue::EEditAction eAction);
void ReorderFiles(ItemList* pItemList);

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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,9 +47,11 @@
#include "Util.h"
#include "NZBFile.h"
#include "Scanner.h"
#include "DiskState.h"
extern Options* g_pOptions;
extern Scanner* g_pScanner;
extern DiskState* g_pDiskState;
UrlDownloader::UrlDownloader() : WebDownloader()
{
@@ -453,6 +455,7 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
if (bDeleteObj)
{
g_pDiskState->DiscardFiles(pNZBInfo);
delete pNZBInfo;
}
}
@@ -490,6 +493,7 @@ bool UrlCoordinator::DeleteQueueEntry(DownloadQueue* pDownloadQueue, NZBInfo* pN
}
else
{
g_pDiskState->DiscardFiles(pNZBInfo);
delete pNZBInfo;
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -61,7 +61,7 @@ extern void Reload();
const char* g_szMessageRequestNames[] =
{ "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug",
"Edit queue", "Log", "Quit", "Reload", "Version", "Post-queue", "Write log", "Scan",
"Pause/Unpause postprocessor", "History", "Download URL" };
"Pause/Unpause postprocessor", "History" };
const unsigned int g_iMessageRequestSizes[] =
{ 0,
@@ -78,10 +78,118 @@ const unsigned int g_iMessageRequestSizes[] =
sizeof(SNZBPostQueueRequest),
sizeof(SNZBWriteLogRequest),
sizeof(SNZBScanRequest),
sizeof(SNZBHistoryRequest),
sizeof(SNZBDownloadUrlRequest)
sizeof(SNZBHistoryRequest)
};
class BinCommand
{
protected:
Connection* m_pConnection;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};
class DownloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ListBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class LogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PauseUnpauseBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class EditQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class SetDownloadRateBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DumpDebugBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ShutdownBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ReloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class VersionBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PostQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class WriteLogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ScanBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class HistoryBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class UrlQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
//*****************************************************************
// BinProcessor
@@ -182,10 +290,6 @@ void BinRpcProcessor::Dispatch()
command = new HistoryBinCommand();
break;
case eRemoteRequestDownloadUrl:
command = new DownloadUrlBinCommand();
break;
default:
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
break;
@@ -330,30 +434,59 @@ void DownloadBinCommand::Execute()
}
int iBufLen = ntohl(DownloadRequest.m_iTrailingDataLength);
char* pRecvBuffer = (char*)malloc(iBufLen);
char* szNZBContent = (char*)malloc(iBufLen);
if (!m_pConnection->Recv(pRecvBuffer, iBufLen))
if (!m_pConnection->Recv(szNZBContent, iBufLen))
{
error("invalid request");
free(pRecvBuffer);
free(szNZBContent);
return;
}
int iPriority = ntohl(DownloadRequest.m_iPriority);
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
bool bAddTop = ntohl(DownloadRequest.m_bAddFirst);
int iDupeMode = ntohl(DownloadRequest.m_iDupeMode);
int iDupeScore = ntohl(DownloadRequest.m_iDupeScore);
bool bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory,
iPriority, NULL, 0, dmScore, NULL, bAddTop, bAddPaused, NULL, NULL, pRecvBuffer, iBufLen, NULL) != Scanner::asFailed;
bool bOK = false;
if (!strncasecmp(szNZBContent, "http://", 6) || !strncasecmp(szNZBContent, "https://", 7))
{
// add url
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->SetKind(NZBInfo::nkUrl);
pNZBInfo->SetURL(szNZBContent);
pNZBInfo->SetFilename(DownloadRequest.m_szNZBFilename);
pNZBInfo->SetCategory(DownloadRequest.m_szCategory);
pNZBInfo->SetPriority(iPriority);
pNZBInfo->SetAddUrlPaused(bAddPaused);
pNZBInfo->SetDupeKey(DownloadRequest.m_szDupeKey);
pNZBInfo->SetDupeScore(iDupeScore);
pNZBInfo->SetDupeMode((EDupeMode)iDupeMode);
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
pDownloadQueue->GetQueue()->Add(pNZBInfo, bAddTop);
pDownloadQueue->Save();
DownloadQueue::Unlock();
bOK = true;
}
else
{
bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szNZBFilename, DownloadRequest.m_szCategory, iPriority,
DownloadRequest.m_szDupeKey, iDupeScore, (EDupeMode)iDupeMode, NULL, bAddTop, bAddPaused,
NULL, NULL, szNZBContent, iBufLen, NULL) != Scanner::asFailed;
}
char tmp[1024];
snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s",
Util::BaseFileName(DownloadRequest.m_szFilename));
Util::BaseFileName(DownloadRequest.m_szNZBFilename));
tmp[1024-1] = '\0';
SendBoolResponse(bOK, tmp);
free(pRecvBuffer);
free(szNZBContent);
}
void ListBinCommand::Execute()
@@ -637,7 +770,7 @@ void LogBinCommand::Execute()
return;
}
Log::Messages* pMessages = g_pLog->LockMessages();
MessageList* pMessages = g_pLog->LockMessages();
int iNrEntries = ntohl(LogRequest.m_iLines);
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
@@ -972,6 +1105,8 @@ void HistoryBinCommand::Execute()
return;
}
bool bShowHidden = ntohl(HistoryRequest.m_bHidden);
SNZBHistoryResponse HistoryResponse;
memset(&HistoryResponse, 0, sizeof(HistoryResponse));
HistoryResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
@@ -985,16 +1120,27 @@ void HistoryBinCommand::Execute()
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
// calculate required buffer size for nzbs
int iNrEntries = pDownloadQueue->GetHistory()->size();
int iNrEntries = 0;
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden)
{
iNrEntries++;
}
}
bufsize += iNrEntries * sizeof(SNZBHistoryResponseEntry);
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
bufsize += strlen(szNicename) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden)
{
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
bufsize += strlen(szNicename) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
}
buf = (char*) malloc(bufsize);
@@ -1004,41 +1150,52 @@ void HistoryBinCommand::Execute()
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pHistoryInfo->GetID());
pListAnswer->m_iKind = htonl((int)pHistoryInfo->GetKind());
pListAnswer->m_tTime = htonl((int)pHistoryInfo->GetTime());
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
pListAnswer->m_iNicenameLen = htonl(strlen(szNicename) + 1);
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
unsigned long iSizeHi, iSizeLo;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus());
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
pListAnswer->m_iUrlStatus = htonl(pNZBInfo->GetUrlStatus());
}
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pHistoryInfo->GetID());
pListAnswer->m_iKind = htonl((int)pHistoryInfo->GetKind());
pListAnswer->m_tTime = htonl((int)pHistoryInfo->GetTime());
bufptr += sizeof(SNZBHistoryResponseEntry);
strcpy(bufptr, szNicename);
bufptr += ntohl(pListAnswer->m_iNicenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iNicenameLen = htonl(ntohl(pListAnswer->m_iNicenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
pListAnswer->m_iNicenameLen = htonl(strlen(szNicename) + 1);
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
unsigned long iSizeHi, iSizeLo;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus());
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup && bShowHidden)
{
DupInfo* pDupInfo = pHistoryInfo->GetDupInfo();
unsigned long iSizeHi, iSizeLo;
Util::SplitInt64(pDupInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
pListAnswer->m_iUrlStatus = htonl(pNZBInfo->GetUrlStatus());
}
bufptr += sizeof(SNZBHistoryResponseEntry);
strcpy(bufptr, szNicename);
bufptr += ntohl(pListAnswer->m_iNicenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iNicenameLen = htonl(ntohl(pListAnswer->m_iNicenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
}
@@ -1058,42 +1215,3 @@ void HistoryBinCommand::Execute()
free(buf);
}
void DownloadUrlBinCommand::Execute()
{
SNZBDownloadUrlRequest DownloadUrlRequest;
if (!ReceiveRequest(&DownloadUrlRequest, sizeof(DownloadUrlRequest)))
{
return;
}
URL url(DownloadUrlRequest.m_szURL);
if (!url.IsValid())
{
char tmp[1024];
snprintf(tmp, 1024, "Url %s is not valid", DownloadUrlRequest.m_szURL);
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
return;
}
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->SetKind(NZBInfo::nkUrl);
pNZBInfo->SetURL(DownloadUrlRequest.m_szURL);
pNZBInfo->SetFilename(DownloadUrlRequest.m_szNZBFilename);
pNZBInfo->SetCategory(DownloadUrlRequest.m_szCategory);
pNZBInfo->SetPriority(ntohl(DownloadUrlRequest.m_iPriority));
pNZBInfo->SetAddUrlPaused(ntohl(DownloadUrlRequest.m_bAddPaused));
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
pDownloadQueue->GetQueue()->Add(pNZBInfo, ntohl(DownloadUrlRequest.m_bAddFirst));
pDownloadQueue->Save();
DownloadQueue::Unlock();
info("Request: Queue url %s", DownloadUrlRequest.m_szURL);
char tmp[1024];
snprintf(tmp, 1024, "Url %s added to queue", DownloadUrlRequest.m_szURL);
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}

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-2015 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
@@ -44,116 +44,4 @@ public:
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
};
class BinCommand
{
protected:
Connection* m_pConnection;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};
class DownloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ListBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class LogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PauseUnpauseBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class EditQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class SetDownloadRateBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DumpDebugBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ShutdownBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ReloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class VersionBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PostQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class WriteLogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ScanBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class HistoryBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DownloadUrlBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class UrlQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
#endif

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-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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 = 0x6E7A6225; // = "nzb-XX" (protocol version)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6228; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -61,8 +61,7 @@ enum eRemoteRequest
eRemoteRequestPostQueue,
eRemoteRequestWriteLog,
eRemoteRequestScan,
eRemoteRequestHistory,
eRemoteRequestDownloadUrl
eRemoteRequestHistory
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
@@ -102,11 +101,14 @@ struct SNZBResponseBase
struct SNZBDownloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. For URLs can be empty, then the filename is read from URL download response
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
int32_t m_iDupeScore; // Duplicate score
int32_t m_iDupeMode; // Duplicate mode (EDupeMode)
char m_szDupeKey[NZBREQUESTFILENAMESIZE]; // Duplicate key
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
@@ -437,6 +439,7 @@ struct SNZBScanResponse
struct SNZBHistoryRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bHidden; // 0 - only return visible records, 1 - also return hidden records
};
// history response
@@ -453,12 +456,13 @@ struct SNZBHistoryResponse
struct SNZBHistoryResponseEntry
{
int32_t m_iID; // History-ID
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL, 3 - DUP (hidden record)
int32_t m_tTime; // When the item was added to history. time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iNicenameLen; // Length of Nicename-string (m_szNicename), following to this record
// for Collection items (m_iKind = 1)
// for Collection and Dup items (m_iKind = 1 or 2)
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
// for Collection items (m_iKind = 1)
int32_t m_iFileCount; // Initial number of files included in NZB-file
int32_t m_iParStatus; // See NZBInfo::EParStatus
int32_t m_iScriptStatus; // See NZBInfo::EScriptStatus
@@ -468,25 +472,4 @@ struct SNZBHistoryResponseEntry
//char m_szNicename[m_iNicenameLen]; // variable sized
};
// download url request
struct SNZBDownloadUrlRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szURL[NZBREQUESTFILENAMESIZE]; // url to nzb-file
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. Can be empty, then the filename is read from URL download response
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
int32_t m_bAddFirst; // 1 - add url to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
};
// download url response
struct SNZBDownloadUrlResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -159,15 +159,26 @@ bool RemoteClient::ReceiveBoolResponse()
/*
* Sends a message to the running nzbget process.
*/
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority)
bool RemoteClient::RequestServerDownload(const char* szNZBFilename, const char* szNZBContent,
const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority,
const char* szDupeKey, int iDupeMode, int iDupeScore)
{
// Read the file into the buffer
char* szBuffer = NULL;
int iLength = 0;
if (!Util::LoadFileIntoBuffer(szFilename, &szBuffer, &iLength))
char* szBuffer = NULL;
int iLength = 0;
bool bIsUrl = !strncasecmp(szNZBContent, "http://", 6) || !strncasecmp(szNZBContent, "https://", 7);
if (bIsUrl)
{
printf("Could not load file %s\n", szFilename);
return false;
iLength = strlen(szNZBContent) + 1;
}
else
{
if (!Util::LoadFileIntoBuffer(szNZBContent, &szBuffer, &iLength))
{
printf("Could not load file %s\n", szNZBContent);
return false;
}
iLength--;
}
bool OK = InitConnection();
@@ -178,10 +189,21 @@ 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 - 1);
DownloadRequest.m_iDupeMode = htonl(iDupeMode);
DownloadRequest.m_iDupeScore = htonl(iDupeScore);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
DownloadRequest.m_szNZBFilename[0] = '\0';
if (!Util::EmptyStr(szNZBFilename))
{
strncpy(DownloadRequest.m_szNZBFilename, szNZBFilename, NZBREQUESTFILENAMESIZE - 1);
}
else if (!bIsUrl)
{
strncpy(DownloadRequest.m_szNZBFilename, szNZBContent, NZBREQUESTFILENAMESIZE - 1);
}
DownloadRequest.m_szNZBFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadRequest.m_szCategory[0] = '\0';
if (szCategory)
{
@@ -189,6 +211,13 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
DownloadRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadRequest.m_szDupeKey[0] = '\0';
if (!Util::EmptyStr(szDupeKey))
{
strncpy(DownloadRequest.m_szDupeKey, szDupeKey, NZBREQUESTFILENAMESIZE - 1);
}
DownloadRequest.m_szDupeKey[NZBREQUESTFILENAMESIZE-1] = '\0';
if (!m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)))
{
perror("m_pConnection->Send");
@@ -196,7 +225,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
else
{
m_pConnection->Send(szBuffer, iLength);
m_pConnection->Send(bIsUrl ? szNZBContent : szBuffer, iLength);
OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
}
@@ -1087,12 +1116,13 @@ bool RemoteClient::RequestScan(bool bSyncMode)
return OK;
}
bool RemoteClient::RequestHistory()
bool RemoteClient::RequestHistory(bool bWithHidden)
{
if (!InitConnection()) return false;
SNZBHistoryRequest HistoryRequest;
InitMessageBase(&HistoryRequest.m_MessageBase, eRemoteRequestHistory, sizeof(HistoryRequest));
HistoryRequest.m_bHidden = htonl(bWithHidden);
if (!m_pConnection->Send((char*)(&HistoryRequest), sizeof(HistoryRequest)))
{
@@ -1143,8 +1173,12 @@ bool RemoteClient::RequestHistory()
HistoryInfo::EKind eKind = (HistoryInfo::EKind)ntohl(pListAnswer->m_iKind);
const char* szNicename = pBufPtr + sizeof(SNZBHistoryResponseEntry);
if (eKind == HistoryInfo::hkNzb)
if (eKind == HistoryInfo::hkNzb || eKind == HistoryInfo::hkDup)
{
char szFiles[20];
snprintf(szFiles, sizeof(szFiles), "%i files, ", ntohl(pListAnswer->m_iFileCount));
szFiles[20 - 1] = '\0';
long long lSize = Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo));
char szSize[20];
@@ -1152,17 +1186,20 @@ bool RemoteClient::RequestHistory()
const char* szParStatusText[] = { "", "", ", Par failed", ", Par successful", ", Repair possible", ", Repair needed" };
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
int iParStatus = ntohl(pListAnswer->m_iParStatus);
int iScriptStatus = ntohl(pListAnswer->m_iScriptStatus);
printf("[%i] %s (%i files, %s%s%s)\n", ntohl(pListAnswer->m_iID), szNicename,
ntohl(pListAnswer->m_iFileCount), szSize,
szParStatusText[ntohl(pListAnswer->m_iParStatus)],
szScriptStatusText[ntohl(pListAnswer->m_iScriptStatus)]);
printf("[%i] %s (%s%s%s%s%s)\n", ntohl(pListAnswer->m_iID), szNicename,
(eKind == HistoryInfo::hkDup ? "Hidden, " : ""),
(eKind == HistoryInfo::hkDup ? "" : szFiles), szSize,
(eKind == HistoryInfo::hkDup ? "" : szParStatusText[iParStatus]),
(eKind == HistoryInfo::hkDup ? "" : szScriptStatusText[iScriptStatus]));
}
else if (eKind == HistoryInfo::hkUrl)
{
const char* szUrlStatusText[] = { "", "", "Url download successful", "Url download failed", "" };
const char* szUrlStatusText[] = { "", "", "Url download successful", "Url download failed", "", "Nzb scan skipped", "Nzb scan failed" };
printf("[%i] %s (%s)\n", ntohl(pListAnswer->m_iID), szNicename,
printf("[%i] %s (URL, %s)\n", ntohl(pListAnswer->m_iID), szNicename,
szUrlStatusText[ntohl(pListAnswer->m_iUrlStatus)]);
}
@@ -1177,44 +1214,3 @@ bool RemoteClient::RequestHistory()
return true;
}
bool RemoteClient::RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority)
{
if (!InitConnection()) return false;
SNZBDownloadUrlRequest DownloadUrlRequest;
InitMessageBase(&DownloadUrlRequest.m_MessageBase, eRemoteRequestDownloadUrl, sizeof(DownloadUrlRequest));
DownloadUrlRequest.m_bAddFirst = htonl(bAddFirst);
DownloadUrlRequest.m_bAddPaused = htonl(bAddPaused);
DownloadUrlRequest.m_iPriority = htonl(iPriority);
strncpy(DownloadUrlRequest.m_szURL, szURL, NZBREQUESTFILENAMESIZE - 1);
DownloadUrlRequest.m_szURL[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadUrlRequest.m_szCategory[0] = '\0';
if (szCategory)
{
strncpy(DownloadUrlRequest.m_szCategory, szCategory, NZBREQUESTFILENAMESIZE - 1);
}
DownloadUrlRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadUrlRequest.m_szNZBFilename[0] = '\0';
if (szNZBFilename)
{
strncpy(DownloadUrlRequest.m_szNZBFilename, szNZBFilename, NZBREQUESTFILENAMESIZE - 1);
}
DownloadUrlRequest.m_szNZBFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
bool OK = m_pConnection->Send((char*)(&DownloadUrlRequest), sizeof(DownloadUrlRequest));
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}

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-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -60,7 +60,9 @@ public:
RemoteClient();
~RemoteClient();
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestServerDownload(const char* szNZBFilename, const char* szNZBContent, const char* szCategory,
bool bAddFirst, bool bAddPaused, int iPriority,
const char* szDupeKey, int iDupeMode, int iDupeScore);
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
bool RequestServerSetDownloadRate(int iRate);
@@ -74,8 +76,7 @@ public:
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
bool RequestScan(bool bSyncMode);
bool RequestHistory();
bool RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestHistory(bool bWithHidden);
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
};

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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
@@ -81,6 +81,7 @@ void WebProcessor::Execute()
m_bGZip =false;
char szAuthInfo[1024];
szAuthInfo[0] = '\0';
m_eUserAccess = uaControl;
// reading http header
char szBuffer[1024];
@@ -176,10 +177,10 @@ void WebProcessor::Execute()
debug("Final URL=%s", m_szUrl);
if (strlen(g_pOptions->GetControlPassword()) > 0 &&
!(strlen(g_pOptions->GetAuthorizedIP()) > 0 && IsAuthorizedIP(m_pConnection->GetRemoteAddr())))
if (!Util::EmptyStr(g_pOptions->GetControlPassword()) &&
!(!Util::EmptyStr(g_pOptions->GetAuthorizedIP()) && IsAuthorizedIP(m_pConnection->GetRemoteAddr())))
{
if (strlen(szAuthInfo) == 0)
if (Util::EmptyStr(szAuthInfo))
{
SendAuthResponse();
return;
@@ -188,8 +189,26 @@ void WebProcessor::Execute()
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(szAuthInfo, g_pOptions->GetControlUsername())) ||
(pw && strcmp(pw, g_pOptions->GetControlPassword())))
if ((Util::EmptyStr(g_pOptions->GetControlUsername()) ||
!strcmp(szAuthInfo, g_pOptions->GetControlUsername())) &&
pw && !strcmp(pw, g_pOptions->GetControlPassword()))
{
m_eUserAccess = uaControl;
}
else if (!Util::EmptyStr(g_pOptions->GetRestrictedUsername()) &&
!strcmp(szAuthInfo, g_pOptions->GetRestrictedUsername()) &&
pw && !strcmp(pw, g_pOptions->GetRestrictedPassword()))
{
m_eUserAccess = uaRestricted;
}
else if (!Util::EmptyStr(g_pOptions->GetAddUsername()) &&
!strcmp(szAuthInfo, g_pOptions->GetAddUsername()) &&
pw && !strcmp(pw, g_pOptions->GetAddPassword()))
{
m_eUserAccess = uaAdd;
}
else
{
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr(), szAuthInfo, pw);
@@ -249,13 +268,14 @@ void WebProcessor::Dispatch()
XmlRpcProcessor processor;
processor.SetRequest(m_szRequest);
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
processor.SetUserAccess((XmlRpcProcessor::EUserAccess)m_eUserAccess);
processor.SetUrl(m_szUrl);
processor.Execute();
SendBodyResponse(processor.GetResponse(), strlen(processor.GetResponse()), processor.GetContentType());
return;
}
if (!g_pOptions->GetWebDir() || strlen(g_pOptions->GetWebDir()) == 0)
if (Util::EmptyStr(g_pOptions->GetWebDir()))
{
SendErrorResponse(ERR_HTTP_SERVICE_UNAVAILABLE);
return;

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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,11 +38,19 @@ public:
hmOptions
};
enum EUserAccess
{
uaControl,
uaRestricted,
uaAdd
};
private:
Connection* m_pConnection;
char* m_szRequest;
char* m_szUrl;
EHttpMethod m_eHttpMethod;
EUserAccess m_eUserAccess;
bool m_bGZip;
char* m_szOrigin;

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -50,6 +50,7 @@
#include "Maintenance.h"
#include "StatMeter.h"
#include "ArticleWriter.h"
#include "DiskState.h"
extern Options* g_pOptions;
extern Scanner* g_pScanner;
@@ -58,6 +59,7 @@ extern ServerPool* g_pServerPool;
extern Maintenance* g_pMaintenance;
extern StatMeter* g_pStatMeter;
extern ArticleCache* g_pArticleCache;
extern DiskState* g_pDiskState;
extern void ExitProc();
extern void Reload();
@@ -136,10 +138,12 @@ public:
class LogXmlCommand: public XmlCommand
{
protected:
virtual Log::Messages* LockMessages();
int m_iIDFrom;
int m_iNrEntries;
virtual MessageList* LockMessages();
virtual void UnlockMessages();
public:
virtual void Execute();
virtual void Execute();
};
class NzbInfoXmlCommand: public XmlCommand
@@ -280,7 +284,7 @@ public:
class LogUpdateXmlCommand: public LogXmlCommand
{
protected:
virtual Log::Messages* LockMessages();
virtual MessageList* LockMessages();
virtual void UnlockMessages();
};
@@ -296,6 +300,38 @@ public:
virtual void Execute();
};
class LoadLogXmlCommand: public LogXmlCommand
{
private:
MessageList m_messages;
int m_iNZBID;
NZBInfo* m_pNZBInfo;
protected:
virtual void Execute();
virtual MessageList* LockMessages();
virtual void UnlockMessages();
};
class TestServerXmlCommand: public XmlCommand
{
private:
char* m_szErrText;
class TestConnection : public NNTPConnection
{
protected:
TestServerXmlCommand* m_pOwner;
virtual void PrintError(const char* szErrMsg) { m_pOwner->PrintError(szErrMsg); }
public:
TestConnection(NewsServer* pNewsServer, TestServerXmlCommand* pOwner):
NNTPConnection(pNewsServer), m_pOwner(pOwner) {}
};
void PrintError(const char* szErrMsg);
public:
virtual void Execute();
};
//*****************************************************************
// XmlRpcProcessor
@@ -317,6 +353,7 @@ XmlRpcProcessor::~XmlRpcProcessor()
void XmlRpcProcessor::SetUrl(const char* szUrl)
{
m_szUrl = strdup(szUrl);
WebUtil::URLDecode(m_szUrl);
}
@@ -354,9 +391,13 @@ void XmlRpcProcessor::Execute()
void XmlRpcProcessor::Dispatch()
{
char* szRequest = m_szRequest;
char szMethodName[100];
szMethodName[0] = '\0';
char szRequestId[100];
szRequestId[0] = '\0';
if (m_eHttpMethod == hmGet)
{
szRequest = m_szUrl + 1;
@@ -367,6 +408,7 @@ void XmlRpcProcessor::Dispatch()
if (pend)
{
int iLen = (int)(pend - pstart - 1 < (int)sizeof(szMethodName) - 1 ? pend - pstart - 1 : (int)sizeof(szMethodName) - 1);
iLen = iLen >= sizeof(szMethodName) ? sizeof(szMethodName) - 1 : iLen;
strncpy(szMethodName, pstart + 1, iLen);
szMethodName[iLen] = '\0';
szRequest = pend + 1;
@@ -388,9 +430,16 @@ void XmlRpcProcessor::Dispatch()
int iValueLen = 0;
if (const char* szMethodPtr = WebUtil::JsonFindField(m_szRequest, "method", &iValueLen))
{
iValueLen = iValueLen >= sizeof(szMethodName) ? sizeof(szMethodName) - 1 : iValueLen;
strncpy(szMethodName, szMethodPtr + 1, iValueLen - 2);
szMethodName[iValueLen - 2] = '\0';
}
if (const char* szRequestIdPtr = WebUtil::JsonFindField(m_szRequest, "id", &iValueLen))
{
iValueLen = iValueLen >= sizeof(szRequestId) ? sizeof(szRequestId) - 1 : iValueLen;
strncpy(szRequestId, szRequestIdPtr, iValueLen);
szRequestId[iValueLen] = '\0';
}
}
debug("MethodName=%s", szMethodName);
@@ -405,9 +454,10 @@ void XmlRpcProcessor::Dispatch()
command->SetRequest(szRequest);
command->SetProtocol(m_eProtocol);
command->SetHttpMethod(m_eHttpMethod);
command->SetUserAccess(m_eUserAccess);
command->PrepareParams();
command->Execute();
BuildResponse(command->GetResponse(), command->GetCallbackFunc(), command->GetFault());
BuildResponse(command->GetResponse(), command->GetCallbackFunc(), command->GetFault(), szRequestId);
delete command;
}
}
@@ -470,17 +520,18 @@ void XmlRpcProcessor::MutliCall()
command->SetProtocol(rpXmlRpc);
command->PrepareParams();
command->Execute();
BuildResponse(command->GetResponse(), "", command->GetFault());
BuildResponse(command->GetResponse(), "", command->GetFault(), NULL);
delete command;
}
else
{
cStringBuilder.Append("</data></array>");
BuildResponse(cStringBuilder.GetBuffer(), "", false);
BuildResponse(cStringBuilder.GetBuffer(), "", false, NULL);
}
}
void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault)
void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallbackFunc,
bool bFault, const char* szRequestId)
{
const char XML_HEADER[] = "<?xml version=\"1.0\"?>\n<methodResponse>\n";
const char XML_FOOTER[] = "</methodResponse>";
@@ -490,6 +541,8 @@ void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallba
const char XML_FAULT_CLOSE[] = "</value></fault>\n";
const char JSON_HEADER[] = "{\n\"version\" : \"1.1\",\n";
const char JSON_ID_OPEN[] = "\"id\" : ";
const char JSON_ID_CLOSE[] = ",\n";
const char JSON_FOOTER[] = "\n}";
const char JSON_OK_OPEN[] = "\"result\" : ";
const char JSON_OK_CLOSE[] = "";
@@ -516,6 +569,12 @@ void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallba
}
m_cResponse.Append(szCallbackHeader);
m_cResponse.Append(szHeader);
if (!bXmlRpc && szRequestId && *szRequestId)
{
m_cResponse.Append(JSON_ID_OPEN);
m_cResponse.Append(szRequestId);
m_cResponse.Append(JSON_ID_CLOSE);
}
m_cResponse.Append(szOpenTag);
m_cResponse.Append(szResponse);
m_cResponse.Append(szCloseTag);
@@ -529,7 +588,19 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
XmlCommand* command = NULL;
if (!strcasecmp(szMethodName, "pause") || !strcasecmp(szMethodName, "pausedownload") ||
if (m_eUserAccess == uaAdd &&
!(!strcasecmp(szMethodName, "append") || !strcasecmp(szMethodName, "appendurl") ||
!strcasecmp(szMethodName, "version")))
{
command = new ErrorXmlCommand(401, "Access denied");
warn("Received request \"%s\" from add-user, access denied", szMethodName);
}
else if (m_eUserAccess == uaRestricted && !strcasecmp(szMethodName, "saveconfig"))
{
command = new ErrorXmlCommand(401, "Access denied");
warn("Received request \"%s\" from restricted user, access denied", szMethodName);
}
else if (!strcasecmp(szMethodName, "pause") || !strcasecmp(szMethodName, "pausedownload") ||
!strcasecmp(szMethodName, "pausedownload2"))
{
command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paDownload);
@@ -595,6 +666,10 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
command = new ClearLogXmlCommand();
}
else if (!strcasecmp(szMethodName, "loadlog"))
{
command = new LoadLogXmlCommand();
}
else if (!strcasecmp(szMethodName, "scan"))
{
command = new ScanXmlCommand();
@@ -683,6 +758,10 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
command = new ResetServerVolumeXmlCommand();
}
else if (!strcasecmp(szMethodName, "testserver"))
{
command = new TestServerXmlCommand();
}
else
{
command = new ErrorXmlCommand(1, "Invalid procedure");
@@ -820,6 +899,10 @@ bool XmlCommand::NextParamAsInt(int* iValue)
}
*iValue = atoi(szParam + 1);
m_szRequestPtr = szParam + 1;
while (strchr("-+0123456789&", *m_szRequestPtr))
{
m_szRequestPtr++;
}
return true;
}
else if (IsJson())
@@ -866,12 +949,12 @@ bool XmlCommand::NextParamAsBool(bool* bValue)
if (IsJson())
{
if (!strcmp(szParam, "true"))
if (!strncmp(szParam, "true", 4))
{
*bValue = true;
return true;
}
else if (!strcmp(szParam, "false"))
else if (!strncmp(szParam, "false", 5))
{
*bValue = false;
return true;
@@ -934,7 +1017,7 @@ bool XmlCommand::NextParamAsStr(char** szValue)
}
szParam++; // skip '='
int iLen = 0;
char* szParamEnd = strchr(m_szRequestPtr, '&');
char* szParamEnd = strchr(szParam, '&');
if (szParamEnd)
{
iLen = (int)(szParamEnd - szParam);
@@ -1032,7 +1115,6 @@ ErrorXmlCommand::ErrorXmlCommand(int iErrCode, const char* szErrText)
void ErrorXmlCommand::Execute()
{
error("Received unsupported request: %s", m_szErrText);
BuildErrorResponse(m_iErrCode, m_szErrText);
}
@@ -1335,33 +1417,33 @@ void StatusXmlCommand::Execute()
// struct[] log(idfrom, entries)
void LogXmlCommand::Execute()
{
int iIDFrom = 0;
int iNrEntries = 0;
if (!NextParamAsInt(&iIDFrom) || !NextParamAsInt(&iNrEntries) || (iNrEntries > 0 && iIDFrom > 0))
m_iIDFrom = 0;
m_iNrEntries = 0;
if (!NextParamAsInt(&m_iIDFrom) || !NextParamAsInt(&m_iNrEntries) || (m_iNrEntries > 0 && m_iIDFrom > 0))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
debug("iIDFrom=%i", iIDFrom);
debug("iNrEntries=%i", iNrEntries);
debug("iIDFrom=%i", m_iIDFrom);
debug("iNrEntries=%i", m_iNrEntries);
AppendResponse(IsJson() ? "[\n" : "<array><data>\n");
Log::Messages* pMessages = LockMessages();
MessageList* pMessages = LockMessages();
int iStart = pMessages->size();
if (iNrEntries > 0)
if (m_iNrEntries > 0)
{
if (iNrEntries > (int)pMessages->size())
if (m_iNrEntries > (int)pMessages->size())
{
iNrEntries = pMessages->size();
m_iNrEntries = pMessages->size();
}
iStart = pMessages->size() - iNrEntries;
iStart = pMessages->size() - m_iNrEntries;
}
if (iIDFrom > 0 && !pMessages->empty())
if (m_iIDFrom > 0 && !pMessages->empty())
{
iNrEntries = pMessages->size();
iStart = iIDFrom - pMessages->front()->GetID();
m_iNrEntries = pMessages->size();
iStart = m_iIDFrom - pMessages->front()->GetID();
if (iStart < 0)
{
iStart = 0;
@@ -1412,7 +1494,7 @@ void LogXmlCommand::Execute()
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}
Log::Messages* LogXmlCommand::LockMessages()
MessageList* LogXmlCommand::LockMessages()
{
return g_pLog->LockMessages();
}
@@ -1470,6 +1552,7 @@ void ListFilesXmlCommand::Execute()
"<member><name>Category</name><value><string>%s</string></value></member>\n"
"<member><name>Priority</name><value><i4>%i</i4></value></member>\n" // deprecated, use "Priority" of group instead
"<member><name>ActiveDownloads</name><value><i4>%i</i4></value></member>\n"
"<member><name>Progress</name><value><i4>%u</i4></value></member>\n"
"</struct></value>\n";
const char* JSON_LIST_ITEM =
@@ -1491,7 +1574,8 @@ void ListFilesXmlCommand::Execute()
"\"DestDir\" : \"%s\",\n"
"\"Category\" : \"%s\",\n"
"\"Priority\" : %i,\n" // deprecated, use "Priority" of group instead
"\"ActiveDownloads\" : %i\n"
"\"ActiveDownloads\" : %i,\n"
"\"Progress\" : %i\n"
"}";
int iItemBufSize = 10240;
@@ -1519,12 +1603,15 @@ void ListFilesXmlCommand::Execute()
char* xmlCategory = EncodeStr(pFileInfo->GetNZBInfo()->GetCategory());
char* xmlNZBNicename = EncodeStr(pFileInfo->GetNZBInfo()->GetName());
int iProgress = pFileInfo->GetFailedSize() == 0 && pFileInfo->GetSuccessSize() == 0 ? 0 :
(int)(1000 - pFileInfo->GetRemainingSize() * 1000 / (pFileInfo->GetSize() - pFileInfo->GetMissedSize()));
snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_LIST_ITEM : XML_LIST_ITEM,
pFileInfo->GetID(), iFileSizeLo, iFileSizeHi, iRemainingSizeLo, iRemainingSizeHi,
pFileInfo->GetTime(), BoolToStr(pFileInfo->GetFilenameConfirmed()),
BoolToStr(pFileInfo->GetPaused()), pFileInfo->GetNZBInfo()->GetID(), xmlNZBNicename,
xmlNZBNicename, xmlNZBFilename, xmlSubject, xmlFilename, xmlDestDir, xmlCategory,
pFileInfo->GetNZBInfo()->GetPriority(), pFileInfo->GetActiveDownloads());
pFileInfo->GetNZBInfo()->GetPriority(), pFileInfo->GetActiveDownloads(), iProgress);
szItemBuf[iItemBufSize-1] = '\0';
free(xmlNZBFilename);
@@ -1551,148 +1638,150 @@ void ListFilesXmlCommand::Execute()
void NzbInfoXmlCommand::AppendNZBInfoFields(NZBInfo* pNZBInfo)
{
const char* XML_NZB_ITEM_START =
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n"
"<member><name>NZBName</name><value><string>%s</string></value></member>\n"
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use "NZBName" instead
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
"<member><name>URL</name><value><string>%s</string></value></member>\n"
"<member><name>NZBFilename</name><value><string>%s</string></value></member>\n"
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>FinalDir</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>DeleteStatus</name><value><string>%s</string></value></member>\n"
"<member><name>MarkStatus</name><value><string>%s</string></value></member>\n"
"<member><name>UrlStatus</name><value><string>%s</string></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>MinPostTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>MaxPostTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>TotalArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>SuccessArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>FailedArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>Health</name><value><i4>%i</i4></value></member>\n"
"<member><name>CriticalHealth</name><value><i4>%i</i4></value></member>\n"
"<member><name>DupeKey</name><value><string>%s</string></value></member>\n"
"<member><name>DupeScore</name><value><i4>%i</i4></value></member>\n"
"<member><name>DupeMode</name><value><string>%s</string></value></member>\n"
"<member><name>Deleted</name><value><boolean>%s</boolean></value></member>\n" // deprecated, use "DeleteStatus" instead
"<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>DownloadTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>PostTotalTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>ParTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>RepairTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>UnpackTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>Parameters</name><value><array><data>\n";
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n"
"<member><name>NZBName</name><value><string>%s</string></value></member>\n"
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use "NZBName" instead
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
"<member><name>URL</name><value><string>%s</string></value></member>\n"
"<member><name>NZBFilename</name><value><string>%s</string></value></member>\n"
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>FinalDir</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>DeleteStatus</name><value><string>%s</string></value></member>\n"
"<member><name>MarkStatus</name><value><string>%s</string></value></member>\n"
"<member><name>UrlStatus</name><value><string>%s</string></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>MinPostTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>MaxPostTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>TotalArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>SuccessArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>FailedArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>Health</name><value><i4>%i</i4></value></member>\n"
"<member><name>CriticalHealth</name><value><i4>%i</i4></value></member>\n"
"<member><name>DupeKey</name><value><string>%s</string></value></member>\n"
"<member><name>DupeScore</name><value><i4>%i</i4></value></member>\n"
"<member><name>DupeMode</name><value><string>%s</string></value></member>\n"
"<member><name>Deleted</name><value><boolean>%s</boolean></value></member>\n" // deprecated, use "DeleteStatus" instead
"<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>DownloadTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>PostTotalTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>ParTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>RepairTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>UnpackTimeSec</name><value><i4>%i</i4></value></member>\n"
"<member><name>MessageCount</name><value><i4>%i</i4></value></member>\n"
"<member><name>Parameters</name><value><array><data>\n";
const char* XML_NZB_ITEM_SCRIPT_START =
"</data></array></value></member>\n"
"<member><name>ScriptStatuses</name><value><array><data>\n";
"</data></array></value></member>\n"
"<member><name>ScriptStatuses</name><value><array><data>\n";
const char* XML_NZB_ITEM_STATS_START =
"</data></array></value></member>\n"
"<member><name>ServerStats</name><value><array><data>\n";
"</data></array></value></member>\n"
"<member><name>ServerStats</name><value><array><data>\n";
const char* XML_NZB_ITEM_END =
"</data></array></value></member>\n";
"</data></array></value></member>\n";
const char* JSON_NZB_ITEM_START =
"\"NZBID\" : %i,\n"
"\"NZBName\" : \"%s\",\n"
"\"NZBNicename\" : \"%s\",\n" // deprecated, use NZBName instead
"\"Kind\" : \"%s\",\n"
"\"URL\" : \"%s\",\n"
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
"\"FinalDir\" : \"%s\",\n"
"\"Category\" : \"%s\",\n"
"\"ParStatus\" : \"%s\",\n"
"\"UnpackStatus\" : \"%s\",\n"
"\"MoveStatus\" : \"%s\",\n"
"\"ScriptStatus\" : \"%s\",\n"
"\"DeleteStatus\" : \"%s\",\n"
"\"MarkStatus\" : \"%s\",\n"
"\"UrlStatus\" : \"%s\",\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
"\"FileSizeMB\" : %i,\n"
"\"FileCount\" : %i,\n"
"\"MinPostTime\" : %i,\n"
"\"MaxPostTime\" : %i,\n"
"\"TotalArticles\" : %i,\n"
"\"SuccessArticles\" : %i,\n"
"\"FailedArticles\" : %i,\n"
"\"Health\" : %i,\n"
"\"CriticalHealth\" : %i,\n"
"\"DupeKey\" : \"%s\",\n"
"\"DupeScore\" : %i,\n"
"\"DupeMode\" : \"%s\",\n"
"\"Deleted\" : %s,\n" // deprecated, use "DeleteStatus" instead
"\"DownloadedSizeLo\" : %u,\n"
"\"DownloadedSizeHi\" : %u,\n"
"\"DownloadedSizeMB\" : %i,\n"
"\"DownloadTimeSec\" : %i,\n"
"\"PostTotalTimeSec\" : %i,\n"
"\"ParTimeSec\" : %i,\n"
"\"RepairTimeSec\" : %i,\n"
"\"UnpackTimeSec\" : %i,\n"
"\"Parameters\" : [\n";
"\"NZBID\" : %i,\n"
"\"NZBName\" : \"%s\",\n"
"\"NZBNicename\" : \"%s\",\n" // deprecated, use NZBName instead
"\"Kind\" : \"%s\",\n"
"\"URL\" : \"%s\",\n"
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
"\"FinalDir\" : \"%s\",\n"
"\"Category\" : \"%s\",\n"
"\"ParStatus\" : \"%s\",\n"
"\"UnpackStatus\" : \"%s\",\n"
"\"MoveStatus\" : \"%s\",\n"
"\"ScriptStatus\" : \"%s\",\n"
"\"DeleteStatus\" : \"%s\",\n"
"\"MarkStatus\" : \"%s\",\n"
"\"UrlStatus\" : \"%s\",\n"
"\"FileSizeLo\" : %u,\n"
"\"FileSizeHi\" : %u,\n"
"\"FileSizeMB\" : %i,\n"
"\"FileCount\" : %i,\n"
"\"MinPostTime\" : %i,\n"
"\"MaxPostTime\" : %i,\n"
"\"TotalArticles\" : %i,\n"
"\"SuccessArticles\" : %i,\n"
"\"FailedArticles\" : %i,\n"
"\"Health\" : %i,\n"
"\"CriticalHealth\" : %i,\n"
"\"DupeKey\" : \"%s\",\n"
"\"DupeScore\" : %i,\n"
"\"DupeMode\" : \"%s\",\n"
"\"Deleted\" : %s,\n" // deprecated, use "DeleteStatus" instead
"\"DownloadedSizeLo\" : %u,\n"
"\"DownloadedSizeHi\" : %u,\n"
"\"DownloadedSizeMB\" : %i,\n"
"\"DownloadTimeSec\" : %i,\n"
"\"PostTotalTimeSec\" : %i,\n"
"\"ParTimeSec\" : %i,\n"
"\"RepairTimeSec\" : %i,\n"
"\"UnpackTimeSec\" : %i,\n"
"\"MessageCount\" : %i,\n"
"\"Parameters\" : [\n";
const char* JSON_NZB_ITEM_SCRIPT_START =
"],\n"
"\"ScriptStatuses\" : [\n";
"],\n"
"\"ScriptStatuses\" : [\n";
const char* JSON_NZB_ITEM_STATS_START =
"],\n"
"\"ServerStats\" : [\n";
"],\n"
"\"ServerStats\" : [\n";
const char* JSON_NZB_ITEM_END =
"],\n";
"]\n";
const char* XML_PARAMETER_ITEM =
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>Value</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>Value</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_PARAMETER_ITEM =
"{\n"
"\"Name\" : \"%s\",\n"
"\"Value\" : \"%s\"\n"
"}";
"{\n"
"\"Name\" : \"%s\",\n"
"\"Value\" : \"%s\"\n"
"}";
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";
"<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"
"}";
"{\n"
"\"Name\" : \"%s\",\n"
"\"Status\" : \"%s\"\n"
"}";
const char* XML_STAT_ITEM =
"<value><struct>\n"
"<member><name>ServerID</name><value><i4>%i</i4></value></member>\n"
"<member><name>SuccessArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>FailedArticles</name><value><i4>%i</i4></value></member>\n"
"</struct></value>\n";
"<value><struct>\n"
"<member><name>ServerID</name><value><i4>%i</i4></value></member>\n"
"<member><name>SuccessArticles</name><value><i4>%i</i4></value></member>\n"
"<member><name>FailedArticles</name><value><i4>%i</i4></value></member>\n"
"</struct></value>\n";
const char* JSON_STAT_ITEM =
"{\n"
"\"ServerID\" : %i,\n"
"\"SuccessArticles\" : %i,\n"
"\"FailedArticles\" : %i\n"
"}";
"{\n"
"\"ServerID\" : %i,\n"
"\"SuccessArticles\" : %i,\n"
"\"FailedArticles\" : %i\n"
"}";
const char* szKindName[] = { "NZB", "URL" };
const char* szParStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "REPAIR_POSSIBLE", "MANUAL" };
@@ -1700,7 +1789,7 @@ void NzbInfoXmlCommand::AppendNZBInfoFields(NZBInfo* pNZBInfo)
const char* szMoveStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szDeleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD" };
const char* szMarkStatusName[] = { "NONE", "BAD", "GOOD" };
const char* szMarkStatusName[] = { "NONE", "BAD", "GOOD", "SUCCESS" };
const char* szUrlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
@@ -1715,6 +1804,8 @@ void NzbInfoXmlCommand::AppendNZBInfoFields(NZBInfo* pNZBInfo)
Util::SplitInt64(pNZBInfo->GetDownloadedSize(), &iDownloadedSizeHi, &iDownloadedSizeLo);
iDownloadedSizeMB = (int)(pNZBInfo->GetDownloadedSize() / 1024 / 1024);
int iMessageCount = pNZBInfo->GetMessageCount() > 0 ? pNZBInfo->GetMessageCount() : pNZBInfo->GetCachedMessageCount();
char* xmlURL = EncodeStr(pNZBInfo->GetURL());
char* xmlNZBFilename = EncodeStr(pNZBInfo->GetFilename());
char* xmlNZBNicename = EncodeStr(pNZBInfo->GetName());
@@ -1739,7 +1830,7 @@ void NzbInfoXmlCommand::AppendNZBInfoFields(NZBInfo* pNZBInfo)
BoolToStr(pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone),
iDownloadedSizeLo, iDownloadedSizeHi, iDownloadedSizeMB, pNZBInfo->GetDownloadSec(),
pNZBInfo->GetPostInfo() && pNZBInfo->GetPostInfo()->GetStartTime() ? time(NULL) - pNZBInfo->GetPostInfo()->GetStartTime() : pNZBInfo->GetPostTotalSec(),
pNZBInfo->GetParSec(), pNZBInfo->GetRepairSec(), pNZBInfo->GetUnpackSec());
pNZBInfo->GetParSec(), pNZBInfo->GetRepairSec(), pNZBInfo->GetUnpackSec(), iMessageCount);
free(xmlURL);
free(xmlNZBNicename);
@@ -1913,7 +2004,7 @@ void NzbInfoXmlCommand::AppendPostInfoFields(PostInfo* pPostInfo, int iLogEntrie
if (iLogEntries > 0 && pPostInfo)
{
PostInfo::Messages* pMessages = pPostInfo->LockMessages();
MessageList* pMessages = pPostInfo->GetNZBInfo()->LockCachedMessages();
if (!pMessages->empty())
{
if (iLogEntries > (int)pMessages->size())
@@ -1939,7 +2030,7 @@ void NzbInfoXmlCommand::AppendPostInfoFields(PostInfo* pPostInfo, int iLogEntrie
AppendResponse(szItemBuf);
}
}
pPostInfo->UnlockMessages();
pPostInfo->GetNZBInfo()->UnlockCachedMessages();
}
AppendResponse(IsJson() ? JSON_POSTQUEUE_ITEM_END : XML_POSTQUEUE_ITEM_END);
@@ -2027,6 +2118,10 @@ void ListGroupsXmlCommand::Execute()
AppendResponse(szItemBuf);
AppendNZBInfoFields(pNZBInfo);
if (IsJson())
{
AppendResponse(",\n");
}
AppendPostInfoFields(pNZBInfo->GetPostInfo(), iNrEntries, false);
AppendResponse(IsJson() ? JSON_LIST_ITEM_END : XML_LIST_ITEM_END);
@@ -2101,6 +2196,7 @@ EditCommandEntry EditCommandNameMap[] = {
{ DownloadQueue::eaGroupSetDupeKey, "GroupSetDupeKey" },
{ DownloadQueue::eaGroupSetDupeScore, "GroupSetDupeScore" },
{ DownloadQueue::eaGroupSetDupeMode, "GroupSetDupeMode" },
{ DownloadQueue::eaGroupSort, "GroupSort" },
{ DownloadQueue::eaPostDelete, "PostDelete" },
{ DownloadQueue::eaHistoryDelete, "HistoryDelete" },
{ DownloadQueue::eaHistoryFinalDelete, "HistoryFinalDelete" },
@@ -2114,6 +2210,9 @@ EditCommandEntry EditCommandNameMap[] = {
{ DownloadQueue::eaHistorySetDupeBackup, "HistorySetDupeBackup" },
{ DownloadQueue::eaHistoryMarkBad, "HistoryMarkBad" },
{ DownloadQueue::eaHistoryMarkGood, "HistoryMarkGood" },
{ DownloadQueue::eaHistoryMarkSuccess, "HistoryMarkSuccess" },
{ DownloadQueue::eaHistorySetCategory, "HistorySetCategory" },
{ DownloadQueue::eaHistorySetName, "HistorySetName" },
{ 0, NULL }
};
@@ -2387,6 +2486,10 @@ void PostQueueXmlCommand::Execute()
AppendResponse(szItemBuf);
AppendNZBInfoFields(pPostInfo->GetNZBInfo());
if (IsJson())
{
AppendResponse(",\n");
}
AppendPostInfoFields(pPostInfo, iNrEntries, true);
AppendResponse(IsJson() ? JSON_POSTQUEUE_ITEM_END : XML_POSTQUEUE_ITEM_END);
@@ -2485,44 +2588,22 @@ void HistoryXmlCommand::Execute()
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>RemainingFileCount</name><value><i4>%i</i4></value></member>\n"
"<member><name>HistoryTime</name><value><i4>%i</i4></value></member>\n"
"<member><name>Status</name><value><string>%s</string></value></member>\n";
const char* XML_HISTORY_ITEM_LOG_START =
"<member><name>Log</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_END =
"</data></array></value></member>\n"
"</struct></value>\n";
"<member><name>Status</name><value><string>%s</string></value></member>\n"
"<member><name>Log</name><value><array><data></data></array></value></member>\n"; // Deprected, always empty
const char* JSON_HISTORY_ITEM_START =
"{\n"
"\"ID\" : %i,\n" // Deprecated, use "NZBID" instead
"\"ID\" : %i,\n" // Deprecated, use "NZBID" instead
"\"Name\" : \"%s\",\n"
"\"RemainingFileCount\" : %i,\n"
"\"HistoryTime\" : %i,\n"
"\"Status\" : \"%s\",\n";
"\"Status\" : \"%s\",\n"
"\"Log\" : [],\n"; // Deprected, always empty
const char* JSON_HISTORY_ITEM_LOG_START =
"\"Log\" : [\n";
const char* XML_HISTORY_ITEM_END =
"</struct></value>";
const char* JSON_HISTORY_ITEM_END =
"]\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"
"<member><name>Time</name><value><i4>%i</i4></value></member>\n"
"<member><name>Text</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_LOG_ITEM =
"{\n"
"\"ID\" : %i,\n"
"\"Kind\" : \"%s\",\n"
"\"Time\" : %i,\n"
"\"Text\" : \"%s\"\n"
"}";
const char* XML_HISTORY_DUP_ITEM =
@@ -2555,10 +2636,9 @@ void HistoryXmlCommand::Execute()
"\"DupeScore\" : %i,\n"
"\"DupeMode\" : \"%s\",\n"
"\"DupStatus\" : \"%s\",\n"
"\"Status\" : \"%s\",\n";
"\"Status\" : \"%s\"\n";
const char* szDupStatusName[] = { "UNKNOWN", "SUCCESS", "FAILURE", "DELETED", "DUPE", "BAD", "GOOD" };
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
bool bDup = false;
@@ -2629,34 +2709,6 @@ void HistoryXmlCommand::Execute()
AppendNZBInfoFields(pNZBInfo);
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_LOG_START : XML_HISTORY_ITEM_LOG_START);
if (pNZBInfo)
{
// Log-Messages
NZBInfo::Messages* pMessages = pNZBInfo->LockMessages();
if (!pMessages->empty())
{
int iLogIndex = 0;
for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++)
{
Message* pMessage = *it;
char* xmltext = EncodeStr(pMessage->GetText());
snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM,
pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext);
szItemBuf[iItemBufSize-1] = '\0';
free(xmltext);
if (IsJson() && iLogIndex++ > 0)
{
AppendResponse(",\n");
}
AppendResponse(szItemBuf);
}
}
pNZBInfo->UnlockMessages();
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_END : XML_HISTORY_ITEM_END);
}
free(szItemBuf);
@@ -2779,7 +2831,8 @@ void ConfigXmlCommand::Execute()
Options::OptEntry* pOptEntry = *it;
char* xmlName = EncodeStr(pOptEntry->GetName());
char* xmlValue = EncodeStr(pOptEntry->GetValue());
char* xmlValue = EncodeStr(m_eUserAccess == XmlRpcProcessor::uaRestricted &&
pOptEntry->Restricted() ? "***" : pOptEntry->GetValue());
// option values can sometimes have unlimited length
int iValLen = strlen(xmlValue);
@@ -2843,7 +2896,8 @@ void LoadConfigXmlCommand::Execute()
Options::OptEntry* pOptEntry = *it;
char* xmlName = EncodeStr(pOptEntry->GetName());
char* xmlValue = EncodeStr(pOptEntry->GetValue());
char* xmlValue = EncodeStr(m_eUserAccess == XmlRpcProcessor::uaRestricted &&
pOptEntry->Restricted() ? "***" : pOptEntry->GetValue());
// option values can sometimes have unlimited length
int iValLen = strlen(xmlValue);
@@ -3342,7 +3396,7 @@ void StartUpdateXmlCommand::Execute()
}
// struct[] logupdate(idfrom, entries)
Log::Messages* LogUpdateXmlCommand::LockMessages()
MessageList* LogUpdateXmlCommand::LockMessages()
{
return g_pMaintenance->LockMessages();
}
@@ -3538,3 +3592,101 @@ void ResetServerVolumeXmlCommand::Execute()
BuildBoolResponse(bOK);
}
// struct[] loadlog(nzbid, logidfrom, logentries)
void LoadLogXmlCommand::Execute()
{
m_pNZBInfo = NULL;
m_iNZBID = 0;
if (!NextParamAsInt(&m_iNZBID))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
LogXmlCommand::Execute();
}
MessageList* LoadLogXmlCommand::LockMessages()
{
// TODO: optimize for m_iIDFrom and m_iNrEntries
g_pDiskState->LoadNZBMessages(m_iNZBID, &m_messages);
if (m_messages.empty())
{
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
m_pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iNZBID);
if (m_pNZBInfo)
{
return m_pNZBInfo->LockCachedMessages();
}
else
{
DownloadQueue::Unlock();
}
}
return &m_messages;
}
void LoadLogXmlCommand::UnlockMessages()
{
if (m_pNZBInfo)
{
m_pNZBInfo->UnlockCachedMessages();
DownloadQueue::Unlock();
}
}
// string testserver(string host, int port, string username, string password, bool encryption, string cipher, int timeout);
void TestServerXmlCommand::Execute()
{
const char* XML_RESPONSE_STR_BODY = "<string>%s</string>";
const char* JSON_RESPONSE_STR_BODY = "\"%s\"";
if (!CheckSafeMethod())
{
return;
}
char* szHost;
int iPort;
char* szUsername;
char* szPassword;
bool bEncryption;
char* szCipher;
int iTimeout;
if (!NextParamAsStr(&szHost) || !NextParamAsInt(&iPort) || !NextParamAsStr(&szUsername) ||
!NextParamAsStr(&szPassword) || !NextParamAsBool(&bEncryption) ||
!NextParamAsStr(&szCipher) || !NextParamAsInt(&iTimeout))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
NewsServer server(0, true, "test server", szHost, iPort, szUsername, szPassword, false, bEncryption, szCipher, 1, 0, 0, 0);
TestConnection* pConnection = new TestConnection(&server, this);
pConnection->SetTimeout(iTimeout == 0 ? g_pOptions->GetArticleTimeout() : iTimeout);
pConnection->SetSuppressErrors(false);
m_szErrText = NULL;
bool bOK = pConnection->Connect();
char szContent[1024];
snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_STR_BODY : XML_RESPONSE_STR_BODY,
bOK ? "" : Util::EmptyStr(m_szErrText) ? "Unknown error" : m_szErrText);
szContent[1024-1] = '\0';
AppendResponse(szContent);
delete pConnection;
}
void TestServerXmlCommand::PrintError(const char* szErrMsg)
{
if (!m_szErrText)
{
m_szErrText = EncodeStr(szErrMsg);
}
}

View File

@@ -48,24 +48,33 @@ public:
hmGet
};
enum EUserAccess
{
uaControl,
uaRestricted,
uaAdd
};
private:
char* m_szRequest;
const char* m_szContentType;
ERpcProtocol m_eProtocol;
EHttpMethod m_eHttpMethod;
EUserAccess m_eUserAccess;
char* m_szUrl;
StringBuilder m_cResponse;
void Dispatch();
XmlCommand* CreateCommand(const char* szMethodName);
void MutliCall();
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault, const char* szRequestId);
public:
XmlRpcProcessor();
~XmlRpcProcessor();
void Execute();
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUserAccess(EUserAccess eUserAccess) { m_eUserAccess = eUserAccess; }
void SetUrl(const char* szUrl);
void SetRequest(char* szRequest) { m_szRequest = szRequest; }
const char* GetResponse() { return m_cResponse.GetBuffer(); }
@@ -83,6 +92,7 @@ protected:
bool m_bFault;
XmlRpcProcessor::ERpcProtocol m_eProtocol;
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
XmlRpcProcessor::EUserAccess m_eUserAccess;
void BuildErrorResponse(int iErrCode, const char* szErrText, ...);
void BuildBoolResponse(bool bOK);
@@ -106,6 +116,7 @@ public:
void SetRequest(char* szRequest) { m_szRequest = szRequest; m_szRequestPtr = m_szRequest; }
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUserAccess(XmlRpcProcessor::EUserAccess eUserAccess) { m_eUserAccess = eUserAccess; }
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
const char* GetCallbackFunc() { return m_szCallbackFunc; }
bool GetFault() { return m_bFault; }

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -179,7 +179,7 @@ void debug(const char* msg, ...)
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDebugTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkDebug, tmp2);
g_pLog->AddMessage(Message::mkDebug, tmp2);
}
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
@@ -205,7 +205,7 @@ void error(const char* msg, ...)
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetErrorTarget() : Options::mtBoth;
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkError, tmp2);
g_pLog->AddMessage(Message::mkError, tmp2);
}
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
@@ -230,7 +230,7 @@ void warn(const char* msg, ...)
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetWarningTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkWarning, tmp2);
g_pLog->AddMessage(Message::mkWarning, tmp2);
}
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
@@ -255,7 +255,7 @@ void info(const char* msg, ...)
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetInfoTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkInfo, tmp2);
g_pLog->AddMessage(Message::mkInfo, tmp2);
}
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
@@ -280,7 +280,7 @@ void detail(const char* msg, ...)
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDetailTarget() : Options::mtScreen;
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkDetail, tmp2);
g_pLog->AddMessage(Message::mkDetail, tmp2);
}
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
@@ -334,18 +334,28 @@ Message::~ Message()
free(m_szText);
}
void Log::Clear()
MessageList::~MessageList()
{
m_mutexLog.Lock();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
Clear();
}
void MessageList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
m_Messages.clear();
clear();
}
void Log::Clear()
{
m_mutexLog.Lock();
m_Messages.Clear();
m_mutexLog.Unlock();
}
void Log::AppendMessage(Message::EKind eKind, const char * szText)
void Log::AddMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
@@ -361,7 +371,7 @@ void Log::AppendMessage(Message::EKind eKind, const char * szText)
}
}
Log::Messages* Log::LockMessages()
MessageList* Log::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
@@ -433,7 +443,7 @@ void Log::RotateLog()
char szMessage[1024];
snprintf(szMessage, 1024, "Deleting old log-file %s\n", filename);
szMessage[1024-1] = '\0';
g_pLog->AppendMessage(Message::mkInfo, szMessage);
g_pLog->AddMessage(Message::mkInfo, szMessage);
remove(szFullFilename);
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -76,6 +76,15 @@ public:
const char* GetText() { return m_szText; }
};
typedef std::deque<Message*> MessageListBase;
class MessageList: public MessageListBase
{
public:
~MessageList();
void Clear();
};
class Debuggable
{
protected:
@@ -86,12 +95,11 @@ protected:
class Log
{
public:
typedef std::deque<Message*> Messages;
typedef std::list<Debuggable*> Debuggables;
private:
Mutex m_mutexLog;
Messages m_Messages;
MessageList m_Messages;
Debuggables m_Debuggables;
Mutex m_mutexDebug;
char* m_szLogFilename;
@@ -102,7 +110,7 @@ private:
#endif
void Filelog(const char* msg, ...);
void AppendMessage(Message::EKind eKind, const char* szText);
void AddMessage(Message::EKind eKind, const char* szText);
void RotateLog();
friend void error(const char* msg, ...);
@@ -121,7 +129,7 @@ private:
public:
Log();
~Log();
Messages* LockMessages();
MessageList* LockMessages();
void UnlockMessages();
void Clear();
void ResetLog();

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -132,7 +132,14 @@ void EnvironmentStrings::InitFromCurrentProcess()
for (int i = 0; (*g_szEnvironmentVariables)[i]; i++)
{
char* szVar = (*g_szEnvironmentVariables)[i];
Append(strdup(szVar));
// Ignore all env vars set by NZBGet.
// This is to avoid the passing of env vars after program update (when NZBGet is
// started from a script which was started by a previous instance of NZBGet).
// Format: NZBXX_YYYY (XX are any two characters, YYYY are any number of any characters).
if (!(!strncmp(szVar, "NZB", 3) && strlen(szVar) > 5 && szVar[5] == '_'))
{
Append(strdup(szVar));
}
}
}
@@ -202,7 +209,7 @@ ScriptController::ScriptController()
m_bTerminated = false;
m_bDetached = false;
m_hProcess = 0;
m_environmentStrings.InitFromCurrentProcess();
ResetEnv();
m_mutexRunning.Lock();
m_RunningScripts.push_back(this);
@@ -225,9 +232,13 @@ ScriptController::~ScriptController()
void ScriptController::UnregisterRunningScript()
{
m_mutexRunning.Lock();
m_RunningScripts.erase(std::find(m_RunningScripts.begin(), m_RunningScripts.end(), this));
m_mutexRunning.Unlock();
m_mutexRunning.Lock();
RunningScripts::iterator it = std::find(m_RunningScripts.begin(), m_RunningScripts.end(), this);
if (it != m_RunningScripts.end())
{
m_RunningScripts.erase(it);
}
m_mutexRunning.Unlock();
}
void ScriptController::ResetEnv()
@@ -401,6 +412,8 @@ int ScriptController::Execute()
CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0);
SetHandleInformation(hReadPipe, HANDLE_FLAG_INHERIT, 0);
STARTUPINFO StartupInfo;
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
@@ -422,15 +435,15 @@ int ScriptController::Execute()
szErrMsg[255-1] = '\0';
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErrCode, 0, szErrMsg, 255, NULL))
{
error("Could not start %s: %s", m_szInfoName, szErrMsg);
PrintMessage(Message::mkError, "Could not start %s: %s", m_szInfoName, szErrMsg);
}
else
{
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
PrintMessage(Message::mkError, "Could not start %s: error %i", m_szInfoName, dwErrCode);
}
if (!Util::FileExists(m_szScript))
{
error("Could not find file %s", m_szScript);
PrintMessage(Message::mkError, "Could not find file %s", m_szScript);
}
free(szEnvironmentStrings);
return -1;
@@ -455,7 +468,7 @@ int ScriptController::Execute()
// create the pipe
if (pipe(p))
{
error("Could not open pipe: errno %i", errno);
PrintMessage(Message::mkError, "Could not open pipe: errno %i", errno);
return -1;
}
@@ -469,7 +482,7 @@ int ScriptController::Execute()
if (pid == -1)
{
error("Could not start %s: errno %i", m_szInfoName, errno);
PrintMessage(Message::mkError, "Could not start %s: errno %i", m_szInfoName, errno);
free(pEnvironmentStrings);
return -1;
}
@@ -529,7 +542,7 @@ int ScriptController::Execute()
m_pReadpipe = fdopen(pipein, "r");
if (!m_pReadpipe)
{
error("Could not open pipe to %s", m_szInfoName);
PrintMessage(Message::mkError, "Could not open pipe to %s", m_szInfoName);
return -1;
}
@@ -637,6 +650,12 @@ void ScriptController::Terminate()
// wait 60 seconds for process to terminate
WaitForSingleObject(m_hProcess, 60 * 1000);
}
else
{
DWORD dExitCode = 0;
GetExitCodeProcess(m_hProcess, &dExitCode);
bOK = dExitCode != STILL_ACTIVE;
}
#else
pid_t hKillProcess = m_hProcess;
if (getpgid(hKillProcess) == hKillProcess)
@@ -682,6 +701,12 @@ void ScriptController::Detach()
fclose(pReadpipe);
}
void ScriptController::Resume()
{
m_bTerminated = false;
m_bDetached = false;
m_hProcess = 0;
}
bool ScriptController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -92,6 +92,7 @@ public:
virtual ~ScriptController();
int Execute();
void Terminate();
void Resume();
void Detach();
static void TerminateAll();

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-2015 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
@@ -215,6 +215,13 @@ void Thread::Stop()
m_bStopped = true;
}
void Thread::Resume()
{
debug("Resuming Thread");
m_bStopped = false;
}
bool Thread::Kill()
{
debug("Killing Thread");

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-2015 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
@@ -81,6 +81,7 @@ public:
virtual void Start();
virtual void Stop();
virtual void Resume();
bool Kill();
bool IsStopped() { return m_bStopped; };

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -260,6 +260,14 @@ StringBuilder::~StringBuilder()
free(m_szBuffer);
}
void StringBuilder::Clear()
{
free(m_szBuffer);
m_szBuffer = NULL;
m_iBufferSize = 0;
m_iUsedSize = 0;
}
void StringBuilder::Append(const char* szStr)
{
int iPartLen = strlen(szStr);
@@ -972,6 +980,30 @@ void Util::ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize)
#endif
}
void Util::GetExeFileName(const char* argv0, char* szBuffer, int iBufSize)
{
#ifdef WIN32
GetModuleFileName(NULL, szBuffer, iBufSize);
#else
// Linux
int r = readlink("/proc/self/exe", szBuffer, iBufSize-1);
if (r > 0)
{
szBuffer[r] = '\0';
return;
}
// FreeBSD
r = readlink("/proc/curproc/file", szBuffer, iBufSize-1);
if (r > 0)
{
szBuffer[r] = '\0';
return;
}
ExpandFileName(argv0, szBuffer, iBufSize);
#endif
}
void Util::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
{
if (lFileSize > 1024 * 1024 * 1000)
@@ -1014,6 +1046,14 @@ bool Util::MatchFileExt(const char* szFilename, const char* szExtensionList, con
{
return true;
}
if (strchr(szExt, '*') || strchr(szExt, '?'))
{
WildMask mask(szExt);
if (mask.Match(szFilename))
{
return true;
}
}
}
return false;
@@ -1040,6 +1080,28 @@ char* Util::GetLastErrorMessage(char* szBuffer, int iBufLen)
return szBuffer;
}
void Util::FormatSpeed(int iBytesPerSecond, char* szBuffer, int iBufSize)
{
if (iBytesPerSecond >= 100 * 1024 * 1024)
{
snprintf(szBuffer, iBufSize, "%i MB/s", iBytesPerSecond / 1024 / 1024);
}
else if (iBytesPerSecond >= 10 * 1024 * 1024)
{
snprintf(szBuffer, iBufSize, "%0.1f MB/s", (float)iBytesPerSecond / 1024.0 / 1024.0);
}
else if (iBytesPerSecond >= 1024 * 1000)
{
snprintf(szBuffer, iBufSize, "%0.2f MB/s", (float)iBytesPerSecond / 1024.0 / 1024.0);
}
else
{
snprintf(szBuffer, iBufSize, "%i KB/s", iBytesPerSecond / 1024);
}
szBuffer[iBufSize - 1] = '\0';
}
void Util::InitVersionRevision()
{
#ifndef WIN32
@@ -1279,7 +1341,7 @@ inline int days_from_0(int year)
}
inline int days_from_1970(int year)
{
static const int days_from_0_to_1970 = days_from_0(1970);
static const int days_from_0_to_1970 = 719162; // days_from_0(1970);
return days_from_0(year) - days_from_0_to_1970;
}
inline int days_from_1jan(int year,int month,int day)
@@ -1318,6 +1380,11 @@ inline time_t internal_timegm(tm const *t)
return result;
}
time_t Util::Timegm(tm const *t)
{
return internal_timegm(t);
}
// prevent PC from going to sleep
void Util::SetStandByMode(bool bStandBy)
{
@@ -1326,11 +1393,6 @@ void Util::SetStandByMode(bool bStandBy)
#endif
}
time_t Util::Timegm(tm const *t)
{
return internal_timegm(t);
}
static unsigned long crc32_tab[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@@ -2030,6 +2092,38 @@ BreakLoop:
*output = '\0';
}
void WebUtil::URLDecode(char* raw)
{
char* output = raw;
for (char* p = raw;;)
{
switch (*p)
{
case '\0':
goto BreakLoop;
case '%':
{
p++;
unsigned char c1 = *p++;
unsigned char c2 = *p++;
c1 = '0' <= c1 && c1 <= '9' ? c1 - '0' : 'A' <= c1 && c1 <= 'F' ? c1 - 'A' + 10 :
'a' <= c1 && c1 <= 'f' ? c1 - 'a' + 10 : 0;
c2 = '0' <= c2 && c2 <= '9' ? c2 - '0' : 'A' <= c2 && c2 <= 'F' ? c2 - 'A' + 10 :
'a' <= c2 && c2 <= 'f' ? c2 - 'a' + 10 : 0;
unsigned char ch = (c1 << 4) + c2;
*output++ = (char)ch;
break;
}
default:
*output++ = *p++;
break;
}
}
BreakLoop:
*output = '\0';
}
#ifdef WIN32
bool WebUtil::Utf8ToAnsi(char* szBuffer, int iBufLen)
{

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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,6 +77,7 @@ public:
~StringBuilder();
void Append(const char* szStr);
const char* GetBuffer() { return m_szBuffer; }
void Clear();
};
class Util
@@ -109,10 +110,12 @@ public:
static void FixExecPermission(const char* szFilename);
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
static void GetExeFileName(const char* argv0, char* szBuffer, int iBufSize);
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
static bool SameFilename(const char* szFilename1, const char* szFilename2);
static bool MatchFileExt(const char* szFilename, const char* szExtensionList, const char* szListSeparator);
static char* GetLastErrorMessage(char* szBuffer, int iBufLen);
static void FormatSpeed(int iBytesPerSecond, char* szBuffer, int iBufSize);
/*
* Split command line int arguments.
@@ -235,6 +238,12 @@ public:
*/
static void HttpUnquote(char* raw);
/*
* Decodes URL-string.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void URLDecode(char* raw);
#ifdef WIN32
static bool Utf8ToAnsi(char* szBuffer, int iBufLen);
static bool AnsiToUtf8(char* szBuffer, int iBufLen);

View File

@@ -201,3 +201,24 @@ void InstallUninstallServiceCheck(int argc, char *argv[])
exit(0);
}
}
bool IsServiceRunning()
{
SC_HANDLE scm = OpenSCManager(0, 0, 0);
if (!scm)
{
return false;
}
SC_HANDLE hService = OpenService(scm, "NZBGet", SERVICE_QUERY_STATUS);
SERVICE_STATUS ServiceStatus;
bool bRunning = false;
if (hService && QueryServiceStatus(hService, &ServiceStatus))
{
bRunning = ServiceStatus.dwCurrentState != SERVICE_STOPPED;
}
CloseServiceHandle(scm);
return bRunning;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,5 +30,6 @@ typedef void (*RunProc)(void);
void InstallUninstallServiceCheck(int argc, char *argv[]);
void StartService(RunProc RunProcPtr);
bool IsServiceRunning();
#endif

View File

File diff suppressed because it is too large Load Diff

106
daemon/windows/WinConsole.h Normal file
View File

@@ -0,0 +1,106 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2014-2015 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 WINCONSOLE_H
#define WINCONSOLE_H
#include "Thread.h"
class WinConsole : public Thread
{
private:
bool m_bAppMode;
char** m_pDefaultArguments;
char** m_pInitialArguments;
int m_iInitialArgumentCount;
HWND m_hTrayWindow;
NOTIFYICONDATA* m_pNidIcon;
UINT UM_TASKBARCREATED;
HMENU m_hMenu;
HINSTANCE m_hInstance;
bool m_bModal;
HFONT m_hLinkFont;
HFONT m_hNameFont;
HFONT m_hTitleFont;
HCURSOR m_hHandCursor;
HICON m_hAboutIcon;
HICON m_hRunningIcon;
HICON m_hIdleIcon;
HICON m_hWorkingIcon;
HICON m_hPausedIcon;
bool m_bAutostart;
bool m_bTray;
bool m_bConsole;
bool m_bWebUI;
bool m_bAutoParam;
bool m_bRunning;
bool m_bRunningService;
void CreateResources();
void CreateTrayIcon();
void ShowWebUI();
void ShowMenu();
void ShowInExplorer(const char* szFileName);
void ShowAboutBox();
void OpenConfigFileInTextEdit();
void ShowPrefsDialog();
void SavePrefs();
void LoadPrefs();
void ApplyPrefs();
void ShowRunningDialog();
void CheckRunning();
void UpdateTrayIcon();
void BuildMenu();
void ShowCategoryDir(int iCatIndex);
void SetupConfigFile();
void SetupScripts();
void ShowFactoryResetDialog();
void ResetFactoryDefaults();
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType);
static LRESULT CALLBACK TrayWndProcStat(HWND hwndWin, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT TrayWndProc(HWND hwndWin, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK AboutDialogProcStat(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL AboutDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK PrefsDialogProcStat(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL PrefsDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK RunningDialogProcStat(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL RunningDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
static BOOL CALLBACK FactoryResetDialogProcStat(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL FactoryResetDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
virtual void Run();
public:
WinConsole();
~WinConsole();
virtual void Stop();
void InitAppMode();
bool GetAppMode() { return m_bAppMode; }
void SetupFirstStart();
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2015 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
@@ -71,7 +71,7 @@
/* Define to 1 if spinlocks are supported */
#define HAVE_SPINLOCK
#define VERSION "14.2"
#define VERSION "15.0"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE
@@ -81,6 +81,11 @@
#define _USE_32BIT_TIME_T
#if _WIN32_WINNT < 0x0501
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#ifdef _DEBUG
// detection of memory leaks
#define _CRTDBG_MAP_ALLOC

View File

@@ -586,7 +586,7 @@ bool Par2Repairer::LoadPacketsFromFile(string filename)
// Remember that the file was processed
bool success = diskFileMap.Insert(diskfile);
assert(success);
assert(success); (void)success;
}
else
{
@@ -1285,7 +1285,7 @@ bool Par2Repairer::VerifySourceFiles(void)
// Remember that we have processed this file
bool success = diskFileMap.Insert(diskfile);
assert(success);
assert(success); (void)success;
// Do the actual verification
if (!VerifyDataFile(diskfile, sourcefile))
finalresult = false;
@@ -1347,7 +1347,7 @@ bool Par2Repairer::VerifyExtraFiles(const list<CommandLine::ExtraFile> &extrafil
// Remember that we have processed this file
bool success = diskFileMap.Insert(diskfile);
assert(success);
assert(success); (void)success;
// Do the actual verification
VerifyDataFile(diskfile, 0);
@@ -2031,7 +2031,7 @@ bool Par2Repairer::RenameTargetFiles(void)
return false;
bool success = diskFileMap.Insert(targetfile);
assert(success);
assert(success); (void)success;
// We no longer have a target file
sourcefile->SetTargetExists(false);
@@ -2063,7 +2063,7 @@ bool Par2Repairer::RenameTargetFiles(void)
return false;
bool success = diskFileMap.Insert(targetfile);
assert(success);
assert(success); (void)success;
// This file is now the target file
sourcefile->SetTargetExists(true);
@@ -2112,7 +2112,7 @@ bool Par2Repairer::CreateTargetFiles(void)
// Remember this file
bool success = diskFileMap.Insert(targetfile);
assert(success);
assert(success); (void)success;
u64 offset = 0;
vector<DataBlock>::iterator tb = sourcefile->TargetBlocks();

124
linux/build-info.txt Normal file
View File

@@ -0,0 +1,124 @@
About
-----
"build-nzbget" is a bash script which is used to build universal installer
for Linux. The script compiles NZBGet for each supported CPU-architecture
and then packs all produced files into an installer package.
Prerequisites
-------------
To use the script you need a Linux (virtual) machine.
Building
--------
This script was written to work with toolchain from uClibc's Buildroot-project.
1. Create directory where you want to keep your files to compile NZBGet. Choose
the path wisely because it cannot be changed later without rebuilding all
toolchains again;
2. Put the build script 'build-nzbget' into that directory;
3. Create subdirectories:
toolchain - for toolchains;
nzbget - for source code of NZBGet;
output - to store the results of build script;
setup - for extra files required for installer;
4. Build toolchain for one CPU-architecture (see below);
4.1. Download Buildroot distribution archive from http://buildroot.uclibc.org/download.html
(tested with version "buildroot-2015.02");
4.2. Unpack the tarball into 'toolchain'-directory;
4.3. Rename the buildroot-directory according to the target CPU-architecture name;
Be careful here, after the toolchain is built the directory cannot be renamed
or moved, you will have to rebuild the toolchain if you want another name;
4.4. Run 'make nconfig';
4.5. Configure toolchain:
- Target architecture:
- choose your target architecture;
- Build options:
- libraries (both static and shared);
- Toolchain:
- Kernel Headers (Manually specified Linux version);
- (2.6.30) linux version;
- Custom kernel headers series (2.6.x);
- Enable large file (files > 2 GB) support;
- Enable IPv6 support;
- Enable toolchain locale/i18n support;
- GCC compiler Version (gcc 4.9.x);
- Enable C++ support;
- Build cross gdb for the host;
- Target packages:
- Libraries:
- Compression and decompression: zlib;
- Crypto: openssl;
- JSON/XML: libxml2;
- Text and terminal handling: ncurses;
- Save configuration and exit;
4.6. Do few extra manual adjustments:
- in the file '.config' in the buildroot directory activate define to
build 'ubacktrace';
- in 'packages/ncurses/ncurses.mk' add extra configure parameters to
option 'NCURSES_CONF_OPTS (without quotation marks):
"--with-fallbacks=xterm xterm-color xterm-256color xterm-16color linux vt100 vt200";
- in 'packages/openssl/openssl.mk' replace 'zlib-dynamic' with 'zlib';
4.7. Run 'make' to build the toolchain. It may take an hour or so depending
on your hardware;
5. Now you should have a working toolchain for one CPU-architecture, let's
test it.
5.1. Change to the ROOTBUILD-directory and run the build script:
./nzbget-build release bin <CPU-Architecture>
5.2. The script creates subdirectory 'nzbget/trunk' and checkouts the source
code of NZBGet from subversion repository;
5.3. Then the source code is compiled for chosen CPU-architecture;
5.4. After the compiling a distribution binary archive for the chosen
CPU-architecture is put into output-directory;
6. Build unrar and 7za for the CPU-architecture:
6.1. Download source of unrar; Compile for target, either manually or
using script 'build-unpack';
6.2. Put the compiled binaries of unrar and 7za into setup-directory, add
suffix '-arch' to unrar and 7za names, for example 'unrar-armel';
6.3. Copy license-files from unrar and 7-Zip projects using names
'license-unrar.txt' and 'license-7zip.txt';
7. Now you can build installer for that one CPU-architecture:
7.1. If you build for CPU-architecture which is not supported by NZBGet's
universal installer you have to edit the script 'build-nzbget' and
add the architecture name into variable 'ALLTARGETS';
7.2. Run the build script with:
./nzbget-build release installer <CPU-Architecture>
7.3. The created installer supports only one CPU-Architecture;
7.8. Run the installer on the target machine (with target CPU-Architecture);
8. Repeat step for each CPU-Architecture you intend to build the installer for;
9. To build installer for all CPU-Architectures listed in variable 'ALLTARGETS'
of the script run the script without choosing CPU-Architecture:
./nzbget-build release installer
10. When the script is run without any parameters:
10.1. NZBGet is compiled twice for each CPU-Architecture listed in
'ALLTARGETS': once in release mode and once in debug mode;
10.2. Two installers are built: one for release and another for debug.
Special functions
-----------------
By default the script builds from trunk/HEAD. To specify another tag or branch
pass it to the script, for example to build a tagged version 14.2:
./nzbget-build release bin tags/14.2
Installers can built only for version 15.0 (starting from revision r1279).
Distribution archives can be built for older versions too.
To build certain revision pass it to the script, for example
./nzbget-build release bin r1279
To cleanup the output directory before building pass parameter 'cleanup'.

349
linux/build-nzbget Executable file
View File

@@ -0,0 +1,349 @@
#!/bin/bash
#
# This file is part of nzbget
#
# Copyright (C) 2015 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$
#
# Setup strict bash error handling
set -o nounset
set -o errexit
# Uncomment next line for debuging
#set -x
ALLTARGETS="dist i686 x86_64 armel armhf mipsel mipseb ppc6xx ppc500"
ROOT=`pwd`
ROOTPATH=$PATH
OUTPUTDIR=$ROOT/output
echo "Usage:"
echo " $(basename $0) [targets] [output] [revision] [reppath] [configs] [cleanup]"
echo " targets : all (default) $ALLTARGETS"
echo " output : bin installer"
echo " revision : head (default) rXXXX work"
echo " reppath : trunk (default) tags/XXX branches/XXX"
echo " configs : release debug (default) release-nostrip debug-strip"
echo " cleanup : cleanup output directory before building"
echo
# Parsing command line
BUILD=no
TARGETS=""
OUTPUTS=""
REVISION=""
REPPATH=""
CONFIGS=""
CLEAN=no
for PARAM in "$@"
do
case $PARAM in
release|release-nostrip|debug|debug-strip)
# using xargs to trim spaces
CONFIGS=`echo "$CONFIGS $PARAM" | xargs`
;;
trunk|tags/*|branches/*)
REPPATH="$PARAM"
;;
head|work|r[0-9]|r[0-9][0-9]|r[0-9][0-9][0-9]|r[0-9][0-9][0-9][0-9])
REVISION="$PARAM"
;;
clean|cleanup)
CLEAN=yes
;;
bin|installer)
# using xargs to trim spaces
OUTPUTS=`echo "$OUTPUTS $PARAM" | xargs`
;;
*)
if [[ " $ALLTARGETS " == *" $PARAM "* ]]; then
# using xargs to trim spaces
TARGETS=`echo "$TARGETS $PARAM" | xargs`
if [ "$PARAM" == "all" ]; then
PARAM=$ALLTARGETS
fi
elif [ -d toolchain/$PARAM ]; then
# non-standard target but the toolchain exist
# using xargs to trim spaces
TARGETS=`echo "$TARGETS $PARAM" | xargs`
else
echo "Invalid parameter: $PARAM"
exit 1
fi
;;
esac
done
if [ "$TARGETS" == "" ]; then
TARGETS="$ALLTARGETS"
fi
if [ "$OUTPUTS" == "" ]; then
OUTPUTS="bin installer"
fi
if [ "$REVISION" == "" ]; then
REVISION="head"
fi
if [ "$REPPATH" == "" ]; then
REPPATH="trunk"
fi
if [ "$CONFIGS" == "" ]; then
CONFIGS="release debug"
fi
echo "Active configuration:"
echo " targets : $TARGETS"
echo " outputs : $OUTPUTS"
echo " revision : $REVISION"
echo " reppath : $REPPATH"
echo " configs : $CONFIGS"
echo " cleanup : $CLEAN"
echo
# Checkout and update from svn
cd nzbget
if [ ! -d $REPPATH ]; then
echo "Initial checkout for $REPPATH"
svn co https://svn.code.sf.net/p/nzbget/code/$REPPATH $REPPATH
fi
BUILDDIR=$ROOT/nzbget/$REPPATH
cd $BUILDDIR
if [ "$REVISION" != "work" ]; then
echo "Updating to $REVISION"
svn -r $REVISION up
touch Makefile.in configure config.h.in
fi
# File name format for output files
VERSION=`grep "AM_INIT_AUTOMAKE(nzbget, " configure.ac`
VERSION=`expr "$VERSION" : '.*, \(.*\))'`
REVISION=`svnversion`
BASENAME="nzbget-$VERSION"
if [ `expr "$VERSION" : ".*-testing"` != 0 ]; then
BASENAME="$BASENAME-r$REVISION"
fi
# Building
mkdir -p $OUTPUTDIR
if [ "$CLEAN" == "yes" ]; then
rm -r -f $OUTPUTDIR/*
fi
for CONFIG in $CONFIGS; do
case $CONFIG in
release)
SUFFIX=""
;;
debug)
SUFFIX="-debug"
;;
debug-strip)
SUFFIX="-debug-strip"
;;
release-nostrip)
SUFFIX="-nostrip"
;;
esac
for OUTPUT in $OUTPUTS; do
if [ "$OUTPUT" == "bin" ]; then
for TARGET in $TARGETS; do
cd $BUILDDIR
echo "Building in `pwd` for $TARGET ($CONFIG)"
case $TARGET in
mipsel|i?86|x86_64)
ARCH=$TARGET
;;
mipseb)
ARCH=mips
;;
arm*)
ARCH=arm
;;
ppc*)
ARCH=powerpc
;;
esac
if [ "$TARGET" == "dist" ]; then
if [ ! -f $OUTPUTDIR/$BASENAME.tar.gz ]; then
./configure
make dist
cp nzbget-$VERSION.tar.gz $OUTPUTDIR/$BASENAME.tar.gz
fi
else
PATH=:$ROOTPATH
TOOLCHAIN_ROOT=$ROOT/toolchain/$TARGET
PATH=$TOOLCHAIN_ROOT/output/host/usr/bin:$PATH
STRIP=""
if [ $CONFIG == "debug-strip" -o $CONFIG == "release" ]; then
STRIP="-s"
fi
case $CONFIG in
debug|debug-strip)
LIBPREF="$TOOLCHAIN_ROOT/output/staging/usr" LDFLAGS="-static $STRIP" \
LIBS="-lcrypto -ldl -lz -lubacktrace" CXXFLAGS="-g -fasynchronous-unwind-tables" \
./configure --host=$ARCH-linux --enable-debug
;;
release|release-nostrip)
LIBPREF="$TOOLCHAIN_ROOT/output/staging/usr" LDFLAGS="-static $STRIP" \
LIBS="-lcrypto -ldl -lz" ./configure --host=$ARCH-linux
;;
esac
make clean && make
rm -r -f $OUTPUTDIR/install
make DESTDIR=$OUTPUTDIR/install install
cd $OUTPUTDIR
rm -r -f nzbget
mkdir -p nzbget
mv install/usr/local/bin/nzbget nzbget
mv install/usr/local/share/doc/nzbget/* nzbget
rm nzbget/AUTHORS
mv install/usr/local/share/nzbget/webui nzbget
mv install/usr/local/share/nzbget/scripts nzbget
CONFTEMPLATE=nzbget/webui/nzbget.conf.template
mv install/usr/local/share/nzbget/nzbget.conf $CONFTEMPLATE
rm -r -f $OUTPUTDIR/install
# adjusting nzbget.conf
sed 's:^MainDir=.*:MainDir=${AppDir}/downloads:' -i $CONFTEMPLATE
sed 's:^DestDir=.*:DestDir=${MainDir}/completed:' -i $CONFTEMPLATE
sed 's:^InterDir=.*:InterDir=${MainDir}/intermediate:' -i $CONFTEMPLATE
sed 's:^WebDir=.*:WebDir=${AppDir}/webui:' -i $CONFTEMPLATE
sed 's:^ScriptDir=.*:ScriptDir=${AppDir}/scripts:' -i $CONFTEMPLATE
sed 's:^LogFile=.*:LogFile=${MainDir}/nzbget.log:' -i $CONFTEMPLATE
sed 's:^ConfigTemplate=.*:ConfigTemplate=${AppDir}/webui/nzbget.conf.template:' -i $CONFTEMPLATE
sed 's:^AuthorizedIP=.*:AuthorizedIP=127.0.0.1:' -i $CONFTEMPLATE
tar -czf $BASENAME-bin-linux-$TARGET$SUFFIX.tar.gz nzbget
rm -r -f nzbget
echo "Completed build in `pwd` for $TARGET ($CONFIG)"
fi
done
elif [ "$OUTPUT" == "installer" ]; then
echo "Creating installer for $CONFIG..."
cd $OUTPUTDIR
# checking if all targets exists
for TARGET in $TARGETS
do
ALLEXISTS="yes"
if [ "$TARGET" != "dist" ]; then
if [ ! -f $BASENAME-bin-linux-$TARGET$SUFFIX.tar.gz ]; then
echo "Could not find $BASENAME-bin-linux-$TARGET$SUFFIX.tar.gz"
ALLEXISTS="no"
fi
fi
done
if [ "$ALLEXISTS" == "no" ]; then
exit 1;
fi
echo "Unpacking targets..."
rm -r -f nzbget
for TARGET in $TARGETS
do
ALLEXISTS="yes"
if [ "$TARGET" != "dist" ]; then
tar -xzf $BASENAME-bin-linux-$TARGET$SUFFIX.tar.gz
mv nzbget/nzbget nzbget/nzbget-$TARGET
cp ../setup/unrar-$TARGET nzbget
cp ../setup/7za-$TARGET nzbget
fi
done
# adjusting nzbget.conf
sed 's:^UnrarCmd=unrar:UnrarCmd=${AppDir}/unrar:' -i nzbget/webui/nzbget.conf.template
sed 's:^SevenZipCmd=7z:SevenZipCmd=${AppDir}/7za:' -i nzbget/webui/nzbget.conf.template
INSTFILE=$BASENAME-bin-linux$SUFFIX.run
echo "Building installer package..."
cp $BUILDDIR/linux/installer.sh $INSTFILE
cp $BUILDDIR/linux/package-info.json nzbget/webui
cp $BUILDDIR/linux/install-update.sh nzbget
cp ../setup/license-unrar.txt nzbget
cp ../setup/license-7zip.txt nzbget
# creating payload
cd nzbget
tar czf - * > ../$INSTFILE.data
cd ..
# creating installer script
sed "s:^TITLE=$:TITLE=\"$BASENAME$SUFFIX\":" -i $INSTFILE
DISTTARGETS="${TARGETS/dist/}"
DISTTARGETS=`echo "$DISTTARGETS" | xargs`
sed "s:^DISTARCHS=$:DISTARCHS=\"$DISTTARGETS\":" -i $INSTFILE
PAYLOAD=`stat -c%s "$INSTFILE.data"`
sed "s:^PAYLOAD=$:PAYLOAD=$PAYLOAD:" -i $INSTFILE
MD5=`md5sum "$INSTFILE.data" | cut -b-32`
sed "s:^MD5=$:MD5=\"$MD5\":" -i $INSTFILE
HEADER=`stat -c%s "$INSTFILE"`
LEN=${#HEADER}
HEADER=`expr $HEADER + $LEN`
sed "s:^HEADER=$:HEADER=$HEADER:" -i $INSTFILE
# attaching payload
cat $INSTFILE.data >> $INSTFILE
rm $INSTFILE.data
chmod +x $INSTFILE
rm -r nzbget
fi
done
done

180
linux/build-unpack Executable file
View File

@@ -0,0 +1,180 @@
#!/bin/bash
#
# This file is part of nzbget
#
# Copyright (C) 2015 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$
#
# Setup strict bash error handling
set -o nounset
set -o errexit
# Uncomment next line for debuging
#set -x
ALLTARGETS="i686 x86_64 armel armhf mipsel mipseb ppc6xx ppc500"
ROOT=`pwd`
OUTPUTDIR=$ROOT/setup
BUILDDIR=temp
echo "Usage:"
echo " $(basename $0) [targets] [clean] [unpacker]"
echo " unpacker - unrar, 7zip."
echo
# Parsing command line
TARGETS=""
CLEAN=no
UNPACKERS=""
for PARAM in "$@"
do
case $PARAM in
clean|cleanup)
CLEAN=yes
;;
unrar|7zip)
# using xargs to trim spaces
UNPACKERS=`echo "$UNPACKERS $PARAM" | xargs`
;;
*)
if [ -d toolchain/$PARAM ]; then
TARGETS=`echo "$TARGETS $PARAM" | xargs`
else
echo "Invalid parameter: $PARAM"
exit 1
fi
;;
esac
done
if [ "$TARGETS" == "" ]; then
TARGETS="$ALLTARGETS"
fi
if [ "$UNPACKERS" == "" ]; then
UNPACKERS="unrar 7zip"
fi
echo "Active configuration:"
echo " targets : $TARGETS"
echo " unpackers : $UNPACKERS"
echo " cleanup : $CLEAN"
echo
# Building
for UNPACKER in $UNPACKERS; do
case $UNPACKER in
unrar)
EXENAME=unrar
;;
7zip)
EXENAME=7za
;;
esac
if [ "$CLEAN" == "yes" ]; then
rm -r -f $OUTPUTDIR/$EXENAME-*
fi
for TARGET in $TARGETS; do
case $TARGET in
mipsel|i?86|x86_64)
ARCH=$TARGET
ENDIAN=little
;;
mipseb)
ARCH=mips
ENDIAN=big
;;
arm*)
ARCH=arm
ENDIAN=little
;;
ppc*)
ARCH=powerpc
ENDIAN=big
;;
esac
TOOLCHAIN_ROOT=$ROOT/toolchain/$TARGET
rm -rf "$ROOT/$BUILDDIR/$UNPACKER"
cd $ROOT/$BUILDDIR
case $UNPACKER in
unrar)
tar xzf unrarsrc-*.tar.gz
cd unrar
sed 's:^CXX=g++:#CXX=g++:' -i makefile
sed 's:^STRIP=strip:#STRIP=strip:' -i makefile
sed 's:^LDFLAGS=:LDFLAGS=-static :' -i makefile
sed 's:^CXXFLAGS=-O2:#CXXFLAGS=-O2:' -i makefile
if test "$ENDIAN" = "big"; then
sed 's:^DEFINES=:DEFINES=-DBIG_ENDIAN :' -i makefile
fi
EXEDIR=
LICENSE=license.txt
BUILDTARGET=
;;
7zip)
tar xjf p7zip_*_src_all.tar.bz2
rm -rf 7zip
mkdir 7zip
mv p7zip_*/* 7zip
find p7zip_* -maxdepth 0 -type d -exec rm -r {} \;
cd 7zip
rm makefile.machine
cp makefile.linux_any_cpu_gcc_4.X makefile.machine
sed 's:^CXX=g++:#CXX=g++:' -i makefile.machine
sed 's:^CC=gcc:#CC=gcc:' -i makefile.machine
EXEDIR=bin/
LICENSE=DOC/License.txt
BUILDTARGET=$EXENAME
;;
esac
cd $ROOT/$BUILDDIR/$UNPACKER
make clean
CXX=$TOOLCHAIN_ROOT/output/host/usr/bin/$ARCH-linux-g++ \
CC=$TOOLCHAIN_ROOT/output/host/usr/bin/$ARCH-linux-gcc \
STRIP=$TOOLCHAIN_ROOT/output/host/usr/bin/$ARCH-linux-strip \
CXXFLAGS=-g \
LDFLAGS=-static \
make $BUILDTARGET
cp $EXEDIR$EXENAME ../../setup/$EXENAME-$TARGET
cp $LICENSE ../../setup/license-$UNPACKER.txt
echo "Completed build for $TARGET ($UNPACKER)"
done
done

116
linux/install-update.sh Executable file
View File

@@ -0,0 +1,116 @@
#!/bin/sh
#
# This file is part of nzbget
#
# Copyright (C) 2015 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.
#
BASE_URL="http://nzbget.net/download"
if test "$NZBUP_PROCESSID" = ""; then
echo "This file is not supposed to be executed directly. To update NZBGet please choose Settings -> SYSTEM -> Check for update in the web-interface."
exit 1
fi
INSTALLERCFG=`cat "$NZBOP_APPDIR/installer.cfg" 2>/dev/null`
if test "$INSTALLERCFG" = ""; then
echo "[ERROR] File \"installer.cfg\" is missing in the installation directory. Please reinstall NZBGet."
exit 1
fi
echo "Downloading version information..."
UPDATE_INFO_LINK=`cat "$NZBOP_APPDIR/webui/package-info.json" | sed -n 's/^.*update-info-link.*: "\(.*\)".*/\1/p'`
"$NZBOP_APPBIN" -B webget "$NZBOP_TEMPDIR/NZBGET_UPDATE.txt" "$UPDATE_INFO_LINK" 2>/dev/null
if test "$?" != "0"; then
echo "[ERROR] Download failed, please try again later"
exit 1
fi
if test "$NZBUP_BRANCH" = "TESTING"; then
VER_FIELD="testing-version"
elif test "$NZBUP_BRANCH" = "STABLE"; then
VER_FIELD="stable-version"
else
echo "[ERROR] Unsupported branch $NZBUP_BRANCH"
exit 1
fi
VER=`cat "$NZBOP_TEMPDIR/NZBGET_UPDATE.txt" | sed -n "s/^.*$VER_FIELD.*: \"\(.*\)\".*/\1/p"`
rm -f "$NZBOP_TEMPDIR/NZBGET_UPDATE.txt"
INSTALLER="nzbget-$VER-bin-linux.run"
echo "Downloading $INSTALLER..."
rm -f "$NZBOP_TEMPDIR/$INSTALLER"
"$NZBOP_APPBIN" -B webget "$NZBOP_TEMPDIR/$INSTALLER" "$BASE_URL/$INSTALLER" 2>/dev/null
if test "$?" != "0"; then
echo "[ERROR] Download failed, please try again later"
exit 1
fi
echo "Updating NZBGet..."
echo "..."
sh "$NZBOP_TEMPDIR/$INSTALLER" --update --destdir "$NZBOP_APPDIR"
if test "$?" != "0"; then
echo "[ERROR] Update failed, installer terminated with error status"
exit 1
fi
rm -f "$NZBOP_TEMPDIR/$INSTALLER"
echo "..."
echo "Update completed"
echo "Restarting NZBGet..."
sleep 1
echo "[NZB] QUIT"
echo "Waiting for NZBGet to terminate"
PSOPT="-A"
OPTWORKING=`ps $PSOPT` 2>/dev/null
if test "$?" != "0"; then
PSOPT=""
fi
while true
do
RUNNING=`ps $PSOPT | sed -n "s/^\s*$NZBUP_PROCESSID\s.*/&/p"`
if test "$RUNNING" = ""; then
break
fi
sleep 1
done
echo "Starting NZBGet..."
# Recreating command line used to start NZBGet
CMDLINE=
NUM=1
while true
do
PARAMNAME="NZBUP_CMDLINE$NUM"
eval PARAM="\$NZBUP_CMDLINE${NUM}"
if test "$PARAM" = ""; then
break
fi
if test "$CMDLINE" != ""; then
CMDLINE="$CMDLINE "
fi
CMDLINE="$CMDLINE\"$PARAM\""
NUM=$((NUM + 1))
done
# Starting NZBGet
eval "$NZBOP_APPBIN" $CMDLINE
exit 0

437
linux/installer.sh Executable file
View File

@@ -0,0 +1,437 @@
#!/bin/sh
#
# This file is part of nzbget
#
# Copyright (C) 2015 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.
#
# Strict error handling for debugging
set -o nounset
set -o errexit
# Installer title
TITLE=
# Size of installer script (package header)
HEADER=
# Size of tar.gz archive (package payload)
PAYLOAD=
# Md5 sum of payload
MD5=
# List of included CPU architecture binaries
DISTARCHS=
SILENT=no
ALLARCHS="$DISTARCHS all"
ARCH=""
SELECT=auto
OUTDIR="nzbget"
PRINTEDTITLE=no
JUSTUNPACK=no
UPDATE=no
VERIFY=yes
Info()
{
if test "$SILENT" = "no"; then
echo "$1"
fi
}
Error()
{
Info "ERROR: $1"
exit 1
}
ValidArch()
{
LIMARCH=`echo " $ALLARCHS " | sed "s/ $1 //"`
if test " $ALLARCHS " = "$LIMARCH"; then
return 1
fi
return 0
}
PrintArch()
{
if ValidArch $1; then
Info "$2"
fi
}
PrintHelp()
{
if test "$PRINTEDTITLE" = "no"; then
Info "Installer for $TITLE"
Info ""
fi
Info "This installer supports Linux kernel 2.6 or newer and the following CPU architectures:"
PrintArch "i686" " i686 - x86, 32 or 64 Bit"
PrintArch "x86_64" " x86_64 - x86, 64 Bit"
PrintArch "armel" " armel - ARMv5/v6 (ARM9 and ARM11 families)"
PrintArch "armhf" " armhf - ARMv7 (Cortex family)"
PrintArch "mipsel" " mipsel - MIPS (little endian)"
PrintArch "mipseb" " mipseb - MIPS (big endian)"
PrintArch "ppc6xx" " ppc6xx - PowerPC 6xx (603e series)"
PrintArch "ppc500" " ppc500 - PowerPC e500 (core e500v1/e500v2)"
Info ""
Info "Usage: sh $(basename $0) [options]"
Info " --help - print this help"
Info " --arch <cpu> - set CPU-architecture"
Info " --destdir <path> - set destination directory"
Info " --list - list package content"
Info " --unpack - just unpack, skip setup"
Info " --silent - silent mode"
Info " --nocheck - skip integrity check"
Info " --tar <args> - run custom tar command on archive"
}
Verify()
{
REQSIZE=$((HEADER + PAYLOAD))
ACTSIZE=`stat -c%s "$0" 2>/dev/null | cat`
if test "$ACTSIZE" = ""; then
NUM=1
for FIELD in `ls -l "$0" 2>/dev/null`
do
if test "$NUM" = 5; then
ACTSIZE="$FIELD"
break
fi
NUM=$((NUM + 1))
done
fi
if test "$REQSIZE" != "$ACTSIZE"; then
Error "Corrupted installer package detected: file size mismatch."
fi
ACTMD5=`dd "if=$0" bs=$HEADER skip=1 2>/dev/null | md5sum 2>/dev/null | cut -b-32 2>/dev/null | cat`
LEN=${#ACTMD5}
if test "$LEN" = "32" -a "$MD5" != "$ACTMD5"; then
Error "Corrupted installer package detected: checksum mismatch."
fi
}
DetectEndianness()
{
# Sixth byte of any executable indicates endianness
ENDBYTE=`dd if=/bin/sh bs=1 count=6 2>/dev/null | sed -n 's/ELF.\(.*\)/\1/p'`
ENDIAN=unknown
if test $ENDBYTE="\x01"; then
ENDIAN=little
elif test $ENDBYTE="\x02"; then
ENDIAN=big
fi
}
DetectArch()
{
OS=`uname -s`
if test "$OS" != "Linux"; then
PrintHelp
Error "Operating system ($OS) isn't supported by this installer."
fi
if test "$UPDATE" = "yes"; then
ARCH=`cat "$OUTDIR/installer.cfg" 2>/dev/null | sed -n 's/^arch=\(.*\)$/\1/p'`
SELECT=`cat "$OUTDIR/installer.cfg" 2>/dev/null | sed -n 's/^select=\(.*\)$/\1/p'`
fi
if test "$ARCH" = ""; then
CPU=`uname -m`
case $CPU in
i386|i686)
ARCH=i686
;;
x86_64)
ARCH=x86_64
;;
mips)
ARCH=mipsx
;;
armv5*|armv6*|armel)
ARCH=armel
;;
armv7*|armv8*)
ARCH=armhf
;;
ppc)
ARCH=ppcx
;;
esac
fi
if test "$ARCH" = ""; then
MIPS=`cat /proc/cpuinfo | sed -n 's/.*:.*\(mips\).*/&/p'`
if test "$MIPS" != ""; then
ARCH=mipsx
fi
fi
if test "$ARCH" = "mipsx"; then
DetectEndianness
if test "$ENDIAN" = "big"; then
ARCH=mipseb
else
ARCH=mipsel
fi
fi
if test "$ARCH" = "ppcx"; then
E500=`cat /proc/cpuinfo | sed -n 's/.*:.*\(e500\).*/&/p'`
if test "$E500" != ""; then
ARCH=ppc500
else
ARCH=ppc6xx
fi
fi
if test "$ARCH" = ""; then
PrintHelp
Error "CPU architecture ($CPU) isn't supported by this installer."
fi
if ! ValidArch $ARCH; then
Error "CPU architecture ($ARCH) isn't supported by this installer."
fi
}
Unpack()
{
# Prepare list of files to ignore
EXARCHS=""
if test "$JUSTUNPACK" = "no" -a "$ARCH" != "all"; then
rm -f /tmp/nzbget-installer.tmp
for TARG in $ALLARCHS
do
if test "$TARG" != "$ARCH"; then
echo "nzbget-$TARG" >> /tmp/nzbget-installer.tmp
echo "unrar-$TARG" >> /tmp/nzbget-installer.tmp
echo "7za-$TARG" >> /tmp/nzbget-installer.tmp
EXARCHS="-X /tmp/nzbget-installer.tmp"
fi
done
fi
# Unpack (skip ignorable files)
mkdir -p $OUTDIR
dd "if=$0" bs=$HEADER skip=1 2> /dev/null | gzip -cd | ( cd $OUTDIR; tar x $EXARCHS 2>&1 ) || { Error "Unpacking failed."; kill -15 $$; }
if test "$EXARCHS" != ""; then
rm -f /tmp/nzbget-installer.tmp
fi
# Rename unpacked binaries files and store arch selection
if test "$JUSTUNPACK" = "no" -a "$ARCH" != "all"; then
OLDDIR=`pwd`
cd $OUTDIR;
rm -f nzbget
rm -f unrar
mv nzbget-$ARCH nzbget
mv unrar-$ARCH unrar
mv 7za-$ARCH 7za
echo "arch=$ARCH" > "installer.cfg"
echo "select=$SELECT" >> "installer.cfg"
cd $OLDDIR
fi
}
TAR()
{
dd "if=$0" bs=$HEADER skip=1 2> /dev/null | gzip -cd | tar "$ARG" $@
exit $?
}
Configure()
{
cd $OUTDIR
QUICKHELP=no
if test ! -f nzbget.conf; then
cp ./webui/nzbget.conf.template nzbget.conf
MEMFREE=`cat /proc/meminfo | sed -n 's/^MemFree: *\([0-9]*\).*/\1/p' 2>/dev/null | cat`
MEMCACHED=`cat /proc/meminfo | sed -n 's/^Cached: *\([0-9]*\).*/\1/p' 2>/dev/null | cat`
if test "$MEMFREE" != "" -a "$MEMCACHED" != ""; then
MEMFREE=$(((MEMFREE + MEMCACHED) / 1024))
Info " Free memory detected: $MEMFREE MB"
if test $MEMFREE -gt 250; then
Info " Activating article cache (ArticleCache=100)"
sed 's:^ArticleCache=.*:ArticleCache=100:' -i nzbget.conf
Info " Increasing write buffer (WriteBuffer=1024)"
sed 's:^WriteBuffer=.*:WriteBuffer=1024:' -i nzbget.conf
Info " Increasing par repair buffer (ParBuffer=100)"
sed 's:^ParBuffer=.*:ParBuffer=100:' -i nzbget.conf
elif test $MEMFREE -gt 25; then
Info " Increasing write buffer (WriteBuffer=256)"
sed 's:^WriteBuffer=.*:WriteBuffer=256:' -i nzbget.conf
fi
fi
BOGOLIST=`cat /proc/cpuinfo | sed -n 's/^bogomips\s*:\s\([0-9]*\).*/\1/pI' 2>/dev/null | cat`
if test "$BOGOLIST" != ""; then
BOGOMIPS=0
for CPU1 in $BOGOLIST
do
BOGOMIPS=$((BOGOMIPS + CPU1))
done
Info " CPU speed detected: $BOGOMIPS BogoMIPS"
if test $BOGOMIPS -lt 4000; then
Info " Disabling download during par check/repair (ParPauseQueue=yes)"
sed 's:^ParPauseQueue=.*:ParPauseQueue=yes:' -i nzbget.conf
Info " Disabling download during unpack (UnpackPauseQueue=yes)"
sed 's:^UnpackPauseQueue=.*:UnpackPauseQueue=yes:' -i nzbget.conf
Info " Disabling download during post-processing (ScriptPauseQueue=yes)"
sed 's:^ScriptPauseQueue=.*:ScriptPauseQueue=yes:' -i nzbget.conf
else
Info " Simultaneous download and post-processing is on"
fi
fi
QUICKHELP=yes
fi
}
# ParseCommandLine
while true
do
PARAM=${1:-}
case $PARAM in
-h|--help)
PrintHelp
exit 0
;;
--silent)
SILENT=yes
shift
;;
--arch)
ARCH=${2:-}
SELECT=manual
if ! ValidArch $ARCH; then
PrintHelp
Info ""
Error "Bad argument ($ARCH) to option --arch."
exit 1
fi
shift 2
;;
--destdir)
OUTDIR=${2:-}
if test "$OUTDIR" = ""; then
PrintHelp
exit 1
fi
shift 2
;;
--unpack)
JUSTUNPACK=yes
shift
;;
--list)
ARG=t
TAR
exit $?
;;
--tar)
ARG=${2:-}
if test "$ARG" = ""; then
PrintHelp
exit 1
fi
shift 2
TAR
exit $?
;;
--update)
UPDATE=yes
shift
;;
--nocheck)
VERIFY=no
shift
;;
"")
break
;;
*)
PrintHelp
exit 1
;;
esac
done
Info "Installer for $TITLE"
if test "$SILENT" = "no"; then
PRINTEDTITLE=yes
fi
if test "$VERIFY" = "yes"; then
Info "Verifying package..."
Verify
fi
if test "$JUSTUNPACK" = "no"; then
Info "Checking system..."
DetectArch
if test "$SELECT" = "manual"; then
Info "CPU-Architecture: $ARCH (manually set)"
else
Info "CPU-Architecture: $ARCH"
fi
fi
Info "Unpacking..."
Unpack
ABSOUTDIR=`cd "$OUTDIR"; pwd`
if test "$JUSTUNPACK" = "no"; then
Info "Configuring..."
Configure
Info "Installation completed"
if test "$QUICKHELP" = "yes" -a "$SILENT" = "no"; then
Info ""
Info "Quick help (from nzbget-directory):"
Info " ./nzbget -s - start nzbget in console mode"
Info " ./nzbget -D - start nzbget in daemon mode (in background)"
Info " ./nzbget -C - connect to background process"
Info " ./nzbget -Q - stop background process"
Info " ./nzbget -h - help screen with all commands"
Info ""
Info "Successfully installed into $ABSOUTDIR"
IP=""
{
IP=`ifconfig | sed -rn 's/.*r:([^ ]+) .*/\1/p' | head -n 1` || true
} > /dev/null 2>&1
if test "$IP" = ""; then
IP="localhost"
fi
Info "Web-interface runs on http://$IP:6789"
else
Info "Successfully installed into $ABSOUTDIR"
fi
Info "For support please visit http://nzbget.net/forum"
else
Info "Unpacked into $ABSOUTDIR"
fi
exit
#END-OF-INSTALLER

4
linux/package-info.json Normal file
View File

@@ -0,0 +1,4 @@
{
"update-info-link": "http://nzbget.net/info/nzbget-version-linux.json",
"install-script": "install-update.sh"
}

View File

@@ -85,8 +85,14 @@ ScriptDir=${MainDir}/scripts
# Lock-file for daemon-mode, POSIX only.
#
# If the option is not empty, NZBGet creates the file and writes process-id
# (PID) into it. That info can be used in shell scripts.
# When started in daemon mode the program creates the lock file and
# writes process-id (PID) into it. That info can be used in shell
# scripts. If the lock file can not be created or the lock to the file
# can not be acquired the daemon terminates, preventing unintentional
# starting of multiple daemons.
#
# Set to empty value to disable the creating of the lock-file and the
# check for another running instance (not recommended).
LockFile=${MainDir}/nzbget.lock
# Where to store log file, if it needs to be created.
@@ -196,6 +202,16 @@ Server1.Cipher=
# Maximum number of simultaneous connections to this server (0-999).
Server1.Connections=4
# Server retention time (days).
#
# How long the articles are stored on the news server. The articles
# whose age exceed the defined server retention time are not tried on
# this news server, the articles are instead considered failed on this
# news server.
#
# Value "0" disables retention check.
Server1.Retention=0
# Second server, on level 0.
#Server2.Level=0
@@ -258,6 +274,52 @@ ControlUsername=nzbget
# Set to empty value to disable authorization request.
ControlPassword=tegbzn6789
# User name for restricted access.
#
# Restricted user can control the program with few restrictions. He
# has access to web-interface and can see most program settings. He
# can not change program settings and can not view security related
# options or options provided by extension scripts.
#
# Use this user to connect to NZBGet from other programs and web-sites.
#
# In terms of RPC-API the user:
# - cannot use method "saveconfig";
# - methods "config" and "saveconfig" return string "***" for
# options those content is protected from the user.
#
# Set to empty value to disable restricted user.
#
# NOTE: Don't forget to change default username/password of the control
# user (options <ControlUsername> and <ControlPassword>).
RestrictedUsername=
# Password for restricted access.
#
# Set to empty value to disable password check.
RestrictedPassword=
# User name to add downloads via RPC-API.
#
# Use the AddUsername/AddPassword to give other programs or web-services
# access to NZBGet with only two permissions:
# - add new downloads using RPC-method "append";
# - check program version using RPC-method "version".
#
# In a case the program/web-service needs more rights use the restricted
# user instead (options <RestrictedUsername> and <RestrictedPassword>).
#
# Set to empty value to disable add-user.
#
# NOTE: Don't forget to change default username/password of the control
# user (options <ControlUsername> and <ControlPassword>).
AddUsername=
# Password for user with add downloads access.
#
# Set to empty value to disable password check.
AddPassword=
# Secure control of NZBGet server (yes, no).
#
# Activate the option if you want to access NZBGet built-in web-server
@@ -278,7 +340,8 @@ SecureKey=
# IP-addresses allowed to connect without authorization.
#
# Comma separated list of privileged IPs for easy access to NZBGet
# built-in web-server (web-interface and RPC).
# built-in web-server (web-interface and RPC). The connected clients
# have full unrestricted access.
#
# Example: 127.0.0.1,192.168.178.2.
#
@@ -707,27 +770,29 @@ CrcCheck=yes
# How many retries should be attempted if a download error occurs (0-99).
#
# 1) If download fails because of "article or group not found error" the
# program tries another news server.
#
# 2) If download fails because of interrupted connection, the program
# tries the same server again until connection can be established.
#
# In both cases 1) and 2) option <Retries> is not used.
#
# If download however fails because of incomplete article, CRC-error or other
# error not mentioned above the program tries to redownload the article from
# the same news server as many times as defined in option <Retries>. If all
# If download fails because of incomplete or damaged article or due to
# CRC-error the program tries to redownload the article from the same
# news server as many times as defined in option <Retries>. If all
# attempts fail the program tries another news server.
#
# If download fails because of "article or group not found error" the
# program tries another news server without retrying on the failed server.
#
# If download fails because of interrupted connection the program
# tries another news server or the same server after the block interval
# expires.
Retries=3
# Set the interval between retries (seconds).
# Wait interval between retries (seconds).
#
# If download of an article fails because of interrupted connection
# the server is temporary blocked until the retry interval expires.
RetryInterval=10
# Set connection timeout for article downloading (seconds).
# Connection timeout for article downloading (seconds).
ArticleTimeout=60
# Set connection timeout for URL fetching (seconds).
# Connection timeout for URL fetching (seconds).
#
# This includes fetching of nzb-files via URLs and fetching of RSS feeds.
UrlTimeout=60
@@ -858,7 +923,6 @@ WriteLog=append
# (option <WriteLog> is set to "rotate").
RotateLog=3
# How error messages must be printed (screen, log, both, none).
ErrorTarget=both
@@ -869,23 +933,29 @@ WarningTarget=both
InfoTarget=both
# How detail messages must be printed (screen, log, both, none).
DetailTarget=both
DetailTarget=log
# How debug messages must be printed (screen, log, both, none).
#
# Debug-messages can be printed only if the program was compiled in
# debug-mode: "./configure --enable-debug".
DebugTarget=both
DebugTarget=log
# Number of messages stored in buffer and available for remote
# clients (messages).
LogBufferSize=1000
# Create a log of all broken files (yes ,no).
# Create log for each downloaded nzb-file (yes, no).
#
# The messages are saved for each download separately and can be viewed
# at any time in download details dialog or history details dialog.
NzbLog=yes
# Create a log of all broken files (yes, no).
#
# It is a text file placed near downloaded files, which contains
# the names of broken files.
CreateBrokenLog=yes
BrokenLog=yes
# Create memory dump (core-file) on abnormal termination, Linux only (yes, no).
#
@@ -1148,9 +1218,13 @@ ParThreads=0
# Files to ignore during par-check.
#
# List of file extensions or file names to ignore by par-rename and
# par-check. The entries must be separated with commas. The entries
# can be file extensions or any text the file name may end with.
# List of file extensions, file names or file masks to ignore by
# par-rename and par-check. The entries must be separated with
# commas.
#
# The entries must be separated with commas. The entries can be file
# extensions, file names or file masks containing wildcard
# characters * and ?.
#
# If par-rename or par-check detect missing or damaged files they
# will ignore files matching this option and will not initiate
@@ -1170,8 +1244,9 @@ ParIgnoreExt=.sfv, .nzb, .nfo
# None - do nothing (continue download).
#
# NOTE: For automatic duplicate handling option must be set to "Delete"
# or "None". If it is set to "Pause" you will need to manually unpause
# another duplicate (if any exists in queue). See also option <DupeCheck>.
# or "None". If it is set to "Pause" you will need to manually return
# another duplicate to queue (if any exists in history). See also
# option <DupeCheck>.
HealthCheck=delete
# Maximum allowed time for par-repair (minutes).
@@ -1212,20 +1287,6 @@ ParPauseQueue=no
# from download queue after successful check/repair.
ParCleanupQueue=yes
# Files to delete after successful check/repair.
#
# List of file extensions or file names to delete after successful
# download. The entries must be separated with commas. The entries
# can be file extensions or any text the file name may end with.
#
# Files or extensions listed here are also ignored by par-rename
# and par-check.
#
# NOTE: See also option <ParIgnoreExt>.
#
# Example: .par2, .sfv
ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
##############################################################################
### UNPACK ###
@@ -1262,6 +1323,24 @@ UnpackCleanupDisk=yes
#
# Example: /usr/bin/unrar.
#
# The option can also contain extra switches to pass to unrar. To the
# here defined command line NZBGet adds the following switches:
# x -y -p- -o+ *.rar ./_unpack/
#
# Switch "x" is added only if neither "x" nor "e" were defined in
# the option (this allows you to use switch "e" instead of "x"). switch
# "-o+" is added only if neither "-o+" nor "-o-" were defined
# in the command line. All other paramaters are always added. Parameter
# "-p-" is replaced with "-ppassword" if a password is set for nzb-file.
#
# Examples:
# 1) ignore file attributes (pemissions):
# /usr/bin/unrar x -ai;
# 2) decrease priority of unrar-process:
# nice -n 19 unrar.
#
# For other useful switches refer to unrar documentation.
#
# If unrar is in your PATH you may leave the path part and set only
# the executable name ("unrar" on POSIX or "unrar.exe" on Windows).
UnrarCmd=unrar
@@ -1270,10 +1349,43 @@ UnrarCmd=unrar
#
# Example: /usr/bin/7z.
#
# Similar to option <UnrarCmd> this option can also include extra switches.
#
# If 7-Zip binary is in your PATH you may leave the path part and set only
# the executable name ("7z" or "7za" on POSIX or "7z.exe" on Windows).
SevenZipCmd=7z
# Files to delete after successful download.
#
# List of file extensions, file names or file masks to delete after
# successful download. If either unpack or par-check fail the cleanup is
# not performed. If neither unpack nor par-check were made (because they
# were disabled or the download doesn't contain archives and/or par-files
# the cleanup is performed if the health is 100%.
#
# The entries must be separated with commas. The entries can be file
# extensions, file names or file masks containing wildcard
# characters * and ?.
#
# Files listed here are also ignored by par-rename and par-check.
#
# NOTE: See also option <ParIgnoreExt>.
#
# Example: .par2, .sfv
ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
# Path to file containing unpack passwords.
#
# If the option is set the program will try all passwords from the file
# when unpacking the archives. The file must be a text file containing
# one password per line.
#
# If an nzb-file has a defined password (in the post-processing settings)
# then the password-file is not used for that nzb-file.
#
# NOTE: Trying multiple passwords is a time consuming task. Whenever possible
# passwords should be set per nzb-file in their post-processing settings.
UnpackPassFile=
##############################################################################
### EXTENSION SCRIPTS ###
@@ -1534,13 +1646,14 @@ PostScript=
# http://nzbget.net/Extension_scripts.
ScanScript=
# List of queue scripts to execute after a nzb-file is added to queue.
# List of queue scripts to execute on queue events.
#
# The scripts in the list must be separated with commas or semicolons. Only
# filenames without path must be used. All scripts must be stored in directory
# pointed by option <ScriptDir>.
#
# The scripts are executed each time a new file is added to queue.
# The scripts are executed on certain queue events such as adding
# a new nzb-file to queue, etc.
#
# Example: DeleteQueueSamples.sh, NzbAddedNotify.py.
#

View File

@@ -39,7 +39,7 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".\daemon\connect;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2"
AdditionalIncludeDirectories=".\daemon\connect;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403"
MinimalRebuild="true"
BasicRuntimeChecks="3"
@@ -61,7 +61,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib Winmm.lib dbghelp.lib ssleay32MTd.lib libeay32MTd.lib regex.lib zlib.lib $(NOINHERIT)"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib Winmm.lib gdi32.lib shell32.lib dbghelp.lib ssleay32MTd.lib libeay32MTd.lib regex.lib zlib.lib $(NOINHERIT)"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@@ -115,7 +115,7 @@
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=".\daemon\connect;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2"
AdditionalIncludeDirectories=".\daemon\connect;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403"
ExceptionHandling="1"
RuntimeLibrary="0"
@@ -136,7 +136,7 @@
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib Winmm.lib ssleay32MT.lib libeay32MT.lib regex.lib zlib.lib $(NOINHERIT)"
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib gdi32.lib shell32.lib Winmm.lib ssleay32MT.lib libeay32MT.lib regex.lib zlib.lib $(NOINHERIT)"
LinkIncremental="0"
GenerateDebugInformation="true"
SubSystem="1"
@@ -480,6 +480,14 @@
RelativePath=".\daemon\windows\win32.h"
>
</File>
<File
RelativePath=".\daemon\windows\WinConsole.cpp"
>
</File>
<File
RelativePath=".\daemon\windows\WinConsole.h"
>
</File>
</Filter>
<Filter
Name="postprocess"
@@ -918,6 +926,34 @@
</File>
</Filter>
</Filter>
<Filter
Name="resources"
>
<File
RelativePath=".\windows\resources\mainicon.ico"
>
</File>
<File
RelativePath=".\windows\resources\nzbget.rc"
>
</File>
<File
RelativePath=".\windows\resources\resource.h"
>
</File>
<File
RelativePath=".\windows\resources\trayicon_idle.ico"
>
</File>
<File
RelativePath=".\windows\resources\trayicon_paused.ico"
>
</File>
<File
RelativePath=".\windows\resources\trayicon_working.ico"
>
</File>
</Filter>
</Files>
<Globals>
</Globals>

75
nzbgetd
View File

@@ -1,75 +0,0 @@
#!/bin/sh
#
# Script used to start and stop the nzbget usenet service
#
# Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
# Copyright (C) 2009-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
# --- CONFIGURATION -----------------------------------------------
# Location of the nzbget executable
NZBGET_BINARY="/usr/local/bin/nzbget"
# Additional options, e. g. config file location:
# NZBGET_OPTS="-c /mnt/hdd/tools/nzbget/conf/nzbget.conf"
NZBGET_OPTS=""
# -----------------------------------------------------------------
if [ -z "$1" ] ; then
case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
S??*) rc="start" ;;
K??*) rc="stop" ;;
*) rc="usage" ;;
esac
else
rc="$1"
fi
case "$rc" in
start)
"$NZBGET_BINARY" $NZBGET_OPTS -D
;;
stop)
"$NZBGET_BINARY" $NZBGET_OPTS -Q
;;
restart)
"$NZBGET_BINARY" $NZBGET_OPTS -Q
sleep 10 # since stop is backgrounded
"$NZBGET_BINARY" $NZBGET_OPTS -D
;;
status)
"$NZBGET_BINARY" $NZBGET_OPTS -L S
;;
pstatus)
retval=$(pgrep -l -f nzbget > /dev/null ; echo $?)
if [ "$retval" = "0" ] ; then
echo " ------- nzbget *is* running -------"
ps -Ho user,pid,cmd:32,pcpu -C nzbget
exit 0
else
echo " ------- nzbget is *not* running -------"
exit 0
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status|pstatus|usage}"
exit 1
esac
exit 0

View File

@@ -478,7 +478,15 @@ void InstallSignalHandlers()
}
} else {
int speed = [(NSNumber*)[status objectForKey:@"DownloadRate"] integerValue];
info1 = [NSString stringWithFormat:NSLocalizedString(@"Status.Downloading", nil), speed / 1024];
if (speed >= 1024 * 1024 * 10) {
info1 = [NSString stringWithFormat:NSLocalizedString(@"Status.DownloadingMB10", nil), speed / 1024 / 1024];
}
else if (speed >= 1024 * 1024) {
info1 = [NSString stringWithFormat:NSLocalizedString(@"Status.DownloadingMB", nil), (float)speed / 1024.0 / 1024.0];
}
else {
info1 = [NSString stringWithFormat:NSLocalizedString(@"Status.DownloadingKB", nil), speed / 1024];
}
preventSleep = YES;
if (speed > 0) {

View File

@@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>13.0-testing-r1011M</string>
<string>15.0-testing</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
@@ -25,7 +25,7 @@
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2007-2014 Andrey Prygunkov</string>
<string>Copyright © 2007-2015 Andrey Prygunkov</string>
<key>NSMainNibFile</key>
<string>MainApp</string>
<key>NSPrincipalClass</key>

View File

@@ -22,7 +22,9 @@
*
*/
"Status.Downloading"="Downloading at %i KB/s";
"Status.DownloadingKB"="Downloading at %i KB/s";
"Status.DownloadingMB"="Downloading at %1.1f MB/s";
"Status.DownloadingMB10"="Downloading at %i MB/s";
"Status.Post-Processing"="Post-Processing";
"Status.Fetching NZBs"="Fetching NZBs";
"Status.Fetching Feeds"="Fetching Feeds";

View File

@@ -2,7 +2,7 @@
#
# E-Mail post-processing script for NZBGet
#
# Copyright (C) 2013-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2013-2015 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,9 @@
##############################################################################
### OPTIONS ###
# When to send the message (Always, OnFailure).
#SendMail=Always
# Email address you want this email to be sent from.
#From="NZBGet" <myaccount@gmail.com>
@@ -71,10 +74,10 @@
# is usually deleted by cleanup-script and therefore is not sent.
#BrokenLog=yes
# Append post-processing log to the message (Always, Never, OnFailure).
# Append nzb log to the message (Always, Never, OnFailure).
#
# Add the post-processing log of active job.
#PostProcessLog=OnFailure
# Add the download and post-processing log of active job.
#NzbLog=OnFailure
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
@@ -93,11 +96,12 @@ except ImportError:
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_NONE=95
# Check if the script is called from nzbget 11.0 or later
if not 'NZBPP_TOTALSTATUS' in os.environ:
# Check if the script is called from nzbget 15.0 or later
if not 'NZBOP_NZBLOG' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (13.0 or later).')
print('This script is supposed to be called from nzbget (15.0 or later).')
sys.exit(POSTPROCESS_ERROR)
print('[DETAIL] Script successfully started')
@@ -122,6 +126,11 @@ if total_status == 'SUCCESS' and os.environ['NZBPP_SCRIPTSTATUS'] == 'FAILURE':
status = 'WARNING/SCRIPT'
success = total_status == 'SUCCESS'
if success and os.environ.get('NZBPO_SENDMAIL') == 'OnFailure':
print('[INFO] Skipping sending of message for successful download')
sys.exit(POSTPROCESS_NONE)
if success:
subject = 'Success for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" has successfully completed.' % (os.environ['NZBPP_NZBNAME'])
@@ -132,8 +141,8 @@ else:
text += '\nStatus: %s' % status
if os.environ.get('NZBPO_STATISTICS') == 'yes' or \
os.environ.get('NZBPO_POSTPROCESSLOG') == 'Always' or \
(os.environ.get('NZBPO_POSTPROCESSLOG') == 'OnFailure' and not success):
os.environ.get('NZBPO_NZBLOG') == 'Always' or \
(os.environ.get('NZBPO_NZBLOG') == 'OnFailure' and not success):
# To get statistics or the post-processing log we connect to NZBGet via XML-RPC.
# For more info visit http://nzbget.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
@@ -203,7 +212,7 @@ if os.environ.get('NZBPO_FILELIST') == 'yes':
text += '\n' + os.path.join(dirname, filename)[len(os.environ['NZBPP_DIRECTORY']) + 1:]
files = True
if not files:
text += '\n<no files found>'
text += '\n<no files found in the destination directory (moved by a script?)>'
# add _brokenlog.txt (if exists)
if os.environ.get('NZBPO_BROKENLOG') == 'yes':
@@ -212,22 +221,20 @@ if os.environ.get('NZBPO_BROKENLOG') == 'yes':
text += '\n\nBrokenlog:\n' + open(brokenlog, 'r').read().strip()
# add post-processing log
if os.environ.get('NZBPO_POSTPROCESSLOG') == 'Always' or \
(os.environ.get('NZBPO_POSTPROCESSLOG') == 'OnFailure' and not success):
# To get the post-processing log we 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.
if os.environ.get('NZBPO_NZBLOG') == 'Always' or \
(os.environ.get('NZBPO_NZBLOG') == 'OnFailure' and not success):
# To get the item log we connect to NZBGet via XML-RPC and call
# method "loadlog", which returns the log for a given nzb item.
# For more info visit http://nzbget.net/RPC_API_reference
# 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']
# Call remote method 'loadlog'
nzbid = int(os.environ['NZBPP_NZBID'])
log = server.loadlog(nzbid, 0, 10000)
# Now iterate through entries and save them to message text
if len(log) > 0:
text += '\n\nPost-processing log:';
text += '\n\nNzb-log:';
for entry in log:
text += '\n%s\t%s\t%s' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])

View File

@@ -2,7 +2,7 @@
#
# Logger post-processing script for NZBGet
#
# Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
# Copyright (C) 2013-2015 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,10 +26,10 @@
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Save post-processing log into a file.
# Save nzb log into a file.
#
# This script saves post-processing log of nzb-file into file
# _postprocesslog.txt in the destination directory.
# This script saves the download and post-processing log of nzb-file
# into file _nzblog.txt in the destination directory.
#
# NOTE: This script requires Python to be installed on your system.
@@ -50,20 +50,18 @@ 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:
# Check if the script is called from nzbget 15.0 or later
if not 'NZBOP_NZBLOG' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (11.0 or later).')
print('This script is supposed to be called from nzbget (15.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.
# To get the item log we connect to NZBGet via XML-RPC and call
# method "loadlog", which returns the log for a given nzb item.
# For more info visit http://nzbget.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
@@ -82,15 +80,13 @@ 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']
# Call remote method 'loadlog'
nzbid = int(os.environ['NZBPP_NZBID'])
log = server.loadlog(nzbid, 0, 10000)
# 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')
f = open('%s/_nzblog.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()

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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,6 +39,7 @@ var Options = (new function($)
this.options;
this.postParamConfig;
this.categories = [];
this.restricted = false;
// State
var _this = this;
@@ -62,6 +63,7 @@ var Options = (new function($)
RPC.call('config', [], function(_options) {
_this.options = _options;
initCategories();
_this.restricted = _this.option('ControlPort') === '***';
// loading config templates and build list of post-processing parameters
_this.postParamConfig = [];
@@ -643,7 +645,7 @@ var Config = (new function($)
/*** GENERATE HTML PAGE *****************************************************************/
function buildOptionsContent(section)
function buildOptionsContent(section, extensionsec)
{
var html = '';
@@ -653,6 +655,13 @@ var Config = (new function($)
for (var i=0, op=0; i < section.options.length; i++)
{
if (i > 0 && extensionsec && Options.restricted)
{
// in restricted mode don't show any options for extension scripts,
// option's content is hidden content anyway (***)
break;
}
var option = section.options[i];
if (!option.template)
{
@@ -867,14 +876,23 @@ var Config = (new function($)
if (hasoptions)
{
html += '<div class="' + section.id + ' multiid' + multiid + ' multiset">';
html += '<button type="button" class="btn config-delete" data-multiid="' + multiid + ' multiset" ' +
html += '<div class="' + section.id + ' multiid' + multiid + ' multiset multiset-toolbar">';
html += '<button type="button" class="btn config-button config-delete" data-multiid="' + multiid + '" ' +
'onclick="Config.deleteSet(this, \'' + setname + '\',\'' + section.id + '\')">Delete ' + setname + multiid + '</button>';
html += ' <button type="button" class="btn config-button" data-multiid="' + multiid + '" ' +
'onclick="Config.moveSet(this, \'' + setname + '\',\'' + section.id + '\', \'up\')">Move Up</button>';
html += ' <button type="button" class="btn config-button" data-multiid="' + multiid + '" ' +
'onclick="Config.moveSet(this, \'' + setname + '\',\'' + section.id + '\', \'down\')">Move Down</button>';
if (setname.toLowerCase() === 'feed')
{
html += ' <button type="button" class="btn config-previewfeed config-feed" data-multiid="' + multiid + ' multiset" ' +
html += ' <button type="button" class="btn config-button" data-multiid="' + multiid + '" ' +
'onclick="Config.previewFeed(this, \'' + setname + '\',\'' + section.id + '\')">Preview Feed</button>';
}
if (setname.toLowerCase() === 'server')
{
html += ' <button type="button" class="btn config-button" data-multiid="' + multiid + '" ' +
'onclick="Config.testConnection(this, \'' + setname + '\',\'' + section.id + '\')">Test Connection</button>';
}
html += '<hr>';
html += '</div>';
}
@@ -917,7 +935,7 @@ var Config = (new function($)
{
var html = $('<li><a href="#' + section.id + '">' + section.name + '</a></li>');
$ConfigNav.append(html);
var content = buildOptionsContent(section);
var content = buildOptionsContent(section, k > 0);
$ConfigData.append(content);
added = true;
}
@@ -1209,37 +1227,57 @@ var Config = (new function($)
function reformatSection(section, setname)
{
var oldMultiId = -1;
var newMultiId = 0;
var hasOptions = false;
var lastMultiId = 0;
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template)
{
if (option.multiid !== oldMultiId)
if (option.multiid !== lastMultiId && option.multiid !== lastMultiId + 1)
{
oldMultiId = option.multiid;
newMultiId++;
// reformat multiid
var div = $('#' + setname + oldMultiId);
div.attr('id', setname + newMultiId);
// update captions
$('.config-settitle.' + section.id + '.multiid' + oldMultiId, $ConfigData).text(setname + newMultiId);
$('.' + section.id + '.multiid' + oldMultiId + ' .config-multicaption', $ConfigData).text(setname + newMultiId + '.');
$('.' + section.id + '.multiid' + oldMultiId + ' .config-delete', $ConfigData).text('Delete ' + setname + newMultiId).attr('data-multiid', newMultiId);
$('.' + section.id + '.multiid' + oldMultiId + ' .config-feed', $ConfigData).attr('data-multiid', newMultiId);
//update class
$('.' + section.id + '.multiid' + oldMultiId, $ConfigData).removeClass('multiid' + oldMultiId).addClass('multiid' + newMultiId);
reformatSet(section, setname, option.multiid, lastMultiId + 1);
}
lastMultiId = option.multiid;
hasOptions = true;
}
}
// update add-button
var addButton = $('.config-add.' + section.id, $ConfigData);
addButton.text('Add ' + (hasOptions ? 'another ' : '') + setname);
}
function reformatSet(section, setname, oldMultiId, newMultiId)
{
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template && option.multiid == oldMultiId)
{
// reformat multiid
var div = $('#' + setname + oldMultiId);
div.attr('id', setname + newMultiId);
// update captions
$('.config-settitle.' + section.id + '.multiid' + oldMultiId, $ConfigData).text(setname + newMultiId);
$('.' + section.id + '.multiid' + oldMultiId + ' .config-multicaption', $ConfigData).text(setname + newMultiId + '.');
$('.' + section.id + '.multiid' + oldMultiId + ' .config-delete', $ConfigData).text('Delete ' + setname + newMultiId);
//update data id
$('.' + section.id + '.multiid' + oldMultiId + ' .config-button', $ConfigData).attr('data-multiid', newMultiId);
//update class
$('.' + section.id + '.multiid' + oldMultiId, $ConfigData).removeClass('multiid' + oldMultiId).addClass('multiid' + newMultiId);
// update input id
var oldFormId = option.formId;
option.formId = option.formId.replace(new RegExp(option.multiid), newMultiId);
$('#' + oldFormId).attr('id', option.formId);
// update label data-optid
$('a[data-optid=' + oldFormId + ']').attr('data-optid', option.formId);
// update editor id
$('#' + oldFormId + '_Editor').attr('id', option.formId + '_Editor');
@@ -1249,12 +1287,8 @@ var Config = (new function($)
option.multiid = newMultiId;
}
}
// update add-button
var addButton = $('.config-add.' + section.id, $ConfigData);
addButton.text('Add ' + (newMultiId > 0 ? 'another ' : '') + setname);
}
this.addSet = function(setname, sectionId)
{
// find section
@@ -1322,6 +1356,31 @@ var Config = (new function($)
});
}
this.moveSet = function(control, setname, sectionId, direction)
{
var id1 = parseInt($(control).attr('data-multiid'));
var id2 = direction === 'down' ? id1 + 1 : id1 - 1;
// swap options in two sets
var opts1 = $('.' + sectionId + '.multiid' + (direction === 'down' ? id1 : id2), $ConfigData);
var opts2 = $('.' + sectionId + '.multiid' + (direction === 'down' ? id2 : id1), $ConfigData);
if (opts1.length === 0 || opts2.length === 0)
{
return;
}
opts1.first().before(opts2);
// reformat remaining sets (captions, input IDs, etc.)
var section = findSectionById(sectionId);
reformatSet(section, setname, id2, 10000 + id2);
reformatSet(section, setname, id1, id2);
reformatSet(section, setname, 10000 + id2, id1);
section.modified = true;
}
this.viewMode = function()
{
compactMode = !compactMode;
@@ -1411,6 +1470,56 @@ var Config = (new function($)
getOptionValue(findOptionByName('Feed' + multiid + '.Priority')));
}
/*** TEST SERVER ********************************************************************/
var connecting = false;
this.testConnection = function(control, setname, sectionId)
{
if (connecting)
{
return;
}
connecting = true;
$('#Notif_Config_TestConnectionProgress').fadeIn(function() {
var multiid = parseInt($(control).attr('data-multiid'));
var timeout = Math.min(parseInt(getOptionValue(findOptionByName('ArticleTimeout'))), 10);
RPC.call('testserver', [
getOptionValue(findOptionByName('Server' + multiid + '.Host')),
parseInt(getOptionValue(findOptionByName('Server' + multiid + '.Port'))),
getOptionValue(findOptionByName('Server' + multiid + '.Username')),
getOptionValue(findOptionByName('Server' + multiid + '.Password')),
getOptionValue(findOptionByName('Server' + multiid + '.Encryption')) === 'yes',
getOptionValue(findOptionByName('Server' + multiid + '.Cipher')),
timeout
],
function(errtext) {
$('#Notif_Config_TestConnectionProgress').fadeOut(function() {
if (errtext == '')
{
Notification.show('#Notif_Config_TestConnectionOK');
}
else
{
AlertDialog.showModal('Connection test failed', errtext);
}
});
connecting = false;
},
function(message, resultObj) {
$('#Notif_Config_TestConnectionProgress').fadeOut(function() {
if (resultObj && resultObj.error && resultObj.error.message)
{
message = resultObj.error.message;
}
AlertDialog.showModal('Connection test failed', message);
connecting = false;
});
});
});
}
/*** SAVE ********************************************************************/
function getOptionValue(option)
@@ -2445,6 +2554,7 @@ var UpdateDialog = (new function($)
var UpdateInfo;
var lastUpTimeSec;
var installing = false;
var logReceived = false;
this.init = function()
{
@@ -2487,7 +2597,7 @@ var UpdateDialog = (new function($)
$UpdateDialog.modal({backdrop: 'static'});
RPC.call('readurl', ['http://nzbget.net/info/nzbget-version.php?nocache=' + new Date().getTime(), 'version info'], loadedUpstreamInfo, error);
RPC.call('readurl', ['http://nzbget.net/info/nzbget-version.json?nocache=' + new Date().getTime(), 'version info'], loadedUpstreamInfo, error);
}
function error(e)
@@ -2651,7 +2761,7 @@ var UpdateDialog = (new function($)
if (!script)
{
alert('Something is wrong with a package configuration file "package-info.json".');
alert('Something is wrong with the package configuration file "package-info.json".');
return;
}
@@ -2687,16 +2797,24 @@ var UpdateDialog = (new function($)
{
RPC.call('logupdate', [0, 100], function(data)
{
updateLogTable(data);
setTimeout(updateLog, 500);
},
function()
{
// rpc-failure: the program has been terminated. Waiting for new instance.
setLogContentAndScroll($UpdateProgressDialog_Log.html() + '\n' + 'NZBGet has been terminated. Waiting for restart...');
setTimeout(checkStatus, 500);
},
1000);
logReceived = logReceived || data.length > 0;
if (logReceived && data.length === 0)
{
terminated();
}
else
{
updateLogTable(data);
setTimeout(updateLog, 500);
}
}, terminated);
}
function terminated()
{
// rpc-failure: the program has been terminated. Waiting for new instance.
setLogContentAndScroll($UpdateProgressDialog_Log.html() + '\n' + 'NZBGet has been terminated. Waiting for restart...');
setTimeout(checkStatus, 500);
}
function setLogContentAndScroll(html)
@@ -2734,7 +2852,10 @@ var UpdateDialog = (new function($)
{
// the old instance is not restarted yet
// waiting 0.5 sec. and retrying
setTimeout(checkStatus, 500);
if ($('#UpdateProgressDialog').is(':visible'))
{
setTimeout(checkStatus, 500);
}
}
else
{
@@ -2749,9 +2870,11 @@ var UpdateDialog = (new function($)
function()
{
// Failure, waiting 0.5 sec. and retrying
setTimeout(checkStatus, 500);
},
1000);
if ($('#UpdateProgressDialog').is(':visible'))
{
setTimeout(checkStatus, 500);
}
});
}
}(jQuery));

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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
@@ -79,7 +79,7 @@ var Downloads = (new function($)
$DownloadsRecordsPerPage = $('#DownloadsRecordsPerPage');
$DownloadsTable_Name = $('#DownloadsTable_Name');
var recordsPerPage = UISettings.read('$DownloadsRecordsPerPage', 10);
var recordsPerPage = UISettings.read('DownloadsRecordsPerPage', 10);
$DownloadsRecordsPerPage.val(recordsPerPage);
$DownloadsTable.fasttable(
@@ -100,8 +100,8 @@ var Downloads = (new function($)
});
$DownloadsTable.on('click', 'a', itemClick);
$DownloadsTable.on('click', 'tbody div.check',
function(event) { $DownloadsTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
$DownloadsTable.on('click', UISettings.rowSelect ? 'tbody tr' : 'tbody div.check',
function(event) { $DownloadsTable.fasttable('itemCheckClick', UISettings.rowSelect ? this : this.parentNode.parentNode, event); });
$DownloadsTable.on('click', 'thead div.check',
function() { $DownloadsTable.fasttable('titleCheckClick') });
$DownloadsTable.on('mousedown', Util.disableShiftMouseDown);
@@ -109,7 +109,7 @@ var Downloads = (new function($)
this.applyTheme = function()
{
$DownloadsTable.fasttable('setPageSize', UISettings.read('$DownloadsRecordsPerPage', 10),
$DownloadsTable.fasttable('setPageSize', UISettings.read('DownloadsRecordsPerPage', 10),
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
}
@@ -120,7 +120,7 @@ var Downloads = (new function($)
$('#DownloadsTable_Category').css('width', DownloadsUI.calcCategoryColumnWidth());
}
RPC.call('listgroups', [100], groups_loaded);
RPC.call('listgroups', [], groups_loaded);
}
function groups_loaded(_groups)
@@ -303,7 +303,7 @@ var Downloads = (new function($)
this.recordsPerPageChange = function()
{
var val = $DownloadsRecordsPerPage.val();
UISettings.write('$DownloadsRecordsPerPage', val);
UISettings.write('DownloadsRecordsPerPage', val);
$DownloadsTable.fasttable('setPageSize', val);
}
@@ -334,6 +334,7 @@ var Downloads = (new function($)
function itemClick(e)
{
e.preventDefault();
e.stopPropagation();
var nzbid = $(this).attr('data-nzbid');
var area = $(this).attr('data-area');
$(this).blur();
@@ -352,7 +353,7 @@ var Downloads = (new function($)
/*** CHECKMARKS ******************************************************/
function checkBuildEditIDList(allowPostProcess, allowUrl)
function checkBuildEditIDList(allowPostProcess, allowUrl, allowEmpty)
{
var checkedRows = $DownloadsTable.fasttable('checkedRows');
@@ -378,7 +379,7 @@ var Downloads = (new function($)
}
}
if (checkedEditIDs.length === 0)
if (checkedEditIDs.length === 0 && !allowEmpty)
{
Notification.show('#Notif_Downloads_Select');
return null;
@@ -546,6 +547,13 @@ var Downloads = (new function($)
notification = '';
RPC.call('editqueue', [EditAction, EditOffset, '', checkedEditIDs], editCompleted);
}
this.sort = function(order)
{
var checkedEditIDs = checkBuildEditIDList(true, true, true);
notification = '#Notif_Downloads_Sorted';
RPC.call('editqueue', ['GroupSort', 0, order, checkedEditIDs], editCompleted);
}
}(jQuery));
@@ -703,26 +711,13 @@ var DownloadsUI = (new function($)
{
switch (group.Status)
{
case "REPAIRING":
break;
case "LOADING_PARS":
case "VERIFYING_SOURCES":
case "VERIFYING_REPAIRED":
case "UNPACKING":
case "RENAMING":
text = group.PostInfoText;
break;
case "EXECUTING_SCRIPT":
if (group.Log && group.Log.length > 0)
{
text = group.Log[group.Log.length-1].Text;
// remove "for <nzb-name>" from label text
text = text.replace(' for ' + group.NZBName, ' ');
}
else
{
text = group.PostInfoText;
}
text = group.PostInfoText;
break;
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -39,7 +39,6 @@ var DownloadsEditDialog = (new function($)
// Controls
var $DownloadsEditDialog;
var $DownloadsLogTable;
var $DownloadsFileTable;
var $DownloadsEdit_ParamData;
var $ServStatsTable;
@@ -70,17 +69,7 @@ var DownloadsEditDialog = (new function($)
$('#DownloadsEdit_Back').click(backClick);
$('#DownloadsEdit_Category').change(categoryChange);
$DownloadsLogTable = $('#DownloadsEdit_LogTable');
$DownloadsLogTable.fasttable(
{
filterInput: '#DownloadsEdit_LogTable_filter',
pagerContainer: '#DownloadsEdit_LogTable_pager',
filterCaseSensitive: false,
pageSize: 100,
maxPages: 3,
hasHeader: true,
renderCellCallback: logTableRenderCellCallback
});
LogTab.init('Downloads');
$DownloadsFileTable = $('#DownloadsEdit_FileTable');
$DownloadsFileTable.fasttable(
@@ -105,8 +94,8 @@ var DownloadsEditDialog = (new function($)
renderCellCallback: EditUI.servStatsTableRenderCellCallback
});
$DownloadsFileTable.on('click', 'tbody div.check',
function(event) { $DownloadsFileTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
$DownloadsFileTable.on('click', UISettings.rowSelect ? 'tbody tr' : 'tbody div.check',
function(event) { $DownloadsFileTable.fasttable('itemCheckClick', UISettings.rowSelect ? this : this.parentNode.parentNode, event); });
$DownloadsFileTable.on('click', 'thead div.check',
function() { $DownloadsFileTable.fasttable('titleCheckClick') });
$DownloadsFileTable.on('mousedown', Util.disableShiftMouseDown);
@@ -114,7 +103,7 @@ var DownloadsEditDialog = (new function($)
$DownloadsEditDialog.on('hidden', function()
{
// cleanup
$DownloadsLogTable.fasttable('update', []);
LogTab.reset('Downloads');
$DownloadsFileTable.fasttable('update', []);
$DownloadsEdit_ParamData.empty();
clearTimeout(refreshTimer);
@@ -225,7 +214,6 @@ var DownloadsEditDialog = (new function($)
$('#DownloadsEdit_DupeScore').val(group.DupeScore);
$('#DownloadsEdit_DupeMode').val(group.DupeMode);
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
var postParamConfig = ParamTab.createPostParamConfig();
@@ -243,7 +231,7 @@ var DownloadsEditDialog = (new function($)
var dupeCheck = Options.option('DupeCheck') === 'yes';
Util.show('#DownloadsEdit_Dupe', dupeCheck);
var postParam = postParamConfig[0].options.length > 0 && group.Kind === 'NZB';
var postLog = group.postprocess && group.Log.length > 0;
var postLog = group.MessageCount > 0;
Util.show('#DownloadsEdit_Param', postParam);
Util.show('#DownloadsEdit_Log', postLog);
@@ -291,8 +279,8 @@ var DownloadsEditDialog = (new function($)
$DownloadsEditDialog.restoreTab();
$('#DownloadsEdit_FileTable_filter').val('');
$('#DownloadsEdit_LogTable_filter').val('');
$('#DownloadsEdit_LogTable_pagerBlock').hide();
LogTab.reset('Downloads');
files = null;
logFilled = false;
@@ -359,10 +347,10 @@ var DownloadsEditDialog = (new function($)
}
}});
if (tab === '#DownloadsEdit_LogTab' && !logFilled && curGroup.postprocess &&
curGroup.Log && curGroup.Log.length > 0)
if (tab === '#DownloadsEdit_LogTab' && !logFilled && (curGroup.postprocess || curGroup.MessageCount > 0))
{
fillLog();
LogTab.fill('Downloads', curGroup);
logFilled = true;
}
if (tab === '#DownloadsEdit_FileTab' && files === null)
@@ -576,64 +564,6 @@ var DownloadsEditDialog = (new function($)
:saveParam();
}
/*** TAB: LOG *************************************************************************/
function fillLog()
{
logFilled = true;
var data = [];
for (var i=0; i < curGroup.Log.length; i++)
{
var message = curGroup.Log[i];
var kind;
switch (message.Kind)
{
case 'INFO': kind = '<span class="label label-status label-success">info</span>'; break;
case 'DETAIL': kind = '<span class="label label-status label-info">detail</span>'; break;
case 'WARNING': kind = '<span class="label label-status label-warning">warning</span>'; break;
case 'ERROR': kind = '<span class="label label-status label-important">error</span>'; break;
case 'DEBUG': kind = '<span class="label label-status">debug</span>'; break;
}
var text = Util.textToHtml(message.Text);
var time = Util.formatDateTime(message.Time + UISettings.timeZoneCorrection*60*60);
var fields;
if (!UISettings.miniTheme)
{
fields = [kind, time, text];
}
else
{
var info = kind + ' <span class="label">' + time + '</span> ' + text;
fields = [info];
}
var item =
{
id: message,
fields: fields,
search: message.Kind + ' ' + time + ' ' + message.Text
};
data.unshift(item);
}
$DownloadsLogTable.fasttable('update', data);
$DownloadsLogTable.fasttable('setCurPage', 1);
Util.show('#DownloadsEdit_LogTable_pagerBlock', data.length > 100);
}
function logTableRenderCellCallback(cell, index, item)
{
if (index === 0)
{
cell.width = '65px';
}
}
/*** TAB: FILES *************************************************************************/
function fillFiles()
@@ -805,7 +735,7 @@ var DownloadsEditDialog = (new function($)
}
break;
case 'split':
if (file.ActiveDownloads > 0 || file.FileSizeLo !== file.RemainingSizeLo)
if (file.ActiveDownloads > 0 || file.Progress > 0)
{
splitError = true;
}
@@ -1157,6 +1087,114 @@ var ParamTab = (new function($)
}(jQuery));
/*** LOG TAB FOR EDIT DIALOGS ************************************************************/
var LogTab = (new function($)
{
'use strict'
this.init = function(name)
{
var recordsPerPage = UISettings.read('ItemLogRecordsPerPage', 10);
$('#' + name + 'LogRecordsPerPage').val(recordsPerPage);
var $LogTable = $('#' + name + 'Edit_LogTable');
$LogTable.fasttable(
{
filterInput: '#' + name + 'Edit_LogTable_filter',
pagerContainer: '#' + name + 'Edit_LogTable_pager',
filterCaseSensitive: false,
pageSize: recordsPerPage,
maxPages: 3,
hasHeader: true,
renderCellCallback: logTableRenderCellCallback
});
}
this.reset = function(name)
{
var $LogTable = $('#' + name + 'Edit_LogTable');
$LogTable.fasttable('update', []);
$('#' + name + 'Edit_LogTable_filter').val('');
}
this.fill = function(name, item)
{
function logLoaded(log)
{
$('#' + name + 'EditDialog .loading-block').hide();
var $LogTable = $('#' + name + 'Edit_LogTable');
var data = [];
for (var i=0; i < log.length; i++)
{
var message = log[i];
var kind;
switch (message.Kind)
{
case 'INFO': kind = '<span class="label label-status label-success">info</span>'; break;
case 'DETAIL': kind = '<span class="label label-status label-info">detail</span>'; break;
case 'WARNING': kind = '<span class="label label-status label-warning">warning</span>'; break;
case 'ERROR': kind = '<span class="label label-status label-important">error</span>'; break;
case 'DEBUG': kind = '<span class="label label-status">debug</span>'; break;
}
var text = Util.textToHtml(message.Text);
var time = Util.formatDateTime(message.Time + UISettings.timeZoneCorrection*60*60);
var fields;
if (!UISettings.miniTheme)
{
fields = [kind, time, text];
}
else
{
var info = kind + ' <span class="label">' + time + '</span> ' + text;
fields = [info];
}
var item =
{
id: message,
fields: fields,
search: message.Kind + ' ' + time + ' ' + message.Text
};
data.unshift(item);
}
$LogTable.fasttable('update', data);
$LogTable.fasttable('setCurPage', 1);
}
var recordsPerPage = UISettings.read('ItemLogRecordsPerPage', 10);
$('#' + name + 'LogRecordsPerPage').val(recordsPerPage);
$('#' + name + 'EditDialog .loading-block').show();
RPC.call('loadlog', [item.NZBID, 0, 1000], logLoaded);
}
function logTableRenderCellCallback(cell, index, item)
{
if (index === 0)
{
cell.width = '65px';
}
}
this.recordsPerPageChange = function(name)
{
var val = $('#' + name + 'LogRecordsPerPage').val();
UISettings.write('ItemLogRecordsPerPage', val);
var $LogTable = $('#' + name + 'Edit_LogTable');
$LogTable.fasttable('setPageSize', val);
}
}(jQuery));
/*** DOWNLOAD MULTI EDIT DIALOG ************************************************************/
var DownloadsMultiDialog = (new function($)
@@ -1497,6 +1535,7 @@ var HistoryEditDialog = (new function()
var lastPage;
var lastFullscreen;
var saveCompleted;
var logFilled;
this.init = function()
{
@@ -1508,11 +1547,14 @@ var HistoryEditDialog = (new function()
$('#HistoryEdit_Return, #HistoryEdit_ReturnURL').click(itemReturn);
$('#HistoryEdit_Reprocess').click(itemReprocess);
$('#HistoryEdit_Redownload').click(itemRedownload);
$('#HistoryEdit_Param, #HistoryEdit_Dupe').click(tabClick);
$('#HistoryEdit_Param, #HistoryEdit_Dupe, #HistoryEdit_Log').click(tabClick);
$('#HistoryEdit_Back').click(backClick);
$('#HistoryEdit_MarkSuccess').click(itemSuccess);
$('#HistoryEdit_MarkGood').click(itemGood);
$('#HistoryEdit_MarkBad').click(itemBad);
LogTab.init('History');
$ServStatsTable = $('#HistoryEdit_ServStatsTable');
$ServStatsTable.fasttable(
{
@@ -1527,6 +1569,7 @@ var HistoryEditDialog = (new function()
$HistoryEditDialog.on('hidden', function ()
{
$HistoryEdit_ParamData.empty();
LogTab.reset('History');
// resume updates
Refresher.resume();
});
@@ -1603,14 +1646,23 @@ var HistoryEditDialog = (new function()
(hist.Kind === 'DUP' ? 'hidden' : hist.Kind) + '</span>');
}
$('#HistoryEdit_NZBName').val(hist.Name);
if (hist.Kind !== 'DUP')
{
$('#HistoryEdit_Category').text(hist.Category);
// Category
var v = $('#HistoryEdit_Category');
DownloadsUI.fillCategoryCombo(v);
v.val(hist.Category);
if (v.val() != hist.Category)
{
v.append($('<option selected="selected"></option>').text(hist.Category));
}
}
if (hist.Kind === 'NZB')
{
$('#HistoryEdit_Path').text(hist.FinalDir !== '' ? hist.FinalDir : hist.DestDir);
$('#HistoryEdit_Path').val(hist.FinalDir !== '' ? hist.FinalDir : hist.DestDir);
var size = Util.formatSizeMB(hist.FileSizeMB, hist.FileSizeLo);
var completion = hist.SuccessArticles + hist.FailedArticles > 0 ? Util.round0(hist.SuccessArticles * 100.0 / (hist.SuccessArticles + hist.FailedArticles)) + '%' : '--';
@@ -1655,6 +1707,8 @@ var HistoryEditDialog = (new function()
Util.show('#HistoryEdit_CategoryGroup', hist.Kind !== 'DUP');
Util.show('#HistoryEdit_DupGroup', hist.Kind === 'DUP');
var dupeCheck = Options.option('DupeCheck') === 'yes';
Util.show('#HistoryEdit_MarkSuccess', dupeCheck && ((hist.Kind === 'NZB' && hist.MarkStatus !== 'SUCCESS') || (hist.Kind === 'DUP' && hist.DupStatus !== 'SUCCESS')) &&
hist.Status.substr(0, 7) !== 'SUCCESS');
Util.show('#HistoryEdit_MarkGood', dupeCheck && ((hist.Kind === 'NZB' && hist.MarkStatus !== 'GOOD') || (hist.Kind === 'DUP' && hist.DupStatus !== 'GOOD')));
Util.show('#HistoryEdit_MarkBad', dupeCheck && hist.Kind !== 'URL');
Util.show('#HistoryEdit_Dupe', dupeCheck);
@@ -1672,6 +1726,9 @@ var HistoryEditDialog = (new function()
postParams = ParamTab.buildPostParamTab($HistoryEdit_ParamData, postParamConfig, curHist.Parameters);
}
var postLog = hist.MessageCount > 0;
Util.show('#HistoryEdit_Log', postLog);
EditUI.buildDNZBLinks(curHist.Parameters ? curHist.Parameters : [], 'HistoryEdit_DNZB');
enableAllButtons();
@@ -1681,10 +1738,14 @@ var HistoryEditDialog = (new function()
$('#HistoryEdit_ServStatsTab').hide();
$('#HistoryEdit_TimeStatsTab').hide();
$('#HistoryEdit_DupeTab').hide();
$('#HistoryEdit_LogTab').hide();
$('#HistoryEdit_Back').hide();
$('#HistoryEdit_BackSpace').show();
$HistoryEditDialog.restoreTab();
LogTab.reset('History');
logFilled = false;
notification = null;
$HistoryEditDialog.modal({backdrop: 'static'});
@@ -1754,6 +1815,12 @@ var HistoryEditDialog = (new function()
$HistoryEditDialog.switchTab($('#HistoryEdit_GeneralTab'), lastPage,
e.shiftKey || !UISettings.slideAnimation ? 0 : 500,
{fullscreen: lastFullscreen, mini: UISettings.miniTheme});
if (tab === '#HistoryEdit_LogTab' && !logFilled && curHist.MessageCount > 0)
{
LogTab.fill('History', curHist);
logFilled = true;
}
}
function backClick(e)
@@ -1811,7 +1878,8 @@ var HistoryEditDialog = (new function()
e.preventDefault();
if (curHist.SuccessArticles > 0)
{
ConfirmDialog.showModal('HistoryEditRedownloadConfirmDialog', doItemRedownload);
ConfirmDialog.showModal('HistoryEditRedownloadConfirmDialog', doItemRedownload,
function () { HistoryUI.confirmMulti(false); });
}
else
{
@@ -1836,7 +1904,7 @@ var HistoryEditDialog = (new function()
function reprocess()
{
notification = '#Notif_History_Reproces';
notification = '#Notif_History_Reprocess';
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
}
@@ -1857,13 +1925,50 @@ var HistoryEditDialog = (new function()
disableAllButtons();
notification = null;
saveCompleted = completed;
saveDupeKey();
saveName();
}
function saveName()
{
var name = $('#HistoryEdit_NZBName').val();
name !== curHist.Name && !curHist.postprocess ?
RPC.call('editqueue', ['HistorySetName', 0, name, [curHist.ID]], function()
{
notification = '#Notif_History_Saved';
saveCategory();
})
:saveCategory();
}
function saveCategory()
{
var category = $('#HistoryEdit_Category').val();
category !== curHist.Category && curHist.Kind !== 'DUP' ?
RPC.call('editqueue', ['HistorySetCategory', 0, category, [curHist.ID]], function()
{
notification = '#Notif_History_Saved';
saveDupeKey();
})
: saveDupeKey();
}
function itemSuccess(e)
{
e.preventDefault();
ConfirmDialog.showModal('HistoryEditSuccessConfirmDialog', doItemSuccess, function () { HistoryUI.confirmMulti(false); });
}
function doItemSuccess()
{
disableAllButtons();
notification = '#Notif_History_Marked';
RPC.call('editqueue', ['HistoryMarkSuccess', 0, '', [curHist.ID]], completed);
}
function itemGood(e)
{
e.preventDefault();
ConfirmDialog.showModal('HistoryEditGoodConfirmDialog', doItemGood);
ConfirmDialog.showModal('HistoryEditGoodConfirmDialog', doItemGood, function () { HistoryUI.confirmMulti(false); });
}
function doItemGood()
@@ -1876,7 +1981,7 @@ var HistoryEditDialog = (new function()
function itemBad(e)
{
e.preventDefault();
ConfirmDialog.showModal('HistoryEditBadConfirmDialog', doItemBad);
ConfirmDialog.showModal('HistoryEditBadConfirmDialog', doItemBad, function () { HistoryUI.confirmMulti(false); });
}
function doItemBad()

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-2015 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
@@ -60,10 +60,13 @@ var Feeds = (new function($)
{
var item = menuItemTemplate.clone();
var name = Options.option('Feed' + i + '.Name');
var a = $('a', item);
var a = $('span', item);
a.text(name !== '' ? name : 'Feed' + i);
a.attr('data-id', i);
a.click(viewFeed);
var im = $('button', item);
im.click(fetchFeed);
im.attr('data-id', i);
insertPos.before(item);
}
}
@@ -77,11 +80,20 @@ var Feeds = (new function($)
FeedDialog.showModal(id);
}
function fetchFeed()
{
var id = parseInt($(this).attr('data-id'));
RPC.call('fetchfeed', [id], function()
{
Notification.show('#Notif_Feeds_Fetch');
});
}
this.fetchAll = function()
{
RPC.call('fetchfeed', [0], function()
{
Notification.show('#Notif_Feeds_FetchAll');
Notification.show('#Notif_Feeds_Fetch');
});
}
}(jQuery));

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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
@@ -78,8 +78,8 @@ var History = (new function($)
});
$HistoryTable.on('click', 'a', editClick);
$HistoryTable.on('click', 'tbody div.check',
function(event) { $HistoryTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
$HistoryTable.on('click', UISettings.rowSelect ? 'tbody tr' : 'tbody div.check',
function(event) { $HistoryTable.fasttable('itemCheckClick', UISettings.rowSelect ? this : this.parentNode.parentNode, event); });
$HistoryTable.on('click', 'thead div.check',
function() { $HistoryTable.fasttable('titleCheckClick') });
$HistoryTable.on('mousedown', Util.disableShiftMouseDown);
@@ -269,7 +269,7 @@ var History = (new function($)
}
}
this.deleteClick = function()
this.actionClick = function(action)
{
var checkedRows = $HistoryTable.fasttable('checkedRows');
if (checkedRows.length == 0)
@@ -279,6 +279,7 @@ var History = (new function($)
}
var hasNzb = false;
var hasUrl = false;
var hasDup = false;
var hasFailed = false;
for (var i = 0; i < history.length; i++)
@@ -287,23 +288,73 @@ var History = (new function($)
if (checkedRows.indexOf(hist.ID) > -1)
{
hasNzb |= hist.Kind === 'NZB';
hasUrl |= hist.Kind === 'URL';
hasDup |= hist.Kind === 'DUP';
hasFailed |= hist.ParStatus === 'FAILURE' || hist.UnpackStatus === 'FAILURE';
}
}
HistoryUI.deleteConfirm(historyDelete, hasNzb, hasDup, hasFailed, true);
switch (action)
{
case 'DELETE':
notification = '#Notif_History_Deleted';
HistoryUI.deleteConfirm(historyAction, hasNzb, hasDup, hasFailed, true);
break;
case 'REPROCESS':
if (hasUrl || hasDup)
{
Notification.show('#Notif_History_CantReprocess');
return;
}
notification = '#Notif_History_Reprocess';
historyAction('HistoryProcess');
break;
case 'REDOWNLOAD':
if (hasDup)
{
Notification.show('#Notif_History_CantRedownload');
return;
}
notification = '#Notif_History_Returned';
ConfirmDialog.showModal('HistoryEditRedownloadConfirmDialog',
function () { historyAction('HistoryRedownload') },
function () { HistoryUI.confirmMulti(checkedRows.length > 1); });
break;
case 'MARKSUCCESS':
case 'MARKGOOD':
case 'MARKBAD':
if (hasUrl)
{
Notification.show('#Notif_History_CantMark');
return;
}
notification = '#Notif_History_Marked';
ConfirmDialog.showModal(action === 'MARKSUCCESS' ? 'HistoryEditSuccessConfirmDialog' :
action === 'MARKGOOD' ? 'HistoryEditGoodConfirmDialog' : 'HistoryEditBadConfirmDialog',
function () // action
{
historyAction(action === 'MARKSUCCESS' ? 'HistoryMarkSuccess' :
action === 'MARKGOOD' ? 'HistoryMarkGood' :'HistoryMarkBad');
},
function (_dialog) // init
{
HistoryUI.confirmMulti(checkedRows.length > 1);
}
);
break;
}
}
function historyDelete(command)
function historyAction(command)
{
Refresher.pause();
var IDs = $HistoryTable.fasttable('checkedRows');
RPC.call('editqueue', [command, 0, '', [IDs]], function()
{
notification = '#Notif_History_Deleted';
editCompleted();
});
}
@@ -321,6 +372,7 @@ var History = (new function($)
function editClick(e)
{
e.preventDefault();
e.stopPropagation();
var histid = $(this).attr('histid');
$(this).blur();
@@ -456,14 +508,7 @@ var HistoryUI = (new function($)
function init(_dialog)
{
dialog = _dialog;
if (!multi)
{
var html = $('#ConfirmDialog_Text').html();
html = html.replace(/records/g, 'record');
$('#ConfirmDialog_Text').html(html);
}
HistoryUI.confirmMulti(multi);
$('#HistoryDeleteConfirmDialog_Hide', dialog).prop('checked', true);
Util.show($('#HistoryDeleteConfirmDialog_Options', dialog), hasNzb && dupeCheck);
Util.show($('#HistoryDeleteConfirmDialog_Simple', dialog), !(hasNzb && dupeCheck));
@@ -483,5 +528,15 @@ var HistoryUI = (new function($)
ConfirmDialog.showModal('HistoryDeleteConfirmDialog', action, init);
}
this.confirmMulti = function(multi)
{
if (multi === undefined || !multi)
{
var html = $('#ConfirmDialog_Text').html();
html = html.replace(/records/g, 'record');
html = html.replace(/nzbs/g, 'nzb');
$('#ConfirmDialog_Text').html(html);
}
}
}(jQuery));

View File

@@ -2,7 +2,7 @@
<!--
* This file is part of nzbget
*
* Copyright (C) 2012-2014 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012-2015 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
@@ -252,7 +252,7 @@
<button class="btn dropdown-toggle" data-toggle="dropdown" title="RSS Feeds"><i class="icon-rss"></i><span class="btn-caption"> Feeds </span><span class="phone-only inline">&nbsp;</span><span class="caret"></span></button>
<ul class="dropdown-menu" id="RssMenu">
<li class="menu-header">RSS Feeds</li>
<li class="feed-menu-template hide"><a href="#">Feed1</a></li>
<li class="feed-menu-template hide"><a href="#" title="Show feed"><table><tr><td><span></span></td><td><button class="btn btn-primary" title="Fetch feed">Fetch</button></td></tr></table></a></li>
<li class="divider" id="RssMenu_Divider"></li>
<li><a href="#" onclick="Feeds.fetchAll()">Fetch All Feeds</a></li>
</ul>
@@ -286,10 +286,10 @@
<tr>
<th><div class="check img-check"></div></th>
<th width="95px">Status</th>
<th id="DownloadsTable_Name">Name</th>
<th id="DownloadsTable_Category">Category</th>
<th width="30px" class="text-right">Age</th>
<th width="120px"><div style="float:left; position:relative; width:50%; text-align:left;">Size</div><div style="float:right; position:relative; width:50%; text-align:right;">Left</div></th>
<th id="DownloadsTable_Name"><a href="#" onclick="Downloads.sort('name')">Name</a></th>
<th id="DownloadsTable_Category"><a href="#" onclick="Downloads.sort('category')">Category</a></th>
<th width="30px" class="text-right"><a href="#" onclick="Downloads.sort('age')">Age</a></th>
<th width="120px"><div style="float:left; position:relative; width:50%; text-align:left;"><a href="#"onclick="Downloads.sort('size')">Size</a></div><div style="float:right; position:relative; width:50%; text-align:right;"><a href="#" onclick="Downloads.sort('left')">Left</a></div></th>
<th width="58px" class="text-right">Est. Time</th>
</tr>
</thead>
@@ -316,9 +316,18 @@
<div class="btn-toolbar form-inline section-toolbar" id ="HistoryTab_Toolbar">
<div class="btn-group">
<button class="btn" onclick="History.deleteClick()" title="Delete selected records"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button>
<button class="btn" onclick="History.actionClick('DELETE')" title="Delete selected records"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button>
<button class="btn dropdown-toggle" data-toggle="dropdown" title="More actions"><span class="btn-caption"> More </span><span class="phone-only inline">&nbsp;</span><span class="caret"></span></button>
<ul class="dropdown-menu">
<li class="menu-header">Actions</li>
<li><a href="#" id="HistoryTab_Reprocess" onclick="History.actionClick('REPROCESS')"><i class="icon-process"></i> Post-Process Again</a></li>
<li><a href="#" id="HistoryTab_Redownload" onclick="History.actionClick('REDOWNLOAD')"><i class="icon-downloads"></i> Download Again</a></li>
<li><a href="#" id="HistoryTab_MarkSuccess" onclick="History.actionClick('MARKSUCCESS')"><i class="icon-ok"></i> Mark as Success</a></li>
<li><a href="#" id="HistoryTab_MarkGood" onclick="History.actionClick('MARKGOOD')"><i class="icon-plus"></i> Mark as Good</a></li>
<li><a href="#" id="HistoryTab_MarkBad" onclick="History.actionClick('MARKBAD')"><i class="icon-minus"></i> Mark as Bad</a></li>
</ul>
</div>
<!-- STATUS FILTER BUTTONS -->
<div class="btn-group phone-hide">
<button class="btn history-filter btn-inverse" onclick="History.filter('ALL')" title="Show All">All &nbsp;<span class="badge badge-active" id="History_Badge_ALL">?</span></button>
@@ -1019,10 +1028,29 @@
</div>
<div class="modal-tab hide" id="DownloadsEdit_LogTab">
<div class="loading-block">
<img src="img/transmit.gif"></img>
</div>
<div>
<div class="row-fluid">
<div class="pull-left">Post-Processing Messages</div>
<div class="pull-right" style="margin-bottom:10px;"><input id="DownloadsEdit_LogTable_filter" class="search-query" placeholder="Search" type="text"></div>
<div class="form-inline clearfix modal-toolbox">
<div id="DownloadsLogRecordsPerPageBlock" class="pull-left toolbox-length">
<select size="1" id="DownloadsLogRecordsPerPage" onchange="LogTab.recordsPerPageChange('Downloads')">
<option value="5">5</option>
<option selected="selected" value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select><span class="records-label"> records per page</span>
</div>
<div id="DownloadsEdit_LogTable_filterBlock">
<input id="DownloadsEdit_LogTable_filter" class="search-query" placeholder="Search" type="text">
</div>
<div id ="DownloadsEdit_LogTable_pagerBlock" class="pull-right"><div class="pagination" id ="DownloadsEdit_LogTable_pager"></div></div>
</div>
<div id="DownloadsEdit_LogBlock">
<table class="table table-striped table-bordered datatable" id="DownloadsEdit_LogTable" style="margin-bottom:15px;">
@@ -1030,7 +1058,6 @@
<tbody></tbody>
</table>
</div>
<div class="pull-right hide" id="DownloadsEdit_LogTable_pagerBlock"><div class="pagination" id ="DownloadsEdit_LogTable_pager"></div></div>
</div>
</div>
@@ -1258,6 +1285,13 @@
</div>
</div>
<div class="control-group">
<label class="control-label" for="HistoryEdit_NZBNameGroup">Name</label>
<div class="controls" id="HistoryEdit_NZBNameGroup">
<input type="text" class="input-xlarge" id="HistoryEdit_NZBName" />
</div>
</div>
<div class="control-group" id="HistoryEdit_URLGroup">
<label class="control-label" for="HistoryEdit_URL">Link</label>
<div class="controls">
@@ -1268,14 +1302,15 @@
<div class="control-group" id="HistoryEdit_CategoryGroup">
<label class="control-label" for="HistoryEdit_Category">Category</label>
<div class="controls">
<span class="input-medium uneditable-input" id="HistoryEdit_Category"></span>
<select id="HistoryEdit_Category">
</select>
</div>
</div>
<div class="control-group" id="HistoryEdit_PathGroup">
<label class="control-label" for="HistoryEdit_Path">Destination</label>
<div class="controls">
<span class="uneditable-mulitline-input" id="HistoryEdit_Path"></span>
<input type="text" class="input-xlarge" id="HistoryEdit_Path" readonly="true" />
</div>
</div>
@@ -1291,8 +1326,7 @@
<div class="control-group control-group-last" id="HistoryEdit_DupGroup">
<div class="controls">
This hidden history record has no information other than status.
It remains in history for duplicate check.
This hidden history record has little information. It remains in history mostly for duplicate check.
</div>
</div>
@@ -1301,6 +1335,7 @@
<div class="modal-bottom-toolbar">
<button class="btn" data-tab="HistoryEdit_ParamTab" id="HistoryEdit_Param" title="Post-processing parameters">Postprocess <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="HistoryEdit_DupeTab" id="HistoryEdit_Dupe" title="Duplicate properties">Dupe <i class="icon-forward" style="opacity:0.6;"></i></button>
<button class="btn" data-tab="HistoryEdit_LogTab" data-fullscreen="true" id="HistoryEdit_Log" title="Post-processing messages">Log <i class="icon-forward" style="opacity:0.6;"></i></button>
</div>
</div>
</div>
@@ -1316,6 +1351,40 @@
</div>
</div>
<div class="modal-tab hide" id="HistoryEdit_LogTab">
<div class="loading-block">
<img src="img/transmit.gif"></img>
</div>
<div>
<div class="form-inline clearfix modal-toolbox">
<div id="HistoryLogRecordsPerPageBlock" class="pull-left toolbox-length">
<select size="1" id="HistoryLogRecordsPerPage" onchange="LogTab.recordsPerPageChange('History')">
<option value="5">5</option>
<option selected="selected" value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
<option value="30">30</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="200">200</option>
<option value="500">500</option>
<option value="1000">1000</option>
</select><span class="records-label"> records per page</span>
</div>
<div id="HistoryEdit_LogTable_filterBlock">
<input id="HistoryEdit_LogTable_filter" class="search-query" placeholder="Search" type="text">
</div>
<div id ="HistoryEdit_LogTable_pagerBlock" class="pull-right"><div class="pagination" id ="HistoryEdit_LogTable_pager"></div></div>
</div>
<div id="HistoryEdit_LogBlock">
<table class="table table-striped table-bordered datatable" id="HistoryEdit_LogTable" style="margin-bottom:15px;">
<thead><tr><th width="65px">Kind</th><th width="170px" class="text-center">Time</th><th>Text</th></tr></thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
<div class="modal-tab padded-tab hide" id="HistoryEdit_ServStatsTab">
<div>
<div id="HistoryEdit_ServStatsBlock">
@@ -1392,8 +1461,9 @@
<li><a href="#" id="HistoryEdit_Return"><i class="icon-downloads"></i> Download Remaining Files</a></li>
<li><a href="#" id="HistoryEdit_ReturnURL"><i class="icon-downloads"></i> Download Again</a></li>
<li><a href="#" id="HistoryEdit_Redownload"><i class="icon-downloads"></i> Download Again</a></li>
<li><a href="#" id="HistoryEdit_MarkGood"><i class="icon-ok"></i> Mark as Good</a></li>
<li><a href="#" id="HistoryEdit_MarkBad"><i class="icon-remove"></i> Mark as Bad</a></li>
<li><a href="#" id="HistoryEdit_MarkSuccess"><i class="icon-ok"></i> Mark as Success</a></li>
<li><a href="#" id="HistoryEdit_MarkGood"><i class="icon-plus"></i> Mark as Good</a></li>
<li><a href="#" id="HistoryEdit_MarkBad"><i class="icon-minus"></i> Mark as Bad</a></li>
<li class="menu-header" id="HistoryEdit_DNZB_Section">Indexer Links</li>
<li><a href="#" target="_blank" class="HistoryEdit_DNZB" id="HistoryEdit_DNZB_Details"><i class="icon-postcard"></i> Details</a></li>
<li><a href="#" target="_blank" class="HistoryEdit_DNZB" id="HistoryEdit_DNZB_MoreInfo"><i class="icon-link"></i> More Info</a></li>
@@ -1890,22 +1960,40 @@
<div id="HistoryEditRedownloadConfirmDialog_Title">History</div>
<div id="HistoryEditRedownloadConfirmDialog_Text">
<p>
Download this nzb again?
Download selected nzbs again?
</p>
<p class="confirm-help-block">
All downloaded files will be deleted and the nzb-file will be downloaded again from scratch.
All downloaded files will be deleted and the nzbs will be downloaded again from scratch.
</p>
</div>
<div id="HistoryEditRedownloadConfirmDialog_OK">Download Again</div>
</div>
<!-- *** MARK AS SUCCESS CURRENT HISTORY RECORD CONFIRMATION DIALOG DATA ******************************** -->
<div class="hide" id="HistoryEditSuccessConfirmDialog">
<div id="HistoryEditSuccessConfirmDialog_Title">History</div>
<div id="HistoryEditSuccessConfirmDialog_Text">
<p>
Mark selected history records as success?
</p>
<p class="confirm-help-block">
Marking has an effect on duplicate handling and RSS.<br>Records marked as success
considered successfully downloaded and processed. This is useful for downloads repaired
or processed outside of NZBGet. Duplicates with higher duplicate scores may be downloaded
in the future.
</p>
</div>
<div id="HistoryEditSuccessConfirmDialog_OK">Mark Success</div>
</div>
<!-- *** MARK AS GOOD CURRENT HISTORY RECORD CONFIRMATION DIALOG DATA ******************************** -->
<div class="hide" id="HistoryEditGoodConfirmDialog">
<div id="HistoryEditGoodConfirmDialog_Title">History</div>
<div id="HistoryEditGoodConfirmDialog_Text">
<p>
Mark this history record as good?
Mark selected history records as good?
</p>
<p class="confirm-help-block">
Marking has an effect on duplicate handling and RSS.<br>For titles marked as good no more duplicates
@@ -1921,7 +2009,7 @@
<div id="HistoryEditBadConfirmDialog_Title">History</div>
<div id="HistoryEditBadConfirmDialog_Text">
<p>
Mark this history record as Bad?
Mark selected history records as Bad?
</p>
<p class="confirm-help-block">
Marking has an effect on duplicate handling and RSS.<br>If dupe-backups exist in the history
@@ -2420,6 +2508,10 @@
<strong>URLs cannot be merged or paused</strong>
</div>
<div id="Notif_Downloads_Sorted" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Sorted</strong>
</div>
<div id="Notif_Edit_Select" data-duration="2000" class="alert alert-error alert-center alert-center-small hide">
<strong>Please select records first</strong>
</div>
@@ -2440,7 +2532,7 @@
<strong>Returned to Queue</strong>
</div>
<div id="Notif_History_Reproces" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<div id="Notif_History_Reprocess" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Post-Processing</strong>
</div>
@@ -2452,11 +2544,23 @@
<strong>Please select records first</strong>
</div>
<div id="Notif_History_CantMark" data-duration="2000" class="alert alert-error alert-center alert-center-small hide">
<strong>Cannot mark URL-records</strong>
</div>
<div id="Notif_History_Marked" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Marked</strong>
</div>
<div id="Notif_Feeds_FetchAll" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<div id="Notif_History_CantReprocess" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Cannot post-process URL- or hidden records</strong>
</div>
<div id="Notif_History_CantRedownload" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Cannot redownload hidden records</strong>
</div>
<div id="Notif_Feeds_Fetch" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Fetching new items</strong>
</div>
@@ -2505,5 +2609,13 @@
<strong>Volume reset</strong>
</div>
<div id="Notif_Config_TestConnectionProgress" data-duration="20000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Testing connection...</strong>
</div>
<div id="Notif_Config_TestConnectionOK" data-duration="1000" class="alert alert-success alert-center alert-center-small hide">
<strong>Connection successful</strong>
</div>
</body>
</html>

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