Compare commits

...

432 Commits
v13.0 ... v16.2

Author SHA1 Message Date
Andrey Prygunkov
665645b510 updated version string to "16.2" 2015-10-24 15:26:50 +02:00
Andrey Prygunkov
eb87111204 fixed #100: workaround to deal with malformed responses
…which still may contain useful data.
2015-10-24 15:13:04 +02:00
Andrey Prygunkov
94aa547a85 updated version string to "16.1" 2015-10-19 22:17:18 +02:00
Andrey Prygunkov
dfa18b50a4 corrected file permissions 2015-10-19 22:01:52 +02:00
Andrey Prygunkov
2820ee4bc5 fixed #95: starting nzbget from setup (Windows)
When launching NZBGet at the end of setup the program is now started
with regular user permissions.
2015-10-19 21:55:21 +02:00
Andrey Prygunkov
c9ff56cc7e #89: fixed unpack failure on certain CPUs
unrar shipped with nzbget was compiled in optimization mode O3. In that
mode GCC activates loop vectorization. On x86_64 architecture that
caused the usage of CPU commands from extended set SSSE3. Some older
AMD processors doesn’t support SSSE3, which lead to abortion with
“illegal instruction”-message. Now using O2-mode; that solves the
issues.
2015-10-19 21:53:06 +02:00
Andrey Prygunkov
9ea9da8d33 #77: fixed issues with reverse proxies (3)
merging into 16.x-branch: when very long headers were sent from the
proxy, in particular if htdigest authorization were used.
2015-10-19 21:52:33 +02:00
Andrey Prygunkov
297a966da3 #77: fixed issues with reverse proxies
merging into 16.x-branch: removing of authorization-header wasn’t such
a good idea.
2015-10-19 21:51:23 +02:00
Andrey Prygunkov
dafb956e6e updated version string to "16.0" 2015-10-10 20:52:12 +02:00
Andrey Prygunkov
263f669873 Merge branch 'develop' 2015-10-10 19:54:45 +02:00
Andrey Prygunkov
ceec19c6fd updated ChangeLog 2015-10-10 19:54:24 +02:00
Andrey Prygunkov
a513dc0c01 fixed compilation error on Linux
…when compiled with “--enable-test”
2015-10-10 19:52:05 +02:00
Andrey Prygunkov
1948dd2420 closed #87: added hint about RC4 cipher considered insecure 2015-10-07 23:28:58 +02:00
Andrey Prygunkov
87f9cc68a0 compatibility with Visual Studio 2015 2015-09-28 21:35:41 +02:00
Andrey Prygunkov
d0897c2e09 #72: fixed: filter was not reset when reopening dialogs 2015-09-23 22:01:40 +02:00
Andrey Prygunkov
5e392e98b6 #18: fixed one failed test 2015-09-17 20:56:36 +02:00
Andrey Prygunkov
caf2d919b4 closes #16: renamed "svn_version.cpp" to "code_revision.cpp" 2015-09-17 20:44:38 +02:00
Andrey Prygunkov
111630b6b7 improved performance in web ui with large queue or history
Improved performance in web-interface when working with very large
queue or history (thousands of items).
2015-09-14 22:54:19 +02:00
Andrey Prygunkov
5418865a1b improved performance on mass delete
Better performance when deleting many items from queue at once
(hundreds or thousands).
2015-09-14 22:42:11 +02:00
Andrey Prygunkov
752d27ee08 speed optimizations in built-in web-server
- big speed improvement in built-in web-server on Windows when serving
API requests (web-interface) for very large queue or history (with
thousands items);
- refactoring in API server: clearer code yet faster.
2015-09-11 21:32:02 +02:00
Andrey Prygunkov
afa32676ac fixed #20: option Encryption=Force in EMail.py
PP-Script EMail.py now supports new TLS/SSL mode “Force”. When active
the secure communication with SMTP server is built using secure socket
on connection level instead of plain connection and following switch
into secure mode using SMTP-command “STARTLS”. This new mode is in
particular required when using GMail on port 465.
2015-09-08 19:16:14 +02:00
Andrey Prygunkov
e4d3773c22 Merge branch '77-comm-errors' into develop 2015-09-07 23:14:46 +02:00
Andrey Prygunkov
04558bc25e #77: authorization via X-Auth-Token
Implemented authorization via X-Auth-Token to overcome Safari’s bug,
where it may stop sending HTTP Basic Auth header when executing ajax
requests leading to communication errors in web-interface. With
X-Auth-Token only the first request must include HTTP Basic Auth, for
sub-sequential requests the web-interface sends X-Auth-Token, received
from server on first request. The web-interface even tries to remove
the HTTP Basic Auth header from request to improve security; this
however works only in Chrome, other tested browsers still send the Auth
data anyway (IE, Safari, Firefox).
2015-09-07 18:46:45 +02:00
Andrey Prygunkov
12b6a2602a #77: graceful disconnect in web-server
Implemented graceful disconnect strategy in web-server. This may help
with communication errors in web-interface in certain web-browsers.
2015-09-05 22:01:10 +02:00
Andrey Prygunkov
9e493c6c4d #64: fixed: par-checker may not look for missing files
When option ParScan was set to “Dupe” the extra-files were not scanned
in current directory.
2015-09-04 23:36:40 +02:00
hugbug
fdebae5cc2 d48a16598f: fixed line endings 2015-09-04 21:22:01 +02:00
Andrey Prygunkov
ff8f2472a0 #64: fixed: option ParScan=Extended didn't work
When option ParScan was set to “Extended” the extra-files were not
found. Bug introduced v16.
2015-09-04 20:55:49 +02:00
Andrey Prygunkov
f732a0edc1 #18: fixed: feed id wasn't passed to script from preview 2015-09-04 20:36:08 +02:00
Andrey Prygunkov
d48a16598f closed #59: new preference for tray icon behavior on Windows 2015-09-04 19:51:48 +02:00
Andrey Prygunkov
fb5a254b83 #18: new option "FeedX.FeedScript"
…to define per rss feed scripts; in addition to existing global option
“FeedScript”.
2015-09-03 21:45:36 +02:00
Andrey Prygunkov
e01f1a37e5 fixed #78: updating on Windows may fail
Updating the Windows version fails if NZBGet is not installed on the
system drive.
2015-09-03 21:32:04 +02:00
Andrey Prygunkov
4887941170 Merge branch '76-add-with-rename' into develop 2015-08-30 20:36:44 +02:00
Andrey Prygunkov
d57c895127 #76: setting password when adding nzbs 2015-08-30 18:01:42 +02:00
Andrey Prygunkov
7385a1744b #76: change name when adding nzbs
- when nzbs are selected for adding via web-interface they are shown in
the upload-list in the add files dialog;
- the list items are now clickable;
- a click opens a properties dialog where the name, password, duplicate
key and duplicate score can be changed;
- the password-field is currently not implemented yet.
2015-08-30 17:28:33 +02:00
Andrey Prygunkov
dc678b37f5 showing tooltips on article completion tab
The completion tab of download details dialog (and history details
dialog) shows per servers article completion in percents. Now there are
also tooltips to show article counts.
2015-08-26 22:06:10 +02:00
Andrey Prygunkov
cdcbd783cd increased limit for log-entries in history dialog
from 1000 to 10000.
2015-08-26 21:59:37 +02:00
Andrey Prygunkov
20b43ec736 fixed #74: added support for UNC-paths to par2-module
(affects Windows only)
2015-08-26 21:56:40 +02:00
Andrey Prygunkov
cb41e3314c closed #73: new option FeedX.Backlog
- option to reset RSS backlog protection;
- useful for bookmark feeds but also for feeds where filter is often
changed;
- API-method “previewfeed” has new parameter “bool backlog”.
2015-08-21 21:40:31 +02:00
Andrey Prygunkov
a9edcdf4fd Merge branch '72-advanced-search' into develop 2015-08-20 21:31:13 +02:00
Andrey Prygunkov
f5daf39bb8 fixed #67: mark as bad may return items to queue
… if multiple duplicate-items were marked at once
2015-08-20 21:23:37 +02:00
Andrey Prygunkov
aeab407dc0 #72: saved filters in filter menu
- filters can be saved, deleted and renamed.
2015-08-19 22:47:36 +02:00
Andrey Prygunkov
d2770d7a33 #72: popup menu for filter input
- currently with only one menu item “quick help”;
- will be expanded in the future with recent searches.
2015-08-18 22:12:09 +02:00
Andrey Prygunkov
ab86d0efe2 #72: reset filter input on page refresh 2015-08-18 21:08:31 +02:00
Andrey Prygunkov
5b4d3d8038 Merge branch '72-advanced-search' into develop 2015-08-17 23:48:24 +02:00
Andrey Prygunkov
95fffe619d #72: advanced search in settings
- searchable fields: name, description, value.
2015-08-17 22:59:11 +02:00
Andrey Prygunkov
ea95a819c1 #72: comparison operators for integer fields
- search box now supports operators : = <> > < >= <=
- new search fields in downloads list: sizemb, sizegb, leftmb, leftgb,
agem, ageh, aged - download size in MB, GB, left size in MB, GB, age in
minutes, hours, days;
- the same fields except leftmb, leftgb are also available in history.
2015-08-17 18:18:34 +02:00
Andrey Prygunkov
44197148d0 #72: search field names using any letter case
- field names can be typed in any letter case, for example “parstatus”
instead of “ParStatus”;
- there is a potential ambiguity when searching through field “status”:
this field exists in both as column in table and as field in the API;
although it has the same meaning the content is slightly different, the
field in API is more technical and includes extra text, the field in
column is more user friendly;
- to avoid ambiguity use the correct letter case (“Status”) to search
in API field, use low letter case (“status”) to search in table; any
other letter case form (like “sTatus” or “STATUS”) will search in table
too since it has precedence;
2015-08-16 21:37:05 +02:00
Andrey Prygunkov
434ded22e2 Merge branch 'develop' into 72-advanced-search 2015-08-16 19:29:43 +02:00
Andrey Prygunkov
46c8398942 #64: renamed status "Repair" to "ExPar"
- new field “ExParStatus” returned by API-method “history” with values:
“NONE”, “RECIPIENT”, “DONOR”;
- history dialog shows dupe repair status as “EXPAR” instead of
“REPAIR”.
2015-08-16 19:24:27 +02:00
Andrey Prygunkov
1369b23974 #72: search in hidden fields
- in addition to fields shown in tables all other fields coming from
API are now searchable;
- for list of available fields on downloads-tab see API-method
“listgroups”;
- for history-tab see API-method “history”;
- for messages-tab see API-method “log”;
- field names must be typed with correct letter-case;
- visible fields (shown in tables) must be typed in lower case.
2015-08-15 22:11:17 +02:00
Andrey Prygunkov
d21badb6d5 #72: search in specified fields
- column name and a colon should be used as prefix;
- example: “status:downloading|status:processing”
- if no field is specified the search is performed through all fields.
2015-08-15 17:29:29 +02:00
Andrey Prygunkov
4c51d2dc28 #72: better error recovery 2015-08-15 15:25:09 +02:00
Andrey Prygunkov
c0b3058f7c #72: fixed parser errors and better error recovery 2015-08-14 19:42:15 +02:00
Andrey Prygunkov
e5eb29be05 #72: integrated advanced search into module "tasttable"
- removed old word-search default search engine;
- the new advanced search is always used now;
2015-08-14 19:28:12 +02:00
Andrey Prygunkov
3d3fa4980e #72: replaced jison-generated parser with own
- rewritten the parser manually;
- greatly reduced file size;
- better handling of errors;
- supports non-ascii characters.
2015-08-14 18:44:37 +02:00
Andrey Prygunkov
405ed766f4 #72: fixed: clear-button in search box overlapped with text 2015-08-14 18:38:51 +02:00
Andrey Prygunkov
cc62387292 #72: implemented advanced search 2015-08-13 23:16:06 +02:00
Andrey Prygunkov
1594345775 #72: removed search option "CaseSensitive"
it was always set to false anyway.
2015-08-13 22:59:39 +02:00
Andrey Prygunkov
2d3d5af25e #72: made search engine pluggable
the default search engine is words-searcher (the old one).
2015-08-13 22:58:02 +02:00
Andrey Prygunkov
c6656cffbf #64: indication of par-repair using dupe sources
in history details dialog:
- status “REPAIR: RECIPIENT” or “STATUS:DONOR” with hint details;
- in statistics details (via click on “Total downloaded ->”);
- new field “ExtraParBlocks” returned by API-method “history”: positive
numbers for recipient, negative numbers for donor.
2015-08-13 19:38:28 +02:00
Andrey Prygunkov
e4d62ebbc8 par-checker: do not report bad blocks for missing files
- do not report bad blocks for missing files (which are already
reported as missing);
- reporting of bad blocks for empty files could print garbage file
names.
2015-08-12 21:16:44 +02:00
Andrey Prygunkov
af422050b6 #60: don't allow editing of items queued for queue-scripts 2015-08-12 21:14:16 +02:00
Andrey Prygunkov
5f52512a5f fixed #71: crash on reload if a queue-script is running 2015-08-12 21:10:49 +02:00
Andrey Prygunkov
ca0af70d53 #60: active queue-scripts indicated in webui
- the number of active (and queued) scripts are shown in the status
dialog in web-interface; this new row is hidden if no scripts are
queued;
- active queue scripts accounts for activity indicator in web-interface
(rotating button);
- new field “QueueScriptCount” in API-method “status” indicates number
of queue-scripts queued for execution including the currently running.
2015-08-11 22:43:25 +02:00
Andrey Prygunkov
6e0a3e0ceb fixed #69: total articles wasn't reset when downloading again 2015-08-11 22:34:22 +02:00
Andrey Prygunkov
24fd4e8c15 #26, #60, #64, #70: corrected file permissions 2015-08-11 22:31:59 +02:00
Andrey Prygunkov
1e64a7d453 #60: new download status to indicate queue script activity
- new values for field “Status” in method “listgroups”: QS_QUEUED,
QS_EXECUTING;
- QS_QUEUED means that nzb is queued for processing by a queue-script;
- QS_EXECUTING means for that nzb a queue script is currently running;
- indication in web UI: status “QS-QUEUED” (gray) or “QUEUE-SCRIPT”
(green).
2015-08-11 22:27:41 +02:00
Andrey Prygunkov
8a03c64021 #26: executing queue scripts for URLs
- queue-scripts are now also called for failed URLs;
- new queue event “URL_COMPLETED” with possible values: FAILURE,
SCAN_SKIPPED, SCAN_FAILURE;
- queue scripts are not called when URL was successfully fetched and
added queue; event “NZB_ADDED” is fired in this case;
2015-08-11 18:46:43 +02:00
Andrey Prygunkov
f8c1df1856 fixed #70: incorrect reading of UrlStatus from diskstate 2015-08-11 18:43:15 +02:00
Andrey Prygunkov
5b460b7512 #64: improved progress calculation during extra par-scan 2015-08-10 19:52:27 +02:00
Andrey Prygunkov
45277c85e0 fixed: relative redirects did not work
- Absolute redirects to other sites (like “http://host.com/page.html”
did work;
- Absolute redirects to other resource on the same site (like
“/page.html” did work;
- Relative redirects (like “page.html”) did NOT work. Fixed.
2015-08-09 22:56:54 +02:00
Andrey Prygunkov
c38069443d closes #68: better description for option "ServerX.Active" 2015-08-09 01:05:24 +02:00
Andrey Prygunkov
6da5730356 Merge branch '64-dupe-repair' into develop 2015-08-08 21:56:37 +02:00
Andrey Prygunkov
2a9f9f8e98 #64: implemented dupe matcher
Before scanning of dupe directories the directories are quickly checked
by dupe matcher. It determines if they contain or may contain the same
content. If the dupe checker detects a dupe containing different
content as the download being currently processed by par-checker, such
extra dupe is skipped to save time during dupe par scan.
2015-08-07 18:38:51 +02:00
Andrey Prygunkov
a334a32b35 ec47da608f: fixed: crash if an nzb contains only par2-files 2015-08-06 17:52:37 +02:00
Andrey Prygunkov
1583e84927 #64: adjusted health check in dupe par scan mode
When option “ParScan” is set to "Dupe" the delete-action is performed
only if article completion is below 10% (empirical threshold). This is
to improve efficiency of dupe par scan mode.
2015-08-04 22:37:23 +02:00
Andrey Prygunkov
8a2fef7c46 Merge branch 'develop' into 64-dupe-repair 2015-08-03 23:35:04 +02:00
Andrey Prygunkov
6c3f2a9871 #21: refactor: new modules Service and DiskService
Extracted secondary functions from module PrePostProcessor into new
modules Service and DiskService.
2015-08-03 23:27:02 +02:00
Andrey Prygunkov
3ac2ccbc80 #64: do not skip par-check in dupe scan mode
Do not skip par-check when health is below critical health and
ParScan=dupe. The download may be still repairable if the data from
other duplicates can be used.
2015-08-03 00:50:11 +02:00
Andrey Prygunkov
a11d22d3e0 #64: small adjustments in debug logging 2015-08-02 16:59:49 +02:00
Andrey Prygunkov
d07b9af366 #64: process more pars again, after dupe par-check 2015-08-02 16:59:25 +02:00
Andrey Prygunkov
1a65409d0e #64: par-checker should wait for download completion
If par-checker requests extra par2-files, it should wait until all of
them are completely downloaded, even if it becomes clear that repair
isn’t possible. This may happen if par2-files were damaged or not from
the par-set. The waiting is required for correct further processing.
2015-08-02 16:56:51 +02:00
Andrey Prygunkov
941c2efb52 #64: first use par2-files, then scan dupes 2015-08-02 01:32:59 +02:00
Andrey Prygunkov
e99a0c5975 #61: 860f05eb70: fixed: crash in debug mode when reloading 2015-08-02 00:59:48 +02:00
Andrey Prygunkov
6b8c27fdcc #61: 860f05eb70: fixed: crash in debug mode when reloading 2015-08-02 00:57:39 +02:00
Andrey Prygunkov
820260cb6c #64: in option "ParScan" renamed mode "auto" to "extended"
Now having four modes: limited, extended, full and dupe.
2015-08-02 00:38:39 +02:00
Andrey Prygunkov
96c73f05af #64: c674405b44: corrected missing renaming in config file 2015-08-01 21:36:59 +02:00
Andrey Prygunkov
c674405b44 #64: renamed value "Beyond" to "Dupe"
in option ParScan. The new value name suits better.
2015-08-01 01:23:34 +02:00
Andrey Prygunkov
6ac82f03d8 #64: refactor: extracted classes from module "Unpack" into new module "Cleanup" 2015-08-01 00:20:31 +02:00
Andrey Prygunkov
1a206457a2 #64: new par-scan mode "beyond"
- new value for option “ParScan” - “Beyond”;
- in this mode the files from other downloads (duplicates) are scanned
as well;
- this helps if both downloads contain the same file inside archive,
even if the archives were created with different split-settings and
different par-sets.
2015-07-31 21:26:14 +02:00
Andrey Prygunkov
6fd407b200 Merge remote-tracking branch 'origin/develop' into develop 2015-07-31 12:00:29 +02:00
Andrey Prygunkov
d8c6be9f52 Merge branch '26-history' into develop 2015-07-31 12:00:07 +02:00
Andrey Prygunkov
46039698ca #48: added .sln to ignore list
.sln is a Visual Studio Solution file which is generated when opening project file (.vcproj) and therefore can be omitted from repository.
2015-07-28 12:20:09 +02:00
Andrey Prygunkov
002547fde5 #26: passing DupeKey, DupeMode and DupeScore to queue- and pp-scripts
- new env. parameters passed to queue-scripts: NZBNA_DUPEKEY,
NZBNA_DUPEMODE, NZBNA_DUPESCORE;
- new env. parameters passed to pp-scripts: NZBPP_DUPEKEY,
NZBPP_DUPEMODE, NZBPP_DUPESCORE.
2015-07-27 19:12:38 +02:00
Andrey Prygunkov
547b430c91 closed #46 (again): devel version-revision in "Check for Updates"
Showing correct development version-revision in "Check for
Updates"-dialog.
2015-07-25 14:55:21 +02:00
Andrey Prygunkov
860f05eb70 fixed #61: config error messages were not printed to log or screen
but only to stdout, where users typically don’t see them.
2015-07-25 14:45:44 +02:00
Andrey Prygunkov
4cc2beaaca #26: new queue-script event "NZB_DELETED"
- when an nzb is deleted from queue and moved to history the
queue-scripts are called with event “NZB_DELETED”.
- no scripts are called if items are deleted permanently without
putting to history or if the history is completely disabled. This is
because queue-scripts are executed asynchronously and at the time the
script is executed the item must exist (either in queue or in history)
in order to pass item info to the script.
2015-07-24 19:45:44 +02:00
Andrey Prygunkov
d49ab2a087 #26: removed unneeded extra check 2015-07-24 19:34:51 +02:00
Andrey Prygunkov
c208eec5c3 #26: removed code "SUCCESS" from DeleteStatus
A more general code “GOOD” is used instead.
2015-07-24 19:29:41 +02:00
Andrey Prygunkov
c7047b1e33 #26: RPC-method "append" return ID on parsing failure
… instead of error code “-1” as in previous version. Since now a
history item is created on parsing failure its ID is returned. The
caller can check the status from history.
2015-07-24 19:26:31 +02:00
Andrey Prygunkov
659ed48652 #26: ignored nzbs are now added to history
When an nzb-file isn’t added to queue for some reason, the file is now
also added to history (in addition to messages printed to log):
- for malformed nzb-files which cannot be parsed the status in history
“DELETE: SCAN”;
- for duplicate files with exactly same content status “DELETE: COPY”;
- for duplicate files having history items with status “GOOD” - status
“DELETE: GOOD”;
- for duplicate files having history items with status “SUCCESS” -
status “DELETE: SUCCESS”;
- history items have log-entries with explanation;
- new values for field “DeleteStatus” of RPC-Method “history”: GOOD,
SUCCESS, COPY, SCAN;
- new values for field “Status” of RPC-Method “history”:
"FAILURE/SCAN”, ”DELETED/COPY”, "DELETED/GOOD”, "DELETED/SUCCESS”;
- one exception: for files added from RSS-feeds no history items are
created, the files are ignored as if they were filtered.
2015-07-23 23:47:59 +02:00
Andrey Prygunkov
a9a73b635c #21: new option "RequiredDir" 2015-07-23 23:07:47 +02:00
Andrey Prygunkov
fc484ba0dd #28: fixed: files were deleted during flush (Windows only) 2015-07-21 18:22:34 +02:00
Andrey Prygunkov
c6ad5523c7 #56: also supporting ctrl+click in addition to meta+click
Meta+Click doesn’t work on Windows. Ctrl+Click doesn’t work on Mac.
Therefore supporting both.
2015-07-20 20:24:12 +02:00
hugbug
0a8edc2388 fixed #57: activated optimizations in unpack build script 2015-07-19 23:49:14 +02:00
hugbug
3e5bb54ca4 #35: auto-selecting "armhf"-architecture on ARM 64 bit systems (aarch64) 2015-07-19 15:04:05 +02:00
Andrey Prygunkov
7fc8238b23 #56: quick toggle of speed limit
Meta+click-on-speed-icon toggles between "all servers active and
speed-limit=none" and "servers and speed limit as in the config file".
Meta-key: “Command” on Mac; “Win” on Windows.
2015-07-19 14:56:02 +02:00
Andrey Prygunkov
06454eddcc #41: removed workaround which disabled spinlocks for Linux builds 2015-07-17 23:44:56 +02:00
Andrey Prygunkov
c93c0e9dce #41: removed spinlocks support 2015-07-17 23:43:53 +02:00
Andrey Prygunkov
4ec9f947c6 #41: removed spin lock support detection
from configure script (POSIX).
2015-07-17 23:39:07 +02:00
Andrey Prygunkov
6436c3657d fixed compiler warning in par2-module 2015-07-17 00:11:26 +02:00
Andrey Prygunkov
8d1ffa4947 Merge branch '28-disk-flush' into develop 2015-07-17 00:06:49 +02:00
Andrey Prygunkov
5d6dab779e #28: option "FlushQueue" is now enabled by default 2015-07-17 00:06:03 +02:00
Andrey Prygunkov
36d1378881 #28: implemented disk flush on POSIX
with extra specifics for Linux and OS X.
2015-07-16 23:56:37 +02:00
Andrey Prygunkov
187679443f fixed: false memory leaks reports when running tests on Windows 2015-07-16 20:16:33 +02:00
Andrey Prygunkov
97e2776480 #28: implemented disk flush on Windows 2015-07-15 23:17:49 +02:00
Andrey Prygunkov
d7ab37ad31 #28: disk state handling for disk flush
Reworked disk state handling to use disk flush function. The function
itself is not implemented yet.
2015-07-15 23:17:17 +02:00
Andrey Prygunkov
1f7c15628a #28: new option "FlushQueue"
The function is without function yet.
2015-07-15 23:13:47 +02:00
hugbug
15e8a853fb fixed #55: not working endianness detection in Linux installer (affected only mipseb achitecture) 2015-07-14 22:19:40 +02:00
Andrey Prygunkov
1b248721e9 Merge branch '51-signing' into develop 2015-07-13 21:19:37 +02:00
Andrey Prygunkov
fde5f7e744 #51: removed extra switch when calling nzbget
from Linux update script
2015-07-13 21:17:35 +02:00
Andrey Prygunkov
f28e1e76ff #51: implemented verification in Windows update script 2015-07-13 21:15:53 +02:00
Andrey Prygunkov
4daf01e683 fixed few compiler warnings in Windows 2015-07-13 21:13:26 +02:00
Andrey Prygunkov
3752a78fa1 bbc86a15a1: fixed compilation error on Windows 2015-07-13 21:13:05 +02:00
Andrey Prygunkov
bbc86a15a1 corrected an include to fix compiling error on certain systems 2015-07-12 20:54:24 +02:00
hugbug
9864184606 #51: implemented signature verification in Linux update script 2015-07-12 17:35:09 +02:00
hugbug
a0730475f1 Merge branch 'develop' into 51-signing 2015-07-12 14:25:45 +02:00
Andrey Prygunkov
d2f6350fab #51: signatures file format compatible with jsonp 2015-07-11 19:40:55 +02:00
hugbug
d535ac781e closes #27: taking download URL from info file
when updating via built-in update function on Linux.
2015-07-11 15:43:52 +02:00
Andrey Prygunkov
332647c296 #54: corrected message text 2015-07-11 12:58:13 +02:00
Andrey Prygunkov
30f0051976 #54: improved error reporting for passworded archives 2015-07-11 12:51:48 +02:00
Andrey Prygunkov
1efb67b60c #52: fixed: file size was not shown correctly...
for very large files on tab “Files” in “download details dialog”.
2015-07-11 12:03:56 +02:00
Andrey Prygunkov
4c3bec2a3f fixed #52: supporting creating of very large sparse files 2015-07-11 02:37:18 +02:00
hugbug
39063e4bcf #51: added public key to Linux installer 2015-07-11 00:29:10 +02:00
hugbug
da67342419 Merge branch 'develop' into 51-signing 2015-07-11 00:27:51 +02:00
hugbug
50155a0838 #48: fixed broken linux build script 2015-07-11 00:02:42 +02:00
Andrey Prygunkov
25f773efa8 #51: implement signature verification
Command to verify:
    nzbget -n -B verify pubkey.pem signatures.txt installer-package

File “signatures.txt” can contain multiple signatures for many files -
one line per file, in format:
    RSA-SHA256(installer-package)= signature-hex-dump
2015-07-10 20:32:19 +02:00
Andrey Prygunkov
c1fcf0b075 #51: added public key to repository 2015-07-10 19:37:21 +02:00
Andrey Prygunkov
063d5a22ba Merge branch '48-cleanup-rootdir' into develop 2015-07-10 18:04:13 +02:00
hugbug
c92c1c9a3d #48: removed unnecessary Visual Studio solution file (project file is sufficient) 2015-07-10 00:00:39 +02:00
hugbug
213eb2c7c1 #48: removed unnecessary files from project root directory; moved other (necessary) files into new subdirectory "posix" 2015-07-09 23:49:17 +02:00
Andrey Prygunkov
b284dcc7ef #27: taking download URL from info file
… when updating via built-in update function on Windows.
2015-07-08 19:23:43 +02:00
Andrey Prygunkov
9822eae8ff closed #46: devel version-revision in "Check for Updates"
Showing correct development version-revision in "Check for
Updates"-dialog.
2015-07-07 22:19:48 +02:00
Andrey Prygunkov
c3dd57abc6 fixed #47, #14: RPC-API method "readurl" follows redirects
the method is used by "Check for Updates"-function in web-interface
2015-07-07 22:10:31 +02:00
Andrey Prygunkov
3c65d13c00 fixed #45: incorrect subject parsing
… for obfuscated filenames without extensions.
2015-07-06 22:13:42 +02:00
Andrey Prygunkov
b84bab52e0 #45: created test case for subject parsing 2015-07-06 22:12:46 +02:00
hugbug
059bd2b54e set correct file permissions for source code 2015-07-06 21:56:25 +02:00
Andrey Prygunkov
ec29f55f53 fixed: compiler error when building tests on Windows 2015-07-06 21:49:58 +02:00
Andrey Prygunkov
547e0e73de #5: corrected an URL 2015-07-05 15:07:30 +02:00
Andrey Prygunkov
d4f1660a1a #5: corrected typo 2015-07-05 15:05:13 +02:00
Andrey Prygunkov
6c81365ee7 #5: created README.md
as entry point for GitHub's first time visitors
2015-07-05 14:15:03 +02:00
Andrey Prygunkov
f0e779c9ea addition to ec47da608f: fixed: option "DownloadRate" were incorrectly read from config file 2015-07-01 18:39:47 +02:00
Andrey Prygunkov
71bf3815c3 Merge branch '18-feed-script' into develop 2015-06-30 23:51:25 +02:00
Andrey Prygunkov
d7c14201ac closes #19: hidden option "rowSelect" now works for feed view too 2015-06-30 23:48:27 +02:00
hugbug
e963ccefe5 #42: shortened the texts, improved robustness 2015-06-30 00:35:12 +02:00
Andrey Prygunkov
37252faedc Merge pull request #42 from sanderjo/show-ip-address
Improved detection of local IP-address in Linux installer
2015-06-30 00:28:07 +02:00
sanderjo
5144f8ba02 Show correct LAN/WLAN ip address after installing 2015-06-29 23:16:58 +02:00
Andrey Prygunkov
16f83417fe #18: added new files to VC project file 2015-06-29 20:16:09 +02:00
hugbug
17a506009e #18: added new files to Makefile 2015-06-29 19:43:08 +02:00
Andrey Prygunkov
0b0d7784e0 #18: added missing files 2015-06-29 19:46:00 +02:00
Andrey Prygunkov
151204c8d1 #18: UI editor for option "FeedScript"
… in web-interface.
2015-06-29 19:31:12 +02:00
Andrey Prygunkov
ced1536195 #18: add support for feed scripts
via new option “FeedScript”.
2015-06-29 19:30:31 +02:00
Andrey Prygunkov
cce367b83c #18: new option "FeedScript" 2015-06-29 19:29:57 +02:00
hugbug
8101780fa1 #41: disabled spin locks in builds for Linux installer 2015-06-28 22:04:16 +02:00
hugbug
20fcd3436c #9: merge 5b8dc9788966d68248dd04d516c46623c7a31038 into develop 2015-06-28 21:44:36 +02:00
Andrey Prygunkov
215521b800 Merge pull request #38 from teonar/develop
Search string (in the search box) now supports or(|) and not(!) qualifiers.
2015-06-26 22:12:06 +02:00
teonar
34f8b9f82c was using svn sources 2015-06-26 19:47:09 +02:00
teonar
23fd1fb35e Merge branch 'develop' of https://github.com/teonar/nzbget into develop 2015-06-26 19:43:38 +02:00
teonar
91e362ca15 Implemented or(|) not(!) qualifiers. and(&) is the default.
e.g.
!earth !moon
will display items that doesn't have the words (earth or moon)
|earth |moon
will display items that have moon or earth
!earth moon
will display items that does not have earth but does have moon
!earth |moon
will display items that does not have earth but does have moon
2015-06-26 19:42:14 +02:00
Andrey Prygunkov
0027df28a3 fixed #8: spaces in URLs are now automatically encoded 2015-06-26 17:05:07 +02:00
teonar
9f9fcaedee Implemented or(|) not(!) qualifiers. and(&) is the default.
e.g.
!earth !moon
  will display items that doesn't have the words (earth or moon)
|earth |moon
  will display items that have moon or earth
!earth moon
  will display items that does not have earth but does have moon
!earth |moon
  will display items that does not have earth but does have moon
2015-06-26 15:16:10 +02:00
Andrey Prygunkov
a91f296562 Merge branch '32-rss-description-cleanup' into develop 2015-06-25 23:42:16 +02:00
Andrey Prygunkov
708b9d93ff #32: replacing unknown html-entities (better version)
When fetching rss feeds the unknown html-entities in the description
field are now replaced with spaces (example: &mdash;).
2015-06-24 23:05:16 +02:00
Andrey Prygunkov
08c9c8f5fb #32, 517f860c6b: reverted
Reverted the replacing of unknown xml-entities. It was buggy and not a
good idea. Replacing of unknown html-entities should be separate
function (if needed).
2015-06-24 22:37:28 +02:00
Andrey Prygunkov
b12b51d17a fixed #33: can't deleted active URL download 2015-06-24 18:53:25 +02:00
Andrey Prygunkov
7b99aadb3f Merge branch 'develop' into 32-rss-description-cleanup 2015-06-24 00:32:27 +02:00
Andrey Prygunkov
c5b551d68e increased size of version string buffer
since the version name now can include branch name, which can be long
2015-06-24 00:25:36 +02:00
Andrey Prygunkov
fdfb7ce628 #32: cleanup field "description"
… when reading rss feed.
2015-06-24 00:16:59 +02:00
hugbug
eceb7bd14b #32: fixed test-unit compilation error on Linux 2015-06-23 23:54:57 +02:00
hugbug
1e527c900a #32: added new test-unit to Makefile 2015-06-23 23:54:21 +02:00
Andrey Prygunkov
517f860c6b #32: replacing unknown html-entities
Function **XmlDecode** replaces unknown xml-entities with spaces.
Example: &mdash; It’s better to replace them than keep unchanged.
2015-06-23 23:41:04 +02:00
Andrey Prygunkov
0e4da5719c #32: created cleanup-function
… and a test for it.
2015-06-23 22:48:49 +02:00
Andrey Prygunkov
7f1f9d6394 ec47da608f: fixed compiling error on Windows 2015-06-21 23:42:36 +02:00
Andrey Prygunkov
d25f723ae2 #4: corrected .gitattributes for line endings
as suggested by @rcdailey in the issue discussion.
2015-06-21 18:14:14 +02:00
Andrey Prygunkov
dd81ffeb00 #4: normalize all the line endings (again) 2015-06-21 12:26:27 +02:00
Andrey Prygunkov
f225ea8905 #4: normalize all the line endings 2015-06-21 12:24:26 +02:00
Andrey Prygunkov
5b6999232f #4: corrections for line endings 2015-06-21 12:23:07 +02:00
Andrey Prygunkov
17d2b0da7c closes #6: deleted unneeded Makefile.cvs 2015-06-21 00:05:31 +02:00
Andrey Prygunkov
7768035da2 closes #4: configured line endings
... for git repository
2015-06-20 23:53:26 +02:00
Andrey Prygunkov
d4b53ac007 #3: corrected a comment
For naming testing releases.
2015-06-20 23:49:07 +02:00
hugbug
67e07c8fcd #4: updated .gitignore for GNU automake 2015-06-20 23:35:04 +02:00
Andrey Prygunkov
2737c63903 Merge pull request #17 from nzbget/build-nzbget
Updated linux build script
2015-06-20 01:40:21 +02:00
Andrey Prygunkov
a367698670 #9: removed debug code 2015-06-20 01:38:45 +02:00
Andrey Prygunkov
eb904280e9 closes #9: updated linux build script
The script now works with git.
2015-06-20 01:36:41 +02:00
Andrey Prygunkov
2ec4ad331c #4: created .gitattributes 2015-06-19 20:29:04 +02:00
Andrey Prygunkov
393c9af054 Merge pull request #7 from nzbget/migration-naming
Naming for testing builds
2015-06-18 01:30:36 +02:00
Andrey Prygunkov
0f28e3e689 #3: implemented naming for testing builds
Naming:
 - for master-branch: no suffix;
 - for develop-branch: suffix “rXXXX”, for example “16.0-testing-r1257”;
 - for other branches: suffix “rXXXX (<branch>)”, for example
“16.0-testing-r1257M (migration-naming)”.
2015-06-18 01:21:20 +02:00
Andrey Prygunkov
c072ff570b #4: created .gitignore-file 2015-06-18 01:05:40 +02:00
Andrey Prygunkov
ec47da608f refactor: reducing usage of "float"-type where possible 2015-06-05 11:24:05 +00:00
Andrey Prygunkov
734dbfc98b refactor: changed one parameter in par2-module from "double" to "int" 2015-06-05 10:55:11 +00:00
Andrey Prygunkov
a78649773f improved linux installer: 1) better compatibility with android; 2) added support for paths with spaces in parameter "--destdir" 2015-05-26 21:30:10 +00:00
Andrey Prygunkov
dc2429d9b7 addition to r1304: added missing file 2015-05-25 20:52:56 +00:00
Andrey Prygunkov
965dabc415 integrated unit testing framework; created few first unit tests for: command line parser, options parser, rss feed filter, par-checker/repairer and par-renamer; new configure parameter "--enable-tests" to build the program with tests; use "nzbget --tests" to execute all tests or "nzbget --tests -h" for more options 2015-05-25 20:36:29 +00:00
Andrey Prygunkov
b04bdb0b9f addition to r1298: fixed compilation error on Windows 2015-05-25 18:04:35 +00:00
Andrey Prygunkov
9ce304ea52 addition to r1292: fixed: an error message was printed by command line commands "-h" and "-v" 2015-05-25 13:26:51 +00:00
Andrey Prygunkov
77a351c94c fixed: installation via universal Linux installer failed on certain WD NAS models due to Busybox limitations 2015-05-24 12:11:25 +00:00
Andrey Prygunkov
5d24697b0c refactor: reworked declaration of global objects (singletones) 2015-05-22 20:28:05 +00:00
Andrey Prygunkov
0cbeb2fc52 addition to r1297: fixed compilation error on Linux 2015-05-21 18:56:13 +00:00
Andrey Prygunkov
934c9e23ba refactor: new directory "extension" to hold all extension script related modules 2015-05-21 18:42:33 +00:00
Andrey Prygunkov
2441cc208f refactor: reducing module dependencies for easier testing: extracted config handling code from module "Options" into new module "ScriptConfig", which can be instantiated separately 2015-05-21 16:10:17 +00:00
Andrey Prygunkov
27fd8989d8 fixed: par-verification of repaired files were sometimes not skipped in quick verification mode (option "ParQuick=yes") 2015-05-21 14:41:14 +00:00
Andrey Prygunkov
a641bd8bd1 refactor: reducing module dependencies for easier testing: extracted par file name parsing code from module "ParCoordinator" into new module "ParParser", which can be instantiated separately 2015-05-21 14:33:16 +00:00
Andrey Prygunkov
366cb2e456 refactor: extracted function "CopyFile" from "MoveFile"; the new function is useful for testing 2015-05-21 10:59:25 +00:00
Andrey Prygunkov
31a7e133fa refactor: moved initialization of logging from main module into module "Log"; removed (now unused) global function "abort" 2015-05-21 10:55:06 +00:00
Andrey Prygunkov
c808b38778 refactor: reducing module dependencies for easier testing: extracted command line parsing code from module "Options" into new module "CommandLineParser"; modules "Options" and "CommandLineParser" do not depend on other modules and can be instantiated separately; they do not immediately abort program execution on fatal errors and instead report errors via state variables 2015-05-21 10:42:18 +00:00
Andrey Prygunkov
ac04dc248c refactor: reworked dependencies in rss feeds for easier filter tests 2015-05-20 20:29:58 +00:00
Andrey Prygunkov
15eb927137 updated version string to 16.0-testing 2015-05-20 18:17:01 +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
Andrey Prygunkov
f1b6492d1c fixed: unlike to all other scripts the update-script should not be automatically terminated when the program quits 2015-02-14 21:12:42 +00:00
Andrey Prygunkov
19d297f934 fixed: the program could crash during download when article cache was active (more likely on very high download speeds) 2015-02-11 22:38:59 +00:00
Andrey Prygunkov
a23128f25f addition to r1205: when sorting by priority in auto mode (without specifying + or -) the default order is descending since it is more logical to use for priority 2015-02-07 22:23:17 +00:00
Andrey Prygunkov
567f7c3028 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 sort to explicitly define ascending or descending order (for example "name-"); if none of these characters are used the auto-mode is active: the items are sorted in ascending order first, if nothing changed - they are sorted again in descending order 2015-02-07 19:17:49 +00:00
Andrey Prygunkov
30af4cfc3d fixed: XML-RPC method "history" returned invalid xml when used with parameter "hidden=true" (JSON-RPC worked correct) 2015-02-06 21:33:42 +00:00
Andrey Prygunkov
019fcf519a addition to r1182 and fix for r1193: unused connections are now closed only if there are no active connections on the same level; this reduces the reconnects during active download (which may be caused by the random connection pick-up implemented in r1182) 2015-02-03 20:05:49 +00:00
Andrey Prygunkov
1645562d78 eliminated compiler warning 2015-02-01 15:02:10 +00:00
Andrey Prygunkov
fab726482c improved windows installer: during an update the installer stops a possibly running NZBGet automatically 2015-01-27 20:32:08 +00:00
Andrey Prygunkov
351cb9835f suppress printing of memory leaks reports when the program terminates because of wrong command line switches (Windows debug mode only) 2015-01-27 20:30:14 +00:00
Andrey Prygunkov
d0d59469bc fixed: remote command "-L HA" (which prints the history including hidden records) could crash 2015-01-27 20:26:41 +00:00
Andrey Prygunkov
577d934ccd improved timeout handling during establishing of connections 2015-01-27 20:23:46 +00:00
Andrey Prygunkov
4438131d56 fixed: web-browser was launched on program reload; now it is launched only if the reload is initiated via tray menu (Windows only) 2015-01-26 21:26:32 +00:00
Andrey Prygunkov
7b13c9a9ba addition to r1182: fixed compilation error on certain systems (added missing include-directive) 2015-01-25 20:15:18 +00:00
Andrey Prygunkov
7d60566f3c reverted r1193 because of many problems reported by users (as a temporary solution) 2015-01-25 20:08:59 +00:00
Andrey Prygunkov
e9a7c2f184 fixed possible crash when using remote command "-B dump" to print debug info 2015-01-24 19:25:43 +00:00
Andrey Prygunkov
3e07873575 addition to r1182: unused connections are now closed only if there are no active connections on the same level; this reduces the reconnects during active download (which may be caused by the random connection pick-up implemented in r1182) 2015-01-24 18:49:59 +00:00
Andrey Prygunkov
2f17584ab4 update info in about dialogs (Windows and Mac OSX) 2015-01-24 12:44:27 +00:00
Andrey Prygunkov
02f87b23fb fixed: command "download again" was not disabled for hidden history records and resulted in a crash 2015-01-23 20:01:08 +00:00
Andrey Prygunkov
31032e29f5 when launching web-browser from the tray icon now using the real IP-address from option "ControlIP" instead of hard coded "127.0.0.1" (windows only) 2015-01-23 19:41:45 +00:00
Andrey Prygunkov
11bfb57809 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 2015-01-22 20:57:39 +00:00
Andrey Prygunkov
0e83ef32bb addition to r1187: renaming of hidden history items is now also supported 2015-01-17 16:34:49 +00:00
Andrey Prygunkov
86ae9e94cd name and category of history items can now be changed in web-interface; RPC-API method "editqueue" extended with new actions "HistorySetName" and "HistorySetCategory" 2015-01-15 20:46:17 +00:00
Andrey Prygunkov
2388250dfa 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 2015-01-15 18:09:37 +00:00
Andrey Prygunkov
d947ea65a2 added confirmation dialogs to recently implemented mass history edit commands "mark as good" and "mark as bad" 2015-01-13 21:37:04 +00:00
Andrey Prygunkov
4a11c04742 added subcommand "HA" to remote command "--list/-L" to list the whole history including hidden records 2015-01-06 20:00:22 +00:00
Andrey Prygunkov
14b24e6050 added support for negative numeric values in rss filter (useful for fields "dupescore" and "priority") 2014-12-21 19:28:38 +00:00
Andrey Prygunkov
4402d6fbd6 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 2014-12-21 18:21:49 +00:00
Andrey Prygunkov
c69b81404c small change in error message text 2014-12-21 18:21:16 +00:00
Andrey Prygunkov
241a3efacf 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. 2014-12-12 18:22:20 +00:00
Andrey Prygunkov
185d52a9d4 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 2014-12-11 20:45:08 +00:00
Andrey Prygunkov
6b933f18dd options "ParIgnoreExt" and "ExtCleanupDisk" can now contain wildcard characters * and ? 2014-12-08 21:36:23 +00:00
Andrey Prygunkov
31dbbb546c 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 2014-11-30 17:08:00 +00:00
Andrey Prygunkov
ac4f8a30e5 improved application for Windows: added tray icon (near clock); left click on icon pauses/resumes download; right lick 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 2014-11-30 14:24:23 +00:00
Andrey Prygunkov
a060531ae3 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) 2014-11-25 19:23:17 +00:00
Andrey Prygunkov
fb77937acd fixed: unrar may sometimes fail with message "no files to extract" 2014-11-25 19:18:07 +00:00
Andrey Prygunkov
9d9a81710f fixed false memory leak warning when compiled in debug mode (Windows only) 2014-11-24 22:31:57 +00:00
Andrey Prygunkov
c3b4438d1f fixed: program could crash during unpack (bug introduced in v14-r1130) 2014-11-22 18:03:08 +00:00
Andrey Prygunkov
eeb3679b82 addition to r1159: fixed: menubar icon was not visible on OSX in dark mode 2014-11-18 18:26:24 +00:00
Andrey Prygunkov
d2d9bfb4bd system sleep on idle state is now prevented during download and post-processing (Mac OSX only) 2014-11-16 16:24:06 +00:00
Andrey Prygunkov
2dcbe4628b fixed: menubar icon was not visible on OSX in dark mode 2014-11-15 19:05:45 +00:00
Andrey Prygunkov
634247676a fixed: quick par-check could hang on certain nzb-files containing multiple par-sets (occured only in 64 bit mode) 2014-11-14 19:38:41 +00:00
Andrey Prygunkov
1a01b323e5 updated version string to 15.0-testing 2014-11-14 19:29:27 +00:00
Andrey Prygunkov
c71a33eba0 updated version string (preparing to release 14.0) 2014-11-09 10:04:04 +00:00
Andrey Prygunkov
0387c7a8e1 updated ChangeLog 2014-11-09 09:50:41 +00:00
Andrey Prygunkov
1ae0404592 addition to r1152: fixed: the old directory was sometimes not removed when the download was renamed or assigned to another category (bug introduced in v14) 2014-11-03 19:55:25 +00:00
Andrey Prygunkov
6796bef261 fixed: the old directory was sometimes not removed when the download was renamed or assigned to another category (bug introduced in v14) 2014-11-01 13:05:30 +00:00
Andrey Prygunkov
a5bd6dc7c5 fixed: description was not shown correctly for queue scripts with defined events (bug introduced in r1148) 2014-11-01 11:00:40 +00:00
Andrey Prygunkov
4e7b9290ac fixed: program could crash during restart if an extension script was running; now all active scripts are terminated during restart 2014-10-21 20:21:31 +00:00
Andrey Prygunkov
9acbee976d fixed potential crash which could happen in debug mode during program restart 2014-10-21 19:32:07 +00:00
Andrey Prygunkov
e6f4f8c05e queue scripts can now define what events they are interested in; this avoids unnecessary calling of the scripts which do not process certain events 2014-10-20 21:17:54 +00:00
Andrey Prygunkov
c89cb3d287 addition to r1145: fixed a compiling error on OS/2 2014-10-19 21:03:11 +00:00
Andrey Prygunkov
c5cb95fd8c additional parameters (env. vars) are now passed to scan scripts: NZBNP_DUPEKEY, NZBNP_DUPESCORE, NZBNP_DUPEMODE; scan-scripts can now set dupekey, dupemode and dupescore by printing new special commands 2014-10-16 20:40:09 +00:00
Andrey Prygunkov
fa46714b19 debug builds for Windows now print call stack on crash to the log-file, which is very useful for debugging 2014-10-15 21:58:30 +00:00
Andrey Prygunkov
bfbcde3b47 fixed: RPC-method "editqueue" with action "HistoryReturn" caused a crash if the history item did not have any remaining (parked) files 2014-10-14 16:12:25 +00:00
Andrey Prygunkov
c6dc66cb45 addition to r1128: paths with drive letters are now considered absolute on all OSes not only on Windows because there are also other OSes using drive letters 2014-10-12 21:34:26 +00:00
Andrey Prygunkov
a9e6912a2f added column "age" to history tab in web-interface 2014-10-12 14:23:54 +00:00
Andrey Prygunkov
eb8885b915 fixed: a superfluous comma at the end of option "TaskX.Time" was interpreted as an error or may cause a crash 2014-10-11 22:10:51 +00:00
Andrey Prygunkov
029c808458 added news server name to message "Cancelling hanging download ..." to help identifying problematic servers 2014-10-10 21:13:02 +00:00
Andrey Prygunkov
9269f69a38 improvement in quick par-verification: if unpack fails (excluding invalid password errors) and quick par-check does not find any errors or quick par-check was already performed the full par-check is performed; this helps in rare situations when files were correctly downloaded (and therefore assumed correct by quick par-check) but incorrectly written into disk due to abnormal program termination (caused by bugs or hardware crashes) 2014-10-09 21:11:42 +00:00
Andrey Prygunkov
63d938ae04 fixed: RPC-method "saveconfig" did not work via XML-RPC (but worked via JSON-RPC) 2014-10-09 16:06:39 +00:00
Andrey Prygunkov
a8aa110f43 added missing new line character at the end of the help screen printed by "nzbget -h" 2014-10-05 15:13:30 +00:00
Andrey Prygunkov
6f7af5aef4 option "ParThreads" can now be set to "0" (which is a default setting now) to let the program automatically determine the number of CPU cores; this works on major modern platforms) 2014-10-04 19:34:03 +00:00
Andrey Prygunkov
6afbade8f7 improved scan-scripts: if the category of nzb-file is changed by the scan-script the assigned post-processing scripts are now automatically reset according to the new category 2014-10-03 20:58:11 +00:00
Andrey Prygunkov
5ec38498f1 quick par verification now works even if articles do not contain CRCs (although it is a rare case) 2014-09-27 22:18:49 +00:00
Andrey Prygunkov
e206d3a833 fixed several compiler warnings 2014-09-27 21:04:06 +00:00
Andrey Prygunkov
6529cf6498 addition to r1089: fixed: env. var "NZBPP_PARSTATUS" were not set to "FAILURE" for deleted/marked downloads 2014-09-27 21:03:23 +00:00
Andrey Prygunkov
21f5de8de8 improved cleanup: disk cleanup is now not performed if unrar failed even if par-check was successful; 2) queue cleanup (for remaining par2-files) is now made more smarter: the files are kept (parked) if they can be used by command "post-process again" and are removed otherwise 2014-09-25 22:08:57 +00:00
Andrey Prygunkov
837d5c7f68 unpack is now immediately aborted if unrar reports wrong password (works for rar5 as well as for older formats); the unpack error status "PASSWORD" is now set for older formats (not only rar5) 2014-09-24 20:52:28 +00:00
Andrey Prygunkov
f90a53c2b0 addition to r1127: better compatibility with unrar 5 2014-09-22 19:37:52 +00:00
Andrey Prygunkov
e184e5b7c5 fixed: relative destination paths (options "DestDir" and "CategoryX.DestDir") caused failures during unrar 2014-09-21 15:37:10 +00:00
Andrey Prygunkov
1ca1381e05 unpack is now automatically immediately aborted when unrar reports CRC errors 2014-09-18 16:48:00 +00:00
Andrey Prygunkov
811f807de6 fixed: splitted .cbr-files were not properly joined 2014-09-16 22:15:02 +00:00
Andrey Prygunkov
95b76bc586 when option "ContinuePartial" is active the current state is saved not more often than once per second instead of after every downloaded article; this significantly reduce the amount of disk writings on high download speeds 2014-09-16 20:54:50 +00:00
Andrey Prygunkov
90fac39a26 added commands "PausePostProcess" and "UnpausePostProcess" to scheduler 2014-09-15 16:28:55 +00:00
Andrey Prygunkov
44cf680f14 an improvement in duplicate check: if a new download with empty dupekey and empty dupescore is marked as "dupe" and the another download with the same name have non empty dupekey or dupescore these properties are copied from that download; this is useful because the new download is most likely another upload of the same file and it should have the same duplicate properties for best duplicate handling results 2014-09-13 21:30:42 +00:00
Andrey Prygunkov
d0754e022f addition to r1121: now fixed on windows too: inner files (files listed in nzb) bigger than 2GB could not be downloaded 2014-09-08 19:35:11 +00:00
Andrey Prygunkov
ed7245c852 fixed: inner files (files listed in nzb) bigger than 2GB could not be downloaded 2014-09-07 10:00:52 +00:00
Andrey Prygunkov
2b44618858 added validation check for option "ParBuffer" when compiled in 32-bit 2014-09-06 19:50:23 +00:00
Andrey Prygunkov
a3634d689e fixed: web interface showed an error box when trying to submit files with extensions other than .nzb, although these files could be processed by a scan-script; now the error is not shown if any scan-script is set in options 2014-09-05 20:22:49 +00:00
Andrey Prygunkov
96e8cbd3c1 small improvement in multithreading par-repair: the number of repair threads is now automatically reduced to the amount of bad blocks if there are too few of them; if there is only one bad block the multithreading par-repair is switched off to avoid overhead of thread synchronisation (which does not make sense for one working thread) 2014-09-03 17:34:36 +00:00
Andrey Prygunkov
658d41f0fd refactor: moved nzbget specific code from libpar2 into nzbget units in order to make updates of libpar2 easier in the future 2014-09-03 17:28:29 +00:00
Andrey Prygunkov
9dab8fd7dc added multithreading par-repair: does not depend on other libraries and works on all platforms and all CPUs (with multiple cores); new option "ParThreads" to set the number of threads for repairing; new option "ParBuffer" to define the memory limit to use during par-repair 2014-09-02 23:07:32 +00:00
Andrey Prygunkov
2cb9d81a3c fixed: the program could crash during deleting of active download (bug introduced in r1108) 2014-08-30 15:10:49 +00:00
Andrey Prygunkov
2b4662856e better error reporting if a temp file could not be found 2014-08-29 18:12:44 +00:00
Andrey Prygunkov
44e949eafe fixed: crash and possible queue corruption when option "ParCleanuQueue" was active (which is a default setting) (bug introduced in r1108) 2014-08-29 15:05:27 +00:00
Andrey Prygunkov
aa3acd12a6 for downloads delayed due to propagation delay (option "PropagationDelay") a new badge "propagation" is now shown near download name 2014-08-28 20:51:29 +00:00
Andrey Prygunkov
1c00e62d3e fixed: the "pause extra pars"-state was missing in the pause/resume-loop of curses interface, key "P" 2014-08-28 20:29:51 +00:00
Andrey Prygunkov
0d630d9ea3 when connecting in remote mode using command line parameter "--connect/-C" the option "ControlIP" is now interpreted as "127.0.0.1" if it is set to "0.0.0.0" (instead of failing with an error message) 2014-08-28 20:22:20 +00:00
Andrey Prygunkov
7de78cd088 added new option "UrlTimeout" to set timeout for URL fetching and RSS feed fetching; renamed option "ConnectionTimeout" to "ArticleTimeout" 2014-08-28 19:31:31 +00:00
Andrey Prygunkov
0f98c72f1e fixed: cancelling of post-processing could delete the nzb-item completely (bug introduced in v14) 2014-08-28 19:15:42 +00:00
Andrey Prygunkov
459a79a1f1 improved pp-script EMail.py: now it can send time statistics (thanks to JVM for the patch) 2014-08-27 16:27:40 +00:00
Andrey Prygunkov
aaea8d9717 fixed: scheduler tasks were not checked after wake up if the sleep time was longer than 90 minutes 2014-08-25 20:12:38 +00:00
Andrey Prygunkov
d5b99732d1 fixed: no warning were printed for invalid values of option "ArticleCache" (max value 1900 when compiled in 32 bit mode) 2014-08-25 20:04:09 +00:00
Andrey Prygunkov
f5cef8a997 fixed: par-check could fail on valid files (bug introduced in libpar2 0.3) 2014-08-24 12:51:42 +00:00
Andrey Prygunkov
44907aa700 when quick par verification is active the repaired files are not verified to save time; the only reason for incorrect files after repair can be hardware errors (memory, disk) but this is not something NZBGet should care about 2014-08-22 17:24:34 +00:00
Andrey Prygunkov
54303d464b fixed: one log-message was printed only to global log but not to nzb-item pp-log 2014-08-22 17:05:30 +00:00
Andrey Prygunkov
4e83a68bf1 when a download is downloaded again (from history) the queue-scripts are now called with event "NZB_ADDED" 2014-08-22 16:57:54 +00:00
Andrey Prygunkov
00893a6cca updated configure-script to not require gcrypt for newer GnuTLS versions (when gcrypt is not needed) 2014-08-20 20:57:40 +00:00
Andrey Prygunkov
008768cea1 better error reporting during par-check 2014-08-20 18:51:13 +00:00
Andrey Prygunkov
43e096c6dc refactor: eliminated two compiler warnings 2014-08-19 20:53:00 +00:00
Andrey Prygunkov
b10b48f5e9 the list of scripts (pp-scripts, queue-scripts, etc.) is now read once on program start instead of reading everytime a script is executed; that eliminates the unnecessary disk access; the list of post-processing parameters shown on page "Postprocess" of download details dialog is now built using the preloaded list of scripts instead of reading the script config sections on every load of web-interface; the settings page of web-interface loads available scripts every time the page is shown; this allows to configure newly added scripts without restarting the program first (just like it was before); a restart is still required to apply the settings (just like it was before); RPC-method "configtemplates" has new parameter "loadFromDisk" 2014-08-19 19:56:09 +00:00
Andrey Prygunkov
1a76c72bf3 fixed: the program could crash during executing of queue-scripts (bug introduced in r1094); the list of queue-scripts is now read only once, at program start; queue-scripts added to scripts-directory after the program was started can be selected in download details dialog on page "Postprocess" but will not be executed until the program is restarted 2014-08-19 19:47:49 +00:00
Andrey Prygunkov
74a1f6301a added option "EventInterval" allowing to reduce the number of calls of queue-scripts, which can be useful on slow systems 2014-08-19 19:45:30 +00:00
Andrey Prygunkov
dd22ec68fc improvement in support for detection of bad downloads (fakes, etc.): scripts supporting two modes (post-processing-mode and queue-mode) are now executed if selected in post-processing parameters: either in options "PostScript" and "CategoryX.PostScript" or manually on page "Postprocess" of download details dialog in web-interface; it is not necessary to select dual-mode scripts in option "QueueScript"; that provides more flexibility: the scripts can be selected per-category or activated/deactivated for each nzb individually 2014-08-17 23:07:48 +00:00
Andrey Prygunkov
6ecdfc25fd updated description in config file template 2014-08-15 22:28:09 +00:00
Andrey Prygunkov
f439f09c2e improvement in support for detection of bad downloads (fakes, etc.): queue-scripts are now called after every downloaded file included in nzb; new event "FILE_DOWNLOADED" of parameter "NZBNA_EVENT"; event "UNPACK" removed; instead added event "NZB_DOWNLOADED" which is similar to "UNPACK" but is called for every download even not having archive files and even if unpack is disabled; the execution of queue-scripts is serialized - only one script is executed at a time and other scripts wait in script-queue; the script-queue is compressed so that the same script for the same event is not queued more than once; this reduces the number of calls of scripts if files are downloaded faster than queue-scripts can work up them; a call for event "NZB_DOWNLOADED" is always performed even if the previous calls for events "FILE_DOWNLOADED" were skipped; when a script marks nzb as bad the nzb is deleted from queue, no further internal post-processing (par, unrar, etc.) is made for the nzb but all post-processing scripts are executed; if option "DeleteCleanupDisk" is active the already downloaded files are deleted; new status "BAD" for field "DeleteStatus" of nzb-item in RPC-method "history"; queue-scripts can set post-processing parameters by printing special command, just like post-processing-scripts can do that; this simplifies transferring (of small amount) of information between queue-scripts and post-processing-scripts 2014-08-15 22:24:53 +00:00
Andrey Prygunkov
ebe955020c addition to r1072: fixed: renaming of active downloads was broken (bug introduced in r1070) 2014-08-15 17:17:05 +00:00
Andrey Prygunkov
60119a89c0 fixed: compiler error if configured using parameter "--disable-gzip" 2014-08-13 21:14:11 +00:00
Andrey Prygunkov
6a14353391 added support for detection of bad downloads (fakes, etc.): extended queue-scripts with new event "UNPACK", scripts are called before unpack and have a chance to detect bad downloads before unpacking; queue-scripts and post-processing scripts can mark downloads as bad by printing special command; marked downloads become status "FAILURE/BAD" and are processed by the program as failures (triggering duplicate handling); scripts executed thereafter see the new status and can react accordingly (inform an indexer or a third-party automation tool); new env. var "NZBNA_DIRECTORY" passed to queue scripts 2014-08-11 23:15:58 +00:00
Andrey Prygunkov
9090fe5fc9 fixed: not all statistic fields were reset when using command "Download again" (bug introduced in v14) 2014-08-11 18:10:47 +00:00
Andrey Prygunkov
93bc9a4293 fixed: malformed articles could crash the program (bug introduced in v14) 2014-08-11 18:02:15 +00:00
Andrey Prygunkov
80b2e22d9d added new search field "dupestatus" for use in rss filters: the search is performed through download queue and history testing items with the same dupekey or title as current rss item; the field contains comma-separated list of following possible statuses (if duplicates were found): QUEUED, DOWNLOADING, SUCCESS, WARNING, FAILURE or an empty string if there were no matching items found 2014-08-10 22:14:03 +00:00
Andrey Prygunkov
5a6a098990 suppressed certain warning types in VC++ project file (Windows) 2014-08-10 21:59:06 +00:00
Andrey Prygunkov
c64ef201ff addition to r1079: fixed: par-check could not be cancelled. 2014-08-10 16:42:23 +00:00
Andrey Prygunkov
817ae02295 fixed: damaged files could be ignored during par-check and not repair was performed (bug introduced in r1071) 2014-08-09 22:39:39 +00:00
Andrey Prygunkov
910dab98f1 fixed memory error which could lead to segfault (bug introduced in r1074) 2014-08-09 21:50:50 +00:00
Andrey Prygunkov
b9c59ffad4 fixed few compiler warnings 2014-08-09 15:50:09 +00:00
Andrey Prygunkov
79426ec959 fixed: when rotating log-files option TimeCorrection were not respected when bulding new file name - the filename could have wrong date stamp in the name (bug introduced in r1059) 2014-08-09 10:42:13 +00:00
Andrey Prygunkov
2e0ba0e3d1 integrated par2-module (libpar2) into NZBGet’s source code tree; the par2-module is now built automatically during building of NZBGet; this eliminates dependency from external libpar2 and libsigc++ making it much easier for users to compile NZBGet with newest recommended patches for libpar2 2014-08-08 22:37:30 +00:00
Andrey Prygunkov
0c3ce58ffa fixed: cleanup may leave some files undeleted (Mac OSX only) 2014-08-06 19:56:12 +00:00
Andrey Prygunkov
c482820746 addition to r1074: changed few info messages to debug as they supposed to be 2014-08-06 19:43:39 +00:00
Andrey Prygunkov
195bc1f290 addition to r1075: added missing changed file 2014-08-06 18:29:43 +00:00
Andrey Prygunkov
d8108f998b disabled block-by-block scan during par verification because: 1) it could cause incorrect verification results for certain kinds of damaged files; 1) after implementing of quick scan for damaged files the block-by-block scan was not necessary anymore; block-by-block scan was also removed from the libpar2-patch 2014-08-06 15:24:25 +00:00
Andrey Prygunkov
40de60dd8b added quick par verification for damaged (partially downloaded) files 2014-08-06 00:11:07 +00:00
Andrey Prygunkov
c9981472a8 refactor: disk state now holds info about failed files: their IDs, CRCs of download articles and full intitial article information; these data can be used later to retry download of failed articles and for quick par-verification of damaged files 2014-08-05 23:45:28 +00:00
Andrey Prygunkov
83b3789282 fixed: renaming of active downloads was broken (bug introduced in r1070) 2014-08-02 16:41:27 +00:00
Andrey Prygunkov
0078e9e225 options "ParIgnoreExt" and "ExtCleanupDisk" are now respected by par-check (in addition to being respected by par-rename): if all damaged or missing files are covered by these options then no par-repair is performed and the download assumed successful 2014-07-30 22:10:50 +00:00
Andrey Prygunkov
a62966227a added quick file verification during par-check/repair; if par-repair is required for download the files downloaded without errors are verified quickly by comparing their checksums against the checksums stored in the par2-file; this makes the verification of undamaged files almost instant; damaged files are verified as usual; new option "ParQuick" (active by default); added support for block-by-block scan of files during verification, which improves scan speed of damaged files; the quick par-verification requires a patch for libpar2 (see http://nzbget.net/libpar2 for details) 2014-07-27 21:59:00 +00:00
Andrey Prygunkov
5f0ccf3257 fixed: certain nzb-files failed to download (with decoding errors) if article cache was active 2014-07-25 22:16:33 +00:00
Andrey Prygunkov
61d0a1d498 fixed: program could crash during download if there were missing articles, DirectWrite was disabled and ArticleCache was enabled 2014-07-25 21:57:14 +00:00
Andrey Prygunkov
c626528a83 fixed: post-process time (statistic) was not correctly reset when post-processing again 2014-07-25 21:53:40 +00:00
Andrey Prygunkov
2e0e8e18ef removed accidentally committed debug logging 2014-07-25 21:51:36 +00:00
Andrey Prygunkov
54d98a6cad if an nzb has only few failed articles it may have completion shown as 100%; now it is shown as 99.9% to indicate that not everything was successfully downloaded 2014-07-21 19:44:35 +00:00
Andrey Prygunkov
0fe503658b pp-script "EMail.py" now supports mail server relays (thanks l2g for the patch) 2014-07-20 16:20:24 +00:00
Andrey Prygunkov
5941464402 addition to r1057 (added article cache): fixed a segfault which could happen if none of articles could be downloaded for a file 2014-07-19 00:17:39 +00:00
Andrey Prygunkov
3074ea62dc added per-nzb time and size statistics: total time, download, verify, repair and unpack times, downloaded size and average speed, shown in history details dialog via click on the row with total size in statistics block; RPC-methods "listgroups" and "history" return new fields: "DownloadedSizeLo", "DownloadedSizeHi", "DownloadedSizeMB", "DownloadTimeSec", "PostTotalTimeSec", "ParTimeSec", "RepairTimeSec", "UnpackTimeSec" 2014-07-19 00:06:28 +00:00
Andrey Prygunkov
312bf91003 improved joining of splitted files: instead of performing par-repair the files are now joined by unpacker, which is much faster; the files splitted before creating of par-sets are now joined as well (they were not joined in v13 because par-repair has nothing to repair in this case); the unpacker can detect missing fragments and requests par-check if necessary 2014-07-18 23:27:41 +00:00
Andrey Prygunkov
a42c323343 refactor: removed an old commented code 2014-07-18 23:19:46 +00:00
Andrey Prygunkov
39d9fe2794 added log file rotation; options "CreateLog" and "ResetLog" replaced with new option "WriteLog (none, append, reset, rotate)"; new option "RotateLog" defines rotation period; when compiled in debug mode new field "process id" is printed to the file log for each row (it is easier to identify processes than threads) 2014-07-18 23:17:16 +00:00
Andrey Prygunkov
7993e2971c renamed option "WriteBufferSize" into "WriteBuffer"; changed the dimension - now option is set in kilobytes instead of bytes; old name and value are automatically converted; if the size of article is below the value defined by the option, the buffer is allocated with the articles size (to not waste memory); therefore the special value "-1" is not required anymore; during conversion "-1" is replaced with "1024" (1 megabyte) but it can be of course manually changed to any other value later 2014-07-18 23:06:45 +00:00
Andrey Prygunkov
ba9efe43be added article cache: new option "ArticleCache" defines memory limit to use for cache; when cache is active the articles are written into cache first and then all flushed to disk into the destination file; article cache reduces disk IO and may reduce file fragmentation improving post-processing speed (unpack); it works with both writing modes (direct write on and off); when option "DirectWrite" is disabled the cache should be big enough (for best performance) to accommodate all articles of one file (sometimes up to 500 MB) in order to avoid writing articles into temporary files, otherwise temporary files are used for articles which do not fill into cache; when used in combination with DirectWrite there is no such limitation and even a small cache (100 MB or even less) can be used effectively; when the cache becomes full it is flushed automatically (directly into destination file) providing room for new articles; new row in the "statistics and status dialog" in web-interface indicates the amount of memory used for cache; new fields "ArticleCacheLo", "ArticleCacheHi" and "ArticleCacheMB" returned by RPC-method "status"; refactor: parts of unit "ArticleDownloader" responsible for writing into disk were moved into new unit "ArticleWriter" 2014-07-18 22:48:35 +00:00
Andrey Prygunkov
cfa5e7d19c updated version string to 14.0-testing 2014-07-18 15:51:38 +00:00
256 changed files with 49060 additions and 13199 deletions

25
.gitattributes vendored Normal file
View File

@@ -0,0 +1,25 @@
* text=auto
# Use CRLF for certain Windows files.
*.vcproj eol=crlf
*.sln eol=crlf
*.bat eol=crlf
README-WINDOWS.txt eol=crlf
nzbget-setup.nsi eol=crlf
windows/package-info.json eol=crlf
windows/resources/resource.h eol=crlf
windows/resources/nzbget.rc eol=crlf
# Configure GitHub's language detector
lib/* linguist-vendored linguist-language=C++
webui/lib/* linguist-vendored
Makefile.in linguist-vendored
configure linguist-vendored
config.sub linguist-vendored
aclocal.m4 linguist-vendored
config.guess linguist-vendored
depcomp linguist-vendored
install-sh linguist-vendored
missing linguist-vendored
configure.ac linguist-vendored=false
Makefile.am linguist-vendored=false

63
.gitignore vendored Executable file
View File

@@ -0,0 +1,63 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
# GNU Autotools
.deps/
config.h
config.log
config.status
Makefile
stamp-h1
autom4te.cache/
# Visual Studio User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
.vs/
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
*.ilk
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.vspscc
*.vssscc
*.pidb
*.svclog
*.scc
*.sln
# NZBGet specific
nzbget
code_revision.cpp

View File

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

1517
ChangeLog
View File

File diff suppressed because it is too large Load Diff

167
INSTALL
View File

@@ -1,167 +0,0 @@
Basic Installation
==================
These are generic installation instructions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, a file
`config.cache' that saves the results of its tests to speed up
reconfiguring, and a file `config.log' containing compiler output
(useful mainly for debugging `configure').
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If at some point `config.cache'
contains results you don't want to keep, you may remove or edit it.
The file `configure.in' is used to create `configure' by a program
called `autoconf'. You only need `configure.in' if you want to change
it or regenerate `configure' using a newer version of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system. If you're
using `csh' on an old version of System V, you might need to type
`sh ./configure' instead to prevent `csh' from trying to execute
`configure' itself.
Running `configure' takes a while. While running, it prints some
messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Type `make install' to install the programs and any data files and
documentation.
4. You can remove the program binaries and object files from the
source code directory by typing `make clean'.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. You can give `configure'
initial values for variables by setting them in the environment. Using
a Bourne-compatible shell, you can do that on the command line like
this:
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
Or on systems that have the `env' program, you can do it like this:
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you must use a version of `make' that
supports the `VPATH' variable, such as GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
If you have to use a `make' that does not supports the `VPATH'
variable, you have to compile the package for one architecture at a time
in the source code directory. After you have installed the package for
one architecture, use `make distclean' before reconfiguring for another
architecture.
Installation Names
==================
By default, `make install' will install the package's files in
`/usr/local/bin', `/usr/local/man', etc. You can specify an
installation prefix other than `/usr/local' by giving `configure' the
option `--prefix=PATH'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
give `configure' the option `--exec-prefix=PATH', the package will use
PATH as the prefix for installing programs and libraries.
Documentation and other data files will still use the regular prefix.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' can not figure out
automatically, but needs to determine by the type of host the package
will run on. Usually `configure' can figure that out, but if it prints
a message saying it can not guess the host type, give it the
`--host=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name with three fields:
CPU-COMPANY-SYSTEM
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the host type.
If you are building compiler tools for cross-compiling, you can also
use the `--target=TYPE' option to select the type of system they will
produce code for and the `--build=TYPE' option to select the type of
system on which you are compiling the package.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Operation Controls
==================
`configure' recognizes the following options to control how it
operates.
`--cache-file=FILE'
Use and save the results of the tests in FILE instead of
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
debugging `configure'.
`--help'
Print a summary of the options to `configure', and exit.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made.
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--version'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`configure' also accepts some other, not widely useful, options.

View File

@@ -1,7 +1,7 @@
#
# This file if part of nzbget
# 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
@@ -28,6 +28,20 @@ nzbget_SOURCES = \
daemon/connect/TLS.h \
daemon/connect/WebDownloader.cpp \
daemon/connect/WebDownloader.h \
daemon/extension/FeedScript.cpp \
daemon/extension/FeedScript.h \
daemon/extension/NzbScript.cpp \
daemon/extension/NzbScript.h \
daemon/extension/PostScript.cpp \
daemon/extension/PostScript.h \
daemon/extension/QueueScript.cpp \
daemon/extension/QueueScript.h \
daemon/extension/ScanScript.cpp \
daemon/extension/ScanScript.h \
daemon/extension/SchedulerScript.cpp \
daemon/extension/SchedulerScript.h \
daemon/extension/ScriptConfig.cpp \
daemon/extension/ScriptConfig.h \
daemon/feed/FeedCoordinator.cpp \
daemon/feed/FeedCoordinator.h \
daemon/feed/FeedFile.cpp \
@@ -44,6 +58,10 @@ nzbget_SOURCES = \
daemon/frontend/LoggableFrontend.h \
daemon/frontend/NCursesFrontend.cpp \
daemon/frontend/NCursesFrontend.h \
daemon/main/CommandLineParser.cpp \
daemon/main/CommandLineParser.h \
daemon/main/DiskService.cpp \
daemon/main/DiskService.h \
daemon/main/Maintenance.cpp \
daemon/main/Maintenance.h \
daemon/main/nzbget.cpp \
@@ -52,8 +70,12 @@ nzbget_SOURCES = \
daemon/main/Options.h \
daemon/main/Scheduler.cpp \
daemon/main/Scheduler.h \
daemon/main/StackTrace.cpp \
daemon/main/StackTrace.h \
daemon/nntp/ArticleDownloader.cpp \
daemon/nntp/ArticleDownloader.h \
daemon/nntp/ArticleWriter.cpp \
daemon/nntp/ArticleWriter.h \
daemon/nntp/Decoder.cpp \
daemon/nntp/Decoder.h \
daemon/nntp/NewsServer.cpp \
@@ -64,14 +86,18 @@ nzbget_SOURCES = \
daemon/nntp/ServerPool.h \
daemon/nntp/StatMeter.cpp \
daemon/nntp/StatMeter.h \
daemon/postprocess/Cleanup.cpp \
daemon/postprocess/Cleanup.h \
daemon/postprocess/DupeMatcher.cpp \
daemon/postprocess/DupeMatcher.h \
daemon/postprocess/ParChecker.cpp \
daemon/postprocess/ParChecker.h \
daemon/postprocess/ParCoordinator.cpp \
daemon/postprocess/ParCoordinator.h \
daemon/postprocess/ParParser.cpp \
daemon/postprocess/ParParser.h \
daemon/postprocess/ParRenamer.cpp \
daemon/postprocess/ParRenamer.h \
daemon/postprocess/PostScript.cpp \
daemon/postprocess/PostScript.h \
daemon/postprocess/PrePostProcessor.cpp \
daemon/postprocess/PrePostProcessor.h \
daemon/postprocess/Unpack.cpp \
@@ -90,8 +116,6 @@ nzbget_SOURCES = \
daemon/queue/QueueCoordinator.h \
daemon/queue/QueueEditor.cpp \
daemon/queue/QueueEditor.h \
daemon/queue/QueueScript.cpp \
daemon/queue/QueueScript.h \
daemon/queue/Scanner.cpp \
daemon/queue/Scanner.h \
daemon/queue/UrlCoordinator.cpp \
@@ -115,12 +139,61 @@ nzbget_SOURCES = \
daemon/util/Script.h \
daemon/util/Thread.cpp \
daemon/util/Thread.h \
daemon/util/Service.cpp \
daemon/util/Service.h \
daemon/util/Util.cpp \
daemon/util/Util.h \
svn_version.cpp
code_revision.cpp
if WITH_PAR2
nzbget_SOURCES += \
lib/par2/commandline.cpp \
lib/par2/commandline.h \
lib/par2/crc.cpp \
lib/par2/crc.h \
lib/par2/creatorpacket.cpp \
lib/par2/creatorpacket.h \
lib/par2/criticalpacket.cpp \
lib/par2/criticalpacket.h \
lib/par2/datablock.cpp \
lib/par2/datablock.h \
lib/par2/descriptionpacket.cpp \
lib/par2/descriptionpacket.h \
lib/par2/diskfile.cpp \
lib/par2/diskfile.h \
lib/par2/filechecksummer.cpp \
lib/par2/filechecksummer.h \
lib/par2/galois.cpp \
lib/par2/galois.h \
lib/par2/letype.h \
lib/par2/mainpacket.cpp \
lib/par2/mainpacket.h \
lib/par2/md5.cpp \
lib/par2/md5.h \
lib/par2/par2cmdline.h \
lib/par2/par2creatorsourcefile.cpp \
lib/par2/par2creatorsourcefile.h \
lib/par2/par2fileformat.cpp \
lib/par2/par2fileformat.h \
lib/par2/par2repairer.cpp \
lib/par2/par2repairer.h \
lib/par2/par2repairersourcefile.cpp \
lib/par2/par2repairersourcefile.h \
lib/par2/parheaders.cpp \
lib/par2/parheaders.h \
lib/par2/recoverypacket.cpp \
lib/par2/recoverypacket.h \
lib/par2/reedsolomon.cpp \
lib/par2/reedsolomon.h \
lib/par2/verificationhashtable.cpp \
lib/par2/verificationhashtable.h \
lib/par2/verificationpacket.cpp \
lib/par2/verificationpacket.h
endif
AM_CPPFLAGS = \
-I$(srcdir)/daemon/connect \
-I$(srcdir)/daemon/extension \
-I$(srcdir)/daemon/feed \
-I$(srcdir)/daemon/frontend \
-I$(srcdir)/daemon/main \
@@ -128,21 +201,55 @@ AM_CPPFLAGS = \
-I$(srcdir)/daemon/postprocess \
-I$(srcdir)/daemon/queue \
-I$(srcdir)/daemon/remote \
-I$(srcdir)/daemon/util
-I$(srcdir)/daemon/util \
-I$(srcdir)/lib/par2
if WITH_TESTS
nzbget_SOURCES += \
lib/catch/catch.h \
tests/suite/TestMain.cpp \
tests/suite/TestMain.h \
tests/suite/TestUtil.cpp \
tests/suite/TestUtil.h \
tests/main/CommandLineParserTest.cpp \
tests/main/OptionsTest.cpp \
tests/feed/FeedFilterTest.cpp \
tests/postprocess/ParCheckerTest.cpp \
tests/postprocess/ParRenamerTest.cpp \
tests/queue/NZBFileTest.cpp \
tests/util/UtilTest.cpp
AM_CPPFLAGS += \
-I$(srcdir)/lib/catch \
-I$(srcdir)/tests/suite
endif
EXTRA_DIST = \
Makefile.cvs \
nzbgetd \
$(windows_FILES) \
$(osx_FILES)
$(osx_FILES) \
$(linux_FILES) \
$(testdata_FILES)
windows_FILES = \
daemon/windows/NTService.cpp \
daemon/windows/NTService.h \
daemon/windows/win32.h \
nzbget.sln \
daemon/windows/WinConsole.cpp \
daemon/windows/WinConsole.h \
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 \
@@ -168,8 +275,6 @@ osx_FILES = \
osx/Resources/Images/mainicon.icns \
osx/Resources/Images/statusicon.png \
osx/Resources/Images/statusicon@2x.png \
osx/Resources/Images/statusicon-inv.png \
osx/Resources/Images/statusicon-inv@2x.png \
osx/Resources/licenses/license-bootstrap.txt \
osx/Resources/licenses/license-jquery-GPL.txt \
osx/Resources/licenses/license-jquery-MIT.txt \
@@ -177,7 +282,17 @@ 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 \
README \
ChangeLog \
COPYING
@@ -221,8 +336,24 @@ scripts_FILES = \
scripts/EMail.py \
scripts/Logger.py
testdata_FILES = \
tests/testdata/dupematcher1/testfile.part01.rar \
tests/testdata/dupematcher1/testfile.part24.rar \
tests/testdata/dupematcher2/testfile.part04.rar \
tests/testdata/dupematcher2/testfile.part43.rar \
tests/testdata/nzbfile/dotless.nzb \
tests/testdata/nzbfile/dotless.txt \
tests/testdata/nzbfile/plain.nzb \
tests/testdata/nzbfile/plain.txt \
tests/testdata/parchecker/crc.txt \
tests/testdata/parchecker/testfile.dat \
tests/testdata/parchecker/testfile.nfo \
tests/testdata/parchecker/testfile.par2 \
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
tests/testdata/parchecker/testfile.vol03+3.PAR2
# Install
sbin_SCRIPTS = nzbgetd
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
@@ -240,13 +371,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"
@@ -269,44 +393,57 @@ install-conf:
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
# Determining git revision:
# 1) If directory ".git" exists we take revision from git log.
# File is recreated only if revision number was changed.
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
# 2) If directory ".git" doesn't exists we keep and reuse file "code_revision.cpp",
# which was possibly created early.
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
# we create new file "svn_version.c" with empty revision number.
svn_version.cpp: FORCE
@ if test -d ./.svn ; then \
V="$(shell svnversion -n .)"; \
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
# 3) If neither directory ".git" nor file "code_revision.cpp" are available
# we create new file "code_revision.c" with empty revision number.
code_revision.cpp: FORCE
@ if test -d ./.git ; then \
B="$(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')"; \
M="$(shell git status --porcelain)" ; \
if test "$$M" != "" ; then \
M="M" ; \
fi ; \
if test "$$B" = "master" ; then \
V="$$M" ; \
elif test "$$B" = "develop" ; then \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V="$${V}$$M" ; \
else \
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
V="$${V}$$M ($$B)" ; \
fi ; \
H="$(shell test -f ./code_revision.cpp && head -n 1 code_revision.cpp)"; \
if test "/* $$V */" != "$$H" ; then \
( \
echo "/* $$V */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "const char* code_revision(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"$$V\";" ;\
echo " return SVN_Version;" ;\
echo " const char* revision = \"$$V\";" ;\
echo " return revision;" ;\
echo "}" ;\
) > svn_version.cpp ; \
) > code_revision.cpp ; \
fi \
elif test -f ./svn_version.cpp ; then \
elif test -f ./code_revision.cpp ; then \
test "ok, reuse existing file"; \
else \
( \
echo "/* */" ;\
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
echo "const char* svn_version(void)" ;\
echo "const char* code_revision(void)" ;\
echo "{" ;\
echo " const char* SVN_Version = \"\";" ;\
echo " return SVN_Version;" ;\
echo " const char* revision = \"\";" ;\
echo " return revision;" ;\
echo "}" ;\
) > svn_version.cpp ; \
) > code_revision.cpp ; \
fi
FORCE:
# Ignore "svn_version.cpp" in distcleancheck
# Ignore "code_revision.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
@@ -317,4 +454,6 @@ clean-bak: rm *~
dist-hook:
find $(distdir)/daemon -type f -print -exec chmod -x {} \;
find $(distdir)/webui -type f -print -exec chmod -x {} \;
find $(distdir)/lib -type f -print -exec chmod -x {} \;
find $(distdir)/tests -type f -print -exec chmod -x {} \;

View File

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

1262
Makefile.in vendored
View File

File diff suppressed because it is too large Load Diff

0
NEWS
View File

126
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:
@@ -85,12 +76,7 @@ And the following libraries are optional:
- libcurses (usually part of commercial systems)
or (better)
- libncurses (http://invisible-island.net/ncurses)
- for par-check and -repair (enabled by default):
- libpar2 (http://launchpad.net/libpar2,
http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for encrypted connections (TLS/SSL):
- OpenSSL (http://www.openssl.org)
or
@@ -99,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
@@ -152,7 +138,7 @@ You may run configure with additional arguments:
if you can not use curses/ncurses.
--disable-parcheck - to make without parcheck-support. Use this option
if you can not use libpar2 or libsigc++.
if you have troubles when compiling par2-module.
--with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
should be used for encrypted server connections.
@@ -169,34 +155,13 @@ You may run configure with additional arguments:
Optional package: par-check
---------------------------
NZBGet can check and repair downloaded files for you. For this purpose
it uses library par2 (libpar2), which needs sigc++ on its part.
it uses library par2.
The libpar2 and libsigc++ (version 2 or later) must be installed on your
system. On most linux distributions these libraries are available as packages.
If you do not have these packages you can compile them yourself.
Following configure-parameters may be usefull:
For your convenience the source code of libpar2 is integrated into
NZBGets source tree and is compiled automatically when you make NZBGet.
--with-libpar2-includes
--with-libpar2-libraries
--with-libsigc-includes
--with-libsigc-libraries
The library libsigc++ must be installed first, since libpar2 requires it.
Official project libpar2 (http://parchive.sourceforge.net) has not been
updated for many years. The last official version of libpar 0.2 contains
known bugs which may crash the program during par-check. These bugs
have been fixed in the semi-official fork maintained by Debian/Ubuntu
team (http://launchpad.net/libpar2). It is highly recommended to use this
version of libpar2 (version 0.4 or newer) instead of the last official
version 0.2. Recent releases of Debian/Ubuntu include this libpar2 version.
NZBGets configure script checks the installed version of libpar2 and prints
an error if it is older than 0.4. The check can be suppressed if the update
of libpar2 is not possible.
If you are not able to use libpar2 or libsigc++ or do not want them you can
make nzbget without support for par-check using option "--disable-parcheck":
In a case errors occur during this process the inclusion of par2-module
can be disabled using configure option "--disable-parcheck":
./configure --disable-parcheck
@@ -204,10 +169,10 @@ Optional package: curses
-------------------------
For curses-outputmode you need ncurses or curses on your system.
If you do not have one of them you can download and compile ncurses yourself.
Following configure-parameters may be usefull:
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":
@@ -226,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":
@@ -245,24 +210,15 @@ NZBGet is developed using MS Visual C++ 2005. The project file and solution
are provided. If you use MS Visual C++ 2005 Express you need to download
and install Platform SDK.
To compile the program with par-check-support you also need the following
libraries:
- libsigc++ (http://libsigc.sourceforge.net)
- libpar2 (http://launchpad.net/libpar2,
http://parchive.sourceforge.net)
Download these libaries, then use patch-files provided with NZBGet to create
preconfigured project files and solutions for each library.
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
to use patch-files, if you do not familiar with this technique.
To compile the program with TLS/SSL support you also need the library:
To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS:
- OpenSSL (http://www.openssl.org)
or
- GnuTLS (http://www.gnu.org/software/gnutls)
Also required are:
- Regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
- Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm)
=====================================
6. Configuration
=====================================
@@ -288,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
@@ -497,6 +454,35 @@ Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
Since then the program has been completely rewritten.
NZBGet distribution archive includes additional components
written by other authors:
Par2:
Peter Brian Clements <peterbclements@users.sourceforge.net>
Par2 library API:
Francois Lesueur <flesueur@users.sourceforge.net>
Catch:
Two Blue Cubes Ltd <https://github.com/philsquared/Catch>
jQuery:
John Resig <http://jquery.com>
The Dojo Foundation <http://sizzlejs.com>
Bootstrap:
Twitter, Inc <http://twitter.github.com/bootstrap>
Raphaël:
Dmitry Baranovskiy <http://raphaeljs.com>
Sencha Labs <http://sencha.com>
Elycharts:
Void Labs s.n.c. <http://void.it>
iconSweets:
Yummygum <http://yummygum.com>
=====================================
9. Copyright
=====================================

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# NZBGet #
NZBGet is a binary downloader, which downloads files from Usenet
based on information given in nzb-files.
NZBGet is written in C++ and is known for its extraordinary performance and efficiency.
NZBGet can be run at almost every platform - classic PCs, NAS, media players, SAT-receivers, WLAN-routers, etc.
The download area provides precompiled binaries
for Windows, Mac OS X and Linux (compatible with many CPUs and platform variants). For other platforms
the program can be compiled from sources.
- [Home page (nzbget.net)](http://nzbget.net) - for first time visitors, learn more about NZBGet;
- [Downloads](http://nzbget.net/download) - get the binaries and sources;
- [Documentation](https://github.com/nzbget/nzbget/wiki) - installation manuals, HOW-TOs, API;
- [Forum](http://forum.nzbget.net) - get support, share your ideas, scripts, add-ons.

1500
config.guess vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -3,18 +3,25 @@
/* Define to 1 to include debug-code */
#undef DEBUG
/* Define to 1 if deleting of files during reading of directory is not
properly supported by OS */
#undef DIRBROWSER_SNAPSHOT
/* Define to 1 to not use curses */
#undef DISABLE_CURSES
/* Define to 1 to disable gzip-support */
#undef DISABLE_GZIP
/* Define to 1 to disable smart par-verification and restoration */
/* Define to 1 to disable par-verification and repair */
#undef DISABLE_PARCHECK
/* Define to 1 to not use TLS/SSL */
#undef DISABLE_TLS
/* Define to 1 to enable unit and integration tests */
#undef ENABLE_TESTS
/* Define to the name of macro which returns the name of function being
compiled */
#undef FUNCTION_MACRO_NAME
@@ -31,6 +38,22 @@
/* Define to 1 if you have the <curses.h> header file. */
#undef HAVE_CURSES_H
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
*/
#undef HAVE_DIRENT_H
/* Define to 1 if you have the <endian.h> header file. */
#undef HAVE_ENDIAN_H
/* Define to 1 if fdatasync is supported */
#undef HAVE_FDATASYNC
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
#undef HAVE_FSEEKO
/* Define to 1 if F_FULLFSYNC is supported */
#undef HAVE_FULLFSYNC
/* Define to 1 if getaddrinfo is supported */
#undef HAVE_GETADDRINFO
@@ -46,6 +69,12 @@
/* Define to 1 if gethostbyname_r takes 6 arguments */
#undef HAVE_GETHOSTBYNAME_R_6
/* Define to 1 if you have the `getopt' function. */
#undef HAVE_GETOPT
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
/* Define to 1 if getopt_long is supported */
#undef HAVE_GETOPT_LONG
@@ -55,6 +84,9 @@
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
#undef HAVE_LIBGNUTLS
/* Define to 1 if you have the `memcpy' function. */
#undef HAVE_MEMCPY
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
@@ -64,33 +96,53 @@
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
#undef HAVE_OPENSSL
/* Define to 1 if libpar2 has recent bugfixes-patch (version 2) */
#undef HAVE_PAR2_BUGFIXES_V2
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
/* Define to 1 if you have the <regex.h> header file. */
#undef HAVE_REGEX_H
/* Define to 1 if spinlocks are supported */
#undef HAVE_SPINLOCK
/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
#undef HAVE_SC_NPROCESSORS_ONLN
/* Define to 1 if stdbool.h conforms to C99. */
#undef HAVE_STDBOOL_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the `strchr' function. */
#undef HAVE_STRCHR
/* Define to 1 if you have the `stricmp' function. */
#undef HAVE_STRICMP
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_DIR_H
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
*/
#undef HAVE_SYS_NDIR_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
#undef HAVE_SYS_PRCTL_H
@@ -106,6 +158,12 @@
/* Define to 1 if variadic macros are supported */
#undef HAVE_VARIADIC_MACROS
/* 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
@@ -136,8 +194,27 @@
/* Version number of package */
#undef VERSION
/* Define to 1 if your processor stores words with the most significant byte
first (like Motorola and SPARC, unlike Intel and VAX). */
#undef WORDS_BIGENDIAN
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
#undef _LARGEFILE_SOURCE
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t

1616
config.sub vendored
View File

File diff suppressed because it is too large Load Diff

2989
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,31 @@
#
# This file is part of nzbget
#
# 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
# 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.
#
#
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 13.0, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 13.0)
AC_INIT(nzbget, 16.2, hugbug@users.sourceforge.net)
AC_CONFIG_AUX_DIR(posix)
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_SRCDIR([daemon/main/nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -55,6 +76,14 @@ AC_CHECK_FUNC(getopt_long,
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
dnl
dnl fsync
dnl
AC_CHECK_FUNC(fdatasync,
[AC_DEFINE([HAVE_FDATASYNC], 1, [Define to 1 if fdatasync is supported])],)
AC_CHECK_DECL(F_FULLFSYNC,
[AC_DEFINE([HAVE_FULLFSYNC], 1, [Define to 1 if F_FULLFSYNC is supported])],,[#include <fcntl.h>])
dnl
dnl use 64-Bits for file sizes
dnl
@@ -142,14 +171,6 @@ if test "$FOUND" = "no"; then
fi
dnl
dnl cCheck if spinlocks are available
dnl
AC_CHECK_FUNC(pthread_spin_init,
[AC_DEFINE([HAVE_SPINLOCK], 1, [Define to 1 if spinlocks are supported])]
AC_SEARCH_LIBS([pthread_spin_init], [pthread]),)
dnl
dnl Determine what socket length (socklen_t) data type is
dnl
@@ -180,6 +201,31 @@ AC_TRY_COMPILE([
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
dnl
dnl Dir-browser's snapshot
dnl
AC_MSG_CHECKING(whether dir-browser snapshot workaround is needed)
if test "$target_vendor" == "apple"; then
AC_MSG_RESULT([[yes]])
AC_DEFINE([DIRBROWSER_SNAPSHOT], 1, [Define to 1 if deleting of files during reading of directory is not properly supported by OS])
else
AC_MSG_RESULT([[no]])
fi
dnl
dnl check cpu cores via sysconf
dnl
AC_MSG_CHECKING(for cpu cores via sysconf)
AC_TRY_COMPILE(
[#include <unistd.h>],
[ int a = _SC_NPROCESSORS_ONLN; ],
FOUND="yes"
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_SC_NPROCESSORS_ONLN], 1, [Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h]),
FOUND="no")
dnl
dnl checks for libxml2 includes and libraries.
dnl
@@ -196,7 +242,8 @@ AC_ARG_WITH(libxml2_libraries,
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libxml2, libxml-2.0,
[LIBS="${LIBS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"])
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
AC_MSG_ERROR("libxml2 library not found"))
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files not found"))
@@ -252,116 +299,37 @@ fi
dnl
dnl Use libpar2 for par-checking. Deafult: no
dnl Use par-checking. Deafult: yes.
dnl
AC_MSG_CHECKING(whether to include code for par-checking)
AC_ARG_ENABLE(parcheck,
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support (removes dependency from libpar2- and libsigc-libraries)])],
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support])],
[ ENABLEPARCHECK=$enableval ],
[ ENABLEPARCHECK=yes] )
AC_MSG_RESULT($ENABLEPARCHECK)
if test "$ENABLEPARCHECK" = "yes"; then
dnl PAR2 checks.
dnl
dnl checks for libsigc++ includes and libraries (required for libpar2).
dnl
AC_ARG_WITH(libsigc_includes,
[AS_HELP_STRING([--with-libsigc-includes=DIR], [libsigc++-2.0 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libsigc_libraries,
[AS_HELP_STRING([--with-libsigc-libraries=DIR], [libsigc++-2.0 library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
[LIBS="${LIBS} $libsigc_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
fi
AC_CHECK_HEADER(sigc++/type_traits.h,,
AC_MSG_ERROR("libsigc++-2.0 header files not found"))
dnl
dnl checks for libpar2 includes and libraries.
dnl
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libpar2_includes,
[AS_HELP_STRING([--with-libpar2-includes=DIR], [libpar2 include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_CHECK_HEADER(libpar2/libpar2.h,,
AC_MSG_ERROR("libpar2 header files not found"))
AC_ARG_WITH(libpar2_libraries,
[AS_HELP_STRING([--with-libpar2-libraries=DIR], [libpar2 library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_SEARCH_LIBS([_ZN12Par2RepairerC1Ev], [par2], ,
AC_MSG_ERROR("libpar2 library not found"))
dnl
dnl check if libpar2 library is linkable
dnl
AC_MSG_CHECKING(for libpar2 linking)
AC_TRY_LINK(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { }; ],
[ Repairer* p = new Repairer(); ],
AC_MSG_RESULT([[yes]]),
AC_MSG_RESULT([[no]])
AC_MSG_ERROR("libpar2 library not found"))
dnl
dnl check if libpar2 has support for cancelling
dnl
AC_MSG_CHECKING(whether libpar2 supports cancelling)
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { cancelled = true; } }; ],
[],
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
AC_MSG_RESULT([[no]]))
dnl
dnl check if libpar2 has recent bugfixes-patch
dnl
AC_MSG_CHECKING(whether libpar2 has recent bugfixes)
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } }; ],
[],
AC_MSG_RESULT([[yes]])
PAR2PATCHV2=yes
AC_DEFINE([HAVE_PAR2_BUGFIXES_V2], 1, [Define to 1 if libpar2 has recent bugfixes-patch (version 2)]),
AC_MSG_RESULT([[no]])
PAR2PATCHV2=no)
if test "$PAR2PATCHV2" = "no" ; then
AC_ARG_ENABLE(libpar2-bugfixes-check,
[AS_HELP_STRING([--disable-libpar2-bugfixes-check], [do not check libpar2 version])],
[ PAR2PATCHCHECK=$enableval ],
[ PAR2PATCHCHECK=yes] )
if test "$PAR2PATCHCHECK" = "yes"; then
AC_ERROR([Your version of libpar2 doesn't include the recent bugfixes. Please update libpar2 to version 0.4 or newer (http://launchpad.net/libpar2). If you cannot install a newer version of libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The update is highly recommended!])
fi
fi
dnl Checks for header files.
AC_HEADER_DIRENT
AC_HEADER_STDBOOL
AC_HEADER_STDC
AC_CHECK_HEADERS([stdio.h] [endian.h] [getopt.h])
dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
AC_C_BIGENDIAN
AC_C_CONST
AC_C_INLINE
AC_FUNC_FSEEKO
dnl Checks for library functions.
AC_FUNC_MEMCMP
AC_CHECK_FUNCS([stricmp] [strcasecmp])
AC_CHECK_FUNCS([strchr] [memcpy])
AC_CHECK_FUNCS([getopt])
AM_CONDITIONAL(WITH_PAR2, true)
else
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable par-verification and repair])
AM_CONDITIONAL(WITH_PAR2, false)
fi
@@ -394,10 +362,9 @@ if test "$USETLS" = "yes"; then
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(openssl, openssl,
PKG_CHECK_MODULES([openssl], [openssl],
[LIBS="${LIBS} $openssl_LIBS"]
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"],
FOUND=yes
FOUND=no)
fi
@@ -445,10 +412,29 @@ if test "$USETLS" = "yes"; then
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
FOUND=yes,
FOUND=no),
FOUND=yes,
FOUND=no)
if test "$FOUND" = "yes"; then
dnl gcrypt is optional
AC_MSG_CHECKING([whether gcrypt is needed])
AC_TRY_COMPILE(
[#include <gnutls/gnutls.h>]
[#if GNUTLS_VERSION_NUMBER <= 0x020b00]
[compile error]
[#endif],
[int a;],
AC_MSG_RESULT([no])
GCRYPT=no,
AC_MSG_RESULT([yes])
GCRYPT=yes)
if test "$GCRYPT" = "yes"; then
AC_CHECK_HEADER([gcrypt.h],
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
FOUND=yes,
FOUND=no),
FOUND=yes)
fi
fi
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
AC_MSG_ERROR([Couldn't find GnuTLS library])
fi
@@ -538,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
@@ -611,8 +587,26 @@ 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
dnl
dnl Enable test suite. Deafult: no.
dnl
AC_MSG_CHECKING(whether to enable unit and integration tests)
AC_ARG_ENABLE(tests,
[AS_HELP_STRING([--enable-tests], [enable unit and integration tests])],
[ ENABLETESTS=$enableval ],
[ ENABLETESTS=no] )
AC_MSG_RESULT($ENABLETESTS)
if test "$ENABLETESTS" = "yes"; then
AC_DEFINE([ENABLE_TESTS],1,[Define to 1 to enable unit and integration tests])
AM_CONDITIONAL(WITH_TESTS, true)
else
AM_CONDITIONAL(WITH_TESTS, false)
fi
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

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,6 +66,43 @@ Mutex* Connection::m_pMutexGetHostByName = NULL;
#endif
#endif
void closesocket_gracefully(SOCKET iSocket)
{
char buf[1024];
struct linger linger;
// Set linger option to avoid socket hanging out after close. This prevent
// ephemeral port exhaust problem under high QPS.
linger.l_onoff = 1;
linger.l_linger = 1;
setsockopt(iSocket, SOL_SOCKET, SO_LINGER, (char *) &linger, sizeof(linger));
// Send FIN to the client
shutdown(iSocket, SHUT_WR);
// Set non-blocking mode
#ifdef WIN32
unsigned long on = 1;
ioctlsocket(iSocket, FIONBIO, &on);
#else
int flags;
flags = fcntl(iSocket, F_GETFL, 0);
fcntl(iSocket, F_SETFL, flags | O_NONBLOCK);
#endif
// Read and discard pending incoming data. If we do not do that and close the
// socket, the data in the send buffer may be discarded. This
// behaviour is seen on Windows, when client keeps sending data
// when server decides to close the connection; then when client
// does recv() it gets no data back.
int n;
do {
n = recv(iSocket, buf, sizeof(buf), 0);
} while (n > 0);
// Now we know that our FIN is ACK-ed, safe to close
closesocket(iSocket);
}
void Connection::Init()
{
@@ -128,6 +169,8 @@ 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;
m_bGracefull = false;
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_bTLSError = false;
@@ -254,14 +297,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 +392,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 +415,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 +444,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 +583,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 +602,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 +671,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 +680,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 +695,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");
@@ -643,7 +844,14 @@ bool Connection::DoDisconnect()
#ifndef DISABLE_TLS
CloseTLS();
#endif
closesocket(m_iSocket);
if (m_bGracefull)
{
closesocket_gracefully(m_iSocket);
}
else
{
closesocket(m_iSocket);
}
m_iSocket = INVALID_SOCKET;
}
@@ -686,12 +894,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 +924,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 +937,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,32 @@ protected:
bool m_bSuppressErrors;
char m_szRemoteAddr[20];
int m_iTotalBytesRead;
bool m_bBroken;
bool m_bGracefull;
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 +97,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
@@ -110,6 +137,8 @@ public:
void SetSuppressErrors(bool bSuppressErrors);
bool GetSuppressErrors() { return m_bSuppressErrors; }
const char* GetRemoteAddr();
bool GetGracefull() { return m_bGracefull; }
void SetGracefull(bool bGracefull) { m_bGracefull = bGracefull; }
#ifndef DISABLE_TLS
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
#endif

View File

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

@@ -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
@@ -49,8 +49,6 @@
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
WebDownloader::WebDownloader()
{
debug("Creating WebDownloader");
@@ -90,7 +88,7 @@ void WebDownloader::SetInfoName(const char* v)
void WebDownloader::SetURL(const char * szURL)
{
free(m_szURL);
m_szURL = strdup(szURL);
m_szURL = WebUtil::URLEncode(szURL);
}
void WebDownloader::SetStatus(EStatus eStatus)
@@ -113,14 +111,13 @@ void WebDownloader::Run()
iRemainedConnectRetries = 1;
}
m_iRedirects = 0;
EStatus Status = adFailed;
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
{
SetLastUpdateTimeNow();
Status = Download();
Status = DownloadWithRedirects(5);
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
@@ -147,17 +144,6 @@ void WebDownloader::Run()
break;
}
if (Status == adRedirect)
{
m_iRedirects++;
if (m_iRedirects > 5)
{
warn("Too many redirects for %s", m_szInfoName);
Status = adFailed;
break;
}
}
if (Status != adConnectError)
{
iRemainedDownloadRetries--;
@@ -207,6 +193,7 @@ WebDownloader::EStatus WebDownloader::Download()
return Status;
}
m_pConnection->SetTimeout(g_pOptions->GetUrlTimeout());
m_pConnection->SetSuppressErrors(false);
// connection
@@ -245,6 +232,24 @@ WebDownloader::EStatus WebDownloader::Download()
return Status;
}
WebDownloader::EStatus WebDownloader::DownloadWithRedirects(int iMaxRedirects)
{
// do sync download, following redirects
EStatus eStatus = adRedirect;
while (eStatus == adRedirect && iMaxRedirects >= 0)
{
iMaxRedirects--;
eStatus = Download();
}
if (eStatus == adRedirect && iMaxRedirects < 0)
{
warn("Too many redirects for %s", m_szInfoName);
eStatus = adFailed;
}
return eStatus;
}
WebDownloader::EStatus WebDownloader::CreateConnection(URL *pUrl)
{
@@ -365,10 +370,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;
}
@@ -419,10 +420,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;
@@ -594,15 +595,46 @@ void WebDownloader::ParseRedirect(const char* szLocation)
URL newUrl(szNewURL);
if (!newUrl.IsValid())
{
// relative address
// redirect within host
char szResource[1024];
URL oldUrl(m_szURL);
if (oldUrl.GetPort() > 0)
if (*szLocation == '/')
{
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szNewURL);
// absolute path within host
strncpy(szResource, szLocation, 1024);
szResource[1024-1] = '\0';
}
else
{
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szNewURL);
// relative path within host
strncpy(szResource, oldUrl.GetResource(), 1024);
szResource[1024-1] = '\0';
char* p = strchr(szResource, '?');
if (p)
{
*p = '\0';
}
p = strrchr(szResource, '/');
if (p)
{
p[1] = '\0';
}
strncat(szResource, szLocation, 1024 - strlen(szResource));
szResource[1024-1] = '\0';
}
if (oldUrl.GetPort() > 0)
{
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szResource);
}
else
{
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szResource);
}
szUrlBuf[1024-1] = '\0';
szNewURL = szUrlBuf;
@@ -664,9 +696,9 @@ bool WebDownloader::PrepareFile()
error("Could not %s file %s", "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() > 0)
if (g_pOptions->GetWriteBuffer() > 0)
{
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
setvbuf(m_pOutFile, NULL, _IOFBF, g_pOptions->GetWriteBuffer() * 1024);
}
return true;

View File

@@ -64,7 +64,6 @@ private:
bool m_bForce;
bool m_bRedirecting;
bool m_bRedirected;
int m_iRedirects;
bool m_bGZip;
bool m_bRetry;
#ifndef DISABLE_GZIP
@@ -93,6 +92,7 @@ public:
virtual void Run();
virtual void Stop();
EStatus Download();
EStatus DownloadWithRedirects(int iMaxRedirects);
bool Terminate();
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }

View File

@@ -0,0 +1,95 @@
/*
* 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$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#include "nzbget.h"
#include "FeedScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
void FeedScriptController::ExecuteScripts(const char* szFeedScript, const char* szFeedFile, int iFeedID)
{
FeedScriptController* pScriptController = new FeedScriptController();
pScriptController->m_szFeedFile = szFeedFile;
pScriptController->m_iFeedID = iFeedID;
pScriptController->ExecuteScriptList(szFeedScript);
delete pScriptController;
}
void FeedScriptController::ExecuteScript(ScriptConfig::Script* pScript)
{
if (!pScript->GetFeedScript())
{
return;
}
PrintMessage(Message::mkInfo, "Executing feed-script %s for Feed%i", pScript->GetName(), m_iFeedID);
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "feed-script %s for Feed%i", pScript->GetName(), m_iFeedID);
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(pScript->GetDisplayName());
PrepareParams(pScript->GetName());
Execute();
SetLogPrefix(NULL);
}
void FeedScriptController::PrepareParams(const char* szScriptName)
{
ResetEnv();
SetEnvVar("NZBFP_FILENAME", m_szFeedFile);
SetIntEnvVar("NZBFP_FEEDID", m_iFeedID);
PrepareEnvScript(NULL, szScriptName);
}

View File

@@ -0,0 +1,46 @@
/*
* 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$
*
*/
#ifndef FEEDSCRIPT_H
#define FEEDSCRIPT_H
#include "NzbScript.h"
class FeedScriptController : public NZBScriptController
{
private:
const char* m_szFeedFile;
int m_iFeedID;
void PrepareParams(const char* szScriptName);
protected:
virtual void ExecuteScript(ScriptConfig::Script* pScript);
public:
static void ExecuteScripts(const char* szFeedScript, const char* szFeedFile, int iFeedID);
};
#endif

View File

@@ -0,0 +1,125 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#include <algorithm>
#include "nzbget.h"
#include "NzbScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
/**
* If szStripPrefix is not NULL, only pp-parameters, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all pp-parameters are processed; without stripping.
*/
void NZBScriptController::PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix)
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szValue = pParameter->GetValue();
#ifdef WIN32
char* szAnsiValue = strdup(szValue);
WebUtil::Utf8ToAnsi(szAnsiValue, strlen(szAnsiValue) + 1);
szValue = szAnsiValue;
#endif
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, szValue);
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName(), szValue);
}
#ifdef WIN32
free(szAnsiValue);
#endif
}
}
void NZBScriptController::PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName)
{
if (pParameters)
{
PrepareEnvParameters(pParameters, NULL);
}
char szParamPrefix[1024];
snprintf(szParamPrefix, 1024, "%s:", szScriptName);
szParamPrefix[1024-1] = '\0';
if (pParameters)
{
PrepareEnvParameters(pParameters, szParamPrefix);
}
PrepareEnvOptions(szParamPrefix);
}
void NZBScriptController::ExecuteScriptList(const char* szScriptList)
{
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
{
ScriptConfig::Script* pScript = *it;
if (szScriptList && *szScriptList)
{
// split szScriptList into tokens
Tokenizer tok(szScriptList, ",;");
while (const char* szScriptName = tok.Next())
{
if (Util::SameFilename(szScriptName, pScript->GetName()))
{
ExecuteScript(pScript);
break;
}
}
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of nzbget
*
* 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
* 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 NZBSCRIPT_H
#define NZBSCRIPT_H
#include "Script.h"
#include "DownloadInfo.h"
#include "ScriptConfig.h"
class NZBScriptController : public ScriptController
{
protected:
void PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix);
void PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName);
void ExecuteScriptList(const char* szScriptList);
virtual void ExecuteScript(ScriptConfig::Script* pScript) = 0;
};
#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
@@ -45,8 +45,6 @@
#include "Util.h"
#include "Options.h"
extern Options* g_pOptions;
static const int POSTPROCESS_PARCHECK = 92;
static const int POSTPROCESS_SUCCESS = 93;
static const int POSTPROCESS_ERROR = 94;
@@ -94,7 +92,7 @@ void PostScriptController::Run()
m_pPostInfo->SetWorking(false);
}
void PostScriptController::ExecuteScript(Options::Script* pScript)
void PostScriptController::ExecuteScript(ScriptConfig::Script* pScript)
{
// if any script has requested par-check, do not execute other scripts
if (!pScript->GetPostScript() || m_pPostInfo->GetRequestParCheck())
@@ -104,6 +102,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);
@@ -112,6 +118,7 @@ void PostScriptController::ExecuteScript(Options::Script* pScript)
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
m_pScript = pScript;
SetLogPrefix(pScript->GetDisplayName());
m_iPrefixLen = strlen(pScript->GetDisplayName()) + 2; // 2 = strlen(": ");
PrepareParams(pScript->GetName());
@@ -146,6 +153,12 @@ void PostScriptController::PrepareParams(const char* szScriptName)
SetIntEnvVar("NZBPP_HEALTH", m_pPostInfo->GetNZBInfo()->CalcHealth());
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_pPostInfo->GetNZBInfo()->CalcCriticalHealth(false));
SetEnvVar("NZBPP_DUPEKEY", m_pPostInfo->GetNZBInfo()->GetDupeKey());
SetIntEnvVar("NZBPP_DUPESCORE", m_pPostInfo->GetNZBInfo()->GetDupeScore());
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBPP_DUPEMODE", szDupeModeName[m_pPostInfo->GetNZBInfo()->GetDupeMode()]);
char szStatus[256];
strncpy(szStatus, m_pPostInfo->GetNZBInfo()->MakeTextStatus(true), sizeof(szStatus));
szStatus[256-1] = '\0';
@@ -160,7 +173,15 @@ void PostScriptController::PrepareParams(const char* szScriptName)
// deprecated
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
SetIntEnvVar("NZBPP_PARSTATUS", iParStatus[m_pPostInfo->GetNZBInfo()->GetParStatus()]);
NZBInfo::EParStatus eParStatus = m_pPostInfo->GetNZBInfo()->GetParStatus();
// for downloads marked as bad and for deleted downloads pass par status "Failure"
// for compatibility with older scripts which don't check "NZBPP_TOTALSTATUS"
if (m_pPostInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsNone ||
m_pPostInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad)
{
eParStatus = NZBInfo::psFailure;
}
SetIntEnvVar("NZBPP_PARSTATUS", iParStatus[eParStatus]);
// deprecated
int iUnpackStatus[] = { 0, 0, 1, 2, 3, 4 };
@@ -224,6 +245,7 @@ ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int iExitCode)
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", GetInfoName());
m_pPostInfo->SetRequestParCheck(true);
m_pPostInfo->SetForceRepair(true);
return ScriptStatus::srSuccess;
}
break;
@@ -267,23 +289,30 @@ 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);
}
else if (!strncmp(szMsgText + 6, "MARK=BAD", 8))
{
SetLogPrefix(NULL);
PrintMessage(Message::mkWarning, "Marking %s as bad", m_pPostInfo->GetNZBInfo()->GetName());
SetLogPrefix(m_pScript->GetDisplayName());
m_pPostInfo->GetNZBInfo()->SetMarkStatus(NZBInfo::ksBad);
}
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
@@ -27,23 +27,20 @@
#define POSTSCRIPT_H
#include "Thread.h"
#include "Log.h"
#include "QueueScript.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "NzbScript.h"
class PostScriptController : public Thread, public NZBScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szNZBName[1024];
int m_iPrefixLen;
ScriptConfig::Script* m_pScript;
void PrepareParams(const char* szScriptName);
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
protected:
virtual void ExecuteScript(Options::Script* pScript);
virtual void ExecuteScript(ScriptConfig::Script* pScript);
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:

View File

@@ -0,0 +1,525 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#include "nzbget.h"
#include "QueueScript.h"
#include "NzbScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
static const char* QUEUE_EVENT_NAMES[] = { "FILE_DOWNLOADED", "URL_COMPLETED", "NZB_ADDED", "NZB_DOWNLOADED", "NZB_DELETED" };
class QueueScriptController : public Thread, public NZBScriptController
{
private:
char* m_szNZBName;
char* m_szNZBFilename;
char* m_szUrl;
char* m_szCategory;
char* m_szDestDir;
int m_iID;
int m_iPriority;
char* m_szDupeKey;
EDupeMode m_eDupeMode;
int m_iDupeScore;
NZBParameterList m_Parameters;
int m_iPrefixLen;
ScriptConfig::Script* m_pScript;
QueueScriptCoordinator::EEvent m_eEvent;
bool m_bMarkBad;
NZBInfo::EDeleteStatus m_eDeleteStatus;
NZBInfo::EUrlStatus m_eUrlStatus;
void PrepareParams(const char* szScriptName);
protected:
virtual void ExecuteScript(ScriptConfig::Script* pScript);
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual ~QueueScriptController();
virtual void Run();
static void StartScript(NZBInfo* pNZBInfo, ScriptConfig::Script* pScript, QueueScriptCoordinator::EEvent eEvent);
};
QueueScriptController::~QueueScriptController()
{
free(m_szNZBName);
free(m_szNZBFilename);
free(m_szUrl);
free(m_szCategory);
free(m_szDestDir);
free(m_szDupeKey);
}
void QueueScriptController::StartScript(NZBInfo* pNZBInfo, ScriptConfig::Script* pScript, QueueScriptCoordinator::EEvent eEvent)
{
QueueScriptController* pScriptController = new QueueScriptController();
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
pScriptController->m_szNZBFilename = strdup(pNZBInfo->GetFilename());
pScriptController->m_szUrl = strdup(pNZBInfo->GetURL());
pScriptController->m_szCategory = strdup(pNZBInfo->GetCategory());
pScriptController->m_szDestDir = strdup(pNZBInfo->GetDestDir());
pScriptController->m_iID = pNZBInfo->GetID();
pScriptController->m_iPriority = pNZBInfo->GetPriority();
pScriptController->m_szDupeKey = strdup(pNZBInfo->GetDupeKey());
pScriptController->m_eDupeMode = pNZBInfo->GetDupeMode();
pScriptController->m_iDupeScore = pNZBInfo->GetDupeScore();
pScriptController->m_Parameters.CopyFrom(pNZBInfo->GetParameters());
pScriptController->m_pScript = pScript;
pScriptController->m_eEvent = eEvent;
pScriptController->m_iPrefixLen = 0;
pScriptController->m_bMarkBad = false;
pScriptController->m_eDeleteStatus = pNZBInfo->GetDeleteStatus();
pScriptController->m_eUrlStatus = pNZBInfo->GetUrlStatus();
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void QueueScriptController::Run()
{
ExecuteScript(m_pScript);
SetLogPrefix(NULL);
if (m_bMarkBad)
{
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
if (pNZBInfo)
{
PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", m_szNZBName);
pNZBInfo->SetDeleteStatus(NZBInfo::dsBad);
pDownloadQueue->EditEntry(m_iID, DownloadQueue::eaGroupDelete, 0, NULL);
}
DownloadQueue::Unlock();
}
g_pQueueScriptCoordinator->CheckQueue();
}
void QueueScriptController::ExecuteScript(ScriptConfig::Script* pScript)
{
PrintMessage(m_eEvent == QueueScriptCoordinator::qeFileDownloaded ? Message::mkDetail : Message::mkInfo,
"Executing queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(pScript->GetDisplayName());
m_iPrefixLen = strlen(pScript->GetDisplayName()) + 2; // 2 = strlen(": ");
PrepareParams(pScript->GetName());
Execute();
SetLogPrefix(NULL);
}
void QueueScriptController::PrepareParams(const char* szScriptName)
{
ResetEnv();
SetEnvVar("NZBNA_NZBNAME", m_szNZBName);
SetIntEnvVar("NZBNA_NZBID", m_iID);
SetEnvVar("NZBNA_FILENAME", m_szNZBFilename);
SetEnvVar("NZBNA_DIRECTORY", m_szDestDir);
SetEnvVar("NZBNA_URL", m_szUrl);
SetEnvVar("NZBNA_CATEGORY", m_szCategory);
SetIntEnvVar("NZBNA_PRIORITY", m_iPriority);
SetIntEnvVar("NZBNA_LASTID", m_iID); // deprecated
SetEnvVar("NZBNA_DUPEKEY", m_szDupeKey);
SetIntEnvVar("NZBNA_DUPESCORE", m_iDupeScore);
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBNA_DUPEMODE", szDupeModeName[m_eDupeMode]);
SetEnvVar("NZBNA_EVENT", QUEUE_EVENT_NAMES[m_eEvent]);
const char* szDeleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD", "GOOD", "COPY", "SCAN" };
SetEnvVar("NZBNA_DELETESTATUS", szDeleteStatusName[m_eDeleteStatus]);
const char* szUrlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
SetEnvVar("NZBNA_URLSTATUS", szUrlStatusName[m_eUrlStatus]);
PrepareEnvScript(&m_Parameters, szScriptName);
}
void QueueScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
const char* szMsgText = szText + m_iPrefixLen;
if (!strncmp(szMsgText, "[NZB] ", 6))
{
debug("Command %s detected", szMsgText + 6);
if (!strncmp(szMsgText + 6, "NZBPR_", 6))
{
char* szParam = strdup(szMsgText + 6 + 6);
char* szValue = strchr(szParam, '=');
if (szValue)
{
*szValue = '\0';
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
if (pNZBInfo)
{
pNZBInfo->GetParameters()->SetParameter(szParam, szValue + 1);
}
DownloadQueue::Unlock();
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
free(szParam);
}
else if (!strncmp(szMsgText + 6, "MARK=BAD", 8))
{
m_bMarkBad = true;
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
if (pNZBInfo)
{
SetLogPrefix(NULL);
PrintMessage(Message::mkWarning, "Marking %s as bad", m_szNZBName);
SetLogPrefix(m_pScript->GetDisplayName());
pNZBInfo->SetMarkStatus(NZBInfo::ksBad);
}
DownloadQueue::Unlock();
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
}
else
{
ScriptController::AddMessage(eKind, szText);
}
}
QueueScriptCoordinator::QueueItem::QueueItem(int iNZBID, ScriptConfig::Script* pScript, EEvent eEvent)
{
m_iNZBID = iNZBID;
m_pScript = pScript;
m_eEvent = eEvent;
}
QueueScriptCoordinator::QueueScriptCoordinator()
{
m_pCurItem = NULL;
m_bStopped = false;
}
QueueScriptCoordinator::~QueueScriptCoordinator()
{
delete m_pCurItem;
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); it++ )
{
delete *it;
}
}
void QueueScriptCoordinator::InitOptions()
{
m_bHasQueueScripts = false;
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
{
ScriptConfig::Script* pScript = *it;
if (pScript->GetQueueScript())
{
m_bHasQueueScripts = true;
break;
}
}
}
void QueueScriptCoordinator::EnqueueScript(NZBInfo* pNZBInfo, EEvent eEvent)
{
if (!m_bHasQueueScripts)
{
return;
}
m_mutexQueue.Lock();
if (eEvent == qeNzbDownloaded)
{
// delete all other queued scripts for this nzb
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); )
{
QueueItem* pQueueItem = *it;
if (pQueueItem->GetNZBID() == pNZBInfo->GetID())
{
delete pQueueItem;
it = m_Queue.erase(it);
continue;
}
it++;
}
}
// respect option "EventInterval"
time_t tCurTime = time(NULL);
if (eEvent == qeFileDownloaded &&
(g_pOptions->GetEventInterval() == -1 ||
(g_pOptions->GetEventInterval() > 0 && tCurTime - pNZBInfo->GetQueueScriptTime() > 0 &&
(int)(tCurTime - pNZBInfo->GetQueueScriptTime()) < g_pOptions->GetEventInterval())))
{
m_mutexQueue.Unlock();
return;
}
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
{
ScriptConfig::Script* pScript = *it;
if (!pScript->GetQueueScript())
{
continue;
}
bool bUseScript = false;
// check queue-scripts
const char* szQueueScript = g_pOptions->GetQueueScript();
if (!Util::EmptyStr(szQueueScript))
{
// split szQueueScript into tokens
Tokenizer tok(szQueueScript, ",;");
while (const char* szScriptName = tok.Next())
{
if (Util::SameFilename(szScriptName, pScript->GetName()))
{
bUseScript = true;
break;
}
}
}
// check post-processing-scripts
if (!bUseScript)
{
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szVarname = pParameter->GetName();
if (strlen(szVarname) > 0 && szVarname[0] != '*' && szVarname[strlen(szVarname)-1] == ':' &&
(!strcasecmp(pParameter->GetValue(), "yes") ||
!strcasecmp(pParameter->GetValue(), "on") ||
!strcasecmp(pParameter->GetValue(), "1")))
{
char szScriptName[1024];
strncpy(szScriptName, szVarname, 1024);
szScriptName[1024-1] = '\0';
szScriptName[strlen(szScriptName)-1] = '\0'; // remove trailing ':'
if (Util::SameFilename(szScriptName, pScript->GetName()))
{
bUseScript = true;
break;
}
}
}
}
bUseScript &= Util::EmptyStr(pScript->GetQueueEvents()) || strstr(pScript->GetQueueEvents(), QUEUE_EVENT_NAMES[eEvent]);
if (bUseScript)
{
bool bAlreadyQueued = false;
if (eEvent == qeFileDownloaded)
{
// check if this script is already queued for this nzb
for (Queue::iterator it2 = m_Queue.begin(); it2 != m_Queue.end(); it2++)
{
QueueItem* pQueueItem = *it2;
if (pQueueItem->GetNZBID() == pNZBInfo->GetID() && pQueueItem->GetScript() == pScript)
{
bAlreadyQueued = true;
break;
}
}
}
if (!bAlreadyQueued)
{
QueueItem* pQueueItem = new QueueItem(pNZBInfo->GetID(), pScript, eEvent);
if (m_pCurItem)
{
m_Queue.push_back(pQueueItem);
}
else
{
StartScript(pNZBInfo, pQueueItem);
}
}
pNZBInfo->SetQueueScriptTime(time(NULL));
}
}
m_mutexQueue.Unlock();
}
NZBInfo* QueueScriptCoordinator::FindNZBInfo(DownloadQueue* pDownloadQueue, int iNZBID)
{
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(iNZBID);
if (!pNZBInfo)
{
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetNZBInfo() && pHistoryInfo->GetNZBInfo()->GetID() == iNZBID)
{
pNZBInfo = pHistoryInfo->GetNZBInfo();
break;
}
}
}
return pNZBInfo;
}
void QueueScriptCoordinator::CheckQueue()
{
if (m_bStopped)
{
return;
}
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
m_mutexQueue.Lock();
delete m_pCurItem;
m_pCurItem = NULL;
NZBInfo* pCurNZBInfo = NULL;
Queue::iterator itCurItem = m_Queue.end();
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); )
{
QueueItem* pQueueItem = *it;
NZBInfo* pNZBInfo = FindNZBInfo(pDownloadQueue, pQueueItem->GetNZBID());
// in a case this nzb must not be processed further - delete queue script from queue
if (!pNZBInfo ||
(pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone && pQueueItem->GetEvent() != qeNzbDeleted) ||
pNZBInfo->GetMarkStatus() == NZBInfo::ksBad)
{
delete pQueueItem;
it = m_Queue.erase(it);
continue;
}
if (!m_pCurItem || pQueueItem->GetEvent() > m_pCurItem->GetEvent())
{
m_pCurItem = pQueueItem;
itCurItem = it;
pCurNZBInfo = pNZBInfo;
}
it++;
}
if (m_pCurItem)
{
m_Queue.erase(itCurItem);
StartScript(pCurNZBInfo, m_pCurItem);
}
m_mutexQueue.Unlock();
DownloadQueue::Unlock();
}
void QueueScriptCoordinator::StartScript(NZBInfo* pNZBInfo, QueueItem* pQueueItem)
{
m_pCurItem = pQueueItem;
QueueScriptController::StartScript(pNZBInfo, pQueueItem->GetScript(), pQueueItem->GetEvent());
}
bool QueueScriptCoordinator::HasJob(int iNZBID, bool* pActive)
{
m_mutexQueue.Lock();
bool bWorking = m_pCurItem && m_pCurItem->GetNZBID() == iNZBID;
if (pActive)
{
*pActive = bWorking;
}
if (!bWorking)
{
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); it++)
{
QueueItem* pQueueItem = *it;
bWorking = pQueueItem->GetNZBID() == iNZBID;
if (bWorking)
{
break;
}
}
}
m_mutexQueue.Unlock();
return bWorking;
}
int QueueScriptCoordinator::GetQueueSize()
{
m_mutexQueue.Lock();
int iQueuedCount = m_Queue.size();
if (m_pCurItem)
{
iQueuedCount++;
}
m_mutexQueue.Unlock();
return iQueuedCount;
}

View File

@@ -0,0 +1,84 @@
/*
* This file is part of nzbget
*
* 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
* 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 QUEUESCRIPT_H
#define QUEUESCRIPT_H
#include <list>
#include "DownloadInfo.h"
#include "ScriptConfig.h"
class QueueScriptCoordinator
{
public:
enum EEvent
{
qeFileDownloaded, // lowest priority
qeUrlCompleted,
qeNzbAdded,
qeNzbDownloaded,
qeNzbDeleted // highest priority
};
private:
class QueueItem
{
private:
int m_iNZBID;
ScriptConfig::Script* m_pScript;
EEvent m_eEvent;
public:
QueueItem(int iNZBID, ScriptConfig::Script* pScript, EEvent eEvent);
int GetNZBID() { return m_iNZBID; }
ScriptConfig::Script* GetScript() { return m_pScript; }
EEvent GetEvent() { return m_eEvent; }
};
typedef std::list<QueueItem*> Queue;
Queue m_Queue;
Mutex m_mutexQueue;
QueueItem* m_pCurItem;
bool m_bHasQueueScripts;
bool m_bStopped;
void StartScript(NZBInfo* pNZBInfo, QueueItem* pQueueItem);
NZBInfo* FindNZBInfo(DownloadQueue* pDownloadQueue, int iNZBID);
public:
QueueScriptCoordinator();
~QueueScriptCoordinator();
void Stop() { m_bStopped = true; }
void InitOptions();
void EnqueueScript(NZBInfo* pNZBInfo, EEvent eEvent);
void CheckQueue();
bool HasJob(int iNZBID, bool* pActive);
int GetQueueSize();
};
extern QueueScriptCoordinator* g_pQueueScriptCoordinator;
#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
@@ -41,96 +41,16 @@
#include <stdio.h>
#include "nzbget.h"
#include "QueueScript.h"
#include "ScanScript.h"
#include "Scanner.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
/**
* If szStripPrefix is not NULL, only pp-parameters, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all pp-parameters are processed; without stripping.
*/
void NZBScriptController::PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix)
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szValue = pParameter->GetValue();
#ifdef WIN32
char* szAnsiValue = strdup(szValue);
WebUtil::Utf8ToAnsi(szAnsiValue, strlen(szAnsiValue) + 1);
szValue = szAnsiValue;
#endif
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, szValue);
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName(), szValue);
}
#ifdef WIN32
free(szAnsiValue);
#endif
}
}
void NZBScriptController::PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName)
{
if (pParameters)
{
PrepareEnvParameters(pParameters, NULL);
}
char szParamPrefix[1024];
snprintf(szParamPrefix, 1024, "%s:", szScriptName);
szParamPrefix[1024-1] = '\0';
if (pParameters)
{
PrepareEnvParameters(pParameters, szParamPrefix);
}
PrepareEnvOptions(szParamPrefix);
}
void NZBScriptController::ExecuteScriptList(const char* szScriptList)
{
Options::ScriptList scriptList;
g_pOptions->LoadScriptList(&scriptList);
for (Options::ScriptList::iterator it = scriptList.begin(); it != scriptList.end(); it++)
{
Options::Script* pScript = *it;
if (szScriptList && *szScriptList)
{
// split szScriptList into tokens
Tokenizer tok(szScriptList, ",;");
while (const char* szScriptName = tok.Next())
{
if (Util::SameFilename(szScriptName, pScript->GetName()))
{
ExecuteScript(pScript);
break;
}
}
}
}
}
void ScanScriptController::ExecuteScripts(const char* szNZBFilename,
const char* szUrl, const char* szDirectory, char** pNZBName, char** pCategory,
int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused)
int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused,
char** pDupeKey, int* iDupeScore, EDupeMode* eDupeMode)
{
ScanScriptController* pScriptController = new ScanScriptController();
@@ -143,6 +63,9 @@ void ScanScriptController::ExecuteScripts(const char* szNZBFilename,
pScriptController->m_iPriority = iPriority;
pScriptController->m_bAddTop = bAddTop;
pScriptController->m_bAddPaused = bAddPaused;
pScriptController->m_pDupeKey = pDupeKey;
pScriptController->m_iDupeScore = iDupeScore;
pScriptController->m_eDupeMode = eDupeMode;
pScriptController->m_iPrefixLen = 0;
pScriptController->ExecuteScriptList(g_pOptions->GetScanScript());
@@ -150,7 +73,7 @@ void ScanScriptController::ExecuteScripts(const char* szNZBFilename,
delete pScriptController;
}
void ScanScriptController::ExecuteScript(Options::Script* pScript)
void ScanScriptController::ExecuteScript(ScriptConfig::Script* pScript)
{
if (!pScript->GetScanScript() || !Util::FileExists(m_szNZBFilename))
{
@@ -187,6 +110,11 @@ void ScanScriptController::PrepareParams(const char* szScriptName)
SetIntEnvVar("NZBNP_PRIORITY", *m_iPriority);
SetIntEnvVar("NZBNP_TOP", *m_bAddTop ? 1 : 0);
SetIntEnvVar("NZBNP_PAUSED", *m_bAddPaused ? 1 : 0);
SetEnvVar("NZBNP_DUPEKEY", *m_pDupeKey);
SetIntEnvVar("NZBNP_DUPESCORE", *m_iDupeScore);
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBNP_DUPEMODE", szDupeModeName[*m_eDupeMode]);
// remove trailing slash
char szDir[1024];
@@ -218,6 +146,7 @@ void ScanScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
free(*m_pCategory);
*m_pCategory = strdup(szMsgText + 6 + 9);
g_pScanner->InitPPParameters(*m_pCategory, m_pParameters, true);
}
else if (!strncmp(szMsgText + 6, "NZBPR_", 6))
{
@@ -246,6 +175,26 @@ void ScanScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
*m_bAddPaused = atoi(szMsgText + 6 + 7) != 0;
}
else if (!strncmp(szMsgText + 6, "DUPEKEY=", 8))
{
free(*m_pDupeKey);
*m_pDupeKey = strdup(szMsgText + 6 + 8);
}
else if (!strncmp(szMsgText + 6, "DUPESCORE=", 10))
{
*m_iDupeScore = atoi(szMsgText + 6 + 10);
}
else if (!strncmp(szMsgText + 6, "DUPEMODE=", 9))
{
const char* szDupeMode = szMsgText + 6 + 9;
if (strcasecmp(szDupeMode, "score") && strcasecmp(szDupeMode, "all") && strcasecmp(szDupeMode, "force"))
{
error("Invalid value \"%s\" for command \"DUPEMODE\" received from %s", szDupeMode, GetInfoName());
return;
}
*m_eDupeMode = !strcasecmp(szDupeMode, "all") ? dmAll :
!strcasecmp(szDupeMode, "force") ? dmForce : dmScore;
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
@@ -256,75 +205,3 @@ void ScanScriptController::AddMessage(Message::EKind eKind, const char* szText)
ScriptController::AddMessage(eKind, szText);
}
}
QueueScriptController::~QueueScriptController()
{
free(m_szNZBName);
free(m_szNZBFilename);
free(m_szUrl);
free(m_szCategory);
}
void QueueScriptController::StartScripts(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo)
{
QueueScriptController* pScriptController = new QueueScriptController();
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
pScriptController->m_szNZBFilename = strdup(pNZBInfo->GetFilename());
pScriptController->m_szUrl = strdup(pNZBInfo->GetURL());
pScriptController->m_szCategory = strdup(pNZBInfo->GetCategory());
pScriptController->m_iID = pNZBInfo->GetID();
pScriptController->m_iPriority = pNZBInfo->GetPriority();
pScriptController->m_Parameters.CopyFrom(pNZBInfo->GetParameters());
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void QueueScriptController::Run()
{
ExecuteScriptList(g_pOptions->GetQueueScript());
}
void QueueScriptController::ExecuteScript(Options::Script* pScript)
{
if (!pScript->GetQueueScript())
{
return;
}
PrintMessage(Message::mkInfo, "Executing queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(pScript->GetDisplayName());
PrepareParams(pScript->GetName());
Execute();
SetLogPrefix(NULL);
}
void QueueScriptController::PrepareParams(const char* szScriptName)
{
ResetEnv();
SetEnvVar("NZBNA_NZBNAME", m_szNZBName);
SetIntEnvVar("NZBNA_NZBID", m_iID);
SetEnvVar("NZBNA_FILENAME", m_szNZBFilename);
SetEnvVar("NZBNA_URL", m_szUrl);
SetEnvVar("NZBNA_EVENT", "NZB_ADDED");
SetEnvVar("NZBNA_CATEGORY", m_szCategory);
SetIntEnvVar("NZBNA_PRIORITY", m_iPriority);
SetIntEnvVar("NZBNA_LASTID", m_iID); // deprecated
PrepareEnvScript(&m_Parameters, szScriptName);
}

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
@@ -23,22 +23,10 @@
*/
#ifndef QUEUESCRIPT_H
#define QUEUESCRIPT_H
#ifndef SCANSCRIPT_H
#define SCANSCRIPT_H
#include "Script.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "Options.h"
class NZBScriptController : public ScriptController
{
protected:
void PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix);
void PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName);
void ExecuteScriptList(const char* szScriptList);
virtual void ExecuteScript(Options::Script* pScript) = 0;
};
#include "NzbScript.h"
class ScanScriptController : public NZBScriptController
{
@@ -52,40 +40,22 @@ private:
NZBParameterList* m_pParameters;
bool* m_bAddTop;
bool* m_bAddPaused;
char** m_pDupeKey;
int* m_iDupeScore;
EDupeMode* m_eDupeMode;
int m_iPrefixLen;
void PrepareParams(const char* szScriptName);
protected:
virtual void ExecuteScript(Options::Script* pScript);
virtual void ExecuteScript(ScriptConfig::Script* pScript);
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
static void ExecuteScripts(const char* szNZBFilename, const char* szUrl,
const char* szDirectory, char** pNZBName, char** pCategory, int* iPriority,
NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused);
};
class QueueScriptController : public Thread, public NZBScriptController
{
private:
char* m_szNZBName;
char* m_szNZBFilename;
char* m_szUrl;
char* m_szCategory;
int m_iID;
int m_iPriority;
NZBParameterList m_Parameters;
void PrepareParams(const char* szScriptName);
protected:
virtual void ExecuteScript(Options::Script* pScript);
public:
virtual ~QueueScriptController();
virtual void Run();
static void StartScripts(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo);
NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused,
char** pDupeKey, int* iDupeScore, EDupeMode* eDupeMode);
};
#endif

View File

@@ -0,0 +1,143 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <stdio.h>
#include "nzbget.h"
#include "SchedulerScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
SchedulerScriptController::~SchedulerScriptController()
{
free(m_szScript);
}
void SchedulerScriptController::StartScript(const char* szParam, bool bExternalProcess, int iTaskID)
{
char** argv = NULL;
if (bExternalProcess && !Util::SplitCommandLine(szParam, &argv))
{
error("Could not execute scheduled process-script, failed to parse command line: %s", szParam);
return;
}
SchedulerScriptController* pScriptController = new SchedulerScriptController();
pScriptController->m_bExternalProcess = bExternalProcess;
pScriptController->m_szScript = strdup(szParam);
pScriptController->m_iTaskID = iTaskID;
if (bExternalProcess)
{
pScriptController->SetScript(argv[0]);
pScriptController->SetArgs((const char**)argv, true);
}
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void SchedulerScriptController::Run()
{
if (m_bExternalProcess)
{
ExecuteExternalProcess();
}
else
{
ExecuteScriptList(m_szScript);
}
}
void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* pScript)
{
if (!pScript->GetSchedulerScript())
{
return;
}
PrintMessage(Message::mkInfo, "Executing scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(pScript->GetDisplayName());
PrepareParams(pScript->GetName());
Execute();
SetLogPrefix(NULL);
}
void SchedulerScriptController::PrepareParams(const char* szScriptName)
{
ResetEnv();
SetIntEnvVar("NZBSP_TASKID", m_iTaskID);
PrepareEnvScript(NULL, szScriptName);
}
void SchedulerScriptController::ExecuteExternalProcess()
{
info("Executing scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
Execute();
}

View File

@@ -0,0 +1,50 @@
/*
* This file is part of nzbget
*
* 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
* 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 SCHEDULERSCRIPT_H
#define SCHEDULERSCRIPT_H
#include "NzbScript.h"
class SchedulerScriptController : public Thread, public NZBScriptController
{
private:
char* m_szScript;
bool m_bExternalProcess;
int m_iTaskID;
void PrepareParams(const char* szScriptName);
void ExecuteExternalProcess();
protected:
virtual void ExecuteScript(ScriptConfig::Script* pScript);
public:
virtual ~SchedulerScriptController();
virtual void Run();
static void StartScript(const char* szParam, bool bExternalProcess, int iTaskID);
};
#endif

View File

@@ -0,0 +1,559 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include <set>
#include "nzbget.h"
#include "Util.h"
#include "Options.h"
#include "Log.h"
#include "ScriptConfig.h"
static const char* BEGIN_SCRIPT_SIGNATURE = "### NZBGET ";
static const char* POST_SCRIPT_SIGNATURE = "POST-PROCESSING";
static const char* SCAN_SCRIPT_SIGNATURE = "SCAN";
static const char* QUEUE_SCRIPT_SIGNATURE = "QUEUE";
static const char* SCHEDULER_SCRIPT_SIGNATURE = "SCHEDULER";
static const char* FEED_SCRIPT_SIGNATURE = "FEED";
static const char* END_SCRIPT_SIGNATURE = " SCRIPT";
static const char* QUEUE_EVENTS_SIGNATURE = "### QUEUE EVENTS:";
ScriptConfig* g_pScriptConfig = NULL;
ScriptConfig::ConfigTemplate::ConfigTemplate(Script* pScript, const char* szTemplate)
{
m_pScript = pScript;
m_szTemplate = strdup(szTemplate ? szTemplate : "");
}
ScriptConfig::ConfigTemplate::~ConfigTemplate()
{
delete m_pScript;
free(m_szTemplate);
}
ScriptConfig::ConfigTemplates::~ConfigTemplates()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
ScriptConfig::Script::Script(const char* szName, const char* szLocation)
{
m_szName = strdup(szName);
m_szLocation = strdup(szLocation);
m_szDisplayName = strdup(szName);
m_bPostScript = false;
m_bScanScript = false;
m_bQueueScript = false;
m_bSchedulerScript = false;
m_bFeedScript = false;
m_szQueueEvents = NULL;
}
ScriptConfig::Script::~Script()
{
free(m_szName);
free(m_szLocation);
free(m_szDisplayName);
free(m_szQueueEvents);
}
void ScriptConfig::Script::SetDisplayName(const char* szDisplayName)
{
free(m_szDisplayName);
m_szDisplayName = strdup(szDisplayName);
}
void ScriptConfig::Script::SetQueueEvents(const char* szQueueEvents)
{
free(m_szQueueEvents);
m_szQueueEvents = szQueueEvents ? strdup(szQueueEvents) : NULL;
}
ScriptConfig::Scripts::~Scripts()
{
Clear();
}
void ScriptConfig::Scripts::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
ScriptConfig::Script* ScriptConfig::Scripts::Find(const char* szName)
{
for (iterator it = begin(); it != end(); it++)
{
Script* pScript = *it;
if (!strcmp(pScript->GetName(), szName))
{
return pScript;
}
}
return NULL;
}
ScriptConfig::ScriptConfig()
{
InitScripts();
InitConfigTemplates();
}
ScriptConfig::~ScriptConfig()
{
}
bool ScriptConfig::LoadConfig(Options::OptEntries* pOptEntries)
{
// read config file
FILE* infile = fopen(g_pOptions->GetConfigFilename(), FOPEN_RB);
if (!infile)
{
return false;
}
int iBufLen = (int)Util::FileSize(g_pOptions->GetConfigFilename()) + 1;
char* buf = (char*)malloc(iBufLen);
while (fgets(buf, iBufLen - 1, infile))
{
// remove trailing '\n' and '\r' and spaces
Util::TrimRight(buf);
// skip comments and empty lines
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
{
continue;
}
char* optname;
char* optvalue;
if (g_pOptions->SplitOptionString(buf, &optname, &optvalue))
{
Options::OptEntry* pOptEntry = new Options::OptEntry();
pOptEntry->SetName(optname);
pOptEntry->SetValue(optvalue);
pOptEntries->push_back(pOptEntry);
free(optname);
free(optvalue);
}
}
fclose(infile);
free(buf);
return true;
}
bool ScriptConfig::SaveConfig(Options::OptEntries* pOptEntries)
{
// save to config file
FILE* infile = fopen(g_pOptions->GetConfigFilename(), FOPEN_RBP);
if (!infile)
{
return false;
}
std::vector<char*> config;
std::set<Options::OptEntry*> writtenOptions;
// read config file into memory array
int iBufLen = (int)Util::FileSize(g_pOptions->GetConfigFilename()) + 1;
char* buf = (char*)malloc(iBufLen);
while (fgets(buf, iBufLen - 1, infile))
{
config.push_back(strdup(buf));
}
free(buf);
// write config file back to disk, replace old values of existing options with new values
rewind(infile);
for (std::vector<char*>::iterator it = config.begin(); it != config.end(); it++)
{
char* buf = *it;
const char* eq = strchr(buf, '=');
if (eq && buf[0] != '#')
{
// remove trailing '\n' and '\r' and spaces
Util::TrimRight(buf);
char* optname;
char* optvalue;
if (g_pOptions->SplitOptionString(buf, &optname, &optvalue))
{
Options::OptEntry *pOptEntry = pOptEntries->FindOption(optname);
if (pOptEntry)
{
fputs(pOptEntry->GetName(), infile);
fputs("=", infile);
fputs(pOptEntry->GetValue(), infile);
fputs("\n", infile);
writtenOptions.insert(pOptEntry);
}
free(optname);
free(optvalue);
}
}
else
{
fputs(buf, infile);
}
free(buf);
}
// write new options
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
{
Options::OptEntry* pOptEntry = *it;
std::set<Options::OptEntry*>::iterator fit = writtenOptions.find(pOptEntry);
if (fit == writtenOptions.end())
{
fputs(pOptEntry->GetName(), infile);
fputs("=", infile);
fputs(pOptEntry->GetValue(), infile);
fputs("\n", infile);
}
}
// close and truncate the file
int pos = (int)ftell(infile);
fclose(infile);
Util::TruncateFile(g_pOptions->GetConfigFilename(), pos);
return true;
}
bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* pConfigTemplates)
{
char* szBuffer;
int iLength;
if (!Util::LoadFileIntoBuffer(g_pOptions->GetConfigTemplate(), &szBuffer, &iLength))
{
return false;
}
ConfigTemplate* pConfigTemplate = new ConfigTemplate(NULL, szBuffer);
pConfigTemplates->push_back(pConfigTemplate);
free(szBuffer);
if (!g_pOptions->GetScriptDir())
{
return true;
}
Scripts scriptList;
LoadScripts(&scriptList);
const int iBeginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int iQueueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
for (Scripts::iterator it = scriptList.begin(); it != scriptList.end(); it++)
{
Script* pScript = *it;
FILE* infile = fopen(pScript->GetLocation(), FOPEN_RB);
if (!infile)
{
ConfigTemplate* pConfigTemplate = new ConfigTemplate(pScript, "");
pConfigTemplates->push_back(pConfigTemplate);
continue;
}
StringBuilder stringBuilder;
char buf[1024];
bool bInConfig = false;
while (fgets(buf, sizeof(buf) - 1, infile))
{
if (!strncmp(buf, BEGIN_SCRIPT_SIGNATURE, iBeginSignatureLen) &&
strstr(buf, END_SCRIPT_SIGNATURE) &&
(strstr(buf, POST_SCRIPT_SIGNATURE) ||
strstr(buf, SCAN_SCRIPT_SIGNATURE) ||
strstr(buf, QUEUE_SCRIPT_SIGNATURE) ||
strstr(buf, SCHEDULER_SCRIPT_SIGNATURE) ||
strstr(buf, FEED_SCRIPT_SIGNATURE)))
{
if (bInConfig)
{
break;
}
bInConfig = true;
continue;
}
bool bSkip = !strncmp(buf, QUEUE_EVENTS_SIGNATURE, iQueueEventsSignatureLen);
if (bInConfig && !bSkip)
{
stringBuilder.Append(buf);
}
}
fclose(infile);
ConfigTemplate* pConfigTemplate = new ConfigTemplate(pScript, stringBuilder.GetBuffer());
pConfigTemplates->push_back(pConfigTemplate);
}
// clearing the list without deleting of objects, which are in pConfigTemplates now
scriptList.clear();
return true;
}
void ScriptConfig::InitConfigTemplates()
{
if (!LoadConfigTemplates(&m_ConfigTemplates))
{
error("Could not read configuration templates");
}
}
void ScriptConfig::InitScripts()
{
LoadScripts(&m_Scripts);
}
void ScriptConfig::LoadScripts(Scripts* pScripts)
{
if (strlen(g_pOptions->GetScriptDir()) == 0)
{
return;
}
Scripts tmpScripts;
LoadScriptDir(&tmpScripts, g_pOptions->GetScriptDir(), false);
tmpScripts.sort(CompareScripts);
// first add all scripts from m_szScriptOrder
Tokenizer tok(g_pOptions->GetScriptOrder(), ",;");
while (const char* szScriptName = tok.Next())
{
Script* pScript = tmpScripts.Find(szScriptName);
if (pScript)
{
tmpScripts.remove(pScript);
pScripts->push_back(pScript);
}
}
// second add all other scripts from scripts directory
for (Scripts::iterator it = tmpScripts.begin(); it != tmpScripts.end(); it++)
{
Script* pScript = *it;
if (!pScripts->Find(pScript->GetName()))
{
pScripts->push_back(pScript);
}
}
tmpScripts.clear();
BuildScriptDisplayNames(pScripts);
}
void ScriptConfig::LoadScriptDir(Scripts* pScripts, const char* szDirectory, bool bIsSubDir)
{
int iBufSize = 1024*10;
char* szBuffer = (char*)malloc(iBufSize+1);
const int iBeginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int iQueueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
DirBrowser dir(szDirectory);
while (const char* szFilename = dir.Next())
{
if (szFilename[0] != '.' && szFilename[0] != '_')
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%s", szDirectory, szFilename);
szFullFilename[1024-1] = '\0';
if (!Util::DirectoryExists(szFullFilename))
{
// check if the file contains pp-script-signature
FILE* infile = fopen(szFullFilename, FOPEN_RB);
if (infile)
{
// read first 10KB of the file and look for signature
int iReadBytes = fread(szBuffer, 1, iBufSize, infile);
fclose(infile);
szBuffer[iReadBytes] = 0;
// split buffer into lines
Tokenizer tok(szBuffer, "\n\r", true);
while (char* szLine = tok.Next())
{
if (!strncmp(szLine, BEGIN_SCRIPT_SIGNATURE, iBeginSignatureLen) &&
strstr(szLine, END_SCRIPT_SIGNATURE))
{
bool bPostScript = strstr(szLine, POST_SCRIPT_SIGNATURE);
bool bScanScript = strstr(szLine, SCAN_SCRIPT_SIGNATURE);
bool bQueueScript = strstr(szLine, QUEUE_SCRIPT_SIGNATURE);
bool bSchedulerScript = strstr(szLine, SCHEDULER_SCRIPT_SIGNATURE);
bool bFeedScript = strstr(szLine, FEED_SCRIPT_SIGNATURE);
if (bPostScript || bScanScript || bQueueScript || bSchedulerScript || bFeedScript)
{
char szScriptName[1024];
if (bIsSubDir)
{
char szDirectory2[1024];
snprintf(szDirectory2, 1024, "%s", szDirectory);
szDirectory2[1024-1] = '\0';
int iLen = strlen(szDirectory2);
if (szDirectory2[iLen-1] == PATH_SEPARATOR || szDirectory2[iLen-1] == ALT_PATH_SEPARATOR)
{
// trim last path-separator
szDirectory2[iLen-1] = '\0';
}
snprintf(szScriptName, 1024, "%s%c%s", Util::BaseFileName(szDirectory2), PATH_SEPARATOR, szFilename);
}
else
{
snprintf(szScriptName, 1024, "%s", szFilename);
}
szScriptName[1024-1] = '\0';
char* szQueueEvents = NULL;
if (bQueueScript)
{
while (char* szLine = tok.Next())
{
if (!strncmp(szLine, QUEUE_EVENTS_SIGNATURE, iQueueEventsSignatureLen))
{
szQueueEvents = szLine + iQueueEventsSignatureLen;
break;
}
}
}
Script* pScript = new Script(szScriptName, szFullFilename);
pScript->SetPostScript(bPostScript);
pScript->SetScanScript(bScanScript);
pScript->SetQueueScript(bQueueScript);
pScript->SetSchedulerScript(bSchedulerScript);
pScript->SetFeedScript(bFeedScript);
pScript->SetQueueEvents(szQueueEvents);
pScripts->push_back(pScript);
break;
}
}
}
}
}
else if (!bIsSubDir)
{
snprintf(szFullFilename, 1024, "%s%s%c", szDirectory, szFilename, PATH_SEPARATOR);
szFullFilename[1024-1] = '\0';
LoadScriptDir(pScripts, szFullFilename, true);
}
}
}
free(szBuffer);
}
bool ScriptConfig::CompareScripts(Script* pScript1, Script* pScript2)
{
return strcmp(pScript1->GetName(), pScript2->GetName()) < 0;
}
void ScriptConfig::BuildScriptDisplayNames(Scripts* pScripts)
{
// trying to use short name without path and extension.
// if there are other scripts with the same short name - using a longer name instead (with ot without extension)
for (Scripts::iterator it = pScripts->begin(); it != pScripts->end(); it++)
{
Script* pScript = *it;
char szShortName[256];
strncpy(szShortName, pScript->GetName(), 256);
szShortName[256-1] = '\0';
if (char* ext = strrchr(szShortName, '.')) *ext = '\0'; // strip file extension
const char* szDisplayName = Util::BaseFileName(szShortName);
for (Scripts::iterator it2 = pScripts->begin(); it2 != pScripts->end(); it2++)
{
Script* pScript2 = *it2;
char szShortName2[256];
strncpy(szShortName2, pScript2->GetName(), 256);
szShortName2[256-1] = '\0';
if (char* ext = strrchr(szShortName2, '.')) *ext = '\0'; // strip file extension
const char* szDisplayName2 = Util::BaseFileName(szShortName2);
if (!strcmp(szDisplayName, szDisplayName2) && pScript->GetName() != pScript2->GetName())
{
if (!strcmp(szShortName, szShortName2))
{
szDisplayName = pScript->GetName();
}
else
{
szDisplayName = szShortName;
}
break;
}
}
pScript->SetDisplayName(szDisplayName);
}
}

View File

@@ -0,0 +1,128 @@
/*
* This file is part of nzbget
*
* 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
* 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 SCRIPTCONFIG_H
#define SCRIPTCONFIG_H
#include <vector>
#include <list>
#include <time.h>
#include "Options.h"
class ScriptConfig
{
public:
class Script
{
private:
char* m_szName;
char* m_szLocation;
char* m_szDisplayName;
bool m_bPostScript;
bool m_bScanScript;
bool m_bQueueScript;
bool m_bSchedulerScript;
bool m_bFeedScript;
char* m_szQueueEvents;
public:
Script(const char* szName, const char* szLocation);
~Script();
const char* GetName() { return m_szName; }
const char* GetLocation() { return m_szLocation; }
void SetDisplayName(const char* szDisplayName);
const char* GetDisplayName() { return m_szDisplayName; }
bool GetPostScript() { return m_bPostScript; }
void SetPostScript(bool bPostScript) { m_bPostScript = bPostScript; }
bool GetScanScript() { return m_bScanScript; }
void SetScanScript(bool bScanScript) { m_bScanScript = bScanScript; }
bool GetQueueScript() { return m_bQueueScript; }
void SetQueueScript(bool bQueueScript) { m_bQueueScript = bQueueScript; }
bool GetSchedulerScript() { return m_bSchedulerScript; }
void SetSchedulerScript(bool bSchedulerScript) { m_bSchedulerScript = bSchedulerScript; }
bool GetFeedScript() { return m_bFeedScript; }
void SetFeedScript(bool bFeedScript) { m_bFeedScript = bFeedScript; }
void SetQueueEvents(const char* szQueueEvents);
const char* GetQueueEvents() { return m_szQueueEvents; }
};
typedef std::list<Script*> ScriptsBase;
class Scripts: public ScriptsBase
{
public:
~Scripts();
void Clear();
Script* Find(const char* szName);
};
class ConfigTemplate
{
private:
Script* m_pScript;
char* m_szTemplate;
friend class Options;
public:
ConfigTemplate(Script* pScript, const char* szTemplate);
~ConfigTemplate();
Script* GetScript() { return m_pScript; }
const char* GetTemplate() { return m_szTemplate; }
};
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
class ConfigTemplates: public ConfigTemplatesBase
{
public:
~ConfigTemplates();
};
private:
Scripts m_Scripts;
ConfigTemplates m_ConfigTemplates;
void InitScripts();
void InitConfigTemplates();
static bool CompareScripts(Script* pScript1, Script* pScript2);
void LoadScriptDir(Scripts* pScripts, const char* szDirectory, bool bIsSubDir);
void BuildScriptDisplayNames(Scripts* pScripts);
void LoadScripts(Scripts* pScripts);
public:
ScriptConfig();
~ScriptConfig();
Scripts* GetScripts() { return &m_Scripts; }
bool LoadConfig(Options::OptEntries* pOptEntries);
bool SaveConfig(Options::OptEntries* pOptEntries);
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
ConfigTemplates* GetConfigTemplates() { return &m_ConfigTemplates; }
};
extern ScriptConfig* g_pScriptConfig;
#endif

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
@@ -47,10 +47,9 @@
#include "Util.h"
#include "FeedFile.h"
#include "FeedFilter.h"
#include "FeedScript.h"
#include "DiskState.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#include "DupeCoordinator.h"
FeedCoordinator::FeedCacheItem::FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
time_t tLastUsage, FeedItemInfos* pFeedItemInfos)
@@ -70,6 +69,42 @@ FeedCoordinator::FeedCacheItem::~FeedCacheItem()
m_pFeedItemInfos->Release();
}
FeedCoordinator::FilterHelper::FilterHelper()
{
m_pSeasonEpisodeRegEx = NULL;
}
FeedCoordinator::FilterHelper::~FilterHelper()
{
delete m_pSeasonEpisodeRegEx;
}
void FeedCoordinator::FilterHelper::CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen)
{
const char* szDupeStatusName[] = { "", "QUEUED", "DOWNLOADING", "3", "SUCCESS", "5", "6", "7", "WARNING",
"9", "10", "11", "12", "13", "14", "15", "FAILURE" };
char szStatuses[200];
szStatuses[0] = '\0';
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
DupeCoordinator::EDupeStatus eDupeStatus = g_pDupeCoordinator->GetDupeStatus(pDownloadQueue, szTitle, szDupeKey);
DownloadQueue::Unlock();
for (int i = 1; i <= (int)DupeCoordinator::dsFailure; i = i << 1)
{
if (eDupeStatus & i)
{
if (*szStatuses)
{
strcat(szStatuses, ",");
}
strcat(szStatuses, szDupeStatusName[i]);
}
}
strncpy(szStatusBuf, szStatuses, iBufLen);
}
FeedCoordinator::FeedCoordinator()
{
debug("Creating FeedCoordinator");
@@ -361,6 +396,9 @@ void FeedCoordinator::FeedCompleted(FeedDownloader* pFeedDownloader)
{
if (!pFeedInfo->GetPreview())
{
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(pFeedInfo->GetFeedScript()) ? pFeedInfo->GetFeedScript(): g_pOptions->GetFeedScript(),
pFeedInfo->GetOutputFilename(), pFeedInfo->GetID());
FeedFile* pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
remove(pFeedInfo->GetOutputFilename());
@@ -414,6 +452,7 @@ void FeedCoordinator::FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemIn
pFeedItemInfo->SetAddCategory(pFeedInfo->GetCategory());
pFeedItemInfo->SetDupeScore(0);
pFeedItemInfo->SetDupeMode(dmScore);
pFeedItemInfo->SetFeedFilterHelper(&m_FilterHelper);
pFeedItemInfo->BuildDupeKey(NULL, NULL);
if (pFeedFilter)
{
@@ -440,7 +479,7 @@ void FeedCoordinator::ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemI
{
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
FeedHistoryInfo::EStatus eStatus = FeedHistoryInfo::hsUnknown;
if (bFirstFetch)
if (bFirstFetch && pFeedInfo->GetBacklog())
{
eStatus = FeedHistoryInfo::hsBacklog;
}
@@ -479,6 +518,7 @@ NZBInfo* FeedCoordinator::CreateNZBInfo(FeedInfo* pFeedInfo, FeedItemInfo* pFeed
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->SetKind(NZBInfo::nkUrl);
pNZBInfo->SetFeedID(pFeedInfo->GetID());
pNZBInfo->SetURL(pFeedItemInfo->GetUrl());
// add .nzb-extension if not present
@@ -517,18 +557,19 @@ bool FeedCoordinator::ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos)
FeedInfo* pFeedInfo = m_Feeds.at(iID - 1);
return PreviewFeed(pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(), pFeedInfo->GetPriority(),
0, NULL, ppFeedItemInfos);
return PreviewFeed(pFeedInfo->GetID(), pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
pFeedInfo->GetBacklog(), pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(),
pFeedInfo->GetPriority(), pFeedInfo->GetInterval(), pFeedInfo->GetFeedScript(), 0, NULL, ppFeedItemInfos);
}
bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
bool FeedCoordinator::PreviewFeed(int iID, const char* szName, const char* szUrl, const char* szFilter,
bool bBacklog, bool bPauseNzb, const char* szCategory, int iPriority, int iInterval, const char* szFeedScript,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos)
{
debug("Preview feed %s", szName);
FeedInfo* pFeedInfo = new FeedInfo(0, szName, szUrl, 0, szFilter, bPauseNzb, szCategory, iPriority);
FeedInfo* pFeedInfo = new FeedInfo(iID, szName, szUrl, bBacklog, iInterval,
szFilter, bPauseNzb, szCategory, iPriority, szFeedScript);
pFeedInfo->SetPreview(true);
FeedItemInfos* pFeedItemInfos = NULL;
@@ -583,6 +624,9 @@ bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const c
if (pFeedInfo->GetStatus() == FeedInfo::fsFinished)
{
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(pFeedInfo->GetFeedScript()) ? pFeedInfo->GetFeedScript(): g_pOptions->GetFeedScript(),
pFeedInfo->GetOutputFilename(), pFeedInfo->GetID());
pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
}
@@ -601,7 +645,7 @@ bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const c
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
pFeedItemInfo->SetStatus(bFirstFetch ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
pFeedItemInfo->SetStatus(bFirstFetch && pFeedInfo->GetBacklog() ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
if (pFeedHistoryInfo)
{

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
@@ -36,6 +36,7 @@
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "Observer.h"
#include "Util.h"
class FeedDownloader;
@@ -71,6 +72,17 @@ private:
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
};
class FilterHelper : public FeedFilterHelper
{
private:
RegEx* m_pSeasonEpisodeRegEx;
public:
FilterHelper();
~FilterHelper();
virtual RegEx** GetSeasonEpisodeRegEx() { return &m_pSeasonEpisodeRegEx; };
virtual void CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen);
};
typedef std::deque<FeedCacheItem*> FeedCache;
typedef std::list<FeedDownloader*> ActiveDownloads;
@@ -83,6 +95,7 @@ private:
bool m_bForce;
bool m_bSave;
FeedCache m_FeedCache;
FilterHelper m_FilterHelper;
void StartFeedDownload(FeedInfo* pFeedInfo, bool bForce);
void FeedCompleted(FeedDownloader* pFeedDownloader);
@@ -105,8 +118,8 @@ public:
virtual void Stop();
void Update(Subject* pCaller, void* pAspect);
void AddFeed(FeedInfo* pFeedInfo);
bool PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
bool PreviewFeed(int iID, const char* szName, const char* szUrl, const char* szFilter, bool bBacklog,
bool bPauseNzb, const char* szCategory, int iPriority, int iInterval, const char* szFeedScript,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos);
bool ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos);
void FetchFeed(int iID);
@@ -114,6 +127,8 @@ public:
Feeds* GetFeeds() { return &m_Feeds; }
};
extern FeedCoordinator* g_pFeedCoordinator;
class FeedDownloader : public WebDownloader
{
private:

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
@@ -51,8 +51,6 @@ using namespace MSXML;
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
FeedFile::FeedFile(const char* szFileName)
{
debug("Creating FeedFile");
@@ -246,7 +244,13 @@ bool FeedFile::ParseFeed(IUnknown* nzb)
if (tag)
{
_bstr_t description(tag->Gettext());
pFeedItemInfo->SetDescription(description);
// cleanup CDATA
char* szDescription = strdup((const char*)description);
WebUtil::XmlStripTags(szDescription);
WebUtil::XmlDecode(szDescription);
WebUtil::XmlRemoveEntities(szDescription);
pFeedItemInfo->SetDescription(szDescription);
free(szDescription);
}
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
@@ -494,7 +498,13 @@ void FeedFile::Parse_EndElement(const char *name)
}
else if (!strcmp("description", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetDescription(m_szTagContent);
// cleanup CDATA
char* szDescription = strdup(m_szTagContent);
WebUtil::XmlStripTags(szDescription);
WebUtil::XmlDecode(szDescription);
WebUtil::XmlRemoveEntities(szDescription);
m_pFeedItemInfo->SetDescription(szDescription);
free(szDescription);
ResetTagContent();
}
else if (!strcmp("pubDate", name) && m_pFeedItemInfo)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2013-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
@@ -330,7 +330,7 @@ bool FeedFilter::Term::Compile(char* szToken)
}
/*
* If pFeedItemInfo is NULL, only field type info is returned
* If pFeedItemInfo is NULL, only field name is validated
*/
bool FeedFilter::Term::GetFieldData(const char* szField, FeedItemInfo* pFeedItemInfo,
const char** StrValue, long long* IntValue)
@@ -408,6 +408,11 @@ bool FeedFilter::Term::GetFieldData(const char* szField, FeedItemInfo* pFeedItem
*IntValue = pFeedItemInfo ? pFeedItemInfo->GetDupeScore() : 0;
return true;
}
else if (!strcasecmp(szField, "dupestatus"))
{
*StrValue = pFeedItemInfo ? pFeedItemInfo->GetDupeStatus() : NULL;
return true;
}
else if (!strncasecmp(szField, "attr-", 5))
{
if (pFeedItemInfo)
@@ -516,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

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-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
@@ -41,17 +41,18 @@
#include "FeedInfo.h"
#include "Util.h"
FeedInfo::FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority)
FeedInfo::FeedInfo(int iID, const char* szName, const char* szUrl, bool bBacklog, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority, const char* szFeedScript)
{
m_iID = iID;
m_szName = strdup(szName ? szName : "");
m_szUrl = strdup(szUrl ? szUrl : "");
m_szFilter = strdup(szFilter ? szFilter : "");
m_iFilterHash = Util::HashBJ96(szFilter, strlen(szFilter), 0);
m_bBacklog = bBacklog;
m_iFilterHash = Util::HashBJ96(m_szFilter, strlen(m_szFilter), 0);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iInterval = iInterval;
m_szFeedScript = strdup(szFeedScript ? szFeedScript : "");
m_bPauseNzb = bPauseNzb;
m_iPriority = iPriority;
m_tLastUpdate = 0;
@@ -69,6 +70,7 @@ FeedInfo::~FeedInfo()
free(m_szFilter);
free(m_szCategory);
free(m_szOutputFilename);
free(m_szFeedScript);
}
void FeedInfo::SetOutputFilename(const char* szOutputFilename)
@@ -121,7 +123,7 @@ FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* szName)
FeedItemInfo::FeedItemInfo()
{
m_pSharedFeedData = NULL;
m_pFeedFilterHelper = NULL;
m_szTitle = NULL;
m_szFilename = NULL;
m_szUrl = NULL;
@@ -145,6 +147,7 @@ FeedItemInfo::FeedItemInfo()
m_szDupeKey = NULL;
m_iDupeScore = 0;
m_eDupeMode = dmScore;
m_szDupeStatus = NULL;
}
FeedItemInfo::~FeedItemInfo()
@@ -158,6 +161,7 @@ FeedItemInfo::~FeedItemInfo()
free(m_szEpisode);
free(m_szAddCategory);
free(m_szDupeKey);
free(m_szDupeStatus);
}
void FeedItemInfo::SetTitle(const char* szTitle)
@@ -296,20 +300,24 @@ void FeedItemInfo::ParseSeasonEpisode()
{
m_bSeasonEpisodeParsed = true;
RegEx* pRegEx = m_pSharedFeedData->GetSeasonEpisodeRegEx();
RegEx** ppRegEx = m_pFeedFilterHelper->GetSeasonEpisodeRegEx();
if (!*ppRegEx)
{
*ppRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
}
if (pRegEx->Match(m_szTitle))
if ((*ppRegEx)->Match(m_szTitle))
{
char szRegValue[100];
char szValue[100];
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + pRegEx->GetMatchStart(1)));
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + (*ppRegEx)->GetMatchStart(1)));
szValue[100-1] = '\0';
SetSeason(szValue);
int iLen = pRegEx->GetMatchLen(2);
int iLen = (*ppRegEx)->GetMatchLen(2);
iLen = iLen < 99 ? iLen : 99;
strncpy(szRegValue, m_szTitle + pRegEx->GetMatchStart(2), pRegEx->GetMatchLen(2));
strncpy(szRegValue, m_szTitle + (*ppRegEx)->GetMatchStart(2), (*ppRegEx)->GetMatchLen(2));
szRegValue[iLen] = '\0';
snprintf(szValue, 100, "E%s", szRegValue);
szValue[100-1] = '\0';
@@ -319,6 +327,19 @@ void FeedItemInfo::ParseSeasonEpisode()
}
}
const char* FeedItemInfo::GetDupeStatus()
{
if (!m_szDupeStatus)
{
char szStatuses[200];
szStatuses[0] = '\0';
m_pFeedFilterHelper->CalcDupeStatus(m_szTitle, m_szDupeKey, szStatuses, sizeof(szStatuses));
m_szDupeStatus = strdup(szStatuses);
}
return m_szDupeStatus;
}
FeedHistoryInfo::FeedHistoryInfo(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
{
@@ -415,26 +436,4 @@ void FeedItemInfos::Release()
void FeedItemInfos::Add(FeedItemInfo* pFeedItemInfo)
{
push_back(pFeedItemInfo);
pFeedItemInfo->SetSharedFeedData(&m_SharedFeedData);
}
SharedFeedData::SharedFeedData()
{
m_pSeasonEpisodeRegEx = NULL;
}
SharedFeedData::~SharedFeedData()
{
delete m_pSeasonEpisodeRegEx;
}
RegEx* SharedFeedData::GetSeasonEpisodeRegEx()
{
if (!m_pSeasonEpisodeRegEx)
{
m_pSeasonEpisodeRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
}
return m_pSeasonEpisodeRegEx;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-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
@@ -53,6 +53,7 @@ private:
unsigned int m_iFilterHash;
bool m_bPauseNzb;
char* m_szCategory;
char* m_szFeedScript;
int m_iPriority;
time_t m_tLastUpdate;
bool m_bPreview;
@@ -60,10 +61,12 @@ private:
char* m_szOutputFilename;
bool m_bFetch;
bool m_bForce;
bool m_bBacklog;
public:
FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority);
FeedInfo(int iID, const char* szName, const char* szUrl, bool bBacklog, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority,
const char* szFeedScript);
~FeedInfo();
int GetID() { return m_iID; }
const char* GetName() { return m_szName; }
@@ -74,6 +77,7 @@ public:
bool GetPauseNzb() { return m_bPauseNzb; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
const char* GetFeedScript() { return m_szFeedScript; }
time_t GetLastUpdate() { return m_tLastUpdate; }
void SetLastUpdate(time_t tLastUpdate) { m_tLastUpdate = tLastUpdate; }
bool GetPreview() { return m_bPreview; }
@@ -86,19 +90,17 @@ public:
void SetFetch(bool bFetch) { m_bFetch = bFetch; }
bool GetForce() { return m_bForce; }
void SetForce(bool bForce) { m_bForce = bForce; }
bool GetBacklog() { return m_bBacklog; }
void SetBacklog(bool bBacklog) { m_bBacklog = bBacklog; }
};
typedef std::deque<FeedInfo*> Feeds;
class SharedFeedData
class FeedFilterHelper
{
private:
RegEx* m_pSeasonEpisodeRegEx;
public:
SharedFeedData();
~SharedFeedData();
RegEx* GetSeasonEpisodeRegEx();
virtual RegEx** GetSeasonEpisodeRegEx() = 0;
virtual void CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen) = 0;
};
class FeedItemInfo
@@ -165,7 +167,8 @@ private:
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
SharedFeedData* m_pSharedFeedData;
char* m_szDupeStatus;
FeedFilterHelper* m_pFeedFilterHelper;
Attributes m_Attributes;
int ParsePrefixedInt(const char *szValue);
@@ -174,7 +177,7 @@ private:
public:
FeedItemInfo();
~FeedItemInfo();
void SetSharedFeedData(SharedFeedData* pSharedFeedData) { m_pSharedFeedData = pSharedFeedData; }
void SetFeedFilterHelper(FeedFilterHelper* pFeedFilterHelper) { m_pFeedFilterHelper = pFeedFilterHelper; }
const char* GetTitle() { return m_szTitle; }
void SetTitle(const char* szTitle);
const char* GetFilename() { return m_szFilename; }
@@ -219,6 +222,7 @@ public:
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
const char* GetDupeStatus();
Attributes* GetAttributes() { return &m_Attributes; }
};
@@ -228,7 +232,6 @@ class FeedItemInfos : public FeedItemInfosBase
{
private:
int m_iRefCount;
SharedFeedData m_SharedFeedData;
public:
FeedItemInfos();

View File

@@ -88,7 +88,7 @@ void ColoredFrontend::PrintStatus()
char szDownloadLimit[128];
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
sprintf(szDownloadLimit, ", Limit %i KB/s", m_iDownloadLimit / 1024);
}
else
{
@@ -112,10 +112,12 @@ void ColoredFrontend::PrintStatus()
const char* szControlSeq = "\033[K";
#endif
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
char szFileSize[20];
char szCurrendSpeed[20];
snprintf(tmp, 1024, " %d threads, %s, %s remaining%s%s%s%s%s\n",
m_iThreadCount, Util::FormatSpeed(szCurrendSpeed, sizeof(szCurrendSpeed), iCurrentDownloadSpeed),
Util::FormatSize(szFileSize, sizeof(szFileSize), m_lRemainingSize),
timeString, szPostStatus, m_bPauseDownload ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, szControlSeq);
tmp[1024-1] = '\0';
printf("%s", tmp);

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
@@ -51,9 +51,6 @@
#include "Util.h"
#include "StatMeter.h"
extern Options* g_pOptions;
extern StatMeter* g_pStatMeter;
Frontend::Frontend()
{
debug("Creating Frontend");
@@ -85,7 +82,8 @@ bool Frontend::PrepareData()
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", szControlIP, g_pOptions->GetControlPort());
Stop();
return false;
}
@@ -119,11 +117,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 +125,7 @@ void Frontend::FreeData()
}
}
Log::Messages* Frontend::LockMessages()
MessageList* Frontend::LockMessages()
{
if (IsRemoteMode())
{
@@ -222,7 +216,8 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
bool Frontend::RequestMessages()
{
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
Connection connection(szControlIP, g_pOptions->GetControlPort(), false);
bool OK = connection.Connect();
if (!OK)
@@ -293,7 +288,8 @@ bool Frontend::RequestMessages()
bool Frontend::RequestFileList()
{
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
Connection connection(szControlIP, g_pOptions->GetControlPort(), false);
bool OK = connection.Connect();
if (!OK)

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
@@ -43,6 +43,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
@@ -72,7 +73,6 @@ void curses_clear()
#undef clear
#endif
extern Options* g_pOptions;
extern void ExitProc();
static const int NCURSES_COLORPAIR_TEXT = 1;
@@ -136,7 +136,7 @@ NCursesFrontend::NCursesFrontend()
m_bShowNZBname = g_pOptions->GetCursesNZBName();
m_bShowTimestamp = g_pOptions->GetCursesTime();
m_bGroupFiles = g_pOptions->GetCursesGroup();
m_QueueWindowPercentage = 0.5f;
m_QueueWindowPercentage = 50;
m_iDataUpdatePos = 0;
m_bUpdateNextTime = false;
m_iLastEditEntry = -1;
@@ -362,7 +362,7 @@ void NCursesFrontend::CalcWindowSizes()
int iQueueSize = CalcQueueSize();
m_iQueueWinTop = 0;
m_iQueueWinHeight = (int)((float) (m_iScreenHeight - 2) * m_QueueWindowPercentage);
m_iQueueWinHeight = (m_iScreenHeight - 2) * m_QueueWindowPercentage / 100;
if (m_iQueueWinHeight - 1 > iQueueSize)
{
m_iQueueWinHeight = iQueueSize > 0 ? iQueueSize + 1 : 1 + 1;
@@ -503,7 +503,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--)
@@ -625,7 +625,7 @@ void NCursesFrontend::PrintStatus()
char szDownloadLimit[128];
if (m_iDownloadLimit > 0)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
sprintf(szDownloadLimit, ", Limit %i KB/s", m_iDownloadLimit / 1024);
}
else
{
@@ -642,13 +642,16 @@ void NCursesFrontend::PrintStatus()
szPostStatus[0] = 0;
}
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
char szCurrentSpeed[20];
char szAverageSpeed[20];
char szRemainingSize[20];
int iAverageSpeed = (int)(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s, Avg. %.*f KB/s",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, (fAverageSpeed >= 10 ? 0 : 1), fAverageSpeed);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %s, %s remaining%s%s%s%s, Avg. %s",
m_iThreadCount, Util::FormatSpeed(szCurrentSpeed, sizeof(szCurrentSpeed), iCurrentDownloadSpeed),
Util::FormatSize(szRemainingSize, sizeof(szRemainingSize), m_lRemainingSize),
timeString, szPostStatus, m_bPauseDownload ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, Util::FormatSpeed(szAverageSpeed, sizeof(szAverageSpeed), iAverageSpeed));
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
}
@@ -775,15 +778,13 @@ void NCursesFrontend::PrintFileQueue()
if (iFileNum > 0)
{
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
char szUnpaused[20];
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
m_bUseColor ? "" : "*** ", iFileNum,
iFileNum - iPausedFiles, szRemaining, szUnpaused);
iFileNum - iPausedFiles,
Util::FormatSize(szRemaining, sizeof(szRemaining), lRemaining),
Util::FormatSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused));
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
}
@@ -836,7 +837,7 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
{
sprintf(szCompleted, ", %i%%", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())));
sprintf(szCompleted, ", %i%%", (int)(100 - pFileInfo->GetRemainingSize() * 100 / pFileInfo->GetSize()));
}
char szNZBNiceName[1024];
@@ -852,10 +853,11 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
szNZBNiceName[0] = '\0';
}
char szSize[20];
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%s%s)%s", Brace1, pFileInfo->GetID(),
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
Util::FormatSize(szSize, sizeof(szSize), pFileInfo->GetSize()),
szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
@@ -966,10 +968,10 @@ void NCursesFrontend::PrintGroupQueue()
}
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
Util::FormatSize(szRemaining, sizeof(szRemaining), lRemaining);
char szUnpaused[20];
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
Util::FormatSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
@@ -1011,7 +1013,7 @@ void NCursesFrontend::PrintGroupname(NZBInfo* pNZBInfo, int iRow, bool bSelected
long long lUnpausedRemainingSize = pNZBInfo->GetRemainingSize() - pNZBInfo->GetPausedSize();
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
Util::FormatSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
char szPriority[100];
szPriority[0] = '\0';
@@ -1045,7 +1047,7 @@ void NCursesFrontend::PrintGroupname(NZBInfo* pNZBInfo, int iRow, bool bSelected
szFiles[20-1] = '\0';
char szTotal[20];
Util::FormatFileSize(szTotal, sizeof(szTotal), pNZBInfo->GetSize());
Util::FormatSize(szTotal, sizeof(szTotal), pNZBInfo->GetSize());
char szNameWithIds[1024];
snprintf(szNameWithIds, 1024, "%c%i%c%s%s %s", chBrace1, pNZBInfo->GetID(), chBrace2,
@@ -1058,7 +1060,7 @@ void NCursesFrontend::PrintGroupname(NZBInfo* pNZBInfo, int iRow, bool bSelected
if (pNZBInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
{
snprintf(szTime, 100, "[paused]");
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pNZBInfo->GetRemainingSize());
Util::FormatSize(szRemaining, sizeof(szRemaining), pNZBInfo->GetRemainingSize());
}
else if (iCurrentDownloadSpeed > 0 && !m_bPauseDownload)
{
@@ -1256,17 +1258,17 @@ void NCursesFrontend::UpdateInput(int initialKey)
break;
case 'w':
// swicth window sizes
if (m_QueueWindowPercentage == 0.5)
if (m_QueueWindowPercentage == 50)
{
m_QueueWindowPercentage = 1;
m_QueueWindowPercentage = 100;
}
else if (m_QueueWindowPercentage == 1 && m_eInputMode != eEditQueue)
else if (m_QueueWindowPercentage == 100 && m_eInputMode != eEditQueue)
{
m_QueueWindowPercentage = 0;
}
else
{
m_QueueWindowPercentage = 0.5;
m_QueueWindowPercentage = 50;
}
CalcWindowSizes();
SetCurrentQueueEntry(m_iSelectedQueueEntry);
@@ -1301,7 +1303,7 @@ void NCursesFrontend::UpdateInput(int initialKey)
m_eInputMode = eEditQueue;
if (m_QueueWindowPercentage == 0)
{
m_QueueWindowPercentage = 0.5;
m_QueueWindowPercentage = 50;
}
return;
}

View File

@@ -85,7 +85,7 @@ private:
bool m_bShowNZBname;
bool m_bShowTimestamp;
bool m_bGroupFiles;
float m_QueueWindowPercentage;
int m_QueueWindowPercentage;
#ifdef WIN32
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);

View File

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,169 @@
/*
* This file is part of nzbget
*
* 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
* 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 COMMANDLINEPARSER_H
#define COMMANDLINEPARSER_H
#include <vector>
#include <list>
#include <time.h>
class CommandLineParser
{
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestListFiles,
opClientRequestListGroups,
opClientRequestListStatus,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestReload,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScanSync,
opClientRequestScanAsync,
opClientRequestDownloadPause,
opClientRequestDownloadUnpause,
opClientRequestPostPause,
opClientRequestPostUnpause,
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory,
opClientRequestHistoryAll
};
enum EMatchMode
{
mmID = 1,
mmName,
mmRegEx
};
typedef std::vector<char*> NameList;
private:
bool m_bNoConfig;
char* m_szConfigFilename;
// Parsed command-line parameters
bool m_bErrors;
bool m_bPrintVersion;
bool m_bPrintUsage;
bool m_bServerMode;
bool m_bDaemonMode;
bool m_bRemoteClientMode;
EClientOperation m_eClientOperation;
NameList m_OptionList;
int m_iEditQueueAction;
int m_iEditQueueOffset;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
NameList m_EditQueueNameList;
EMatchMode m_EMatchMode;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szAddCategory;
int m_iAddPriority;
bool m_bAddPaused;
char* m_szAddNZBFilename;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
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;
bool m_bSigVerify;
char* m_szPubKeyFilename;
char* m_szSigFilename;
bool m_bPauseDownload;
void InitCommandLine(int argc, const char* argv[]);
void InitFileArg(int argc, const char* argv[]);
void ParseFileIDList(int argc, const char* argv[], int optind);
void ParseFileNameList(int argc, const char* argv[], int optind);
bool ParseTime(const char* szTime, int* pHours, int* pMinutes);
void ReportError(const char* szErrMessage);
public:
CommandLineParser(int argc, const char* argv[]);
~CommandLineParser();
void PrintUsage(const char* com);
bool GetErrors() { return m_bErrors; }
bool GetNoConfig() { return m_bNoConfig; }
const char* GetConfigFilename() { return m_szConfigFilename; }
bool GetServerMode() { return m_bServerMode; }
bool GetDaemonMode() { return m_bDaemonMode; }
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
NameList* GetOptionList() { return &m_OptionList; }
int GetEditQueueAction() { return m_iEditQueueAction; }
int GetEditQueueOffset() { return m_iEditQueueOffset; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
EMatchMode GetMatchMode() { return m_EMatchMode; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetAddCategory() { return m_szAddCategory; }
bool GetAddPaused() { return m_bAddPaused; }
const char* GetLastArg() { return m_szLastArg; }
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
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; }
bool GetSigVerify() { return m_bSigVerify; }
const char* GetPubKeyFilename() { return m_szPubKeyFilename; }
const char* GetSigFilename() { return m_szSigFilename; }
bool GetPrintOptions() { return m_bPrintOptions; }
bool GetPrintVersion() { return m_bPrintVersion; }
bool GetPrintUsage() { return m_bPrintUsage; }
bool GetPauseDownload() const { return m_bPauseDownload; }
};
extern CommandLineParser* g_pCommandLineParser;
#endif

131
daemon/main/DiskService.cpp Normal file
View File

@@ -0,0 +1,131 @@
/*
* 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$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nzbget.h"
#include "DiskService.h"
#include "Options.h"
#include "StatMeter.h"
#include "Log.h"
#include "Util.h"
DiskService::DiskService()
{
m_iInterval = 0;
m_bWaitingReported = false;
m_bWaitingRequiredDir = true;
}
void DiskService::ServiceWork()
{
m_iInterval++;
if (m_iInterval == 5)
{
if (!g_pOptions->GetPauseDownload() &&
g_pOptions->GetDiskSpace() > 0 && !g_pStatMeter->GetStandBy())
{
// check free disk space every 1 second
CheckDiskSpace();
}
m_iInterval = 0;
}
if (m_bWaitingRequiredDir)
{
CheckRequiredDir();
}
}
void DiskService::CheckDiskSpace()
{
long long lFreeSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetDestDir());
g_pOptions->SetPauseDownload(true);
}
if (!Util::EmptyStr(g_pOptions->GetInterDir()))
{
lFreeSpace = Util::FreeDiskSize(g_pOptions->GetInterDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetInterDir());
g_pOptions->SetPauseDownload(true);
}
}
}
void DiskService::CheckRequiredDir()
{
if (!Util::EmptyStr(g_pOptions->GetRequiredDir()))
{
bool bAllExist = true;
bool bWasWaitingReported = m_bWaitingReported;
// split RequiredDir into tokens
Tokenizer tok(g_pOptions->GetRequiredDir(), ",;");
while (const char* szDir = tok.Next())
{
if (!Util::FileExists(szDir) && !Util::DirectoryExists(szDir))
{
if (!bWasWaitingReported)
{
info("Waiting for required directory %s", szDir);
m_bWaitingReported = true;
}
bAllExist = false;
}
}
if (!bAllExist)
{
return;
}
}
if (m_bWaitingReported)
{
info("All required directories available");
}
g_pOptions->SetTempPauseDownload(false);
g_pOptions->SetTempPausePostprocess(false);
m_bWaitingRequiredDir = false;
}

49
daemon/main/DiskService.h Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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$
*
*/
#ifndef DISKSERVICE_H
#define DISKSERVICE_H
#include "Service.h"
class DiskService : public Service
{
private:
int m_iInterval;
bool m_bWaitingRequiredDir;
bool m_bWaitingReported;
void CheckDiskSpace();
void CheckRequiredDir();
protected:
virtual int ServiceInterval() { return 200; }
virtual void ServiceWork();
public:
DiskService();
};
#endif

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
@@ -40,14 +40,46 @@
#endif
#include <errno.h>
#ifdef HAVE_OPENSSL
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#endif /* HAVE_OPENSSL */
#include "nzbget.h"
#include "Log.h"
#include "Util.h"
#include "Maintenance.h"
#include "Options.h"
#include "CommandLineParser.h"
extern void ExitProc();
extern int g_iArgumentCount;
extern char* (*g_szArguments)[];
#ifdef HAVE_OPENSSL
class Signature
{
private:
const char* m_szInFilename;
const char* m_szSigFilename;
const char* m_szPubKeyFilename;
unsigned char m_InHash[SHA256_DIGEST_LENGTH];
unsigned char m_Signature[256];
RSA* m_pPubKey;
bool ReadSignature();
bool ComputeInHash();
bool ReadPubKey();
public:
Signature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename);
~Signature();
bool Verify();
};
#endif
extern Options* g_pOptions;
extern Maintenance* g_pMaintenance;
Maintenance::Maintenance()
{
@@ -69,7 +101,7 @@ Maintenance::~Maintenance()
}
}
ClearMessages();
m_Messages.Clear();
free(m_szUpdateScript);
}
@@ -81,16 +113,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 +124,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 +160,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);
@@ -227,8 +263,21 @@ bool Maintenance::ReadPackageInfoStr(const char* szKey, char** pValue)
return true;
}
bool Maintenance::VerifySignature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename)
{
#ifdef HAVE_OPENSSL
Signature signature(szInFilename, szSigFilename, szPubKeyFilename);
return signature.Verify();
#else
return false;
#endif
}
void UpdateScriptController::Run()
{
// the update-script should not be automatically terminated when the program quits
UnregisterRunningScript();
m_iPrefixLen = 0;
PrintMessage(Message::mkInfo, "Executing update-script %s", GetScript());
@@ -240,11 +289,21 @@ void UpdateScriptController::Run()
const char* szBranchName[] = { "STABLE", "TESTING", "DEVEL" };
SetEnvVar("NZBUP_BRANCH", szBranchName[m_eBranch]);
SetEnvVar("NZBUP_RUNMODE", g_pCommandLineParser->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';
@@ -266,8 +325,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)
@@ -323,3 +398,110 @@ void UpdateInfoScriptController::AddMessage(Message::EKind eKind, const char* sz
ScriptController::AddMessage(eKind, szText);
}
}
#ifdef HAVE_OPENSSL
Signature::Signature(const char *szInFilename, const char *szSigFilename, const char *szPubKeyFilename)
{
m_szInFilename = szInFilename;
m_szSigFilename = szSigFilename;
m_szPubKeyFilename = szPubKeyFilename;
m_pPubKey = NULL;
}
Signature::~Signature()
{
RSA_free(m_pPubKey);
}
// Calculate SHA-256 for input file (m_szInFilename)
bool Signature::ComputeInHash()
{
FILE* infile = fopen(m_szInFilename, FOPEN_RB);
if (!infile)
{
return false;
}
SHA256_CTX sha256;
SHA256_Init(&sha256);
const int bufSize = 32*1024;
char* buffer = (char*)malloc(bufSize);
while(int bytesRead = fread(buffer, 1, bufSize, infile))
{
SHA256_Update(&sha256, buffer, bytesRead);
}
SHA256_Final(m_InHash, &sha256);
free(buffer);
fclose(infile);
return true;
}
// Read signature from file (m_szSigFilename) into memory
bool Signature::ReadSignature()
{
char szSigTitle[256];
snprintf(szSigTitle, sizeof(szSigTitle), "\"RSA-SHA256(%s)\" : \"", Util::BaseFileName(m_szInFilename));
szSigTitle[256-1] = '\0';
FILE* infile = fopen(m_szSigFilename, FOPEN_RB);
if (!infile)
{
return false;
}
bool bOK = false;
int iTitLen = strlen(szSigTitle);
char buf[1024];
unsigned char* output = m_Signature;
while (fgets(buf, sizeof(buf) - 1, infile))
{
if (!strncmp(buf, szSigTitle, iTitLen))
{
char* szHexSig = buf + iTitLen;
int iSigLen = strlen(szHexSig);
if (iSigLen > 2)
{
szHexSig[iSigLen - 2] = '\0'; // trim trailing ",
}
for (; *szHexSig && *(szHexSig+1);)
{
unsigned char c1 = *szHexSig++;
unsigned char c2 = *szHexSig++;
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;
}
bOK = output == m_Signature + sizeof(m_Signature);
break;
}
}
fclose(infile);
return bOK;
}
// Read public key from file (m_szPubKeyFilename) into memory
bool Signature::ReadPubKey()
{
char* keybuf;
int keybuflen;
if (!Util::LoadFileIntoBuffer(m_szPubKeyFilename, &keybuf, &keybuflen))
{
return false;
}
BIO* mem = BIO_new_mem_buf(keybuf, keybuflen);
m_pPubKey = PEM_read_bio_RSA_PUBKEY(mem, NULL, NULL, NULL);
BIO_free(mem);
free(keybuf);
return m_pPubKey != NULL;
}
bool Signature::Verify()
{
return ComputeInHash() && ReadSignature() && ReadPubKey() &&
RSA_verify(NID_sha256, m_InHash, sizeof(m_InHash), m_Signature, sizeof(m_Signature), m_pPubKey) == 1;
}
#endif /* HAVE_OPENSSL */

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,15 +54,17 @@ 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();
bool CheckUpdates(char** pUpdateInfo);
static bool VerifySignature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename);
};
extern Maintenance* g_pMaintenance;
class UpdateScriptController : public Thread, public ScriptController
{
private:

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -37,32 +37,12 @@
class Options
{
public:
enum EClientOperation
enum EWriteLog
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestListFiles,
opClientRequestListGroups,
opClientRequestListStatus,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestReload,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScanSync,
opClientRequestScanAsync,
opClientRequestDownloadPause,
opClientRequestDownloadUnpause,
opClientRequestPostPause,
opClientRequestPostUnpause,
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory,
opClientRequestDownloadUrl
wlNone,
wlAppend,
wlReset,
wlRotate
};
enum EMessageTarget
{
@@ -87,8 +67,9 @@ public:
enum EParScan
{
psLimited,
psExtended,
psFull,
psAuto
psDupe
};
enum EHealthCheck
{
@@ -96,11 +77,20 @@ public:
hcDelete,
hcNone
};
enum EMatchMode
enum ESchedulerCommand
{
mmID = 1,
mmName,
mmRegEx
scPauseDownload,
scUnpauseDownload,
scPausePostProcess,
scUnpausePostProcess,
scDownloadRate,
scScript,
scProcess,
scPauseScan,
scUnpauseScan,
scActivateServer,
scDeactivateServer,
scFetchFeed
};
class OptEntry
@@ -111,8 +101,6 @@ public:
char* m_szDefValue;
int m_iLineNo;
void SetName(const char* szName);
void SetValue(const char* szValue);
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
friend class Options;
@@ -121,10 +109,13 @@ public:
OptEntry();
OptEntry(const char* szName, const char* szValue);
~OptEntry();
void SetName(const char* szName);
const char* GetName() { return m_szName; }
void SetValue(const char* szValue);
const char* GetValue() { return m_szValue; }
const char* GetDefValue() { return m_szDefValue; }
int GetLineNo() { return m_iLineNo; }
bool Restricted();
};
typedef std::vector<OptEntry*> OptEntriesBase;
@@ -137,6 +128,7 @@ public:
};
typedef std::vector<char*> NameList;
typedef std::vector<const char*> CmdOptList;
class Category
{
@@ -166,75 +158,33 @@ public:
Category* FindCategory(const char* szName, bool bSearchAliases);
};
class Script
{
private:
char* m_szName;
char* m_szLocation;
char* m_szDisplayName;
bool m_bPostScript;
bool m_bScanScript;
bool m_bQueueScript;
bool m_bSchedulerScript;
public:
Script(const char* szName, const char* szLocation);
~Script();
const char* GetName() { return m_szName; }
const char* GetLocation() { return m_szLocation; }
void SetDisplayName(const char* szDisplayName);
const char* GetDisplayName() { return m_szDisplayName; }
bool GetPostScript() { return m_bPostScript; }
void SetPostScript(bool bPostScript) { m_bPostScript = bPostScript; }
bool GetScanScript() { return m_bScanScript; }
void SetScanScript(bool bScanScript) { m_bScanScript = bScanScript; }
bool GetQueueScript() { return m_bQueueScript; }
void SetQueueScript(bool bQueueScript) { m_bQueueScript = bQueueScript; }
bool GetSchedulerScript() { return m_bSchedulerScript; }
void SetSchedulerScript(bool bSchedulerScript) { m_bSchedulerScript = bSchedulerScript; }
};
typedef std::list<Script*> ScriptListBase;
class ScriptList: public ScriptListBase
class Extender
{
public:
~ScriptList();
Script* Find(const char* szName);
};
class ConfigTemplate
{
private:
Script* m_pScript;
char* m_szTemplate;
friend class Options;
public:
ConfigTemplate(Script* pScript, const char* szTemplate);
~ConfigTemplate();
Script* GetScript() { return m_pScript; }
const char* GetTemplate() { return m_szTemplate; }
};
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
class ConfigTemplates: public ConfigTemplatesBase
{
public:
~ConfigTemplates();
virtual void AddNewsServer(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 iRetention,
int iLevel, int iGroup) = 0;
virtual void AddFeed(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bBacklog, bool bPauseNzb, const char* szCategory,
int iPriority, const char* szFeedScript) {}
virtual void AddTask(int iID, int iHours, int iMinutes, int iWeekDaysBits, ESchedulerCommand eCommand,
const char* szParam) {}
virtual void SetupFirstStart() {}
};
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
Categories m_Categories;
bool m_bNoDiskAccess;
bool m_bFatalError;
Extender* m_pExtender;
// Options
bool m_bConfigErrors;
int m_iConfigLine;
char* m_szAppDir;
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szInterDir;
@@ -244,25 +194,32 @@ private:
char* m_szWebDir;
char* m_szConfigTemplate;
char* m_szScriptDir;
char* m_szRequiredDir;
EMessageTarget m_eInfoTarget;
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
EMessageTarget m_eDebugTarget;
EMessageTarget m_eDetailTarget;
bool m_bDecode;
bool m_bCreateBrokenLog;
bool m_bResetLog;
int m_iConnectionTimeout;
bool m_bBrokenLog;
bool m_bNzbLog;
int m_iArticleTimeout;
int m_iUrlTimeout;
int m_iTerminateTimeout;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bFlushQueue;
bool m_bDupeCheck;
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;
@@ -275,17 +232,22 @@ private:
bool m_bReloadQueue;
int m_iUrlConnections;
int m_iLogBufferSize;
bool m_bCreateLog;
EWriteLog m_eWriteLog;
int m_iRotateLog;
char* m_szLogFile;
EParCheck m_eParCheck;
bool m_bParRepair;
EParScan m_eParScan;
bool m_bParQuick;
bool m_bParRename;
int m_iParBuffer;
int m_iParThreads;
EHealthCheck m_eHealthCheck;
char* m_szPostScript;
char* m_szScriptOrder;
char* m_szScanScript;
char* m_szQueueScript;
char* m_szFeedScript;
bool m_bNoConfig;
int m_iUMask;
int m_iUpdateInterval;
@@ -294,7 +256,7 @@ private:
bool m_bCursesGroup;
bool m_bCrcCheck;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iWriteBuffer;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
@@ -312,6 +274,7 @@ private:
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
char* m_szUnpackPassFile;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
char* m_szParIgnoreExt;
@@ -319,89 +282,65 @@ private:
bool m_bUrlForce;
int m_iTimeCorrection;
int m_iPropagationDelay;
// Parsed command-line parameters
bool m_bServerMode;
bool m_bDaemonMode;
bool m_bRemoteClientMode;
int m_iEditQueueAction;
int m_iEditQueueOffset;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
NameList m_EditQueueNameList;
EMatchMode m_EMatchMode;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szAddCategory;
int m_iAddPriority;
bool m_bAddPaused;
char* m_szAddNZBFilename;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
int m_iSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
int m_iArticleCache;
int m_iEventInterval;
// Current state
bool m_bServerMode;
bool m_bRemoteClientMode;
bool m_bPauseDownload;
bool m_bPausePostProcess;
bool m_bPauseScan;
bool m_bTempPauseDownload;
int m_iDownloadRate;
EClientOperation m_eClientOperation;
time_t m_tResumeTime;
int m_iLocalTimeOffset;
bool m_bTempPausePostprocess;
void InitDefault();
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void Init(const char* szExeName, const char* szConfigFilename, bool bNoConfig,
CmdOptList* pCommandLineOptions, bool bNoDiskAccess, Extender* pExtender);
void InitDefaults();
void InitOptions();
void InitFileArg(int argc, char* argv[]);
void InitOptFile();
void InitServers();
void InitCategories();
void InitScheduler();
void InitFeeds();
void InitCommandLineOptions(CmdOptList* pCommandLineOptions);
void CheckOptions();
void PrintUsage(char* com);
void Dump();
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
int ParseIntValue(const char* OptName, int iBase);
float ParseFloatValue(const char* OptName);
OptEntry* FindOption(const char* optname);
const char* GetOption(const char* optname);
void SetOption(const char* optname, const char* value);
bool SetOptionString(const char* option);
bool SplitOptionString(const char* option, char** pOptName, char** pOptValue);
bool ValidateOptionName(const char* optname, const char* optvalue);
void LoadConfigFile();
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
void ParseFileIDList(int argc, char* argv[], int optind);
void ParseFileNameList(int argc, char* argv[], int optind);
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
void CheckDir(char** dir, const char* szOptionName, const char* szParentDir,
bool bAllowEmpty, bool bCreate);
bool ParseTime(const char* szTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
void ConfigError(const char* msg, ...);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *szOptionName);
void ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue, int iValueBufLen);
static bool CompareScripts(Script* pScript1, Script* pScript2);
void LoadScriptDir(ScriptList* pScriptList, const char* szDirectory, bool bIsSubDir);
void BuildScriptDisplayNames(ScriptList* pScriptList);
public:
Options(int argc, char* argv[]);
Options(const char* szExeName, const char* szConfigFilename, bool bNoConfig,
CmdOptList* pCommandLineOptions, Extender* pExtender);
Options(CmdOptList* pCommandLineOptions, Extender* pExtender);
~Options();
bool LoadConfig(OptEntries* pOptEntries);
bool SaveConfig(OptEntries* pOptEntries);
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
void LoadScriptList(ScriptList* pScriptList);
// Options
bool SplitOptionString(const char* option, char** pOptName, char** pOptValue);
bool GetFatalError() { return m_bFatalError; }
OptEntries* LockOptEntries();
void UnlockOptEntries();
// Options
const char* GetConfigFilename() { return m_szConfigFilename; }
bool GetConfigErrors() { return m_bConfigErrors; }
const char* GetAppDir() { return m_szAppDir; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetInterDir() { return m_szInterDir; }
const char* GetTempDir() { return m_szTempDir; }
@@ -410,14 +349,16 @@ 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 GetResetLog() const { return m_bResetLog; }
const char* GetRequiredDir() { return m_szRequiredDir; }
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; }
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetArticleTimeout() { return m_iArticleTimeout; }
int GetUrlTimeout() { return m_iUrlTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
bool GetDecode() { return m_bDecode; };
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
@@ -425,10 +366,15 @@ public:
int GetRetries() { return m_iRetries; }
int GetRetryInterval() { return m_iRetryInterval; }
bool GetSaveQueue() { return m_bSaveQueue; }
bool GetFlushQueue() { return m_bFlushQueue; }
bool GetDupeCheck() { return m_bDupeCheck; }
const char* GetControlIP() { return m_szControlIP; }
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; }
@@ -441,17 +387,22 @@ public:
bool GetReloadQueue() { return m_bReloadQueue; }
int GetUrlConnections() { return m_iUrlConnections; }
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
EWriteLog GetWriteLog() { return m_eWriteLog; }
const char* GetLogFile() { return m_szLogFile; }
int GetRotateLog() { return m_iRotateLog; }
EParCheck GetParCheck() { return m_eParCheck; }
bool GetParRepair() { return m_bParRepair; }
EParScan GetParScan() { return m_eParScan; }
bool GetParQuick() { return m_bParQuick; }
bool GetParRename() { return m_bParRename; }
int GetParBuffer() { return m_iParBuffer; }
int GetParThreads() { return m_iParThreads; }
EHealthCheck GetHealthCheck() { return m_eHealthCheck; }
const char* GetScriptOrder() { return m_szScriptOrder; }
const char* GetPostScript() { return m_szPostScript; }
const char* GetScanScript() { return m_szScanScript; }
const char* GetQueueScript() { return m_szQueueScript; }
const char* GetFeedScript() { return m_szFeedScript; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
bool GetCursesNZBName() { return m_bCursesNZBName; }
@@ -459,7 +410,7 @@ public:
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetWriteBuffer() { return m_iWriteBuffer; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
@@ -477,6 +428,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; }
@@ -484,34 +436,17 @@ public:
bool GetUrlForce() { return m_bUrlForce; }
int GetTimeCorrection() { return m_iTimeCorrection; }
int GetPropagationDelay() { return m_iPropagationDelay; }
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
bool GetServerMode() { return m_bServerMode; }
bool GetDaemonMode() { return m_bDaemonMode; }
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
int GetEditQueueAction() { return m_iEditQueueAction; }
int GetEditQueueOffset() { return m_iEditQueueOffset; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
EMatchMode GetMatchMode() { return m_EMatchMode; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetAddCategory() { return m_szAddCategory; }
bool GetAddPaused() { return m_bAddPaused; }
const char* GetLastArg() { return m_szLastArg; }
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
int GetSetRate() { return m_iSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
// Current state
void SetServerMode(bool bServerMode) { m_bServerMode = bServerMode; }
bool GetServerMode() { return m_bServerMode; }
void SetRemoteClientMode(bool bRemoteClientMode) { m_bRemoteClientMode = bRemoteClientMode; }
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }
bool GetPauseDownload() const { return m_bPauseDownload; }
void SetPausePostProcess(bool bPausePostProcess) { m_bPausePostProcess = bPausePostProcess; }
@@ -520,6 +455,8 @@ public:
bool GetPauseScan() const { return m_bPauseScan; }
void SetTempPauseDownload(bool bTempPauseDownload) { m_bTempPauseDownload = bTempPauseDownload; }
bool GetTempPauseDownload() const { return m_bTempPauseDownload; }
bool GetTempPausePostprocess() const { return m_bTempPausePostprocess; }
void SetTempPausePostprocess(bool bTempPausePostprocess) { m_bTempPausePostprocess = bTempPausePostprocess; }
void SetDownloadRate(int iRate) { m_iDownloadRate = iRate; }
int GetDownloadRate() const { return m_iDownloadRate; }
void SetResumeTime(time_t tResumeTime) { m_tResumeTime = tResumeTime; }
@@ -528,4 +465,6 @@ public:
int GetLocalTimeOffset() { return m_iLocalTimeOffset; }
};
extern Options* g_pOptions;
#endif

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
@@ -45,30 +45,7 @@
#include "ServerPool.h"
#include "FeedInfo.h"
#include "FeedCoordinator.h"
#include "QueueScript.h"
extern Options* g_pOptions;
extern ServerPool* g_pServerPool;
extern FeedCoordinator* g_pFeedCoordinator;
class SchedulerScriptController : public Thread, public NZBScriptController
{
private:
char* m_szScript;
bool m_bExternalProcess;
int m_iTaskID;
void PrepareParams(const char* szScriptName);
void ExecuteExternalProcess();
protected:
virtual void ExecuteScript(Options::Script* pScript);
public:
virtual ~SchedulerScriptController();
virtual void Run();
static void StartScript(const char* szParam, bool bExternalProcess, int iTaskID);
};
#include "SchedulerScript.h"
Scheduler::Task::Task(int iID, int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, const char* szParam)
{
@@ -91,6 +68,7 @@ Scheduler::Scheduler()
{
debug("Creating Scheduler");
m_bFirstChecked = false;
m_tLastCheck = 0;
m_TaskList.clear();
}
@@ -125,16 +103,23 @@ void Scheduler::FirstCheck()
m_mutexTaskList.Unlock();
// check all tasks for the last week
time_t tCurrent = time(NULL);
m_tLastCheck = tCurrent - 60*60*24*7;
m_bDetectClockChanges = false;
m_bExecuteProcess = false;
CheckTasks();
}
void Scheduler::IntervalCheck()
void Scheduler::ServiceWork()
{
m_bDetectClockChanges = true;
if (!DownloadQueue::IsLoaded())
{
return;
}
if (!m_bFirstChecked)
{
FirstCheck();
m_bFirstChecked = true;
return;
}
m_bExecuteProcess = true;
CheckTasks();
CheckScheduledResume();
@@ -150,21 +135,20 @@ void Scheduler::CheckTasks()
if (!m_TaskList.empty())
{
if (m_bDetectClockChanges)
// Detect large step changes of system time
time_t tDiff = tCurrent - m_tLastCheck;
if (tDiff > 60*90 || tDiff < 0)
{
// Detect large step changes of system time
time_t tDiff = tCurrent - m_tLastCheck;
if (tDiff > 60*90 || tDiff < -60*90)
{
debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)");
m_bExecuteProcess = false;
m_tLastCheck = tCurrent;
debug("Reset scheduled tasks (detected clock change greater than 90 minutes or negative)");
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
pTask->m_tLastExecuted = 0;
}
// check all tasks for the last week
m_tLastCheck = tCurrent - 60*60*24*7;
m_bExecuteProcess = false;
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
pTask->m_tLastExecuted = 0;
}
}
@@ -230,7 +214,8 @@ void Scheduler::CheckTasks()
void Scheduler::ExecuteTask(Task* pTask)
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute process", "Execute script",
const char* szCommandName[] = { "Pause", "Unpause", "Pause Post-processing", "Unpause Post-processing",
"Set download rate", "Execute process", "Execute script",
"Pause Scan", "Unpause Scan", "Enable Server", "Disable Server", "Fetch Feed" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
@@ -250,12 +235,10 @@ void Scheduler::ExecuteTask(Task* pTask)
m_bPauseDownloadChanged = true;
break;
case scScript:
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szParam, pTask->m_eCommand == scProcess, pTask->m_iID);
}
case scPausePostProcess:
case scUnpausePostProcess:
g_pOptions->SetPausePostProcess(pTask->m_eCommand == scPausePostProcess);
m_bPausePostProcessChanged = true;
break;
case scPauseScan:
@@ -264,6 +247,14 @@ void Scheduler::ExecuteTask(Task* pTask)
m_bPauseScanChanged = true;
break;
case scScript:
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szParam, pTask->m_eCommand == scProcess, pTask->m_iID);
}
break;
case scActivateServer:
case scDeactivateServer:
EditServer(pTask->m_eCommand == scActivateServer, pTask->m_szParam);
@@ -282,6 +273,7 @@ void Scheduler::PrepareLog()
{
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPausePostProcessChanged = false;
m_bPauseScanChanged = false;
m_bServerChanged = false;
}
@@ -296,6 +288,10 @@ void Scheduler::PrintLog()
{
info("Scheduler: %s download", g_pOptions->GetPauseDownload() ? "pausing" : "unpausing");
}
if (m_bPausePostProcessChanged)
{
info("Scheduler: %s post-processing", g_pOptions->GetPausePostProcess() ? "pausing" : "unpausing");
}
if (m_bPauseScanChanged)
{
info("Scheduler: %s scan", g_pOptions->GetPauseScan() ? "pausing" : "unpausing");
@@ -379,99 +375,3 @@ void Scheduler::CheckScheduledResume()
g_pOptions->SetPauseScan(false);
}
}
SchedulerScriptController::~SchedulerScriptController()
{
free(m_szScript);
}
void SchedulerScriptController::StartScript(const char* szParam, bool bExternalProcess, int iTaskID)
{
char** argv = NULL;
if (bExternalProcess && !Util::SplitCommandLine(szParam, &argv))
{
error("Could not execute scheduled process-script, failed to parse command line: %s", szParam);
return;
}
SchedulerScriptController* pScriptController = new SchedulerScriptController();
pScriptController->m_bExternalProcess = bExternalProcess;
pScriptController->m_szScript = strdup(szParam);
pScriptController->m_iTaskID = iTaskID;
if (bExternalProcess)
{
pScriptController->SetScript(argv[0]);
pScriptController->SetArgs((const char**)argv, true);
}
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void SchedulerScriptController::Run()
{
if (m_bExternalProcess)
{
ExecuteExternalProcess();
}
else
{
ExecuteScriptList(m_szScript);
}
}
void SchedulerScriptController::ExecuteScript(Options::Script* pScript)
{
if (!pScript->GetSchedulerScript())
{
return;
}
PrintMessage(Message::mkInfo, "Executing scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
SetScript(pScript->GetLocation());
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(pScript->GetDisplayName());
PrepareParams(pScript->GetName());
Execute();
SetLogPrefix(NULL);
}
void SchedulerScriptController::PrepareParams(const char* szScriptName)
{
ResetEnv();
SetIntEnvVar("NZBSP_TASKID", m_iTaskID);
PrepareEnvScript(NULL, szScriptName);
}
void SchedulerScriptController::ExecuteExternalProcess()
{
info("Executing scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
Execute();
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 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
@@ -31,14 +31,17 @@
#include <time.h>
#include "Thread.h"
#include "Service.h"
class Scheduler
class Scheduler : public Service
{
public:
enum ECommand
{
scPauseDownload,
scUnpauseDownload,
scPausePostProcess,
scUnpausePostProcess,
scDownloadRate,
scScript,
scProcess,
@@ -75,13 +78,15 @@ private:
TaskList m_TaskList;
Mutex m_mutexTaskList;
time_t m_tLastCheck;
bool m_bDetectClockChanges;
bool m_bDownloadRateChanged;
bool m_bExecuteProcess;
bool m_bPauseDownloadChanged;
bool m_bPausePostProcessChanged;
bool m_bPauseScanChanged;
bool m_bServerChanged;
ServerStatusList m_ServerStatusList;
bool m_bFirstChecked;
void ExecuteTask(Task* pTask);
void CheckTasks();
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
@@ -90,13 +95,18 @@ private:
void EditServer(bool bActive, const char* szServerList);
void FetchFeed(const char* szFeedList);
void CheckScheduledResume();
void FirstCheck();
protected:
virtual int ServiceInterval() { return 1000; }
virtual void ServiceWork();
public:
Scheduler();
~Scheduler();
void AddTask(Task* pTask);
void FirstCheck();
void IntervalCheck();
};
extern Scheduler* g_pScheduler;
#endif

320
daemon/main/StackTrace.cpp Normal file
View File

@@ -0,0 +1,320 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <dbghelp.h>
#else
#include <unistd.h>
#include <sys/resource.h>
#include <signal.h>
#endif
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include "nzbget.h"
#include "Log.h"
#include "Options.h"
#include "StackTrace.h"
extern void ExitProc();
#ifdef WIN32
#ifdef DEBUG
void PrintBacktrace(PCONTEXT pContext)
{
HANDLE hProcess = GetCurrentProcess();
HANDLE hThread = GetCurrentThread();
char szAppDir[MAX_PATH + 1];
GetModuleFileName(NULL, szAppDir, sizeof(szAppDir));
char* end = strrchr(szAppDir, PATH_SEPARATOR);
if (end) *end = '\0';
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
if (!SymInitialize(hProcess, szAppDir, TRUE))
{
warn("Could not obtain detailed exception information: SymInitialize failed");
return;
}
const int MAX_NAMELEN = 1024;
IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN);
memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN);
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
pSym->MaxNameLength = MAX_NAMELEN;
IMAGEHLP_LINE64 ilLine;
memset(&ilLine, 0, sizeof(ilLine));
ilLine.SizeOfStruct = sizeof(ilLine);
STACKFRAME64 sfStackFrame;
memset(&sfStackFrame, 0, sizeof(sfStackFrame));
DWORD imageType;
#ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386;
sfStackFrame.AddrPC.Offset = pContext->Eip;
sfStackFrame.AddrPC.Mode = AddrModeFlat;
sfStackFrame.AddrFrame.Offset = pContext->Ebp;
sfStackFrame.AddrFrame.Mode = AddrModeFlat;
sfStackFrame.AddrStack.Offset = pContext->Esp;
sfStackFrame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
sfStackFrame.AddrPC.Offset = pContext->Rip;
sfStackFrame.AddrPC.Mode = AddrModeFlat;
sfStackFrame.AddrFrame.Offset = pContext->Rsp;
sfStackFrame.AddrFrame.Mode = AddrModeFlat;
sfStackFrame.AddrStack.Offset = pContext->Rsp;
sfStackFrame.AddrStack.Mode = AddrModeFlat;
#else
warn("Could not obtain detailed exception information: platform not supported");
return;
#endif
for (int frameNum = 0; ; frameNum++)
{
if (frameNum > 1000)
{
warn("Endless stack, abort tracing");
return;
}
if (!StackWalk64(imageType, hProcess, hThread, &sfStackFrame, pContext, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{
warn("Could not obtain detailed exception information: StackWalk64 failed");
return;
}
DWORD64 dwAddr = sfStackFrame.AddrPC.Offset;
char szSymName[1024];
char szSrcFileName[1024];
int iLineNumber = 0;
DWORD64 dwSymbolDisplacement;
if (SymGetSymFromAddr64(hProcess, dwAddr, &dwSymbolDisplacement, pSym))
{
UnDecorateSymbolName(pSym->Name, szSymName, sizeof(szSymName), UNDNAME_COMPLETE);
szSymName[sizeof(szSymName) - 1] = '\0';
}
else
{
strncpy(szSymName, "<symbol not available>", sizeof(szSymName));
}
DWORD dwLineDisplacement;
if (SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &ilLine))
{
iLineNumber = ilLine.LineNumber;
char* szUseFileName = ilLine.FileName;
char* szRoot = strstr(szUseFileName, "\\daemon\\");
if (szRoot)
{
szUseFileName = szRoot;
}
strncpy(szSrcFileName, szUseFileName, sizeof(szSrcFileName));
szSrcFileName[sizeof(szSrcFileName) - 1] = '\0';
}
else
{
strncpy(szSrcFileName, "<filename not available>", sizeof(szSymName));
}
info("%s (%i) : %s", szSrcFileName, iLineNumber, szSymName);
if (sfStackFrame.AddrReturn.Offset == 0)
{
break;
}
}
}
#endif
LONG __stdcall ExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
{
error("Unhandled Exception: code: 0x%8.8X, flags: %d, address: 0x%8.8X",
pExPtrs->ExceptionRecord->ExceptionCode,
pExPtrs->ExceptionRecord->ExceptionFlags,
pExPtrs->ExceptionRecord->ExceptionAddress);
#ifdef DEBUG
PrintBacktrace(pExPtrs->ContextRecord);
#else
info("Detailed exception information can be printed by debug version of NZBGet (available from download page)");
#endif
ExitProcess(-1);
return EXCEPTION_CONTINUE_SEARCH;
}
void InstallErrorHandler()
{
SetUnhandledExceptionFilter(ExceptionFilter);
}
#else
#ifdef DEBUG
typedef void(*sighandler)(int);
std::vector<sighandler> SignalProcList;
#endif
#ifdef HAVE_SYS_PRCTL_H
/**
* activates the creation of core-files
*/
void EnableDumpCore()
{
rlimit rlim;
rlim.rlim_cur= RLIM_INFINITY;
rlim.rlim_max= RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rlim);
prctl(PR_SET_DUMPABLE, 1);
}
#endif
void PrintBacktrace()
{
#ifdef HAVE_BACKTRACE
printf("Segmentation fault, tracing...\n");
void *array[100];
size_t size;
char **strings;
size_t i;
size = backtrace(array, 100);
strings = backtrace_symbols(array, size);
// first trace to screen
printf("Obtained %zd stack frames\n", size);
for (i = 0; i < size; i++)
{
printf("%s\n", strings[i]);
}
// then trace to log
error("Segmentation fault, tracing...");
error("Obtained %zd stack frames", size);
for (i = 0; i < size; i++)
{
error("%s", strings[i]);
}
free(strings);
#else
error("Segmentation fault");
#endif
}
/*
* Signal handler
*/
void SignalProc(int iSignal)
{
switch (iSignal)
{
case SIGINT:
signal(SIGINT, SIG_DFL); // Reset the signal handler
ExitProc();
break;
case SIGTERM:
signal(SIGTERM, SIG_DFL); // Reset the signal handler
ExitProc();
break;
case SIGCHLD:
// ignoring
break;
#ifdef DEBUG
case SIGSEGV:
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
PrintBacktrace();
break;
#endif
}
}
void InstallErrorHandler()
{
#ifdef HAVE_SYS_PRCTL_H
if (g_pOptions->GetDumpCore())
{
EnableDumpCore();
}
#endif
signal(SIGINT, SignalProc);
signal(SIGTERM, SignalProc);
signal(SIGPIPE, SIG_IGN);
#ifdef DEBUG
signal(SIGSEGV, SignalProc);
#endif
#ifdef SIGCHLD_HANDLER
// it could be necessary on some systems to activate a handler for SIGCHLD
// however it make troubles on other systems and is deactivated by default
signal(SIGCHLD, SignalProc);
#endif
}
#endif
#ifdef DEBUG
class SegFault
{
public:
void DoSegFault()
{
char* N = NULL;
strcpy(N, "");
}
};
void TestSegFault()
{
SegFault s;
s.DoSegFault();
}
#endif

35
daemon/main/StackTrace.h Normal file
View File

@@ -0,0 +1,35 @@
/*
* This file is part of nzbget
*
* 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
* 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 STACKTRACE_H
#define STACKTRACE_H
void InstallErrorHandler();
#ifdef DEBUG
void TestSegFault();
#endif
#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
@@ -40,7 +40,6 @@
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#ifdef HAVE_SYS_PRCTL_H
#include <sys/prctl.h>
#endif
@@ -53,21 +52,21 @@
#ifndef DISABLE_PARCHECK
#include <iostream>
#endif
#ifdef HAVE_BACKTRACE
#include <execinfo.h>
#endif
#include "nzbget.h"
#include "ServerPool.h"
#include "Log.h"
#include "NZBFile.h"
#include "Options.h"
#include "CommandLineParser.h"
#include "ScriptConfig.h"
#include "Thread.h"
#include "ColoredFrontend.h"
#include "NCursesFrontend.h"
#include "QueueCoordinator.h"
#include "UrlCoordinator.h"
#include "RemoteServer.h"
#include "WebServer.h"
#include "RemoteClient.h"
#include "MessageBase.h"
#include "DiskState.h"
@@ -78,11 +77,21 @@
#include "Scheduler.h"
#include "Scanner.h"
#include "FeedCoordinator.h"
#include "Service.h"
#include "DiskService.h"
#include "Maintenance.h"
#include "ArticleWriter.h"
#include "StatMeter.h"
#include "QueueScript.h"
#include "Util.h"
#include "StackTrace.h"
#ifdef WIN32
#include "NTService.h"
#include "WinConsole.h"
#include "WebDownloader.h"
#endif
#ifdef ENABLE_TESTS
#include "TestMain.h"
#endif
// Prototypes
@@ -91,30 +100,24 @@ void Run(bool bReload);
void Reload();
void Cleanup();
void ProcessClientRequest();
void ProcessWebGet();
void ProcessSigVerify();
#ifndef WIN32
void InstallSignalHandlers();
void Daemonize();
void PrintBacktrace();
#ifdef HAVE_SYS_PRCTL_H
void EnableDumpCore();
#endif
#ifdef DEBUG
void MakeSegFault();
#endif
#endif
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
void BootConfig();
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
CommandLineParser* g_pCommandLineParser = NULL;
ServerPool* g_pServerPool = NULL;
QueueCoordinator* g_pQueueCoordinator = NULL;
UrlCoordinator* g_pUrlCoordinator = NULL;
RemoteServer* g_pRemoteServer = NULL;
RemoteServer* g_pRemoteSecureServer = NULL;
StatMeter* g_pStatMeter = NULL;
Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor = NULL;
HistoryCoordinator* g_pHistoryCoordinator = NULL;
DupeCoordinator* g_pDupeCoordinator = NULL;
@@ -123,10 +126,17 @@ Scheduler* g_pScheduler = NULL;
Scanner* g_pScanner = NULL;
FeedCoordinator* g_pFeedCoordinator = NULL;
Maintenance* g_pMaintenance = NULL;
ArticleCache* g_pArticleCache = NULL;
QueueScriptCoordinator* g_pQueueScriptCoordinator = NULL;
ServiceCoordinator* g_pServiceCoordinator = NULL;
DiskService* g_pDiskService = NULL;
int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
bool g_bReloading = true;
#ifdef WIN32
WinConsole* g_pWinConsole = NULL;
#endif
/*
* Main loop
@@ -137,7 +147,7 @@ int main(int argc, char *argv[], char *argp[])
#ifdef _DEBUG
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF
#ifdef DEBUG_CRTMEMLEAKS
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
#endif
@@ -145,8 +155,26 @@ int main(int argc, char *argv[], char *argp[])
#endif
#endif
Util::InitVersionRevision();
Util::Init();
g_iArgumentCount = argc;
g_szArguments = (char*(*)[])argv;
g_szEnvironmentVariables = (char*(*)[])argp;
if (argc > 1 && (!strcmp(argv[1], "-tests") || !strcmp(argv[1], "--tests")))
{
#ifdef ENABLE_TESTS
return TestMain(argc, argv);
#else
printf("ERROR: Could not start tests, the program was compiled without tests\n");
return 1;
#endif
}
#ifdef ENABLE_TESTS
TestCleanup();
#endif
#ifdef WIN32
InstallUninstallServiceCheck(argc, argv);
#endif
@@ -155,11 +183,7 @@ int main(int argc, char *argv[], char *argp[])
DisableCout();
#endif
srand (time(NULL));
g_iArgumentCount = argc;
g_szArguments = (char*(*)[])argv;
g_szEnvironmentVariables = (char*(*)[])argp;
srand(time(NULL));
#ifdef WIN32
for (int i=0; i < argc; i++)
@@ -174,12 +198,6 @@ int main(int argc, char *argv[], char *argp[])
RunMain();
#ifdef WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}
@@ -204,7 +222,7 @@ void RunMain()
void Run(bool bReload)
{
g_pLog = new Log();
Log::Init();
debug("nzbget %s", Util::VersionRevision());
@@ -213,6 +231,12 @@ void Run(bool bReload)
Thread::Init();
}
#ifdef WIN32
g_pWinConsole = new WinConsole();
g_pWinConsole->InitAppMode();
#endif
g_pServiceCoordinator = new ServiceCoordinator();
g_pServerPool = new ServerPool();
g_pScheduler = new Scheduler();
g_pQueueCoordinator = new QueueCoordinator();
@@ -223,10 +247,12 @@ void Run(bool bReload)
g_pDupeCoordinator = new DupeCoordinator();
g_pUrlCoordinator = new UrlCoordinator();
g_pFeedCoordinator = new FeedCoordinator();
g_pArticleCache = new ArticleCache();
g_pMaintenance = new Maintenance();
g_pQueueScriptCoordinator = new QueueScriptCoordinator();
g_pDiskService = new DiskService();
debug("Reading options");
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
BootConfig();
#ifndef WIN32
if (g_pOptions->GetUMask() < 01000)
@@ -236,16 +262,10 @@ void Run(bool bReload)
}
#endif
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
{
debug("Deleting old log-file");
g_pLog->ResetLog();
}
g_pLog->InitOptions();
g_pScanner->InitOptions();
g_pQueueScriptCoordinator->InitOptions();
if (g_pOptions->GetDaemonMode())
if (g_pCommandLineParser->GetDaemonMode())
{
#ifdef WIN32
info("nzbget %s service-mode", Util::VersionRevision());
@@ -261,7 +281,7 @@ void Run(bool bReload)
{
info("nzbget %s server-mode", Util::VersionRevision());
}
else if (g_pOptions->GetRemoteClientMode())
else if (g_pCommandLineParser->GetRemoteClientMode())
{
info("nzbget %s remote-mode", Util::VersionRevision());
}
@@ -271,33 +291,35 @@ void Run(bool bReload)
Connection::Init();
}
if (!g_pOptions->GetRemoteClientMode())
if (!g_pCommandLineParser->GetRemoteClientMode())
{
g_pServerPool->InitConnections();
g_pStatMeter->Init();
}
#ifndef WIN32
#ifdef HAVE_SYS_PRCTL_H
if (g_pOptions->GetDumpCore())
InstallErrorHandler();
#ifdef DEBUG
if (g_pCommandLineParser->GetTestBacktrace())
{
EnableDumpCore();
TestSegFault();
}
#endif
#endif
#ifndef WIN32
InstallSignalHandlers();
#ifdef DEBUG
if (g_pOptions->GetTestBacktrace())
if (g_pCommandLineParser->GetWebGet())
{
MakeSegFault();
ProcessWebGet();
return;
}
if (g_pCommandLineParser->GetSigVerify())
{
ProcessSigVerify();
return;
}
#endif
#endif
// client request
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
if (g_pCommandLineParser->GetClientOperation() != CommandLineParser::opClientNoOperation)
{
ProcessClientRequest();
Cleanup();
@@ -307,6 +329,7 @@ void Run(bool bReload)
// Setup the network-server
if (g_pOptions->GetServerMode())
{
WebProcessor::Init();
g_pRemoteServer = new RemoteServer(false);
g_pRemoteServer->Start();
@@ -318,7 +341,7 @@ void Run(bool bReload)
}
// Create the frontend
if (!g_pOptions->GetDaemonMode())
if (!g_pCommandLineParser->GetDaemonMode())
{
switch (g_pOptions->GetOutputMode())
{
@@ -343,19 +366,20 @@ void Run(bool bReload)
}
// Starting QueueCoordinator and PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
if (!g_pCommandLineParser->GetRemoteClientMode())
{
// Standalone-mode
if (!g_pOptions->GetServerMode())
if (!g_pCommandLineParser->GetServerMode())
{
const char* szCategory = g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "";
NZBFile* pNZBFile = NZBFile::Create(g_pOptions->GetArgFilename(), szCategory);
if (!pNZBFile)
const char* szCategory = g_pCommandLineParser->GetAddCategory() ? g_pCommandLineParser->GetAddCategory() : "";
NZBFile* pNZBFile = new NZBFile(g_pCommandLineParser->GetArgFilename(), szCategory);
if (!pNZBFile->Parse())
{
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
printf("Parsing NZB-document %s failed\n\n", g_pCommandLineParser->GetArgFilename() ? g_pCommandLineParser->GetArgFilename() : "N/A");
delete pNZBFile;
return;
}
g_pScanner->InitPPParameters(szCategory, pNZBFile->GetNZBInfo()->GetParameters());
g_pScanner->InitPPParameters(szCategory, pNZBFile->GetNZBInfo()->GetParameters(), false);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, NULL, false);
delete pNZBFile;
}
@@ -365,16 +389,29 @@ void Run(bool bReload)
g_pDiskState = new DiskState();
}
#ifdef WIN32
g_pWinConsole->Start();
#endif
g_pQueueCoordinator->Start();
g_pUrlCoordinator->Start();
g_pPrePostProcessor->Start();
g_pFeedCoordinator->Start();
g_pServiceCoordinator->Start();
if (g_pOptions->GetArticleCache() > 0)
{
g_pArticleCache->Start();
}
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() ||
g_pUrlCoordinator->IsRunning() ||
g_pPrePostProcessor->IsRunning() ||
g_pFeedCoordinator->IsRunning())
g_pFeedCoordinator->IsRunning() ||
g_pServiceCoordinator->IsRunning() ||
#ifdef WIN32
g_pWinConsole->IsRunning() ||
#endif
g_pArticleCache->IsRunning())
{
if (!g_pOptions->GetServerMode() &&
!g_pQueueCoordinator->HasMoreJobs() &&
@@ -398,6 +435,14 @@ void Run(bool bReload)
{
g_pFeedCoordinator->Stop();
}
if (!g_pArticleCache->IsStopped())
{
g_pArticleCache->Stop();
}
if (!g_pServiceCoordinator->IsStopped())
{
g_pServiceCoordinator->Stop();
}
}
usleep(100 * 1000);
}
@@ -407,8 +452,12 @@ void Run(bool bReload)
debug("UrlCoordinator stopped");
debug("PrePostProcessor stopped");
debug("FeedCoordinator stopped");
debug("ServiceCoordinator stopped");
debug("ArticleCache stopped");
}
ScriptController::TerminateAll();
// Stop network-server
if (g_pRemoteServer)
{
@@ -449,7 +498,7 @@ void Run(bool bReload)
// Stop Frontend
if (g_pFrontend)
{
if (!g_pOptions->GetRemoteClientMode())
if (!g_pCommandLineParser->GetRemoteClientMode())
{
debug("Stopping Frontend");
g_pFrontend->Stop();
@@ -464,121 +513,224 @@ void Run(bool bReload)
Cleanup();
}
class OptionsExtender : public Options::Extender
{
protected:
#ifdef WIN32
virtual void SetupFirstStart()
{
g_pWinConsole->SetupFirstStart();
}
#endif
virtual void AddNewsServer(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 iRetention,
int iLevel, int iGroup)
{
g_pServerPool->AddServer(new NewsServer(iID, bActive, szName, szHost, iPort, szUser, szPass, bJoinGroup,
bTLS, szCipher, iMaxConnections, iRetention, iLevel, iGroup));
}
virtual void AddFeed(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bBacklog, bool bPauseNzb, const char* szCategory,
int iPriority, const char* szFeedScript)
{
g_pFeedCoordinator->AddFeed(new FeedInfo(iID, szName, szUrl, bBacklog, iInterval, szFilter, bPauseNzb, szCategory, iPriority, szFeedScript));
}
virtual void AddTask(int iID, int iHours, int iMinutes, int iWeekDaysBits,
Options::ESchedulerCommand eCommand, const char* szParam)
{
g_pScheduler->AddTask(new Scheduler::Task(iID, iHours, iMinutes, iWeekDaysBits, (Scheduler::ECommand)eCommand, szParam));
}
} g_OptionsExtender;
void BootConfig()
{
debug("Parsing command line");
g_pCommandLineParser = new CommandLineParser(g_iArgumentCount, (const char**)(*g_szArguments));
if (g_pCommandLineParser->GetPrintVersion())
{
printf("nzbget version: %s\n", Util::VersionRevision());
exit(0);
}
if (g_pCommandLineParser->GetPrintUsage() || g_pCommandLineParser->GetErrors() || g_iArgumentCount <= 1)
{
g_pCommandLineParser->PrintUsage(((const char**)(*g_szArguments))[0]);
exit(0);
}
debug("Reading options");
g_pOptions = new Options((*g_szArguments)[0], g_pCommandLineParser->GetConfigFilename(),
g_pCommandLineParser->GetNoConfig(), (Options::CmdOptList*)g_pCommandLineParser->GetOptionList(),
&g_OptionsExtender);
g_pOptions->SetRemoteClientMode(g_pCommandLineParser->GetRemoteClientMode());
g_pOptions->SetServerMode(g_pCommandLineParser->GetServerMode());
g_pOptions->SetPauseDownload(g_pCommandLineParser->GetPauseDownload());
g_pLog->InitOptions();
if (g_pOptions->GetFatalError())
{
exit(1);
}
else if (g_pOptions->GetConfigErrors() &&
g_pCommandLineParser->GetClientOperation() == CommandLineParser::opClientNoOperation)
{
info("Pausing all activities due to errors in configuration");
g_pOptions->SetPauseDownload(true);
g_pOptions->SetPausePostProcess(true);
g_pOptions->SetPauseScan(true);
}
g_pServerPool->SetTimeout(g_pOptions->GetArticleTimeout());
g_pServerPool->SetRetryInterval(g_pOptions->GetRetryInterval());
g_pScriptConfig = new ScriptConfig();
}
void ProcessClientRequest()
{
RemoteClient* Client = new RemoteClient();
switch (g_pOptions->GetClientOperation())
switch (g_pCommandLineParser->GetClientOperation())
{
case Options::opClientRequestListFiles:
Client->RequestServerList(true, false, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
case CommandLineParser::opClientRequestListFiles:
Client->RequestServerList(true, false, g_pCommandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? g_pCommandLineParser->GetEditQueueText() : NULL);
break;
case Options::opClientRequestListGroups:
Client->RequestServerList(false, true, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
case CommandLineParser::opClientRequestListGroups:
Client->RequestServerList(false, true, g_pCommandLineParser->GetMatchMode() == CommandLineParser::mmRegEx ? g_pCommandLineParser->GetEditQueueText() : NULL);
break;
case Options::opClientRequestListStatus:
case CommandLineParser::opClientRequestListStatus:
Client->RequestServerList(false, false, NULL);
break;
case Options::opClientRequestDownloadPause:
case CommandLineParser::opClientRequestDownloadPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload);
break;
case Options::opClientRequestDownloadUnpause:
case CommandLineParser::opClientRequestDownloadUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload);
break;
case Options::opClientRequestSetRate:
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
case CommandLineParser::opClientRequestSetRate:
Client->RequestServerSetDownloadRate(g_pCommandLineParser->GetSetRate());
break;
case Options::opClientRequestDumpDebug:
case CommandLineParser::opClientRequestDumpDebug:
Client->RequestServerDumpDebug();
break;
case Options::opClientRequestEditQueue:
Client->RequestServerEditQueue((DownloadQueue::EEditAction)g_pOptions->GetEditQueueAction(),
g_pOptions->GetEditQueueOffset(), g_pOptions->GetEditQueueText(),
g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(),
g_pOptions->GetEditQueueNameList(), (eRemoteMatchMode)g_pOptions->GetMatchMode());
case CommandLineParser::opClientRequestEditQueue:
Client->RequestServerEditQueue((DownloadQueue::EEditAction)g_pCommandLineParser->GetEditQueueAction(),
g_pCommandLineParser->GetEditQueueOffset(), g_pCommandLineParser->GetEditQueueText(),
g_pCommandLineParser->GetEditQueueIDList(), g_pCommandLineParser->GetEditQueueIDCount(),
g_pCommandLineParser->GetEditQueueNameList(), (eRemoteMatchMode)g_pCommandLineParser->GetMatchMode());
break;
case Options::opClientRequestLog:
Client->RequestServerLog(g_pOptions->GetLogLines());
case CommandLineParser::opClientRequestLog:
Client->RequestServerLog(g_pCommandLineParser->GetLogLines());
break;
case Options::opClientRequestShutdown:
case CommandLineParser::opClientRequestShutdown:
Client->RequestServerShutdown();
break;
case Options::opClientRequestReload:
case CommandLineParser::opClientRequestReload:
Client->RequestServerReload();
break;
case Options::opClientRequestDownload:
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
case CommandLineParser::opClientRequestDownload:
Client->RequestServerDownload(g_pCommandLineParser->GetAddNZBFilename(), g_pCommandLineParser->GetArgFilename(),
g_pCommandLineParser->GetAddCategory(), g_pCommandLineParser->GetAddTop(), g_pCommandLineParser->GetAddPaused(), g_pCommandLineParser->GetAddPriority(),
g_pCommandLineParser->GetAddDupeKey(), g_pCommandLineParser->GetAddDupeMode(), g_pCommandLineParser->GetAddDupeScore());
break;
case Options::opClientRequestVersion:
case CommandLineParser::opClientRequestVersion:
Client->RequestServerVersion();
break;
case Options::opClientRequestPostQueue:
case CommandLineParser::opClientRequestPostQueue:
Client->RequestPostQueue();
break;
case Options::opClientRequestWriteLog:
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
case CommandLineParser::opClientRequestWriteLog:
Client->RequestWriteLog(g_pCommandLineParser->GetWriteLogKind(), g_pCommandLineParser->GetLastArg());
break;
case Options::opClientRequestScanAsync:
case CommandLineParser::opClientRequestScanAsync:
Client->RequestScan(false);
break;
case Options::opClientRequestScanSync:
case CommandLineParser::opClientRequestScanSync:
Client->RequestScan(true);
break;
case Options::opClientRequestPostPause:
case CommandLineParser::opClientRequestPostPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionPostProcess);
break;
case Options::opClientRequestPostUnpause:
case CommandLineParser::opClientRequestPostUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionPostProcess);
break;
case Options::opClientRequestScanPause:
case CommandLineParser::opClientRequestScanPause:
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionScan);
break;
case Options::opClientRequestScanUnpause:
case CommandLineParser::opClientRequestScanUnpause:
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionScan);
break;
case Options::opClientRequestHistory:
Client->RequestHistory();
case CommandLineParser::opClientRequestHistory:
case CommandLineParser::opClientRequestHistoryAll:
Client->RequestHistory(g_pCommandLineParser->GetClientOperation() == CommandLineParser::opClientRequestHistoryAll);
break;
case Options::opClientRequestDownloadUrl:
Client->RequestServerDownloadUrl(g_pOptions->GetLastArg(), g_pOptions->GetAddNZBFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
break;
case Options::opClientNoOperation:
case CommandLineParser::opClientNoOperation:
break;
}
delete Client;
}
void ProcessWebGet()
{
WebDownloader downloader;
downloader.SetURL(g_pCommandLineParser->GetLastArg());
downloader.SetForce(true);
downloader.SetRetry(false);
downloader.SetOutputFilename(g_pCommandLineParser->GetWebGetFilename());
downloader.SetInfoName("WebGet");
WebDownloader::EStatus eStatus = downloader.DownloadWithRedirects(5);
bool bOK = eStatus == WebDownloader::adFinished;
exit(bOK ? 0 : 1);
}
void ProcessSigVerify()
{
#ifdef HAVE_OPENSSL
bool bOK = Maintenance::VerifySignature(g_pCommandLineParser->GetLastArg(),
g_pCommandLineParser->GetSigFilename(), g_pCommandLineParser->GetPubKeyFilename());
exit(bOK ? 93 : 1);
#else
printf("ERROR: Could not verify signature, the program was compiled without OpenSSL support\n");
exit(1);
#endif
}
void ExitProc()
{
if (!g_bReloading)
{
info("Stopping, please wait...");
}
if (g_pOptions->GetRemoteClientMode())
if (g_pCommandLineParser->GetRemoteClientMode())
{
if (g_pFrontend)
{
@@ -591,10 +743,16 @@ void ExitProc()
if (g_pQueueCoordinator)
{
debug("Stopping QueueCoordinator");
g_pServiceCoordinator->Stop();
g_pQueueCoordinator->Stop();
g_pUrlCoordinator->Stop();
g_pPrePostProcessor->Stop();
g_pFeedCoordinator->Stop();
g_pArticleCache->Stop();
g_pQueueScriptCoordinator->Stop();
#ifdef WIN32
g_pWinConsole->Stop();
#endif
}
}
}
@@ -606,114 +764,6 @@ void Reload()
ExitProc();
}
#ifndef WIN32
#ifdef DEBUG
typedef void(*sighandler)(int);
std::vector<sighandler> SignalProcList;
#endif
/*
* Signal handler
*/
void SignalProc(int iSignal)
{
switch (iSignal)
{
case SIGINT:
signal(SIGINT, SIG_DFL); // Reset the signal handler
ExitProc();
break;
case SIGTERM:
signal(SIGTERM, SIG_DFL); // Reset the signal handler
ExitProc();
break;
case SIGCHLD:
// ignoring
break;
#ifdef DEBUG
case SIGSEGV:
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
PrintBacktrace();
break;
#endif
}
}
void InstallSignalHandlers()
{
signal(SIGINT, SignalProc);
signal(SIGTERM, SignalProc);
signal(SIGPIPE, SIG_IGN);
#ifdef DEBUG
signal(SIGSEGV, SignalProc);
#endif
#ifdef SIGCHLD_HANDLER
// it could be necessary on some systems to activate a handler for SIGCHLD
// however it make troubles on other systems and is deactivated by default
signal(SIGCHLD, SignalProc);
#endif
}
void PrintBacktrace()
{
#ifdef HAVE_BACKTRACE
printf("Segmentation fault, tracing...\n");
void *array[100];
size_t size;
char **strings;
size_t i;
size = backtrace(array, 100);
strings = backtrace_symbols(array, size);
// first trace to screen
printf("Obtained %zd stack frames\n", size);
for (i = 0; i < size; i++)
{
printf("%s\n", strings[i]);
}
// then trace to log
error("Segmentation fault, tracing...");
error("Obtained %zd stack frames", size);
for (i = 0; i < size; i++)
{
error("%s", strings[i]);
}
free(strings);
#else
error("Segmentation fault");
#endif
}
#ifdef DEBUG
void MakeSegFault()
{
char* N = NULL;
strcpy(N, "");
}
#endif
#ifdef HAVE_SYS_PRCTL_H
/**
* activates the creation of core-files
*/
void EnableDumpCore()
{
rlimit rlim;
rlim.rlim_cur= RLIM_INFINITY;
rlim.rlim_max= RLIM_INFINITY;
setrlimit(RLIMIT_CORE, &rlim);
prctl(PR_SET_DUMPABLE, 1);
}
#endif
#endif
void Cleanup()
{
debug("Cleaning up global objects");
@@ -768,16 +818,31 @@ void Cleanup()
debug("Deleting Options");
if (g_pOptions)
{
if (g_pOptions->GetDaemonMode() && !g_bReloading)
if (g_pCommandLineParser->GetDaemonMode() && !g_bReloading)
{
info("Deleting lock file");
remove(g_pOptions->GetLockFile());
}
delete g_pOptions;
g_pOptions = NULL;
}
debug("Options deleted");
debug("Deleting CommandLineParser");
if (g_pCommandLineParser)
{
delete g_pCommandLineParser;
g_pCommandLineParser = NULL;
}
debug("CommandLineParser deleted");
debug("Deleting ScriptConfig");
if (g_pScriptConfig)
{
delete g_pScriptConfig;
g_pScriptConfig = NULL;
}
debug("ScriptConfig deleted");
debug("Deleting ServerPool");
delete g_pServerPool;
g_pServerPool = NULL;
@@ -793,6 +858,16 @@ void Cleanup()
g_pFeedCoordinator = NULL;
debug("FeedCoordinator deleted");
debug("Deleting ArticleCache");
delete g_pArticleCache;
g_pArticleCache = NULL;
debug("ArticleCache deleted");
debug("Deleting QueueScriptCoordinator");
delete g_pQueueScriptCoordinator;
g_pQueueScriptCoordinator = NULL;
debug("QueueScriptCoordinator deleted");
debug("Deleting Maintenance");
delete g_pMaintenance;
g_pMaintenance = NULL;
@@ -803,57 +878,105 @@ void Cleanup()
g_pStatMeter = NULL;
debug("StatMeter deleted");
debug("Deleting ServiceCoordinator");
delete g_pServiceCoordinator;
g_pServiceCoordinator = NULL;
debug("ServiceCoordinator deleted");
debug("Deleting DiskService");
delete g_pDiskService;
g_pDiskService = NULL;
debug("DiskService deleted");
if (!g_bReloading)
{
Connection::Final();
Thread::Final();
}
#ifdef WIN32
delete g_pWinConsole;
g_pWinConsole = NULL;
#endif
debug("Global objects cleaned up");
delete g_pLog;
g_pLog = NULL;
Log::Final();
}
#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);
}
@@ -874,3 +997,4 @@ void DisableCout()
std::cout.rdbuf(&NullStreamBufInstance);
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* 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
@@ -39,7 +39,9 @@
#define gmtime_r(time, tm) gmtime_s(tm, time)
#define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr)
#define strerror_r(errnum, buffer, size) strerror_s(buffer, size, errnum)
#if (_MSC_VER < 1600)
#define int32_t __int32
#endif
#define mkdir(dir, flags) _mkdir(dir)
#define rmdir _rmdir
#define strcasecmp(a, b) _stricmp(a, b)
@@ -50,7 +52,6 @@
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
#define S_DIRMODE NULL
#define usleep(usec) Sleep((usec) / 1000)
#define gettimeofday(tm, ignore) _ftime(tm)
#define socklen_t int
#define SHUT_WR 0x01
#define SHUT_RDWR 0x02
@@ -59,6 +60,11 @@
#define LINE_ENDING "\r\n"
#define pid_t int
#define atoll _atoi64
#define fseek _fseeki64
#define ftell _ftelli64
#if _MSC_VER < 1800 // va_copy is available in vc2013 and onwards
#define va_copy(d,s) ((d) = (s))
#endif
#ifndef FSCTL_SET_SPARSE
#define FSCTL_SET_SPARSE 590020
#endif
@@ -69,6 +75,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

File diff suppressed because it is too large Load Diff

View File

@@ -34,6 +34,7 @@
#include "Thread.h"
#include "NNTPConnection.h"
#include "Decoder.h"
#include "ArticleWriter.h"
class ArticleDownloader : public Thread, public Subject
{
@@ -47,13 +48,20 @@ public:
adFailed,
adRetry,
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
class ArticleWriterImpl : public ArticleWriter
{
private:
ArticleDownloader* m_pOwner;
protected:
virtual void SetLastUpdateTimeNow() { m_pOwner->SetLastUpdateTimeNow(); }
public:
void SetOwner(ArticleDownloader* pOwner) { m_pOwner = pOwner; }
};
private:
FileInfo* m_pFileInfo;
@@ -61,31 +69,25 @@ private:
NNTPConnection* m_pConnection;
EStatus m_eStatus;
Mutex m_mutexConnection;
const char* m_szResultFilename;
char* m_szTempFilename;
char* m_szArticleFilename;
char* m_szInfoName;
char* m_szOutputFilename;
char m_szConnectionName[250];
char* m_szArticleFilename;
time_t m_tLastUpdateTime;
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
ArticleWriterImpl m_ArticleWriter;
ServerStatList m_ServerStats;
bool m_bWritingStarted;
int m_iDownloadedSize;
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
bool CreateOutputFile(int iSize);
void BuildOutputFilename();
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
bool Write(char* szLine, int iLen);
void AddServerData();
public:
ArticleDownloader();
@@ -102,11 +104,12 @@ public:
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
void SetInfoName(const char* szInfoName);
const char* GetInfoName() { return m_szInfoName; }
void CompleteFileParts();
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
const char* GetConnectionName() { return m_szConnectionName; }
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
void CompleteFileParts() { m_ArticleWriter.CompleteFileParts(); }
int GetDownloadedSize() { return m_iDownloadedSize; }
void LogDebugInfo();
};

View File

File diff suppressed because it is too large Load Diff

104
daemon/nntp/ArticleWriter.h Normal file
View File

@@ -0,0 +1,104 @@
/*
* 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 ARTICLEWRITER_H
#define ARTICLEWRITER_H
#include "DownloadInfo.h"
#include "Decoder.h"
class ArticleWriter
{
private:
FileInfo* m_pFileInfo;
ArticleInfo* m_pArticleInfo;
FILE* m_pOutFile;
char* m_szTempFilename;
char* m_szOutputFilename;
const char* m_szResultFilename;
Decoder::EFormat m_eFormat;
char* m_pArticleData;
long long m_iArticleOffset;
int m_iArticleSize;
int m_iArticlePtr;
bool m_bFlushing;
bool m_bDuplicate;
char* m_szInfoName;
bool PrepareFile(char* szLine);
bool CreateOutputFile(long long iSize);
void BuildOutputFilename();
bool IsFileCached();
void SetWriteBuffer(FILE* pOutFile, int iRecSize);
protected:
virtual void SetLastUpdateTimeNow() {}
public:
ArticleWriter();
~ArticleWriter();
void SetInfoName(const char* szInfoName);
void SetFileInfo(FileInfo* pFileInfo) { m_pFileInfo = pFileInfo; }
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
void Prepare();
bool Start(Decoder::EFormat eFormat, const char* szFilename, long long iFileSize, long long iArticleOffset, int iArticleSize);
bool Write(char* szBufffer, int iLen);
void Finish(bool bSuccess);
bool GetDuplicate() { return m_bDuplicate; }
void CompleteFileParts();
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
void FlushCache();
};
class ArticleCache : public Thread
{
private:
size_t m_iAllocated;
bool m_bFlushing;
Mutex m_mutexAlloc;
Mutex m_mutexFlush;
Mutex m_mutexContent;
FileInfo* m_pFileInfo;
bool CheckFlush(bool bFlushEverything);
public:
ArticleCache();
virtual void Run();
void* Alloc(int iSize);
void* Realloc(void* buf, int iOldSize, int iNewSize);
void Free(int iSize);
void LockFlush();
void UnlockFlush();
void LockContent() { m_mutexContent.Lock(); }
void UnlockContent() { m_mutexContent.Unlock(); }
bool GetFlushing() { return m_bFlushing; }
size_t GetAllocated() { return m_iAllocated; }
bool FileBusy(FileInfo* pFileInfo) { return pFileInfo == m_pFileInfo; }
};
extern ArticleCache* g_pArticleCache;
#endif

View File

@@ -1,8 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* 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
@@ -45,14 +44,11 @@
#include "Util.h"
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
unsigned int YDecoder::crc_tab[256];
Decoder::Decoder()
{
debug("Creating Decoder");
m_szSrcFilename = NULL;
m_szDestFilename = NULL;
m_szArticleFilename = NULL;
}
@@ -107,17 +103,6 @@ Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
* YDecoder: fast implementation of yEnc-Decoder
*/
void YDecoder::Init()
{
debug("Initializing global decoder");
crc32gentab();
}
void YDecoder::Final()
{
debug("Finalizing global Decoder");
}
YDecoder::YDecoder()
{
Clear();
@@ -135,69 +120,13 @@ void YDecoder::Clear()
m_lExpectedCRC = 0;
m_lCalculatedCRC = 0xFFFFFFFF;
m_iBegin = 0;
m_iEnd = 0xFFFFFFFF;
m_iEnd = 0;
m_iSize = 0;
m_iEndSize = 0;
m_bAutoSeek = false;
m_bNeedSetPos = false;
m_bCrcCheck = false;
}
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
*
* (c) 1999,2000 Krzysztof Dabrowski
* (c) 1999,2000 ElysiuM deeZine
* Released under GPL (thanks)
*
* chksum_crc32gentab() -- to a global crc_tab[256], this one will
* calculate the crcTable for crc32-checksums.
* it is generated to the polynom [..]
*/
void YDecoder::crc32gentab()
{
unsigned long crc, poly;
int i, j;
poly = 0xEDB88320L;
for (i = 0; i < 256; i++)
{
crc = i;
for (j = 8; j > 0; j--)
{
if (crc & 1)
{
crc = (crc >> 1) ^ poly;
}
else
{
crc >>= 1;
}
}
crc_tab[i] = crc;
}
}
/* This is modified version of chksum_crc() from
* crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
* (c) 1999,2000 Krzysztof Dabrowski
* (c) 1999,2000 ElysiuM deeZine
*
* chksum_crc() -- to a given block, this one calculates the
* crc32-checksum until the length is
* reached. the crc32-checksum will be
* the result.
*/
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
{
register unsigned long crc = startCrc;
for (unsigned long i = 0; i < length; i++)
{
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
}
return crc;
}
unsigned int YDecoder::DecodeBuffer(char* buffer)
int YDecoder::DecodeBuffer(char* buffer, int len)
{
if (m_bBody && !m_bEnd)
{
@@ -215,7 +144,7 @@ unsigned int YDecoder::DecodeBuffer(char* buffer)
if (pb)
{
pb += 6; //=strlen(" size=")
m_iEndSize = (int)atoi(pb);
m_iEndSize = (long long)atoll(pb);
}
return 0;
}
@@ -247,9 +176,9 @@ BreakLoop:
if (m_bCrcCheck)
{
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
m_lCalculatedCRC = Util::Crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
}
return (unsigned int)(optr - buffer);
return optr - buffer;
}
else
{
@@ -271,7 +200,7 @@ BreakLoop:
if (pb)
{
pb += 6; //=strlen(" size=")
m_iSize = (int)atoi(pb);
m_iSize = (long long)atoll(pb);
}
m_bPart = strstr(buffer, " part=");
if (!m_bPart)
@@ -289,13 +218,13 @@ BreakLoop:
if (pb)
{
pb += 7; //=strlen(" begin=")
m_iBegin = (int)atoi(pb);
m_iBegin = (long long)atoll(pb);
}
pb = strstr(buffer, " end=");
if (pb)
{
pb += 5; //=strlen(" end=")
m_iEnd = (int)atoi(pb);
m_iEnd = (long long)atoll(pb);
}
}
}
@@ -303,28 +232,6 @@ BreakLoop:
return 0;
}
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer);
if (wcnt > 0)
{
if (m_bNeedSetPos)
{
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
{
return false;
}
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
{
return false;
}
m_bNeedSetPos = false;
}
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus YDecoder::Check()
{
m_lCalculatedCRC ^= 0xFFFFFFFF;
@@ -379,7 +286,7 @@ void UDecoder::Clear()
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
int UDecoder::DecodeBuffer(char* buffer, int len)
{
if (!m_bBody)
{
@@ -446,22 +353,12 @@ unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
}
}
return (unsigned int)(optr - buffer);
return optr - buffer;
}
return 0;
}
bool UDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer, len);
if (wcnt > 0)
{
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus UDecoder::Check()
{
if (!m_bBody)

View File

@@ -1,8 +1,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-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
@@ -50,8 +49,6 @@ public:
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
public:
@@ -59,9 +56,7 @@ public:
virtual ~Decoder();
virtual EStatus Check() = 0;
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
virtual int DecodeBuffer(char* buffer, int len) = 0;
const char* GetArticleFilename() { return m_szArticleFilename; }
static EFormat DetectFormat(const char* buffer, int len);
};
@@ -69,7 +64,6 @@ public:
class YDecoder: public Decoder
{
protected:
static unsigned int crc_tab[256];
bool m_bBegin;
bool m_bPart;
bool m_bBody;
@@ -77,28 +71,23 @@ protected:
bool m_bCrc;
unsigned long m_lExpectedCRC;
unsigned long m_lCalculatedCRC;
unsigned long m_iBegin;
unsigned long m_iEnd;
unsigned long m_iSize;
unsigned long m_iEndSize;
bool m_bAutoSeek;
bool m_bNeedSetPos;
long long m_iBegin;
long long m_iEnd;
long long m_iSize;
long long m_iEndSize;
bool m_bCrcCheck;
unsigned int DecodeBuffer(char* buffer);
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
public:
YDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
virtual int DecodeBuffer(char* buffer, int len);
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
static void Init();
static void Final();
long long GetBegin() { return m_iBegin; }
long long GetEnd() { return m_iEnd; }
long long GetSize() { return m_iSize; }
unsigned long GetExpectedCrc() { return m_lExpectedCRC; }
unsigned long GetCalculatedCrc() { return m_lCalculatedCRC; }
};
class UDecoder: public Decoder
@@ -107,13 +96,11 @@ private:
bool m_bBody;
bool m_bEnd;
unsigned int DecodeBuffer(char* buffer, int len);
public:
UDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
virtual int DecodeBuffer(char* buffer, int len);
};
#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-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-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,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,9 @@ public:
void CloseUnusedConnections();
void Changed();
int GetGeneration() { return m_iGeneration; }
void BlockServer(NewsServer* pNewsServer);
};
extern ServerPool* g_pServerPool;
#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,10 +40,7 @@
#include "Options.h"
#include "ServerPool.h"
#include "DiskState.h"
extern ServerPool* g_pServerPool;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#include "Util.h"
static const int DAYS_UP_TO_2013_JAN_1 = 15706;
static const int DAYS_IN_TWENTY_YEARS = 366*20;
@@ -171,43 +168,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 +352,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 +369,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);
@@ -382,13 +384,16 @@ void StatMeter::AddSpeedReading(int iBytes)
if (g_pOptions->GetAccurateRate())
{
#ifdef HAVE_SPINLOCK
m_spinlockSpeed.Lock();
#else
m_mutexSpeed.Lock();
#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
@@ -432,11 +437,7 @@ void StatMeter::AddSpeedReading(int iBytes)
if (g_pOptions->GetAccurateRate())
{
#ifdef HAVE_SPINLOCK
m_spinlockSpeed.Unlock();
#else
m_mutexSpeed.Unlock();
#endif
}
}
@@ -452,14 +453,16 @@ void StatMeter::ResetSpeedStat()
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
m_tSpeedCorrection = tCurTime;
m_tCurSecTime = 0;
m_iCurSecBytes = 0;
}
void StatMeter::LogDebugInfo()
{
info(" ---------- SpeedMeter");
float fSpeed = (float)(CalcCurrentDownloadSpeed() / 1024.0);
int iSpeed = CalcCurrentDownloadSpeed() / 1024;
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
info(" Speed: %f", fSpeed);
info(" Speed: %i", iSpeed);
info(" SpeedStartTime: %i", m_iSpeedStartTime);
info(" SpeedTotalBytes: %i", m_iSpeedTotalBytes);
info(" SpeedBytesIndex: %i", m_iSpeedBytesIndex);

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
@@ -93,11 +93,9 @@ private:
int m_iSpeedStartTime;
time_t m_tSpeedCorrection;
int m_iSpeedBytesIndex;
#ifdef HAVE_SPINLOCK
SpinLock m_spinlockSpeed;
#else
int m_iCurSecBytes;
time_t m_tCurSecTime;
Mutex m_mutexSpeed;
#endif
// time
long long m_iAllBytes;
@@ -125,6 +123,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);
@@ -137,4 +136,6 @@ public:
bool Load(bool* pPerfectServerMatch);
};
extern StatMeter* g_pStatMeter;
#endif

View File

@@ -0,0 +1,278 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Cleanup.h"
#include "Log.h"
#include "Util.h"
#include "ParParser.h"
#include "Options.h"
void MoveController::StartJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
pMoveController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pMoveController);
pMoveController->Start();
}
void MoveController::Run()
{
// the locking is needed for accessing the members of NZBInfo
DownloadQueue::Lock();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
m_szDestDir[1024-1] = '\0';
DownloadQueue::Unlock();
PrintMessage(Message::mkInfo, "Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
szInfoName[0] = 'M'; // uppercase
if (bOK)
{
PrintMessage(Message::mkInfo, "%s successful", szInfoName);
// save new dest dir
DownloadQueue::Lock();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
DownloadQueue::Unlock();
}
else
{
PrintMessage(Message::mkError, "%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool MoveController::MoveFiles()
{
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
PrintMessage(Message::mkError, "Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
bool bOK = true;
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
Util::MakeUniqueFilename(szDstFile, 1024, m_szDestDir, filename);
bool bHiddenFile = filename[0] == '.';
if (!bHiddenFile)
{
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;
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir, szErrBuf, sizeof(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)
{
CleanupController* pCleanupController = new CleanupController();
pCleanupController->m_pPostInfo = pPostInfo;
pCleanupController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pCleanupController);
pCleanupController->Start();
}
void CleanupController::Run()
{
// the locking is needed for accessing the members of NZBInfo
DownloadQueue::Lock();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
}
else
{
m_szFinalDir[0] = '\0';
}
DownloadQueue::Unlock();
PrintMessage(Message::mkInfo, "Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
if (bOK && m_szFinalDir[0] != '\0')
{
bool bDeleted2 = false;
bOK = Cleanup(m_szFinalDir, &bDeleted2);
bDeleted = bDeleted || bDeleted2;
}
szInfoName[0] = 'C'; // uppercase
if (bOK && bDeleted)
{
PrintMessage(Message::mkInfo, "%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
PrintMessage(Message::mkInfo, "Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
PrintMessage(Message::mkError, "%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
{
*bDeleted = false;
bool bOK = true;
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(), ",;") && !bIsDir;
if (bDeleteIt)
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not delete file %s: %s", szFullFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
bOK = false;
}
*bDeleted = true;
}
}
return bOK;
}
void CleanupController::AddMessage(Message::EKind eKind, const char* szText)
{
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}

View File

@@ -0,0 +1,68 @@
/*
* This file is part of nzbget
*
* 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
* 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 CLEANUP_H
#define CLEANUP_H
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "Script.h"
class MoveController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szInterDir[1024];
char m_szDestDir[1024];
bool MoveFiles();
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
class CleanupController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szDestDir[1024];
char m_szFinalDir[1024];
bool Cleanup(const char* szDestDir, bool *bDeleted);
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
#endif

View File

@@ -0,0 +1,260 @@
/*
* 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$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "DupeMatcher.h"
#include "Log.h"
#include "Util.h"
#include "Options.h"
#include "Script.h"
#include "Thread.h"
class RarLister : public Thread, public ScriptController
{
private:
DupeMatcher* m_pOwner;
long long m_lMaxSize;
bool m_bCompressed;
bool m_bLastSizeMax;
long long m_lExpectedSize;
char* m_szFilenameBuf;
int m_iFilenameBufLen;
char m_szLastFilename[1024];
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
static bool FindLargestFile(DupeMatcher* pOwner, const char* szDirectory,
char* szFilenameBuf, int iFilenameBufLen, long long lExpectedSize,
int iTimeoutSec, long long* pMaxSize, bool* pCompressed);
};
bool RarLister::FindLargestFile(DupeMatcher* pOwner, const char* szDirectory,
char* szFilenameBuf, int iFilenameBufLen, long long lExpectedSize,
int iTimeoutSec, long long* pMaxSize, bool* pCompressed)
{
RarLister unrar;
unrar.m_pOwner = pOwner;
unrar.m_lExpectedSize = lExpectedSize;
unrar.m_lMaxSize = -1;
unrar.m_bCompressed = false;
unrar.m_bLastSizeMax = false;
unrar.m_szFilenameBuf = szFilenameBuf;
unrar.m_iFilenameBufLen = iFilenameBufLen;
char** pCmdArgs = NULL;
if (!Util::SplitCommandLine(g_pOptions->GetUnrarCmd(), &pCmdArgs))
{
return false;
}
const char* szUnrarPath = *pCmdArgs;
unrar.SetScript(szUnrarPath);
const char* szArgs[4];
szArgs[0] = szUnrarPath;
szArgs[1] = "lt";
szArgs[2] = "*.rar";
szArgs[3] = NULL;
unrar.SetArgs(szArgs, false);
unrar.SetWorkingDir(szDirectory);
time_t curTime = time(NULL);
unrar.Start();
// wait up to iTimeoutSec for unrar output
while (unrar.IsRunning() &&
curTime + iTimeoutSec > time(NULL) &&
curTime >= time(NULL)) // in a case clock was changed
{
usleep(200 * 1000);
}
if (unrar.IsRunning())
{
unrar.Terminate();
}
// wait until terminated or killed
while (unrar.IsRunning())
{
usleep(200 * 1000);
}
for (char** szArgPtr = pCmdArgs; *szArgPtr; szArgPtr++)
{
free(*szArgPtr);
}
free(pCmdArgs);
*pMaxSize = unrar.m_lMaxSize;
*pCompressed = unrar.m_bCompressed;
return true;
}
void RarLister::Run()
{
Execute();
}
void RarLister::AddMessage(Message::EKind eKind, const char* szText)
{
if (!strncasecmp(szText, "Archive: ", 9))
{
m_pOwner->PrintMessage(Message::mkDetail, "Reading file %s", szText + 9);
}
else if (!strncasecmp(szText, " Name: ", 14))
{
strncpy(m_szLastFilename, szText + 14, sizeof(m_szLastFilename));
m_szLastFilename[sizeof(m_szLastFilename)-1] = '\0';
}
else if (!strncasecmp(szText, " Size: ", 14))
{
m_bLastSizeMax = false;
long long lSize = atoll(szText + 14);
if (lSize > m_lMaxSize)
{
m_lMaxSize = lSize;
m_bLastSizeMax = true;
strncpy(m_szFilenameBuf, m_szLastFilename, m_iFilenameBufLen);
m_szFilenameBuf[m_iFilenameBufLen-1] = '\0';
}
return;
}
if (m_bLastSizeMax && !strncasecmp(szText, " Compression: ", 14))
{
m_bCompressed = !strstr(szText, " -m0");
if (m_lMaxSize > m_lExpectedSize ||
DupeMatcher::SizeDiffOK(m_lMaxSize, m_lExpectedSize, 20))
{
// alread found the largest file, aborting unrar
Terminate();
}
}
}
DupeMatcher::DupeMatcher(const char* szDestDir, long long lExpectedSize)
{
m_szDestDir = strdup(szDestDir);
m_lExpectedSize = lExpectedSize;
m_lMaxSize = -1;
m_bCompressed = false;
}
DupeMatcher::~DupeMatcher()
{
free(m_szDestDir);
}
bool DupeMatcher::SizeDiffOK(long long lSize1, long long lSize2, int iMaxDiffPercent)
{
if (lSize1 == 0 || lSize2 == 0)
{
return false;
}
long long lDiff = lSize1 - lSize2;
lDiff = lDiff > 0 ? lDiff : -lDiff;
long long lMax = lSize1 > lSize2 ? lSize1 : lSize2;
int lDiffPercent = (int)(lDiff * 100 / lMax);
return lDiffPercent < iMaxDiffPercent;
}
bool DupeMatcher::Prepare()
{
char szFilename[1024];
FindLargestFile(m_szDestDir, szFilename, sizeof(szFilename), &m_lMaxSize, &m_bCompressed);
bool bSizeOK = SizeDiffOK(m_lMaxSize, m_lExpectedSize, 20);
PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
szFilename, m_lMaxSize, bSizeOK ? "" : ", size mismatch");
return bSizeOK;
}
bool DupeMatcher::MatchDupeContent(const char* szDupeDir)
{
long long lDupeMaxSize = 0;
bool lDupeCompressed = false;
char szFilename[1024];
FindLargestFile(szDupeDir, szFilename, sizeof(szFilename), &lDupeMaxSize, &lDupeCompressed);
bool bOK = lDupeMaxSize == m_lMaxSize && lDupeCompressed == m_bCompressed;
PrintMessage(Message::mkDetail, "Found main file %s with size %lli bytes%s",
szFilename, m_lMaxSize, bOK ? "" : ", size mismatch");
return bOK;
}
void DupeMatcher::FindLargestFile(const char* szDirectory, char* szFilenameBuf, int iBufLen,
long long* pMaxSize, bool* pCompressed)
{
*pMaxSize = 0;
*pCompressed = false;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
long long lFileSize = Util::FileSize(szFullFilename);
if (lFileSize > *pMaxSize)
{
*pMaxSize = lFileSize;
strncpy(szFilenameBuf, filename, iBufLen);
szFilenameBuf[iBufLen-1] = '\0';
}
if (Util::MatchFileExt(filename, ".rar", ","))
{
RarLister::FindLargestFile(this, szDirectory, szFilenameBuf, iBufLen,
m_lMaxSize, 60, pMaxSize, pCompressed);
return;
}
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* 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$
*
*/
#ifndef DUPEMATCHER_H
#define DUPEMATCHER_H
#include "Log.h"
class DupeMatcher
{
private:
char* m_szDestDir;
long long m_lExpectedSize;
long long m_lMaxSize;
bool m_bCompressed;
void FindLargestFile(const char* szDirectory, char* szFilenameBuf, int iBufLen,
long long* pMaxSize, bool* pCompressed);
friend class RarLister;
protected:
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
public:
DupeMatcher(const char* szDestDir, long long lExpectedSize);
~DupeMatcher();
bool Prepare();
bool MatchDupeContent(const char* szDupeDir);
static bool SizeDiffOK(long long lSize1, long long lSize2, int iMaxDiffPercent);
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-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
@@ -29,6 +29,7 @@
#ifndef DISABLE_PARCHECK
#include <deque>
#include <vector>
#include <string>
#include "Thread.h"
@@ -53,8 +54,61 @@ public:
ptVerifyingRepaired,
};
enum EFileStatus
{
fsUnknown,
fsSuccess,
fsPartial,
fsFailure
};
class Segment
{
private:
bool m_bSuccess;
long long m_iOffset;
int m_iSize;
unsigned long m_lCrc;
public:
Segment(bool bSuccess, long long iOffset, int iSize, unsigned long lCrc);
bool GetSuccess() { return m_bSuccess; }
long long GetOffset() { return m_iOffset; }
int GetSize() { return m_iSize; }
unsigned long GetCrc() { return m_lCrc; }
};
typedef std::deque<Segment*> SegmentListBase;
class SegmentList : public SegmentListBase
{
public:
~SegmentList();
};
class DupeSource
{
private:
int m_iID;
char* m_szDirectory;
int m_iUsedBlocks;
public:
DupeSource(int iID, const char* szDirectory);
~DupeSource();
int GetID() { return m_iID; }
const char* GetDirectory() { return m_szDirectory; }
int GetUsedBlocks() { return m_iUsedBlocks; }
void SetUsedBlocks(int iUsedBlocks) { m_iUsedBlocks = iUsedBlocks; }
};
typedef std::deque<DupeSource*> DupeSourceList;
typedef std::deque<char*> FileList;
typedef std::deque<void*> SourceList;
typedef std::vector<bool> ValidBlocks;
friend class Repairer;
private:
char* m_szInfoName;
@@ -63,7 +117,8 @@ private:
const char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
// declared as void* to prevent the including of libpar2-headers into this header-file
void* m_pRepairer;
char* m_szErrMsg;
FileList m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
@@ -72,14 +127,22 @@ private:
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
int m_iQuickFiles;
bool m_bVerifyingExtraFiles;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
SourceList m_sourceFiles;
std::string m_lastFilename;
bool m_bHasDamagedFiles;
bool m_bParQuick;
bool m_bForceRepair;
bool m_bParFull;
DupeSourceList m_DupeSources;
void Cleanup();
EStatus RunParCheckAll();
EStatus RunParCheck(const char* szParFilename);
int PreProcessPar();
bool LoadMainParBak();
@@ -87,13 +150,24 @@ private:
bool LoadMorePars();
bool AddSplittedFragments();
bool AddMissingFiles();
bool AddDupeFiles();
bool AddExtraFiles(bool bOnlyMissing, bool bExternalDir, const char* szDirectory);
bool IsProcessedFile(const char* szFilename);
void WriteBrokenLog(EStatus eStatus);
void SaveSourceList();
void DeleteLeftovers();
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_progress(int progress);
void signal_done(std::string str, int available, int total);
// declared as void* to prevent the including of libpar2-headers into this header-file
// DiskFile* pDiskfile, Par2RepairerSourceFile* pSourcefile
EFileStatus VerifyDataFile(void* pDiskfile, void* pSourcefile, int* pAvailableBlocks);
bool VerifySuccessDataFile(void* pDiskfile, void* pSourcefile, unsigned long lDownloadCrc);
bool VerifyPartialDataFile(void* pDiskfile, void* pSourcefile, SegmentList* pSegments, ValidBlocks* pValidBlocks);
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:
/**
@@ -107,6 +181,9 @@ protected:
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
virtual void RegisterParredFile(const char* szFilename) {}
virtual bool IsParredFile(const char* szFilename) { return false; }
virtual EFileStatus FindFileCrc(const char* szFilename, unsigned long* lCrc, SegmentList* pSegments) { return fsUnknown; }
virtual void RequestDupeSources(DupeSourceList* pDupeSourceList) {}
virtual void StatDupeSources(DupeSourceList* pDupeSourceList) {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
@@ -121,6 +198,12 @@ public:
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetNZBName(const char* szNZBName);
void SetParQuick(bool bParQuick) { m_bParQuick = bParQuick; }
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
@@ -44,12 +44,13 @@
#include "nzbget.h"
#include "ParCoordinator.h"
#include "DupeCoordinator.h"
#include "ParParser.h"
#include "Options.h"
#include "DiskState.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
#ifndef DISABLE_PARCHECK
bool ParCoordinator::PostParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
@@ -70,7 +71,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)
@@ -91,6 +92,122 @@ bool ParCoordinator::PostParChecker::IsParredFile(const char* szFilename)
return false;
}
ParChecker::EFileStatus ParCoordinator::PostParChecker::FindFileCrc(const char* szFilename,
unsigned long* lCrc, SegmentList* pSegments)
{
CompletedFile* pCompletedFile = NULL;
for (CompletedFiles::iterator it = m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->begin(); it != m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->end(); it++)
{
CompletedFile* pCompletedFile2 = *it;
if (!strcasecmp(pCompletedFile2->GetFileName(), szFilename))
{
pCompletedFile = pCompletedFile2;
break;
}
}
if (!pCompletedFile)
{
return ParChecker::fsUnknown;
}
debug("Found completed file: %s, CRC: %.8x, Status: %i", Util::BaseFileName(pCompletedFile->GetFileName()), pCompletedFile->GetCrc(), (int)pCompletedFile->GetStatus());
*lCrc = pCompletedFile->GetCrc();
if (pCompletedFile->GetStatus() == CompletedFile::cfPartial && pCompletedFile->GetID() > 0 &&
!m_pPostInfo->GetNZBInfo()->GetReprocess())
{
FileInfo* pTmpFileInfo = new FileInfo(pCompletedFile->GetID());
if (!g_pDiskState->LoadFileState(pTmpFileInfo, NULL, true))
{
delete pTmpFileInfo;
return ParChecker::fsUnknown;
}
for (FileInfo::Articles::iterator it = pTmpFileInfo->GetArticles()->begin(); it != pTmpFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pa = *it;
ParChecker::Segment* pSegment = new Segment(pa->GetStatus() == ArticleInfo::aiFinished,
pa->GetSegmentOffset(), pa->GetSegmentSize(), pa->GetCrc());
pSegments->push_back(pSegment);
}
delete pTmpFileInfo;
}
return pCompletedFile->GetStatus() == CompletedFile::cfSuccess ? ParChecker::fsSuccess :
pCompletedFile->GetStatus() == CompletedFile::cfFailure &&
!m_pPostInfo->GetNZBInfo()->GetReprocess() ? ParChecker::fsFailure :
pCompletedFile->GetStatus() == CompletedFile::cfPartial && pSegments->size() > 0 &&
!m_pPostInfo->GetNZBInfo()->GetReprocess()? ParChecker::fsPartial :
ParChecker::fsUnknown;
}
void ParCoordinator::PostParChecker::RequestDupeSources(DupeSourceList* pDupeSourceList)
{
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
NZBList dupeList;
g_pDupeCoordinator->ListHistoryDupes(pDownloadQueue, m_pPostInfo->GetNZBInfo(), &dupeList);
if (!dupeList.empty())
{
PostDupeMatcher dupeMatcher(m_pPostInfo);
PrintMessage(Message::mkInfo, "Checking %s for dupe scan usability", m_pPostInfo->GetNZBInfo()->GetName());
bool bSizeComparisonPossible = dupeMatcher.Prepare();
for (NZBList::iterator it = dupeList.begin(); it != dupeList.end(); it++)
{
NZBInfo* pDupeNZBInfo = *it;
if (bSizeComparisonPossible)
{
PrintMessage(Message::mkInfo, "Checking %s for dupe scan usability", Util::BaseFileName(pDupeNZBInfo->GetDestDir()));
}
bool bUseDupe = !bSizeComparisonPossible || dupeMatcher.MatchDupeContent(pDupeNZBInfo->GetDestDir());
if (bUseDupe)
{
PrintMessage(Message::mkInfo, "Adding %s to dupe scan sources", Util::BaseFileName(pDupeNZBInfo->GetDestDir()));
pDupeSourceList->push_back(new ParChecker::DupeSource(pDupeNZBInfo->GetID(), pDupeNZBInfo->GetDestDir()));
}
}
if (pDupeSourceList->empty())
{
PrintMessage(Message::mkInfo, "No usable dupe scan sources found");
}
}
DownloadQueue::Unlock();
}
void ParCoordinator::PostParChecker::StatDupeSources(DupeSourceList* pDupeSourceList)
{
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
int iTotalExtraParBlocks = 0;
for (DupeSourceList::iterator it = pDupeSourceList->begin(); it != pDupeSourceList->end(); it++)
{
DupeSource* pDupeSource = *it;
if (pDupeSource->GetUsedBlocks() > 0)
{
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
pHistoryInfo->GetNZBInfo()->GetID() == pDupeSource->GetID())
{
pHistoryInfo->GetNZBInfo()->SetExtraParBlocks(pHistoryInfo->GetNZBInfo()->GetExtraParBlocks() - pDupeSource->GetUsedBlocks());
}
}
}
iTotalExtraParBlocks += pDupeSource->GetUsedBlocks();
}
m_pPostInfo->GetNZBInfo()->SetExtraParBlocks(m_pPostInfo->GetNZBInfo()->GetExtraParBlocks() + iTotalExtraParBlocks);
DownloadQueue::Unlock();
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_pOwner->UpdateParRenameProgress();
@@ -105,7 +222,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)
@@ -113,6 +230,34 @@ void ParCoordinator::PostParRenamer::RegisterParredFile(const char* szFilename)
m_pPostInfo->GetParredFiles()->push_back(strdup(szFilename));
}
/**
* Update file name in the CompletedFiles-list of NZBInfo
*/
void ParCoordinator::PostParRenamer::RegisterRenamedFile(const char* szOldFilename, const char* szNewFileName)
{
for (CompletedFiles::iterator it = m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->begin(); it != m_pPostInfo->GetNZBInfo()->GetCompletedFiles()->end(); it++)
{
CompletedFile* pCompletedFile = *it;
if (!strcasecmp(pCompletedFile->GetFileName(), szOldFilename))
{
pCompletedFile->SetFileName(szNewFileName);
break;
}
}
}
void ParCoordinator::PostDupeMatcher::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
}
#endif
ParCoordinator::ParCoordinator()
@@ -164,111 +309,6 @@ void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
DownloadQueue::eaGroupPauseExtraPars, 0, NULL);
}
bool ParCoordinator::FindMainPars(const char* szPath, ParFileList* pFileList)
{
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (ParFileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
const char* filename2 = *it;
exists = SameParCollection(filename, filename2);
if (exists)
{
break;
}
}
if (!exists)
{
pFileList->push_back(strdup(filename));
}
}
}
return pFileList && !pFileList->empty();
}
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
{
int iBaseLen1 = 0, iBaseLen2 = 0;
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
iBaseLen1 == iBaseLen2 &&
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
}
bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
#ifndef DISABLE_PARCHECK
/**
@@ -280,6 +320,10 @@ void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParChecker.SetNZBName(pPostInfo->GetNZBInfo()->GetName());
m_ParChecker.SetParTime(time(NULL));
m_ParChecker.SetDownloadSec(pPostInfo->GetNZBInfo()->GetDownloadSec());
m_ParChecker.SetParQuick(g_pOptions->GetParQuick() && !pPostInfo->GetForceParFull());
m_ParChecker.SetForceRepair(pPostInfo->GetForceRepair());
m_ParChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
m_ParChecker.Start();
@@ -314,16 +358,12 @@ bool ParCoordinator::Cancel()
{
if (m_eCurrentJob == jkParCheck)
{
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName());
m_ParChecker.Cancel();
return true;
}
#else
warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName());
#endif
}
else if (m_eCurrentJob == jkParRename)
{
@@ -370,6 +410,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)
@@ -381,6 +422,13 @@ void ParCoordinator::ParCheckCompleted()
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
int iWaitTime = pPostInfo->GetNZBInfo()->GetDownloadSec() - m_ParChecker.GetDownloadSec();
pPostInfo->SetStartTime(pPostInfo->GetStartTime() + (time_t)iWaitTime);
int iParSec = (int)(time(NULL) - m_ParChecker.GetParTime()) - iWaitTime;
pPostInfo->GetNZBInfo()->SetParSec(pPostInfo->GetNZBInfo()->GetParSec() + iParSec);
pPostInfo->GetNZBInfo()->SetParFull(m_ParChecker.GetParFull());
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
@@ -392,7 +440,9 @@ void ParCoordinator::ParCheckCompleted()
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
* or false if there are no more files in queue for this collection or not enough blocks.
* special case: returns true if there are any unpaused par2-files in the queue regardless
* of the amount of blocks; this is to keep par-checker wait for download completion.
*/
bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound)
{
@@ -470,6 +520,17 @@ bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilenam
}
}
bool bHasUnpausedParFiles = false;
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetParFile() && !pFileInfo->GetPaused())
{
bHasUnpausedParFiles = true;
break;
}
}
DownloadQueue::Unlock();
if (pBlockFound)
@@ -483,7 +544,7 @@ bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilenam
}
blocks.clear();
bool bOK = iBlockNeeded <= 0;
bool bOK = iBlockNeeded <= 0 || bHasUnpausedParFiles;
return bOK;
}
@@ -497,10 +558,10 @@ void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
char* szBaseParFilename = Util::BaseFileName(szParFilename);
char szMainBaseFilename[1024];
int iMainBaseLen = 0;
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
if (!ParParser::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;
@@ -512,14 +573,14 @@ void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
if (ParParser::ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
bool bUseFile = true;
if (bExactParName)
{
bUseFile = SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
bUseFile = ParParser::SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
}
else if (bStrictParName)
{
@@ -587,19 +648,22 @@ void ParCoordinator::UpdateParCheckProgress()
PostInfo::EStage eStage = StageKind[m_ParChecker.GetStage()];
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != eStage)
{
pPostInfo->SetStage(eStage);
pPostInfo->SetStageTime(tCurrent);
if (pPostInfo->GetStage() == PostInfo::ptRepairing)
{
m_ParChecker.SetRepairTime(tCurrent);
}
else if (pPostInfo->GetStage() == PostInfo::ptVerifyingRepaired)
{
int iRepairSec = (int)(tCurrent - m_ParChecker.GetRepairTime());
pPostInfo->GetNZBInfo()->SetRepairSec(pPostInfo->GetNZBInfo()->GetRepairSec() + iRepairSec);
}
}
bool bParCancel = false;
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
if ((g_pOptions->GetParTimeLimit() > 0) &&
@@ -618,7 +682,6 @@ void ParCoordinator::UpdateParCheckProgress()
}
}
}
#endif
if (bParCancel)
{
@@ -636,12 +699,14 @@ void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
{
time_t tStageTime = pPostInfo->GetStageTime();
time_t tStartTime = pPostInfo->GetStartTime();
time_t tParTime = m_ParChecker.GetParTime();
time_t tRepairTime = m_ParChecker.GetRepairTime();
time_t tWaitTime = time(NULL);
// wait until Post-processor is unpaused
while (g_pOptions->GetPausePostProcess() && !pPostInfo->GetNZBInfo()->GetForcePriority() && !m_bStopped)
{
usleep(100 * 1000);
usleep(50 * 1000);
// update time stamps
@@ -651,11 +716,18 @@ void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
{
pPostInfo->SetStageTime(tStageTime + tDelta);
}
if (tStartTime > 0)
{
pPostInfo->SetStartTime(tStartTime + tDelta);
}
if (tParTime > 0)
{
m_ParChecker.SetParTime(tParTime + tDelta);
}
if (tRepairTime > 0)
{
m_ParChecker.SetRepairTime(tRepairTime + tDelta);
}
}
}
}
@@ -669,12 +741,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());
pPostInfo->SetRequestParCheck(true);
}
else if (m_ParRenamer.HasSplittedFragments() && pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
PrintMessage(pPostInfo, Message::mkInfo, "Requesting par-check/repair for %s to join splitted fragments", m_ParRenamer.GetInfoName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Requesting par-check/repair for %s to restore missing files ", m_ParRenamer.GetInfoName());
pPostInfo->SetRequestParCheck(true);
}
@@ -695,11 +762,6 @@ void ParCoordinator::UpdateParRenameProgress()
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != PostInfo::ptRenaming)
{
pPostInfo->SetStage(PostInfo::ptRenaming);
@@ -711,39 +773,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
@@ -34,6 +34,7 @@
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#include "ParRenamer.h"
#include "DupeMatcher.h"
#endif
class ParCoordinator
@@ -45,6 +46,9 @@ private:
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
time_t m_tParTime;
time_t m_tRepairTime;
int m_iDownloadSec;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
@@ -52,9 +56,18 @@ private:
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void RegisterParredFile(const char* szFilename);
virtual bool IsParredFile(const char* szFilename);
virtual EFileStatus FindFileCrc(const char* szFilename, unsigned long* lCrc, SegmentList* pSegments);
virtual void RequestDupeSources(DupeSourceList* pDupeSourceList);
virtual void StatDupeSources(DupeSourceList* pDupeSourceList);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
time_t GetParTime() { return m_tParTime; }
void SetParTime(time_t tParTime) { m_tParTime = tParTime; }
time_t GetRepairTime() { return m_tRepairTime; }
void SetRepairTime(time_t tRepairTime) { m_tRepairTime = tRepairTime; }
int GetDownloadSec() { return m_iDownloadSec; }
void SetDownloadSec(int iDownloadSec) { m_iDownloadSec = iDownloadSec; }
friend class ParCoordinator;
};
@@ -69,13 +82,27 @@ private:
virtual void Completed() { m_pOwner->ParRenameCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void RegisterParredFile(const char* szFilename);
virtual void RegisterRenamedFile(const char* szOldFilename, const char* szNewFileName);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
class PostDupeMatcher: public DupeMatcher
{
private:
PostInfo* m_pPostInfo;
protected:
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostDupeMatcher(PostInfo* pPostInfo):
DupeMatcher(pPostInfo->GetNZBInfo()->GetDestDir(),
pPostInfo->GetNZBInfo()->GetSize() - pPostInfo->GetNZBInfo()->GetParSize()),
m_pPostInfo(pPostInfo) {}
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
@@ -103,18 +130,11 @@ 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:
typedef std::deque<char*> ParFileList;
public:
ParCoordinator();
virtual ~ParCoordinator();
static bool FindMainPars(const char* szPath, ParFileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK

View File

@@ -0,0 +1,152 @@
/*
* This file is part of nzbget
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nzbget.h"
#include "Util.h"
#include "ParParser.h"
bool ParParser::FindMainPars(const char* szPath, ParFileList* pFileList)
{
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (ParFileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
const char* filename2 = *it;
exists = SameParCollection(filename, filename2);
if (exists)
{
break;
}
}
if (!exists)
{
pFileList->push_back(strdup(filename));
}
}
}
return pFileList && !pFileList->empty();
}
bool ParParser::SameParCollection(const char* szFilename1, const char* szFilename2)
{
int iBaseLen1 = 0, iBaseLen2 = 0;
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
iBaseLen1 == iBaseLen2 &&
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
}
bool ParParser::ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}

View File

@@ -0,0 +1,41 @@
/*
* This file is part of nzbget
*
* 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
* 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 PARPARSER_H
#define PARPARSER_H
#include <deque>
class ParParser
{
public:
typedef std::deque<char*> ParFileList;
static bool FindMainPars(const char* szPath, ParFileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
};
#endif

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
@@ -37,26 +37,21 @@
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#include <md5.h>
#else
#ifndef WIN32
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#include <libpar2/md5.h>
#endif
#include "par2cmdline.h"
#include "par2repairer.h"
#include "md5.h"
#include "nzbget.h"
#include "ParRenamer.h"
#include "ParCoordinator.h"
#include "ParParser.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
class ParRenamerRepairer : public Par2Repairer
{
public:
@@ -86,7 +81,6 @@ ParRenamer::ParRenamer()
m_szProgressLabel = (char*)malloc(1024);
m_iStageProgress = 0;
m_bCancelled = false;
m_bHasSplittedFragments = false;
m_bHasMissedFiles = false;
m_bDetectMissing = false;
}
@@ -146,7 +140,6 @@ void ParRenamer::Run()
m_iFileCount = 0;
m_iCurFile = 0;
m_iRenamedCount = 0;
m_bHasSplittedFragments = false;
m_bHasMissedFiles = false;
m_eStatus = psFailed;
@@ -229,10 +222,10 @@ void ParRenamer::BuildDirList(const char* szDestDir)
void ParRenamer::LoadParFiles(const char* szDestDir)
{
ParCoordinator::ParFileList parFileList;
ParCoordinator::FindMainPars(szDestDir, &parFileList);
ParParser::ParFileList parFileList;
ParParser::FindMainPars(szDestDir, &parFileList);
for (ParCoordinator::ParFileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
for (ParParser::ParFileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
{
char* szParFilename = *it;
@@ -267,7 +260,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(),
@@ -320,11 +313,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;
}
}
@@ -344,12 +337,10 @@ bool ParRenamer::IsSplittedFragment(const char* szFilename, const char* szCorrec
{
for (p++; *p && strchr("0123456789", *p); p++) ;
bSplittedFragement = !*p;
bSplittedFragement = bSplittedFragement && atoi(szDiskBasename + iBaseLen + 1) == 1;
bSplittedFragement = bSplittedFragement && atoi(szDiskBasename + iBaseLen + 1) <= 1; // .000 or .001
}
}
m_bHasSplittedFragments = m_bHasSplittedFragments || bSplittedFragement;
return bSplittedFragement;
}
@@ -403,17 +394,7 @@ void ParRenamer::CheckRegularFile(const char* szDestDir, const char* szFilename)
if (!Util::FileExists(szDstFilename) && !IsSplittedFragment(szFilename, pFileHash->GetFilename()))
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
if (Util::MoveFile(szFilename, szDstFilename))
{
m_iRenamedCount++;
}
else
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not rename %s to %s: %s", szFilename, szDstFilename,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
}
RenameFile(szFilename, szDstFilename);
}
break;
@@ -483,17 +464,24 @@ void ParRenamer::CheckParFile(const char* szDestDir, const char* szFilename)
iNum++;
}
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), Util::BaseFileName(szDestFileName));
if (Util::MoveFile(szFilename, szDestFileName))
{
m_iRenamedCount++;
}
else
RenameFile(szFilename, szDestFileName);
}
void ParRenamer::RenameFile(const char* szSrcFilename, const char* szDestFileName)
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szSrcFilename), Util::BaseFileName(szDestFileName));
if (!Util::MoveFile(szSrcFilename, szDestFileName))
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not rename %s to %s: %s", szFilename, szDestFileName,
PrintMessage(Message::mkError, "Could not rename %s to %s: %s", szSrcFilename, szDestFileName,
Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return;
}
m_iRenamedCount++;
// notify about new file name
RegisterRenamedFile(Util::BaseFileName(szSrcFilename), Util::BaseFileName(szDestFileName));
}
#endif

View File

@@ -59,7 +59,6 @@ public:
};
typedef std::deque<FileHash*> FileHashList;
typedef std::deque<char*> DirList;
private:
@@ -74,7 +73,6 @@ private:
int m_iFileCount;
int m_iCurFile;
int m_iRenamedCount;
bool m_bHasSplittedFragments;
bool m_bHasMissedFiles;
bool m_bDetectMissing;
@@ -89,12 +87,14 @@ private:
void CheckParFile(const char* szDestDir, const char* szFilename);
bool IsSplittedFragment(const char* szFilename, const char* szCorrectName);
void CheckMissing();
void RenameFile(const char* szSrcFilename, const char* szDestFileName);
protected:
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
virtual void RegisterParredFile(const char* szFilename) {}
virtual void RegisterRenamedFile(const char* szOldFilename, const char* szNewFileName) {}
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetStageProgress() { return m_iStageProgress; }
@@ -109,7 +109,6 @@ public:
EStatus GetStatus() { return m_eStatus; }
void Cancel();
bool GetCancelled() { return m_bCancelled; }
bool HasSplittedFragments() { return m_bHasSplittedFragments; }
bool HasMissedFiles() { return m_bHasMissedFiles; }
void SetDetectMissing(bool bDetectMissing) { m_bDetectMissing = bDetectMissing; }
};

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,18 +50,11 @@
#include "DupeCoordinator.h"
#include "PostScript.h"
#include "Util.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "Unpack.h"
#include "Cleanup.h"
#include "NZBFile.h"
#include "StatMeter.h"
extern HistoryCoordinator* g_pHistoryCoordinator;
extern DupeCoordinator* g_pDupeCoordinator;
extern Options* g_pOptions;
extern Scheduler* g_pScheduler;
extern Scanner* g_pScanner;
extern StatMeter* g_pStatMeter;
#include "QueueScript.h"
#include "ParParser.h"
PrePostProcessor::PrePostProcessor()
{
@@ -98,54 +91,19 @@ void PrePostProcessor::Run()
DownloadQueue::Unlock();
}
g_pScheduler->FirstCheck();
int iDiskSpaceInterval = 1000;
int iSchedulerInterval = 1000;
int iHistoryInterval = 600000;
const int iStepMSec = 200;
while (!IsStopped())
{
// check incoming nzb directory
g_pScanner->Check();
if (!g_pOptions->GetPauseDownload() &&
g_pOptions->GetDiskSpace() > 0 && !g_pStatMeter->GetStandBy() &&
iDiskSpaceInterval >= 1000)
if (!g_pOptions->GetTempPausePostprocess())
{
// check free disk space every 1 second
CheckDiskSpace();
iDiskSpaceInterval = 0;
// check post-queue every 200 msec
CheckPostQueue();
}
iDiskSpaceInterval += iStepMSec;
// check post-queue every 200 msec
CheckPostQueue();
if (iSchedulerInterval >= 1000)
{
// check scheduler tasks every 1 second
g_pScheduler->IntervalCheck();
iSchedulerInterval = 0;
}
iSchedulerInterval += iStepMSec;
if (iHistoryInterval >= 600000)
{
// check history (remove old entries) every 10 minutes
g_pHistoryCoordinator->IntervalCheck();
iHistoryInterval = 0;
}
iHistoryInterval += iStepMSec;
Util::SetStandByMode(!m_pCurJob);
usleep(iStepMSec * 1000);
usleep(200 * 1000);
}
g_pHistoryCoordinator->Cleanup();
debug("Exiting PrePostProcessor-loop");
}
@@ -188,9 +146,26 @@ void PrePostProcessor::DownloadQueueUpdate(Subject* Caller, void* Aspect)
{
NZBAdded(pQueueAspect->pDownloadQueue, pQueueAspect->pNZBInfo);
}
else if (pQueueAspect->eAction == DownloadQueue::eaNzbDeleted &&
pQueueAspect->pNZBInfo->GetDeleting() &&
!pQueueAspect->pNZBInfo->GetPostInfo() &&
!pQueueAspect->pNZBInfo->GetParCleanup() &&
pQueueAspect->pNZBInfo->GetFileList()->empty())
{
// 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
pQueueAspect->pNZBInfo->PrintMessage(Message::mkInfo,
"Collection %s deleted from queue", pQueueAspect->pNZBInfo->GetName());
NZBDeleted(pQueueAspect->pDownloadQueue, pQueueAspect->pNZBInfo);
}
else if ((pQueueAspect->eAction == DownloadQueue::eaFileCompleted ||
pQueueAspect->eAction == DownloadQueue::eaFileDeleted))
{
if (pQueueAspect->eAction == DownloadQueue::eaFileCompleted && !pQueueAspect->pNZBInfo->GetPostInfo())
{
g_pQueueScriptCoordinator->EnqueueScript(pQueueAspect->pNZBInfo, QueueScriptCoordinator::qeFileDownloaded);
}
if (
#ifndef DISABLE_PARCHECK
!m_ParCoordinator.AddPar(pQueueAspect->pFileInfo, pQueueAspect->eAction == DownloadQueue::eaFileDeleted) &&
@@ -204,7 +179,9 @@ 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);
}
else if ((pQueueAspect->eAction == DownloadQueue::eaFileDeleted ||
@@ -213,7 +190,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);
}
}
@@ -235,22 +213,30 @@ void PrePostProcessor::NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo
m_ParCoordinator.PausePars(pDownloadQueue, pNZBInfo);
}
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce &&
pNZBInfo->GetDeleteStatus() == NZBInfo::dsDupe)
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsDupe ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsCopy ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsGood ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsScan)
{
NZBCompleted(pDownloadQueue, pNZBInfo, false);
}
else if (!Util::EmptyStr(g_pOptions->GetQueueScript()))
else
{
QueueScriptController::StartScripts(pDownloadQueue, pNZBInfo);
g_pQueueScriptCoordinator->EnqueueScript(pNZBInfo, QueueScriptCoordinator::qeNzbAdded);
}
}
void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsBad)
{
g_pQueueScriptCoordinator->EnqueueScript(pNZBInfo, QueueScriptCoordinator::qeNzbDeleted);
}
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++;
@@ -267,14 +253,6 @@ void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
pNZBInfo->SetRenameStatus(NZBInfo::rsSkipped);
}
if (pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone)
{
pNZBInfo->SetParStatus(NZBInfo::psFailure);
pNZBInfo->SetUnpackStatus(NZBInfo::usFailure);
pNZBInfo->SetRenameStatus(NZBInfo::rsFailure);
pNZBInfo->SetMoveStatus(NZBInfo::msFailure);
}
pDownloadQueue->Save();
}
else
@@ -291,17 +269,79 @@ void PrePostProcessor::NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBIn
}
pNZBInfo->SetDeleting(false);
DeleteCleanup(pNZBInfo);
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsBad)
{
NZBDownloaded(pDownloadQueue, pNZBInfo);
}
else
{
NZBCompleted(pDownloadQueue, pNZBInfo, true);
}
}
void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue)
{
bool bAddToHistory = g_pOptions->GetKeepHistory() > 0 && !pNZBInfo->GetAvoidHistory();
if (bAddToHistory)
{
g_pHistoryCoordinator->AddToHistory(pDownloadQueue, pNZBInfo);
}
pNZBInfo->SetAvoidHistory(false);
bool bNeedSave = bAddToHistory;
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce &&
(pNZBInfo->GetDeleteStatus() == NZBInfo::dsNone ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsBad ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsScan))
{
g_pDupeCoordinator->NZBCompleted(pDownloadQueue, pNZBInfo);
bNeedSave = true;
}
if (pNZBInfo->GetDeleteStatus() > NZBInfo::dsNone &&
pNZBInfo->GetDeleteStatus() != NZBInfo::dsHealth &&
pNZBInfo->GetDeleteStatus() != NZBInfo::dsBad)
// nzbs deleted by health check or marked as bad are processed as downloaded with failure status
{
g_pQueueScriptCoordinator->EnqueueScript(pNZBInfo, QueueScriptCoordinator::qeNzbDeleted);
}
if (!bAddToHistory)
{
g_pHistoryCoordinator->DeleteDiskFiles(pNZBInfo);
pDownloadQueue->GetQueue()->Remove(pNZBInfo);
delete pNZBInfo;
}
if (bSaveQueue && bNeedSave)
{
pDownloadQueue->Save();
}
}
void PrePostProcessor::DeleteCleanup(NZBInfo* pNZBInfo)
{
if ((g_pOptions->GetDeleteCleanupDisk() && pNZBInfo->GetCleanupDisk()) ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsDupe)
{
// download was cancelled, deleting already downloaded files from disk
for (NZBInfo::Files::reverse_iterator it = pNZBInfo->GetCompletedFiles()->rbegin(); it != pNZBInfo->GetCompletedFiles()->rend(); it++)
for (CompletedFiles::reverse_iterator it = pNZBInfo->GetCompletedFiles()->rbegin(); it != pNZBInfo->GetCompletedFiles()->rend(); it++)
{
char* szFilename = *it;
if (Util::FileExists(szFilename))
CompletedFile* pCompletedFile = *it;
char szFullFileName[1024];
snprintf(szFullFileName, 1024, "%s%c%s", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, pCompletedFile->GetFileName());
szFullFileName[1024-1] = '\0';
if (Util::FileExists(szFullFileName))
{
detail("Deleting file %s", Util::BaseFileName(szFilename));
remove(szFilename);
detail("Deleting file %s", pCompletedFile->GetFileName());
remove(szFullFileName);
}
}
@@ -327,67 +367,6 @@ void PrePostProcessor::NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBIn
rmdir(pNZBInfo->GetDestDir());
}
}
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth)
{
NZBDownloaded(pDownloadQueue, pNZBInfo);
}
else
{
NZBCompleted(pDownloadQueue, pNZBInfo, true);
}
}
void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue)
{
bool bAddToHistory = g_pOptions->GetKeepHistory() > 0 && !pNZBInfo->GetAvoidHistory();
if (bAddToHistory)
{
g_pHistoryCoordinator->AddToHistory(pDownloadQueue, pNZBInfo);
}
pNZBInfo->SetAvoidHistory(false);
bool bNeedSave = bAddToHistory;
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce &&
(pNZBInfo->GetDeleteStatus() == NZBInfo::dsNone ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth))
{
g_pDupeCoordinator->NZBCompleted(pDownloadQueue, pNZBInfo);
bNeedSave = true;
}
if (!bAddToHistory)
{
g_pHistoryCoordinator->DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
pDownloadQueue->GetQueue()->Remove(pNZBInfo);
delete pNZBInfo;
}
if (bSaveQueue && bNeedSave)
{
pDownloadQueue->Save();
}
}
void PrePostProcessor::CheckDiskSpace()
{
long long lFreeSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetDestDir());
g_pOptions->SetPauseDownload(true);
}
if (!Util::EmptyStr(g_pOptions->GetInterDir()))
{
lFreeSpace = Util::FreeDiskSize(g_pOptions->GetInterDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetInterDir());
g_pOptions->SetPauseDownload(true);
}
}
}
void PrePostProcessor::CheckPostQueue()
@@ -397,11 +376,6 @@ void PrePostProcessor::CheckPostQueue()
if (!m_pCurJob && m_iJobCount > 0)
{
m_pCurJob = GetNextJob(pDownloadQueue);
if (!m_pCurJob && !g_pOptions->GetPausePostProcess())
{
error("Internal error: no jobs found in queue");
m_iJobCount = 0;
}
}
if (m_pCurJob)
@@ -410,9 +384,12 @@ void PrePostProcessor::CheckPostQueue()
if (!pPostInfo->GetWorking() && !IsNZBFileDownloading(m_pCurJob))
{
#ifndef DISABLE_PARCHECK
if (pPostInfo->GetRequestParCheck() && pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
if (pPostInfo->GetRequestParCheck() &&
(pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped ||
(pPostInfo->GetForceRepair() && !pPostInfo->GetNZBInfo()->GetParFull())) &&
g_pOptions->GetParCheck() != Options::pcManual)
{
pPostInfo->SetForceParFull(pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psNone);
pPostInfo->SetRequestParCheck(false);
pPostInfo->SetStage(PostInfo::ptQueued);
@@ -428,13 +405,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);
}
}
@@ -474,7 +453,8 @@ NZBInfo* PrePostProcessor::GetNextJob(DownloadQueue* pDownloadQueue)
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
{
NZBInfo* pNZBInfo1 = *it;
if (pNZBInfo1->GetPostInfo() && (!pNZBInfo || pNZBInfo1->GetPriority() > pNZBInfo->GetPriority()) &&
if (pNZBInfo1->GetPostInfo() && !g_pQueueScriptCoordinator->HasJob(pNZBInfo1->GetID(), NULL) &&
(!pNZBInfo || pNZBInfo1->GetPriority() > pNZBInfo->GetPriority()) &&
(!g_pOptions->GetPausePostProcess() || pNZBInfo1->GetForcePriority()))
{
pNZBInfo = pNZBInfo1;
@@ -519,23 +499,31 @@ void PrePostProcessor::DeletePostThread(PostInfo* pPostInfo)
void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
{
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(time(NULL));
}
#ifndef DISABLE_PARCHECK
if (pPostInfo->GetNZBInfo()->GetRenameStatus() == NZBInfo::rsNone)
if (pPostInfo->GetNZBInfo()->GetRenameStatus() == NZBInfo::rsNone &&
pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone)
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-rename");
m_ParCoordinator.StartParRenameJob(pPostInfo);
return;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone)
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone &&
pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone)
{
if (m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
if (ParParser::FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-check");
m_ParCoordinator.StartParCheckJob(pPostInfo);
}
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);
@@ -543,20 +531,27 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
return;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSkipped &&
pPostInfo->GetNZBInfo()->CalcHealth() < pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) &&
pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) < 1000 &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
((g_pOptions->GetParScan() != Options::psDupe &&
pPostInfo->GetNZBInfo()->CalcHealth() < pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) &&
pPostInfo->GetNZBInfo()->CalcCriticalHealth(false) < 1000) ||
pPostInfo->GetNZBInfo()->CalcHealth() == 0) &&
ParParser::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,
pPostInfo->GetNZBInfo()->CalcHealth() == 0 ?
"Skipping par-check for %s due to health 0%%" :
"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;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSkipped &&
pPostInfo->GetNZBInfo()->GetFailedSize() - pPostInfo->GetNZBInfo()->GetParFailedSize() > 0 &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
ParParser::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;
@@ -565,7 +560,8 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
NZBParameter* pUnpackParameter = pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
bool bUnpackParam = !(pUnpackParameter && !strcasecmp(pUnpackParameter->GetValue(), "no"));
bool bUnpack = bUnpackParam && (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone);
bool bUnpack = bUnpackParam && pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone &&
pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone;
bool bParFailed = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible ||
@@ -573,10 +569,18 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
bool bCleanup = !bUnpack &&
pPostInfo->GetNZBInfo()->GetCleanupStatus() == NZBInfo::csNone &&
(pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
!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)) &&
strlen(g_pOptions->GetExtCleanupDisk()) > 0;
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 &&
@@ -585,14 +589,16 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usPassword &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psManual &&
strlen(g_pOptions->GetInterDir()) > 0 &&
pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone &&
!Util::EmptyStr(g_pOptions->GetInterDir()) &&
!strncmp(pPostInfo->GetNZBInfo()->GetDestDir(), g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
bool bPostScript = true;
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;
@@ -612,10 +618,6 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
pDownloadQueue->Save();
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(time(NULL));
}
pPostInfo->SetStageTime(time(NULL));
if (bUnpack)
@@ -643,27 +645,44 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
{
NZBInfo* pNZBInfo = pPostInfo->GetNZBInfo();
if (pPostInfo->GetStartTime() > 0)
{
pNZBInfo->SetPostTotalSec((int)(time(NULL) - pPostInfo->GetStartTime()));
pPostInfo->SetStartTime(0);
}
DeletePostThread(pPostInfo);
pNZBInfo->LeavePostProcess();
if (IsNZBFileCompleted(pNZBInfo, true, false))
{
// Cleaning up queue if par-check was successful or unpack was successful or
// script was successful (if unpack was not performed)
bool bCanCleanupQueue = pNZBInfo->GetParStatus() == NZBInfo::psSuccess ||
pNZBInfo->GetParStatus() == NZBInfo::psRepairPossible ||
pNZBInfo->GetUnpackStatus() == NZBInfo::usSuccess ||
(pNZBInfo->GetUnpackStatus() == NZBInfo::usNone &&
pNZBInfo->GetScriptStatuses()->CalcTotalStatus() == ScriptStatus::srSuccess);
if (g_pOptions->GetParCleanupQueue() && bCanCleanupQueue)
// health is 100% (if unpack and par-check were not performed)
// or health is below critical health
bool bCanCleanupQueue =
((pNZBInfo->GetParStatus() == NZBInfo::psSuccess ||
pNZBInfo->GetParStatus() == NZBInfo::psRepairPossible) &&
pNZBInfo->GetUnpackStatus() != NZBInfo::usFailure &&
pNZBInfo->GetUnpackStatus() != NZBInfo::usSpace &&
pNZBInfo->GetUnpackStatus() != NZBInfo::usPassword) ||
(pNZBInfo->GetUnpackStatus() == NZBInfo::usSuccess &&
pNZBInfo->GetParStatus() != NZBInfo::psFailure) ||
(pNZBInfo->GetUnpackStatus() <= NZBInfo::usSkipped &&
pNZBInfo->GetParStatus() != NZBInfo::psFailure &&
pNZBInfo->GetFailedSize() - pNZBInfo->GetParFailedSize() == 0) ||
(pNZBInfo->CalcHealth() < pNZBInfo->CalcCriticalHealth(false) &&
pNZBInfo->CalcCriticalHealth(false) < 1000);
if (g_pOptions->GetParCleanupQueue() && bCanCleanupQueue && !pNZBInfo->GetFileList()->empty())
{
if (!pNZBInfo->GetFileList()->empty())
{
info("Cleaning up download queue for %s", pNZBInfo->GetName());
pNZBInfo->ClearCompletedFiles();
pNZBInfo->SetParCleanup(true);
pDownloadQueue->EditEntry(pNZBInfo->GetID(), DownloadQueue::eaGroupDelete, 0, NULL);
}
pNZBInfo->PrintMessage(Message::mkInfo, "Cleaning up download queue for %s", pNZBInfo->GetName());
pNZBInfo->SetParCleanup(true);
pDownloadQueue->EditEntry(pNZBInfo->GetID(), DownloadQueue::eaGroupDelete, 0, NULL);
}
if (pNZBInfo->GetUnpackCleanedUpDisk())
{
pNZBInfo->ClearCompletedFiles();
}
NZBCompleted(pDownloadQueue, pNZBInfo, false);
@@ -762,7 +781,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)
@@ -787,7 +807,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) 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
@@ -57,16 +57,15 @@ private:
void StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(DownloadQueue* pDownloadQueue);
void CheckDiskSpace();
void UpdatePauseState(bool bNeedPause, const char* szReason);
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool PostQueueDelete(DownloadQueue* pDownloadQueue, IDList* pIDList);
void DeletePostThread(PostInfo* pPostInfo);
NZBInfo* GetNextJob(DownloadQueue* pDownloadQueue);
void DownloadQueueUpdate(Subject* Caller, void* Aspect);
void DeleteCleanup(NZBInfo* pNZBInfo);
public:
PrePostProcessor();
@@ -76,7 +75,10 @@ public:
bool HasMoreJobs() { return m_iJobCount > 0; }
int GetJobCount() { return m_iJobCount; }
bool EditList(DownloadQueue* pDownloadQueue, IDList* pIDList, DownloadQueue::EEditAction eAction, int iOffset, const char* szText);
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
};
extern PrePostProcessor* g_pPrePostProcessor;
#endif

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
@@ -44,11 +44,9 @@
#include "Unpack.h"
#include "Log.h"
#include "Util.h"
#include "ParCoordinator.h"
#include "ParParser.h"
#include "Options.h"
extern Options* g_pOptions;
void UnpackController::FileList::Clear()
{
for (iterator it = begin(); it != end(); it++)
@@ -72,6 +70,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();
@@ -85,6 +105,8 @@ void UnpackController::StartJob(PostInfo* pPostInfo)
void UnpackController::Run()
{
time_t tStart = time(NULL);
// the locking is needed for accessing the members of NZBInfo
DownloadQueue::Lock();
@@ -98,7 +120,14 @@ 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"));
@@ -117,7 +146,7 @@ void UnpackController::Run()
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
m_bHasParFiles = ParParser::FindMainPars(m_szDestDir, NULL);
if (bUnpack)
{
@@ -127,36 +156,51 @@ void UnpackController::Run()
CheckArchiveFiles(bScanNonStdFiles);
}
if (bUnpack && (m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetWorkingDir(m_szDestDir);
SetInfoName(m_szInfoName);
SetWorkingDir(m_szDestDir);
bool bHasFiles = m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles || m_bHasSplittedFiles;
if (m_pPostInfo->GetUnpackTried() && !m_pPostInfo->GetParRepaired() &&
(!Util::EmptyStr(m_szPassword) || Util::EmptyStr(g_pOptions->GetUnpackPassFile()) || m_pPostInfo->GetPassListTried()))
{
PrintMessage(Message::mkInfo, "Second unpack attempt skipped for %s due to par-check not repaired anything", m_szName);
PrintMessage(Message::mkError,
m_pPostInfo->GetLastUnpackStatus() == (int)NZBInfo::usPassword ?
"%s failed: checksum error in the encrypted file. Corrupt file or wrong password." : "%s failed.",
m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus((NZBInfo::EUnpackStatus)m_pPostInfo->GetLastUnpackStatus());
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else if (bUnpack && bHasFiles)
{
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
CreateUnpackDir();
m_bUnpackOK = true;
m_bUnpackStartError = false;
m_bUnpackSpaceError = false;
m_bUnpackPasswordError = false;
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)
{
JoinSplittedFiles();
}
Completed();
m_JoinedFiles.Clear();
}
else
{
@@ -166,7 +210,7 @@ void UnpackController::Run()
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped && m_bHasParFiles)
{
RequestParCheck();
RequestParCheck(false);
}
else
#endif
@@ -176,33 +220,132 @@ void UnpackController::Run()
}
}
int iUnpackSec = (int)(time(NULL) - tStart);
m_pPostInfo->GetNZBInfo()->SetUnpackSec(m_pPostInfo->GetNZBInfo()->GetUnpackSec() + iUnpackSec);
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);
szArgs[3] = szPasswordParam;
return;
}
szArgs[4] = "-o+";
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
szArgs[6] = m_szUnpackDir;
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
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';
params.push_back(strdup(szUnpackDirParam));
params.push_back(NULL);
SetArgs((const char**)&params.front(), false);
SetScript(params.at(0));
SetLogPrefix("Unrar");
ResetEnv();
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
@@ -215,7 +358,7 @@ void UnpackController::ExecuteUnrar()
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
m_bUnpackSpaceError = iExitCode == 5;
m_bUnpackPasswordError = iExitCode == 11; // only for rar5-archives
m_bUnpackPasswordError |= iExitCode == 11; // only for rar5-archives
if (!m_bUnpackOK && iExitCode > 0)
{
@@ -223,35 +366,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);
szArgs[3] = szPasswordParam;
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';
params.push_back(strdup(szPasswordParam));
}
else
{
params.push_back(strdup("-p-"));
}
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
szArgs[4] = szUnpackDirParam;
szUnpackDirParam[1024-1] = '\0';
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;
@@ -272,6 +429,205 @@ 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");
SetProgressLabel("");
m_pPostInfo->SetStageProgress(0);
// determine groups
FileList groups;
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.001$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExSplitExt.Match(filename) && !FileHasRarSignature(szFullFilename))
{
if (!JoinFile(filename))
{
m_bUnpackOK = false;
break;
}
}
}
}
SetLogPrefix(NULL);
SetProgressLabel("");
}
bool UnpackController::JoinFile(const char* szFragBaseName)
{
char szDestBaseName[1024];
strncpy(szDestBaseName, szFragBaseName, 1024);
szDestBaseName[1024-1] = '\0';
// trim extension
char* szExtension = strrchr(szDestBaseName, '.');
*szExtension = '\0';
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFragBaseName);
szFullFilename[1024-1] = '\0';
long long lFirstSegmentSize = Util::FileSize(szFullFilename);
long long lDifSegmentSize = 0;
// Validate joinable file:
// - fragments have continuous numbers (no holes);
// - fragments have the same size (except of the last fragment);
// - the last fragment must be smaller than other fragments,
// if it has the same size it is probably not the last and there are missing fragments.
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.[0-9]{3}$");
int iCount = 0;
int iMin = -1;
int iMax = -1;
int iDifSizeCount = 0;
int iDifSizeMin = 999999;
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename) &&
regExSplitExt.Match(filename))
{
const char* szSegExt = strrchr(filename, '.');
int iSegNum = atoi(szSegExt + 1);
iCount++;
iMin = iSegNum < iMin || iMin == -1 ? iSegNum : iMin;
iMax = iSegNum > iMax ? iSegNum : iMax;
long long lSegmentSize = Util::FileSize(szFullFilename);
if (lSegmentSize != lFirstSegmentSize)
{
iDifSizeCount++;
iDifSizeMin = iSegNum < iDifSizeMin ? iSegNum : iDifSizeMin;
lDifSegmentSize = lSegmentSize;
}
}
}
int iCorrectedCount = iCount - (iMin == 0 ? 1 : 0);
if ((iMin > 1) || iCorrectedCount != iMax ||
((iDifSizeMin != iCorrectedCount || iDifSizeMin > iMax) &&
m_pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psSuccess))
{
PrintMessage(Message::mkWarning, "Could not join splitted file %s: missing fragments detected", szDestBaseName);
return false;
}
// Now can join
PrintMessage(Message::mkInfo, "Joining splitted file %s", szDestBaseName);
m_pPostInfo->SetStageProgress(0);
char szErrBuf[256];
char szDestFilename[1024];
snprintf(szDestFilename, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, szDestBaseName);
szDestFilename[1024-1] = '\0';
FILE* pOutFile = fopen(szDestFilename, FOPEN_WBP);
if (!pOutFile)
{
PrintMessage(Message::mkError, "Could not create file %s: %s", szDestFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
return false;
}
if (g_pOptions->GetWriteBuffer() > 0)
{
setvbuf(pOutFile, NULL, _IOFBF, g_pOptions->GetWriteBuffer() * 1024);
}
long long lTotalSize = lFirstSegmentSize * (iCount - 1) + lDifSegmentSize;
long long lWritten = 0;
static const int BUFFER_SIZE = 1024 * 50;
char* buffer = (char*)malloc(BUFFER_SIZE);
bool bOK = true;
for (int i = iMin; i <= iMax; i++)
{
PrintMessage(Message::mkInfo, "Joining from %s.%.3i", szDestBaseName, i);
char szMessage[1024];
snprintf(szMessage, 1024, "Joining from %s.%.3i", szDestBaseName, i);
szMessage[1024-1] = '\0';
SetProgressLabel(szMessage);
char szFragFilename[1024];
snprintf(szFragFilename, 1024, "%s%c%s.%.3i", m_szDestDir, PATH_SEPARATOR, szDestBaseName, i);
szFragFilename[1024-1] = '\0';
if (!Util::FileExists(szFragFilename))
{
break;
}
FILE* pInFile = fopen(szFragFilename, FOPEN_RB);
if (pInFile)
{
int cnt = BUFFER_SIZE;
while (cnt == BUFFER_SIZE)
{
cnt = (int)fread(buffer, 1, BUFFER_SIZE, pInFile);
fwrite(buffer, 1, cnt, pOutFile);
lWritten += cnt;
m_pPostInfo->SetStageProgress(int(lWritten * 1000 / lTotalSize));
}
fclose(pInFile);
char szFragFilename[1024];
snprintf(szFragFilename, 1024, "%s.%.3i", szDestBaseName, i);
szFragFilename[1024-1] = '\0';
m_JoinedFiles.push_back(strdup(szFragFilename));
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szFragFilename);
bOK = false;
break;
}
}
fclose(pOutFile);
free(buffer);
return bOK;
}
void UnpackController::Completed()
{
bool bCleanupSuccess = Cleanup();
@@ -291,11 +647,16 @@ void UnpackController::Completed()
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
if (!m_bUnpackOK &&
(m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped ||
!m_pPostInfo->GetNZBInfo()->GetParFull()) &&
!m_bUnpackStartError && !m_bUnpackSpaceError && !m_bUnpackPasswordError &&
!GetTerminated() && m_bHasParFiles)
(!GetTerminated() || m_bAutoTerminated) && m_bHasParFiles)
{
RequestParCheck();
RequestParCheck(!Util::EmptyStr(m_szPassword) ||
Util::EmptyStr(g_pOptions->GetUnpackPassFile()) || m_bPassListTried ||
!(m_bUnpackDecryptError || m_bUnpackPasswordError) ||
m_pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped);
}
else
#endif
@@ -303,7 +664,7 @@ void UnpackController::Completed()
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(
m_bUnpackSpaceError ? NZBInfo::usSpace :
m_bUnpackPasswordError ? NZBInfo::usPassword :
m_bUnpackPasswordError || m_bUnpackDecryptError ? NZBInfo::usPassword :
NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
@@ -311,11 +672,17 @@ void UnpackController::Completed()
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck()
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
@@ -339,7 +706,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);
}
}
@@ -350,12 +717,14 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
m_bHasNonStdRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
m_bHasSplittedFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.[0-9]{3}$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
@@ -366,6 +735,9 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
const char* szExt = strchr(filename, '.');
int iExtNum = szExt ? atoi(szExt + 1) : -1;
if (regExRar.Match(filename))
{
m_bHasRarFiles = true;
@@ -378,12 +750,16 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
{
m_bHasSevenZipMultiFiles = true;
}
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles &&
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles && iExtNum > 1 &&
!regExRarMultiSeq.Match(filename) && regExNumExt.Match(filename) &&
FileHasRarSignature(szFullFilename))
{
m_bHasNonStdRarFiles = true;
}
else if (regExSplitExt.Match(filename) && (iExtNum == 0 || iExtNum == 1))
{
m_bHasSplittedFiles = true;
}
}
}
}
@@ -428,8 +804,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);
@@ -442,10 +817,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;
}
@@ -473,6 +851,7 @@ bool UnpackController::Cleanup()
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
RegEx regExSplitExt(".*\\.[a-z,0-9]{3}\\.[0-9]{3}$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
@@ -485,8 +864,9 @@ bool UnpackController::Cleanup()
!Util::DirectoryExists(szFullFilename) &&
(m_bInterDir || !extractedFiles.Exists(filename)) &&
(regExRar.Match(filename) || regExSevenZip.Match(filename) ||
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(szFullFilename)) ||
(m_bHasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(szFullFilename))))
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(szFullFilename)) ||
(m_bHasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(szFullFilename)) ||
(m_bHasSplittedFiles && regExSplitExt.Match(filename) && m_JoinedFiles.Exists(filename))))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
@@ -574,6 +954,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"
@@ -584,8 +965,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"))
@@ -607,6 +987,39 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
SetProgressLabel(szText + 7);
}
if (m_eUnpacker == upUnrar &&
(!strncmp(szText, "Unrar: Checksum error in the encrypted file", 42) ||
!strncmp(szText, "Unrar: CRC failed in the encrypted file", 39)))
{
m_bUnpackDecryptError = true;
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: The specified password is incorrect.'", 43))
{
m_bUnpackPasswordError = true;
}
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)) ||
(iLen > 18 && !strncmp(szText + iLen - 18, " - checksum failed", 18)) ||
!strncmp(szText, "Unrar: WARNING: You need to start extraction from a previous volume", 67)))
{
char szMsgText[1024];
snprintf(szMsgText, 1024, "Cancelling %s due to errors", m_szInfoName);
szMsgText[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->AddMessage(Message::mkWarning, szMsgText);
m_bAutoTerminated = true;
Stop();
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: All OK", 13)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "7-Zip: Everything is Ok", 23)))
{
@@ -627,211 +1040,3 @@ void UnpackController::SetProgressLabel(const char* szProgressLabel)
m_pPostInfo->SetProgressLabel(szProgressLabel);
DownloadQueue::Unlock();
}
void MoveController::StartJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
pMoveController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pMoveController);
pMoveController->Start();
}
void MoveController::Run()
{
// the locking is needed for accessing the members of NZBInfo
DownloadQueue::Lock();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
m_szDestDir[1024-1] = '\0';
DownloadQueue::Unlock();
info("Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
szInfoName[0] = 'M'; // uppercase
if (bOK)
{
info("%s successful", szInfoName);
// save new dest dir
DownloadQueue::Lock();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
DownloadQueue::Unlock();
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool MoveController::MoveFiles()
{
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
bool bOK = true;
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
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))
{
PrintMessage(Message::mkError, "Could not move file %s to %s: %s", szSrcFile, szDstFile, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
bOK = false;
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir, szErrBuf, sizeof(szErrBuf)))
{
PrintMessage(Message::mkError, "Could not delete intermediate directory %s: %s", m_szInterDir, szErrBuf);
}
return bOK;
}
void CleanupController::StartJob(PostInfo* pPostInfo)
{
CleanupController* pCleanupController = new CleanupController();
pCleanupController->m_pPostInfo = pPostInfo;
pCleanupController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pCleanupController);
pCleanupController->Start();
}
void CleanupController::Run()
{
// the locking is needed for accessing the members of NZBInfo
DownloadQueue::Lock();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
}
else
{
m_szFinalDir[0] = '\0';
}
DownloadQueue::Unlock();
info("Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
if (bOK && m_szFinalDir[0] != '\0')
{
bool bDeleted2 = false;
bOK = Cleanup(m_szFinalDir, &bDeleted2);
bDeleted = bDeleted || bDeleted2;
}
szInfoName[0] = 'C'; // uppercase
if (bOK && bDeleted)
{
info("%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
info("Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
{
*bDeleted = false;
bool bOK = true;
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
// check file extension
bool bDeleteIt = Util::MatchFileExt(filename, g_pOptions->GetExtCleanupDisk(), ",;");
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)
{
char szErrBuf[256];
PrintMessage(Message::mkError, "Could not delete file %s: %s", szFullFilename, Util::GetLastErrorMessage(szErrBuf, sizeof(szErrBuf)));
bOK = false;
}
*bDeleted = true;
}
}
return bOK;
}

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
@@ -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];
@@ -67,28 +76,38 @@ private:
bool m_bHasNonStdRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bHasSplittedFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bUnpackSpaceError;
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();
void CreateUnpackDir();
bool Cleanup();
void CheckArchiveFiles(bool bScanNonStdFiles);
void SetProgressLabel(const char* szProgressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck();
void RequestParCheck(bool bForceRepair);
#endif
bool FileHasRarSignature(const char* szFilename);
bool PrepareCmdParams(const char* szCommand, ParamList* pParams, const char* szInfoName);
public:
virtual void Run();
@@ -96,32 +115,4 @@ public:
static void StartJob(PostInfo* pPostInfo);
};
class MoveController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szInterDir[1024];
char m_szDestDir[1024];
bool MoveFiles();
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
class CleanupController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szDestDir[1024];
char m_szFinalDir[1024];
bool Cleanup(const char* szDestDir, bool *bDeleted);
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-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,12 +30,12 @@
#include "FeedInfo.h"
#include "NewsServer.h"
#include "StatMeter.h"
#include "Log.h"
class DiskState
{
private:
int fscanf(FILE* infile, const char* Format, ...);
int ParseFormatVersion(const char* szFormatSignature);
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
void SaveNZBQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
@@ -58,10 +58,10 @@ private:
bool LoadVolumeStat(Servers* pServers, ServerVolumes* pServerVolumes, FILE* infile, int iFormatVersion);
void CalcFileStats(DownloadQueue* pDownloadQueue, int iFormatVersion);
void CalcNZBFileStats(NZBInfo* pNZBInfo, int iFormatVersion);
bool LoadFileState(FileInfo* pFileInfo, Servers* pServers);
bool LoadAllFileStates(DownloadQueue* pDownloadQueue, Servers* pServers);
void SaveServerStats(ServerStatList* pServerStatList, FILE* outfile);
bool LoadServerStats(ServerStatList* pServerStatList, Servers* pServers, FILE* infile);
bool FinishWriteTransaction(const char* szNewFileName, const char* szDestFileName);
// backward compatibility functions (conversions from older formats)
bool LoadPostQueue12(DownloadQueue* pDownloadQueue, NZBList* pNZBList, FILE* infile, int iFormatVersion);
@@ -80,15 +80,23 @@ public:
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue, Servers* pServers);
bool SaveFile(FileInfo* pFileInfo);
bool SaveFileState(FileInfo* pFileInfo);
bool SaveFileState(FileInfo* pFileInfo, bool bCompleted);
bool LoadFileState(FileInfo* pFileInfo, Servers* pServers, bool bCompleted);
bool LoadArticles(FileInfo* pFileInfo);
void DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
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);
bool SaveStats(Servers* pServers, ServerVolumes* pServerVolumes);
bool LoadStats(Servers* pServers, ServerVolumes* pServerVolumes, bool* pPerfectMatch);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
void WriteCacheFlag();
void DeleteCacheFlag();
void AppendNZBMessage(int iNZBID, Message::EKind eKind, const char* szText);
void LoadNZBMessages(int iNZBID, MessageList* pMessages);
};
extern DiskState* g_pDiskState;
#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,17 +35,18 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#include <sys/stat.h>
#include <algorithm>
#include "nzbget.h"
#include "DownloadInfo.h"
#include "ArticleWriter.h"
#include "DiskState.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
int FileInfo::m_iIDMax = 0;
int NZBInfo::m_iIDGen = 0;
@@ -53,7 +54,6 @@ int NZBInfo::m_iIDMax = 0;
DownloadQueue* DownloadQueue::g_pDownloadQueue = NULL;
bool DownloadQueue::g_bLoaded = false;
NZBParameter::NZBParameter(const char* szName)
{
m_szName = strdup(szName);
@@ -303,6 +303,7 @@ NZBInfo::NZBInfo() : m_FileList(true)
m_eDeleteStatus = dsNone;
m_eMarkStatus = ksNone;
m_eUrlStatus = lsNone;
m_iExtraParBlocks = 0;
m_bAddUrlPaused = false;
m_bDeleting = false;
m_bDeletePaused = false;
@@ -330,6 +331,19 @@ NZBInfo::NZBInfo() : m_FileList(true)
m_pPostInfo = NULL;
m_iIDMessageGen = 0;
m_iID = ++m_iIDGen;
m_lDownloadedSize = 0;
m_iDownloadSec = 0;
m_iPostTotalSec = 0;
m_iParSec = 0;
m_iRepairSec = 0;
m_iUnpackSec = 0;
m_tDownloadStartTime = 0;
m_bReprocess = false;
m_tQueueScriptTime = 0;
m_bParFull = false;
m_iMessageCount = 0;
m_iCachedMessageCount = 0;
m_iFeedID = 0;
}
NZBInfo::~NZBInfo()
@@ -348,12 +362,6 @@ NZBInfo::~NZBInfo()
ClearCompletedFiles();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
m_FileList.Clear();
}
@@ -386,9 +394,9 @@ int NZBInfo::GenerateID()
void NZBInfo::ClearCompletedFiles()
{
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
for (CompletedFiles::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
{
free(*it);
delete *it;
}
m_completedFiles.clear();
}
@@ -576,9 +584,8 @@ int NZBInfo::CalcHealth()
return 1000;
}
int iHealth = (int)(Util::Int64ToFloat(m_lSize - m_lParSize -
(m_lCurrentFailedSize - m_lParCurrentFailedSize)) * 1000.0 /
Util::Int64ToFloat(m_lSize - m_lParSize));
int iHealth = (int)((m_lSize - m_lParSize -
(m_lCurrentFailedSize - m_lParCurrentFailedSize)) * 1000 / (m_lSize - m_lParSize));
if (iHealth == 1000 && m_lCurrentFailedSize - m_lParCurrentFailedSize > 0)
{
@@ -595,9 +602,13 @@ int NZBInfo::CalcCriticalHealth(bool bAllowEstimation)
return 1000;
}
if (m_lSize == m_lParSize)
{
return 0;
}
long long lGoodParSize = m_lParSize - m_lParCurrentFailedSize;
int iCriticalHealth = (int)(Util::Int64ToFloat(m_lSize - lGoodParSize*2) * 1000.0 /
Util::Int64ToFloat(m_lSize - lGoodParSize));
int iCriticalHealth = (int)((m_lSize - lGoodParSize*2) * 1000 / (m_lSize - lGoodParSize));
if (lGoodParSize*2 > m_lSize)
{
@@ -646,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();
}
@@ -704,6 +769,7 @@ void NZBInfo::CopyFileList(NZBInfo* pSrcNZBInfo)
SetParFailedSize(pSrcNZBInfo->GetParFailedSize());
SetParCurrentFailedSize(pSrcNZBInfo->GetParCurrentFailedSize());
SetTotalArticles(pSrcNZBInfo->GetTotalArticles());
SetSuccessArticles(pSrcNZBInfo->GetSuccessArticles());
SetFailedArticles(pSrcNZBInfo->GetFailedArticles());
SetCurrentSuccessArticles(pSrcNZBInfo->GetSuccessArticles());
@@ -723,19 +789,41 @@ void NZBInfo::LeavePostProcess()
{
delete m_pPostInfo;
m_pPostInfo = NULL;
ClearMessages();
}
void NZBInfo::SetActiveDownloads(int iActiveDownloads)
{
if (((m_iActiveDownloads == 0 && iActiveDownloads > 0) ||
(m_iActiveDownloads > 0 && iActiveDownloads == 0)) &&
m_eKind == NZBInfo::nkNzb)
{
if (iActiveDownloads > 0)
{
m_tDownloadStartTime = time(NULL);
}
else
{
m_iDownloadSec += time(NULL) - m_tDownloadStartTime;
m_tDownloadStartTime = 0;
}
}
m_iActiveDownloads = 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;
}
@@ -757,6 +845,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";
@@ -769,6 +861,22 @@ const char* NZBInfo::MakeTextStatus(bool bIgnoreScriptStatus)
{
szStatus = "DELETED/DUPE";
}
else if (m_eDeleteStatus == NZBInfo::dsBad)
{
szStatus = "FAILURE/BAD";
}
else if (m_eDeleteStatus == NZBInfo::dsGood)
{
szStatus = "DELETED/GOOD";
}
else if (m_eDeleteStatus == NZBInfo::dsCopy)
{
szStatus = "DELETED/COPY";
}
else if (m_eDeleteStatus == NZBInfo::dsScan)
{
szStatus = "FAILURE/SCAN";
}
else if (m_eParStatus == NZBInfo::psFailure)
{
szStatus = "FAILURE/PAR";
@@ -895,20 +1003,38 @@ void NZBList::Remove(NZBInfo* pNZBInfo)
}
}
NZBInfo* NZBList::Find(int iID)
{
for (iterator it = begin(); it != end(); it++)
{
NZBInfo* pNZBInfo = *it;
if (pNZBInfo->GetID() == iID)
{
return pNZBInfo;
}
}
return NULL;
}
ArticleInfo::ArticleInfo()
{
//debug("Creating ArticleInfo");
m_szMessageID = NULL;
m_iSize = 0;
m_eStatus = aiUndefined;
m_szResultFilename = NULL;
m_szMessageID = NULL;
m_iSize = 0;
m_pSegmentContent = NULL;
m_iSegmentOffset = 0;
m_iSegmentSize = 0;
m_eStatus = aiUndefined;
m_szResultFilename = NULL;
m_lCrc = 0;
}
ArticleInfo::~ ArticleInfo()
{
//debug("Destroying ArticleInfo");
DiscardSegment();
free(m_szMessageID);
free(m_szResultFilename);
}
@@ -925,8 +1051,26 @@ void ArticleInfo::SetResultFilename(const char * v)
m_szResultFilename = strdup(v);
}
void ArticleInfo::AttachSegment(char* pContent, long long iOffset, int iSize)
{
DiscardSegment();
m_pSegmentContent = pContent;
m_iSegmentOffset = iOffset;
m_iSegmentSize = iSize;
}
FileInfo::FileInfo()
void ArticleInfo::DiscardSegment()
{
if (m_pSegmentContent)
{
free(m_pSegmentContent);
m_pSegmentContent = NULL;
g_pArticleCache->Free(m_iSegmentSize);
}
}
FileInfo::FileInfo(int iID)
{
debug("Creating FileInfo");
@@ -956,7 +1100,9 @@ FileInfo::FileInfo()
m_bExtraPriority = false;
m_iActiveDownloads = 0;
m_bAutoDeleted = false;
m_iID = ++m_iIDGen;
m_iCachedArticles = 0;
m_bPartialChanged = false;
m_iID = iID ? iID : ++m_iIDGen;
}
FileInfo::~ FileInfo()
@@ -1088,6 +1234,30 @@ void FileList::Remove(FileInfo* pFileInfo)
erase(std::find(begin(), end(), pFileInfo));
}
CompletedFile::CompletedFile(int iID, const char* szFileName, EStatus eStatus, unsigned long lCrc)
{
m_iID = iID;
if (FileInfo::m_iIDMax < m_iID)
{
FileInfo::m_iIDMax = m_iID;
}
m_szFileName = strdup(szFileName);
m_eStatus = eStatus;
m_lCrc = lCrc;
}
void CompletedFile::SetFileName(const char* szFileName)
{
free(m_szFileName);
m_szFileName = strdup(szFileName);
}
CompletedFile::~CompletedFile()
{
free(m_szFileName);
}
PostInfo::PostInfo()
{
@@ -1097,6 +1267,12 @@ PostInfo::PostInfo()
m_bWorking = false;
m_bDeleted = false;
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;
@@ -1104,7 +1280,6 @@ PostInfo::PostInfo()
m_tStageTime = 0;
m_eStage = ptQueued;
m_pPostThread = NULL;
m_iIDMessageGen = 0;
}
PostInfo::~ PostInfo()
@@ -1113,11 +1288,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);
@@ -1130,32 +1300,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()
{
@@ -1255,6 +1399,29 @@ void HistoryInfo::GetName(char* szBuffer, int iSize)
}
HistoryList::~HistoryList()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
HistoryInfo* HistoryList::Find(int iID)
{
for (iterator it = begin(); it != end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetID() == iID)
{
return pHistoryInfo;
}
}
return NULL;
}
DownloadQueue* DownloadQueue::Lock()
{
g_pDownloadQueue->m_LockMutex.Lock();

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
@@ -89,8 +89,12 @@ private:
int m_iPartNumber;
char* m_szMessageID;
int m_iSize;
char* m_pSegmentContent;
long long m_iSegmentOffset;
int m_iSegmentSize;
EStatus m_eStatus;
char* m_szResultFilename;
unsigned long m_lCrc;
public:
ArticleInfo();
@@ -99,12 +103,21 @@ public:
int GetPartNumber() { return m_iPartNumber; }
const char* GetMessageID() { return m_szMessageID; }
void SetMessageID(const char* szMessageID);
void SetSize(int s) { m_iSize = s; }
void SetSize(int iSize) { m_iSize = iSize; }
int GetSize() { return m_iSize; }
void AttachSegment(char* pContent, long long iOffset, int iSize);
void DiscardSegment();
const char* GetSegmentContent() { return m_pSegmentContent; }
void SetSegmentOffset(long long iSegmentOffset) { m_iSegmentOffset = iSegmentOffset; }
long long GetSegmentOffset() { return m_iSegmentOffset; }
void SetSegmentSize(int iSegmentSize) { m_iSegmentSize = iSegmentSize; }
int GetSegmentSize() { return m_iSegmentSize; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
const char* GetResultFilename() { return m_szResultFilename; }
void SetResultFilename(const char* v);
unsigned long GetCrc() { return m_lCrc; }
void SetCrc(unsigned long lCrc) { m_lCrc = lCrc; }
};
class FileInfo
@@ -142,12 +155,16 @@ private:
bool m_bExtraPriority;
int m_iActiveDownloads;
bool m_bAutoDeleted;
int m_iCachedArticles;
bool m_bPartialChanged;
static int m_iIDGen;
static int m_iIDMax;
friend class CompletedFile;
public:
FileInfo();
FileInfo(int iID = 0);
~FileInfo();
int GetID() { return m_iID; }
void SetID(int iID);
@@ -199,11 +216,15 @@ public:
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool GetExtraPriority() { return m_bExtraPriority; }
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; }
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads);
bool GetAutoDeleted() { return m_bAutoDeleted; }
void SetAutoDeleted(bool bAutoDeleted) { m_bAutoDeleted = bAutoDeleted; }
int GetCachedArticles() { return m_iCachedArticles; }
void SetCachedArticles(int iCachedArticles) { m_iCachedArticles = iCachedArticles; }
bool GetPartialChanged() { return m_bPartialChanged; }
void SetPartialChanged(bool bPartialChanged) { m_bPartialChanged = bPartialChanged; }
ServerStatList* GetServerStats() { return &m_ServerStats; }
};
@@ -220,6 +241,34 @@ public:
void Remove(FileInfo* pFileInfo);
};
class CompletedFile
{
public:
enum EStatus
{
cfUnknown,
cfSuccess,
cfPartial,
cfFailure
};
private:
int m_iID;
char* m_szFileName;
EStatus m_eStatus;
unsigned long m_lCrc;
public:
CompletedFile(int iID, const char* szFileName, EStatus eStatus, unsigned long lCrc);
~CompletedFile();
int GetID() { return m_iID; }
void SetFileName(const char* szFileName);
const char* GetFileName() { return m_szFileName; }
EStatus GetStatus() { return m_eStatus; }
unsigned long GetCrc() { return m_lCrc; }
};
typedef std::deque<CompletedFile*> CompletedFiles;
class NZBParameter
{
@@ -263,7 +312,7 @@ public:
private:
char* m_szName;
EStatus m_eStatus;
friend class ScriptStatusList;
public:
@@ -341,14 +390,19 @@ public:
dsNone,
dsManual,
dsHealth,
dsDupe
dsDupe,
dsBad,
dsGood,
dsCopy,
dsScan
};
enum EMarkStatus
{
ksNone,
ksBad,
ksGood
ksGood,
ksSuccess
};
enum EUrlStatus
@@ -368,9 +422,6 @@ public:
nkUrl
};
typedef std::vector<char*> Files;
typedef std::deque<Message*> Messages;
static const int FORCE_PRIORITY = 900;
friend class DupInfo;
@@ -409,7 +460,7 @@ private:
time_t m_tMinTime;
time_t m_tMaxTime;
int m_iPriority;
Files m_completedFiles;
CompletedFiles m_completedFiles;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
@@ -418,6 +469,7 @@ private:
EDeleteStatus m_eDeleteStatus;
EMarkStatus m_eMarkStatus;
EUrlStatus m_eUrlStatus;
int m_iExtraParBlocks;
bool m_bAddUrlPaused;
bool m_bDeletePaused;
bool m_bManyDupeFiles;
@@ -440,13 +492,28 @@ 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;
time_t m_tDownloadStartTime;
int m_iDownloadSec;
int m_iPostTotalSec;
int m_iParSec;
int m_iRepairSec;
int m_iUnpackSec;
bool m_bReprocess;
time_t m_tQueueScriptTime;
bool m_bParFull;
int m_iMessageCount;
int m_iCachedMessageCount;
int m_iFeedID;
static int m_iIDGen;
static int m_iIDMax;
void ClearMessages();
public:
NZBInfo();
~NZBInfo();
@@ -485,7 +552,7 @@ public:
int GetRemainingParCount() { return m_iRemainingParCount; }
void SetRemainingParCount(int iRemainingParCount) { m_iRemainingParCount = iRemainingParCount; }
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads) { m_iActiveDownloads = iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads);
long long GetSuccessSize() { return m_lSuccessSize; }
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
long long GetFailedSize() { return m_lFailedSize; }
@@ -523,7 +590,7 @@ public:
void SetMaxTime(time_t tMaxTime) { m_tMaxTime = tMaxTime; }
void BuildDestDirName();
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
CompletedFiles* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
void ClearCompletedFiles();
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
@@ -540,6 +607,8 @@ public:
EMarkStatus GetMarkStatus() { return m_eMarkStatus; }
void SetMarkStatus(EMarkStatus eMarkStatus) { m_eMarkStatus = eMarkStatus; }
EUrlStatus GetUrlStatus() { return m_eUrlStatus; }
int GetExtraParBlocks() { return m_iExtraParBlocks; }
void SetExtraParBlocks(int iExtraParBlocks) { m_iExtraParBlocks = iExtraParBlocks; }
void SetUrlStatus(EUrlStatus eUrlStatus) { m_eUrlStatus = eUrlStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
@@ -578,6 +647,29 @@ public:
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
long long GetDownloadedSize() { return m_lDownloadedSize; }
void SetDownloadedSize(long long lDownloadedSize) { m_lDownloadedSize = lDownloadedSize; }
int GetDownloadSec() { return m_iDownloadSec; }
void SetDownloadSec(int iDownloadSec) { m_iDownloadSec = iDownloadSec; }
int GetPostTotalSec() { return m_iPostTotalSec; }
void SetPostTotalSec(int iPostTotalSec) { m_iPostTotalSec = iPostTotalSec; }
int GetParSec() { return m_iParSec; }
void SetParSec(int iParSec) { m_iParSec = iParSec; }
int GetRepairSec() { return m_iRepairSec; }
void SetRepairSec(int iRepairSec) { m_iRepairSec = iRepairSec; }
int GetUnpackSec() { return m_iUnpackSec; }
void SetUnpackSec(int iUnpackSec) { m_iUnpackSec = iUnpackSec; }
time_t GetDownloadStartTime() { return m_tDownloadStartTime; }
void SetDownloadStartTime(time_t tDownloadStartTime) { m_tDownloadStartTime = tDownloadStartTime; }
void SetReprocess(bool bReprocess) { m_bReprocess = bReprocess; }
bool GetReprocess() { return m_bReprocess; }
time_t GetQueueScriptTime() { return m_tQueueScriptTime; }
void SetQueueScriptTime(time_t tQueueScriptTime) { m_tQueueScriptTime = tQueueScriptTime; }
void SetParFull(bool bParFull) { m_bParFull = bParFull; }
bool GetParFull() { return m_bParFull; }
int GetFeedID() { return m_iFeedID; }
void SetFeedID(int iFeedID) { m_iFeedID = iFeedID; }
void CopyFileList(NZBInfo* pSrcNZBInfo);
void UpdateMinMaxTime();
PostInfo* GetPostInfo() { return m_pPostInfo; }
@@ -586,9 +678,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;
@@ -603,6 +699,7 @@ public:
void Clear();
void Add(NZBInfo* pNZBInfo, bool bAddTop);
void Remove(NZBInfo* pNZBInfo);
NZBInfo* Find(int iID);
};
class PostInfo
@@ -622,7 +719,6 @@ public:
ptFinished
};
typedef std::deque<Message*> Messages;
typedef std::vector<char*> ParredFiles;
private:
@@ -630,6 +726,12 @@ private:
bool m_bWorking;
bool m_bDeleted;
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;
@@ -638,9 +740,6 @@ private:
time_t m_tStageTime;
Thread* m_pPostThread;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
ParredFiles m_ParredFiles;
public:
@@ -666,11 +765,20 @@ public:
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetRequestParCheck() { return m_bRequestParCheck; }
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
bool GetForceParFull() { return m_bForceParFull; }
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; }
};
@@ -756,7 +864,14 @@ public:
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
};
typedef std::deque<HistoryInfo*> HistoryList;
typedef std::deque<HistoryInfo*> HistoryListBase;
class HistoryList : public HistoryListBase
{
public:
~HistoryList();
HistoryInfo* Find(int iID);
};
class DownloadQueue : public Subject
{
@@ -765,6 +880,7 @@ public:
{
eaNzbFound,
eaNzbAdded,
eaNzbDeleted,
eaFileCompleted,
eaFileDeleted,
eaUrlCompleted
@@ -809,6 +925,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
@@ -821,7 +938,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
@@ -846,7 +966,6 @@ protected:
static void Loaded() { g_bLoaded = true; }
public:
virtual ~DownloadQueue() {};
static bool IsLoaded() { return g_bLoaded; }
static DownloadQueue* Lock();
static void Unlock();

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,9 +50,6 @@
#include "HistoryCoordinator.h"
#include "DupeCoordinator.h"
extern HistoryCoordinator* g_pHistoryCoordinator;
extern Options* g_pOptions;
bool DupeCoordinator::SameNameOrKey(const char* szName1, const char* szDupeKey1,
const char* szName2, const char* szDupeKey2)
{
@@ -92,22 +89,82 @@ void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
// in queue - the new item is skipped
if (pQueuedNZBInfo != pNZBInfo && bSameContent && pNZBInfo->GetKind() == NZBInfo::nkNzb)
{
char szMessage[1024];
if (!strcmp(pNZBInfo->GetName(), pQueuedNZBInfo->GetName()))
{
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
snprintf(szMessage, 1024, "Skipping duplicate %s, already queued", pNZBInfo->GetName());
}
else
{
warn("Skipping duplicate %s, already queued as %s",
snprintf(szMessage, 1024, "Skipping duplicate %s, already queued as %s",
pNZBInfo->GetName(), pQueuedNZBInfo->GetName());
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
g_pHistoryCoordinator->DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
szMessage[1024-1] = '\0';
if (pNZBInfo->GetFeedID())
{
warn("%s", szMessage);
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
g_pHistoryCoordinator->DeleteDiskFiles(pNZBInfo);
}
else
{
pNZBInfo->SetDeleteStatus(NZBInfo::dsCopy);
pNZBInfo->AddMessage(Message::mkWarning, szMessage);
}
return;
}
}
// if download has empty dupekey and empty dupescore - check if download queue
// or history have an item with the same name and non empty dupekey or dupescore and
// take these properties from this item
if (Util::EmptyStr(pNZBInfo->GetDupeKey()) && pNZBInfo->GetDupeScore() == 0)
{
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
{
NZBInfo* pQueuedNZBInfo = *it;
if (!strcmp(pQueuedNZBInfo->GetName(), pNZBInfo->GetName()) &&
(!Util::EmptyStr(pQueuedNZBInfo->GetDupeKey()) || pQueuedNZBInfo->GetDupeScore() != 0))
{
pNZBInfo->SetDupeKey(pQueuedNZBInfo->GetDupeKey());
pNZBInfo->SetDupeScore(pQueuedNZBInfo->GetDupeScore());
info("Assigning dupekey %s and dupescore %i to %s from existing queue item with the same name",
pNZBInfo->GetDupeKey(), pNZBInfo->GetDupeScore(), pNZBInfo->GetName());
break;
}
}
}
if (Util::EmptyStr(pNZBInfo->GetDupeKey()) && pNZBInfo->GetDupeScore() == 0)
{
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
!strcmp(pHistoryInfo->GetNZBInfo()->GetName(), pNZBInfo->GetName()) &&
(!Util::EmptyStr(pHistoryInfo->GetNZBInfo()->GetDupeKey()) || pHistoryInfo->GetNZBInfo()->GetDupeScore() != 0))
{
pNZBInfo->SetDupeKey(pHistoryInfo->GetNZBInfo()->GetDupeKey());
pNZBInfo->SetDupeScore(pHistoryInfo->GetNZBInfo()->GetDupeScore());
info("Assigning dupekey %s and dupescore %i to %s from existing history item with the same name",
pNZBInfo->GetDupeKey(), pNZBInfo->GetDupeScore(), pNZBInfo->GetName());
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDup &&
!strcmp(pHistoryInfo->GetDupInfo()->GetName(), pNZBInfo->GetName()) &&
(!Util::EmptyStr(pHistoryInfo->GetDupInfo()->GetDupeKey()) || pHistoryInfo->GetDupInfo()->GetDupeScore() != 0))
{
pNZBInfo->SetDupeKey(pHistoryInfo->GetDupInfo()->GetDupeKey());
pNZBInfo->SetDupeScore(pHistoryInfo->GetDupInfo()->GetDupeScore());
info("Assigning dupekey %s and dupescore %i to %s from existing history item with the same name",
pNZBInfo->GetDupeKey(), pNZBInfo->GetDupeScore(), pNZBInfo->GetName());
break;
}
}
}
// find duplicates in history
bool bSkip = false;
@@ -115,9 +172,9 @@ void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
bool bSameContent = false;
const char* szDupeName = NULL;
// find duplicates in queue having exactly same content
// find duplicates in history having exactly same content
// also: nzb-files having duplicates marked as good are skipped
// also (only in score mode): nzb-files having success-duplicates in dup-history but don't having duplicates in recent history are skipped
// also (only in score mode): nzb-files having success-duplicates in dup-history but not having duplicates in recent history are skipped
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
@@ -189,7 +246,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;
}
}
@@ -197,21 +254,33 @@ void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
if (bSkip)
{
char szMessage[1024];
if (!strcmp(pNZBInfo->GetName(), szDupeName))
{
warn("Skipping duplicate %s, found in history with %s", pNZBInfo->GetName(),
snprintf(szMessage, 1024, "Skipping duplicate %s, found in history with %s", pNZBInfo->GetName(),
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
else
{
warn("Skipping duplicate %s, found in history %s with %s",
snprintf(szMessage, 1024, "Skipping duplicate %s, found in history %s with %s",
pNZBInfo->GetName(), szDupeName,
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
szMessage[1024-1] = '\0';
if (pNZBInfo->GetFeedID())
{
warn("%s", szMessage);
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
g_pHistoryCoordinator->DeleteDiskFiles(pNZBInfo);
}
else
{
pNZBInfo->SetDeleteStatus(bSameContent ? NZBInfo::dsCopy : NZBInfo::dsGood);
pNZBInfo->AddMessage(Message::mkWarning, szMessage);
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
g_pHistoryCoordinator->DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
@@ -236,7 +305,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;
}
@@ -287,8 +356,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)
@@ -365,20 +433,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
{
@@ -395,13 +468,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() :
@@ -451,3 +524,89 @@ void DupeCoordinator::HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo*
pDownloadQueue->Save();
}
}
DupeCoordinator::EDupeStatus DupeCoordinator::GetDupeStatus(DownloadQueue* pDownloadQueue,
const char* szName, const char* szDupeKey)
{
EDupeStatus eStatuses = dsNone;
// find duplicates in download queue
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
{
NZBInfo* pNZBInfo = *it;
if (SameNameOrKey(szName, szDupeKey, pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
if (pNZBInfo->GetSuccessArticles() + pNZBInfo->GetFailedArticles() > 0)
{
eStatuses = (EDupeStatus)(eStatuses | dsDownloading);
}
else
{
eStatuses = (EDupeStatus)(eStatuses | dsQueued);
}
}
}
// find duplicates in history
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
SameNameOrKey(szName, szDupeKey, pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey()))
{
const char* szTextStatus = pHistoryInfo->GetNZBInfo()->MakeTextStatus(true);
if (!strncasecmp(szTextStatus, "SUCCESS", 7))
{
eStatuses = (EDupeStatus)(eStatuses | dsSuccess);
}
else if (!strncasecmp(szTextStatus, "FAILURE", 7))
{
eStatuses = (EDupeStatus)(eStatuses | dsFailure);
}
else if (!strncasecmp(szTextStatus, "WARNING", 7))
{
eStatuses = (EDupeStatus)(eStatuses | dsWarning);
}
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDup &&
SameNameOrKey(szName, szDupeKey, pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey()))
{
if (pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess ||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood)
{
eStatuses = (EDupeStatus)(eStatuses | dsSuccess);
}
else if (pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsFailed ||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsBad)
{
eStatuses = (EDupeStatus)(eStatuses | dsFailure);
}
}
}
return eStatuses;
}
void DupeCoordinator::ListHistoryDupes(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, NZBList* pDupeList)
{
if (pNZBInfo->GetDupeMode() == dmForce)
{
return;
}
// find duplicates in history
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
pDupeList->push_back(pHistoryInfo->GetNZBInfo());
}
}
}

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,17 @@
class DupeCoordinator
{
public:
enum EDupeStatus
{
dsNone = 0,
dsQueued = 1,
dsDownloading = 2,
dsSuccess = 4,
dsWarning = 8,
dsFailure = 16
};
private:
void ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey);
void HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo);
@@ -38,7 +49,11 @@ 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);
void ListHistoryDupes(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, NZBList* pDupeList);
};
extern DupeCoordinator* g_pDupeCoordinator;
#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
@@ -51,16 +51,10 @@
#include "Util.h"
#include "NZBFile.h"
#include "DupeCoordinator.h"
#include "ParCoordinator.h"
#include "ParParser.h"
#include "PrePostProcessor.h"
#include "DupeCoordinator.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern DupeCoordinator* g_pDupeCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
HistoryCoordinator::HistoryCoordinator()
{
debug("Creating HistoryCoordinator");
@@ -71,25 +65,10 @@ HistoryCoordinator::~HistoryCoordinator()
debug("Destroying HistoryCoordinator");
}
void HistoryCoordinator::Cleanup()
{
debug("Cleaning up HistoryCoordinator");
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
{
delete *it;
}
pDownloadQueue->GetHistory()->clear();
DownloadQueue::Unlock();
}
/**
* Removes old entries from (recent) history
*/
void HistoryCoordinator::IntervalCheck()
void HistoryCoordinator::ServiceWork()
{
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
@@ -119,7 +98,7 @@ void HistoryCoordinator::IntervalCheck()
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
{
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
DeleteDiskFiles(pHistoryInfo->GetNZBInfo());
}
info("Collection %s removed from history", szNiceName);
@@ -144,16 +123,24 @@ void HistoryCoordinator::IntervalCheck()
DownloadQueue::Unlock();
}
void HistoryCoordinator::DeleteQueuedFile(const char* szQueuedFile)
void HistoryCoordinator::DeleteDiskFiles(NZBInfo* pNZBInfo)
{
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
// delete parked files
g_pDiskState->DiscardFiles(pNZBInfo);
}
pNZBInfo->GetFileList()->Clear();
// delete nzb-file
if (!g_pOptions->GetNzbCleanupDisk())
{
return;
}
// szQueuedFile may contain one filename or several filenames separated
// QueuedFile may contain one filename or several filenames separated
// with "|"-character (for merged groups)
char* szFilename = strdup(szQueuedFile);
char* szFilename = strdup(pNZBInfo->GetQueuedFilename());
char* szEnd = szFilename - 1;
while (szEnd)
@@ -220,7 +207,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)
@@ -242,8 +229,11 @@ 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()->GetDeleteStatus() == NZBInfo::dsManual ||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsGood ||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsCopy ? DupInfo::dsDeleted :
pHistoryInfo->GetNZBInfo()->IsDupeSuccess() ? DupInfo::dsSuccess :
DupInfo::dsFailed);
@@ -251,15 +241,34 @@ void HistoryCoordinator::HistoryHide(DownloadQueue* pDownloadQueue, HistoryInfo*
pNewHistoryInfo->SetTime(pHistoryInfo->GetTime());
(*pDownloadQueue->GetHistory())[pDownloadQueue->GetHistory()->size() - 1 - rindex] = pNewHistoryInfo;
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
DeleteDiskFiles(pHistoryInfo->GetNZBInfo());
delete pHistoryInfo;
info("Collection %s removed from history", szNiceName);
}
void HistoryCoordinator::PrepareEdit(DownloadQueue* pDownloadQueue, IDList* pIDList, DownloadQueue::EEditAction eAction)
{
// First pass: when marking multiple items - mark them bad without performing the mark-logic,
// this will later (on second step) avoid moving other items to download queue, if they are marked bad too.
if (eAction == DownloadQueue::eaHistoryMarkBad)
{
for (IDList::iterator itID = pIDList->begin(); itID != pIDList->end(); itID++)
{
int iID = *itID;
HistoryInfo* pHistoryInfo = pDownloadQueue->GetHistory()->Find(iID);
if (pHistoryInfo && pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
{
pHistoryInfo->GetNZBInfo()->SetMarkStatus(NZBInfo::ksBad);
}
}
}
}
bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList, DownloadQueue::EEditAction eAction, int iOffset, const char* szText)
{
bool bOK = false;
PrepareEdit(pDownloadQueue, pIDList, eAction);
for (IDList::iterator itID = pIDList->begin(); itID != pIDList->end(); itID++)
{
@@ -269,6 +278,8 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
HistoryInfo* pHistoryInfo = *itHistory;
if (pHistoryInfo->GetID() == iID)
{
bOK = true;
switch (eAction)
{
case DownloadQueue::eaHistoryDelete:
@@ -286,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:
@@ -297,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:
@@ -306,7 +332,6 @@ bool HistoryCoordinator::EditList(DownloadQueue* pDownloadQueue, IDList* pIDList
break;
}
bOK = true;
break;
}
}
@@ -329,20 +354,7 @@ void HistoryCoordinator::HistoryDelete(DownloadQueue* pDownloadQueue, HistoryLis
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
// delete parked files
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
{
FileInfo* pFileInfo = *it;
g_pDiskState->DiscardFile(pFileInfo);
}
}
pNZBInfo->GetFileList()->Clear();
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
DeleteDiskFiles(pHistoryInfo->GetNZBInfo());
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb &&
@@ -382,7 +394,6 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Returning %s from history back to download queue", szNiceName);
bool bUnparked = false;
NZBInfo* pNZBInfo = NULL;
if (bReprocess && pHistoryInfo->GetKind() != HistoryInfo::hkNzb)
@@ -396,6 +407,7 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
pNZBInfo = pHistoryInfo->GetNZBInfo();
// unpark files
bool bUnparked = false;
for (FileList::iterator it = pNZBInfo->GetFileList()->begin(); it != pNZBInfo->GetFileList()->end(); it++)
{
FileInfo* pFileInfo = *it;
@@ -403,6 +415,12 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
bUnparked = true;
}
if (!(bUnparked || bReprocess))
{
warn("Could not return %s back from history to download queue: history item does not have any files left for download", szNiceName);
return;
}
pDownloadQueue->GetQueue()->push_front(pNZBInfo);
pHistoryInfo->DiscardNZBInfo();
@@ -413,10 +431,16 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
pNZBInfo->SetUnpackStatus(NZBInfo::usNone);
pNZBInfo->SetCleanupStatus(NZBInfo::csNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->SetPostTotalSec(pNZBInfo->GetPostTotalSec() - pNZBInfo->GetUnpackSec());
pNZBInfo->SetUnpackSec(0);
if (ParCoordinator::FindMainPars(pNZBInfo->GetDestDir(), NULL))
if (ParParser::FindMainPars(pNZBInfo->GetDestDir(), NULL))
{
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetPostTotalSec(pNZBInfo->GetPostTotalSec() - pNZBInfo->GetParSec());
pNZBInfo->SetParSec(0);
pNZBInfo->SetRepairSec(0);
pNZBInfo->SetParFull(false);
}
}
pNZBInfo->SetDeleteStatus(NZBInfo::dsNone);
@@ -428,28 +452,21 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
{
pNZBInfo->SetMoveStatus(NZBInfo::msNone);
}
pNZBInfo->SetReprocess(bReprocess);
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
pNZBInfo = pHistoryInfo->GetNZBInfo();
pHistoryInfo->DiscardNZBInfo();
pNZBInfo->SetUrlStatus(NZBInfo::lsNone);
pNZBInfo->SetDeleteStatus(NZBInfo::dsNone);
pDownloadQueue->GetQueue()->push_front(pNZBInfo);
bUnparked = true;
}
if (bUnparked || bReprocess)
{
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);
}
else
{
warn("Could not return %s back from history to download queue: history item does not have any files left for download", szNiceName);
}
pDownloadQueue->GetHistory()->erase(itHistory);
// the object "pHistoryInfo" is released few lines later, after the call to "NZBDownloaded"
pNZBInfo->PrintMessage(Message::mkInfo, "%s returned from history back to download queue", szNiceName);
if (bReprocess)
{
@@ -458,34 +475,46 @@ void HistoryCoordinator::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryLis
g_pPrePostProcessor->NZBDownloaded(pDownloadQueue, pNZBInfo);
}
if (bUnparked || bReprocess)
{
delete pHistoryInfo;
}
delete pHistoryInfo;
}
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;
}
NZBFile* pNZBFile = NZBFile::Create(pNZBInfo->GetQueuedFilename(), "");
if (pNZBFile == NULL)
NZBFile* pNZBFile = new NZBFile(pNZBInfo->GetQueuedFilename(), "");
if (!pNZBFile->Parse())
{
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());
delete pNZBFile;
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++)
{
@@ -514,11 +543,20 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, Histor
}
}
g_pDiskState->DiscardFiles(pNZBInfo);
// reset status fields (which are not reset by "HistoryReturn")
pNZBInfo->SetMoveStatus(NZBInfo::msNone);
pNZBInfo->SetUnpackCleanedUpDisk(false);
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->SetDownloadedSize(0);
pNZBInfo->SetDownloadSec(0);
pNZBInfo->SetPostTotalSec(0);
pNZBInfo->SetParSec(0);
pNZBInfo->SetRepairSec(0);
pNZBInfo->SetUnpackSec(0);
pNZBInfo->SetExtraParBlocks(0);
pNZBInfo->ClearCompletedFiles();
pNZBInfo->GetServerStats()->Clear();
pNZBInfo->GetCurrentServerStats()->Clear();
@@ -530,14 +568,10 @@ void HistoryCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, Histor
HistoryReturn(pDownloadQueue, itHistory, pHistoryInfo, false);
if (!bPaused && g_pOptions->GetParCheck() != Options::pcForce)
{
pDownloadQueue->EditEntry(pNZBInfo->GetID(),
DownloadQueue::eaGroupPauseExtraPars, 0, NULL);
}
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);
@@ -546,7 +580,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);
@@ -564,6 +598,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
@@ -27,28 +27,36 @@
#define HISTORYCOORDINATOR_H
#include "DownloadInfo.h"
#include "Service.h"
class HistoryCoordinator
class HistoryCoordinator : public Service
{
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);
void PrepareEdit(DownloadQueue* pDownloadQueue, IDList* pIDList, DownloadQueue::EEditAction eAction);
protected:
virtual int ServiceInterval() { return 600000; }
virtual void ServiceWork();
public:
HistoryCoordinator();
virtual ~HistoryCoordinator();
void AddToHistory(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool EditList(DownloadQueue* pDownloadQueue, IDList* pIDList, DownloadQueue::EEditAction eAction, int iOffset, const char* szText);
void DeleteQueuedFile(const char* szQueuedFile);
void DeleteDiskFiles(NZBInfo* pNZBInfo);
void HistoryHide(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
void Redownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
void IntervalCheck();
void Cleanup();
};
extern HistoryCoordinator* g_pHistoryCoordinator;
#endif

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