Compare commits

...

2047 Commits

Author SHA1 Message Date
Andrey Prygunkov
5e26d52d70 #784: removed expired root certificate
from ca root certificate store: certificate “DST Root CA X3” used by
Lets Encrypt
2021-10-01 00:08:45 +02:00
Andrey Prygunkov
ae81c9403d updated version string to "21.2-testing" 2021-09-30 22:12:56 +02:00
Andrey Prygunkov
b0d35f9a09 updated version string to "21.1" 2021-06-03 16:55:57 +02:00
Andrey Prygunkov
ce7cd631c2 updated ChangeLog for v21.1 2021-06-03 14:20:06 +02:00
Andrey Prygunkov
0432cf13d3 #715: improved reporting for binding errors
on Windows
2021-04-23 20:24:42 +02:00
Andrey Prygunkov
799de88b3e #704: corrected line endings 2021-04-22 20:59:47 +02:00
Andrey Prygunkov
7ff3251dcf #682: allow special characters in URL for username and password 2021-04-21 20:20:21 +02:00
Andrey Prygunkov
97ae03bbd3 #704: corrected icon in Windows "uninstall program" list 2021-04-21 18:12:30 +02:00
Captain Trips
6bbfb6b7b7 #736: cast time_t to int for printf (#742)
This fixes crashes on systems with 64-bit time_t.
2021-04-20 23:56:23 +02:00
Andrey Prygunkov
f02bbbefd7 #725: set SameSite attribute for cooikes 2021-04-19 20:45:04 +02:00
Andrey Prygunkov
4d19c899bd #749, #688: fixed crash on windows 2021-04-18 21:36:53 +02:00
Andrey Prygunkov
1d008bd1f5 #748: removed outdated links from web interface
and merged Info and About tabs
2021-04-15 22:17:00 +02:00
Andrey Prygunkov
8c1e62ef49 fixed #731: file selector in WebKit based browsers doesn't allow to choose the same file again 2021-04-15 21:28:47 +02:00
Andrey Prygunkov
e18c25c231 #747: updated url of the global certificate storage file
in the build scripts
2021-04-15 20:55:20 +02:00
Lucas Held
6dbe6edbab #739: fixed processing of group command in nserv 2021-04-15 01:43:29 +02:00
Andrey Prygunkov
1ee8e02586 #745: fixed crash caused by malformed nzb files
: for file elements not having subject attribute a (somewhat) random
subject is generated and is used as file name; correct file names are
read from articles later anyway
2021-04-15 01:26:36 +02:00
Andrey Prygunkov
f8f9dd2b6d #720: fixed file allocating
on file systems where sparse files are not supported
2020-11-01 16:59:38 +01:00
Andrey Prygunkov
414ffcbc35 #688: always using dirbrowser snapshot
to fix issues with leftovers on cleanup
2020-05-21 18:42:42 +02:00
Andrey Prygunkov
0522b5f49d #694: support new error messages in unrar 5.80 2020-04-20 21:03:56 +02:00
Disconnect3d
575b823758 #679: fix strncasecmp size parameter off by ones 2020-04-20 19:36:01 +02:00
Andrey Prygunkov
a124a91a84 fixed #693: negative values for "FileSizeLo" in JSON-RPC 2020-04-20 19:32:15 +02:00
Sander
4546b0f368 #650, #651: corrected space characters in one js-file 2019-06-29 23:44:57 +02:00
Andrey Prygunkov
625e7a61e1 #648: update license text
because of change of Free Software Foundation address but also includes
minor formatting changes.
2019-06-22 22:01:13 +02:00
Andrey Prygunkov
f1c1373c7d #637: nzbget version on about page 2019-05-13 18:20:51 +02:00
pfidr34
5dda6b2e49 #634: correct typo in about dialog of web interface 2019-05-10 20:27:12 +02:00
Andrey Prygunkov
81aa56324f #635: fixed PC sleep mode not working (Windows only) 2019-05-09 23:20:00 +02:00
Andrey Prygunkov
a8533e7f0a updated version string to "21.1-testing" 2019-05-09 22:30:00 +02:00
Andrey Prygunkov
bbfcf07689 updated version string to "21.0" 2019-05-02 21:48:05 +02:00
Andrey Prygunkov
fd35e05b61 updated ChangeLog for v21.0 2019-05-02 21:45:50 +02:00
Andrey Prygunkov
3e0be12cb3 #629: added aarch64 binaries to Linux installer and Android installer 2019-04-20 23:11:14 +02:00
Andrey Prygunkov
d6e8f67927 #622, #135, ff69fbbeb9: fixed trimming of relative paths in config 2019-04-07 15:56:31 +02:00
Andrey Prygunkov
a159a1ff5a #612: better description of option UMask 2019-03-10 22:06:44 +01:00
Andrey Prygunkov
15f4955f38 #620: wildcards in option AuthorizedIP 2019-03-10 21:52:08 +01:00
Andrey Prygunkov
aac98b53ee #621: fixed: remote server could crash when feed with invalid api request 2019-03-10 13:54:53 +01:00
Andrey Prygunkov
fa4a5bb261 #618: 32-bit and 64-bit unrar and 7-zip on Windows 2019-03-04 01:11:44 +01:00
Andrey Prygunkov
d19c9b80e7 #611: removed suggestion of RC4 cipher 2019-02-28 21:31:08 +01:00
Andrey Prygunkov
c7716ae9b7 #351, #610, e3bd94189a: fixed: remote clients not displaying current download speed 2019-02-13 18:20:04 +01:00
Andrey Prygunkov
e07a6b9443 #351: sleep longer in frontend when console window is hidden
(only in Windows app)
2019-02-09 09:45:38 +01:00
Joe Groocock
fa57474d78 #608, #607: fix compilation with OpenSSL no-comp 2019-02-03 22:01:43 +01:00
Andrey Prygunkov
4299ac1354 #599: url encoding in macOS app
Fixed: macOS menubar widget can't connect if password contains special
characters.
2019-02-02 22:37:57 +01:00
Andrey Prygunkov
82dfec471b #351: sleep longer in curses frontend
This reduces CPU usage, especially in idle.
2019-02-02 14:55:08 +01:00
Andrey Prygunkov
3a5bc85962 #351: notify about url or nzb returned to queue
from history.
2019-01-26 21:09:48 +01:00
Andrey Prygunkov
25dc60e71f #351: sleep longer in queue coordinator
Up to 2 second when queue is empty or downloads are paused.
2019-01-26 18:58:09 +01:00
Andrey Prygunkov
855f3e8649 #351: sleep longer in feed coordintator
up to 60 seconds.
2019-01-26 18:54:52 +01:00
Andrey Prygunkov
bdc7ba38db #351: sleep longer in disk service
If there are no active downloads the disk service can now sleep for 10
seconds instead of 1.
2019-01-26 18:17:36 +01:00
Andrey Prygunkov
89427f42ce #351: "WorkState" is now observable 2019-01-26 18:16:17 +01:00
Andrey Prygunkov
a665dc5375 fixed compiler warning
'register' storage class specifier is deprecated and incompatible with
C++17
2019-01-26 16:33:30 +01:00
Andrey Prygunkov
adf3e05e1d #351: refactor: utility function "Sleep"
to replace direct calls to “usleep”, with parameter in milliseconds
instead of microseconds.
2019-01-22 22:23:40 +01:00
Andrey Prygunkov
e3bd94189a #351: refactor: moved changeable state into new Unit "WorkState.cpp"
from Unit “Options.cpp”. The latter now contains only program options
(which cannot be changed without reload).
2019-01-22 21:57:00 +01:00
Andrey Prygunkov
bb1cb68653 #351: sleep longer in queue coordinator
In idle sleeps for 0.5 sec. Wake up immediately when a new item is
added to queue. Waking up from paused queue can take longer (up to 0.5
sec).
2019-01-22 18:32:34 +01:00
Andrey Prygunkov
e91f37d566 #351: protect vars depended on condition
to avoid race conditions and lock ups
2019-01-22 00:11:13 +01:00
Andrey Prygunkov
57f4d2864b #351: refactor: use same name for cond var and mutex 2019-01-21 23:40:12 +01:00
Andrey Prygunkov
05c841880f #593: 794f240f48: fixed potential lock up
due to race condition
2019-01-21 21:39:58 +01:00
Andrey Prygunkov
92828acab0 #351: reworked timed services
Now sleeping much longer, up to next scheduled work, instead of often
wake ups to check if the work needs to be performed. This improves CPU
usage in idle.
2019-01-21 21:21:16 +01:00
Andrey Prygunkov
137c936830 #351: full pausing UrlCoordinator in idle
UrlCoordinator is now completely paused (waits without wake ups) when
there are no work for it.
2019-01-19 23:42:57 +01:00
Andrey Prygunkov
4826f04778 #351: corrected formatting 2019-01-19 23:40:26 +01:00
hugbug
15b4f55310 added new badges and updated texts in README 2019-01-19 14:11:53 +01:00
Andrey Prygunkov
0461f2ad55 #604: fixed an LGTM alert for Python 2019-01-19 13:44:09 +01:00
Andrey Prygunkov
67ca371c6b #604: suppress an LGTM alert
A false positive.
2019-01-19 13:19:45 +01:00
Andrey Prygunkov
a97a6d7c7f #604: fixed LGTM alerts for Python 2019-01-19 11:56:18 +01:00
Andrey Prygunkov
b6927e992e #604: fixed LGTM alerts for JavaScript 2019-01-19 11:55:59 +01:00
Andrey Prygunkov
59cae49344 #604: fixed LGTM alerts for C++ 2019-01-19 11:55:25 +01:00
Andrey Prygunkov
6bf097f1c3 #604: fine tune LGTM config 2019-01-19 11:23:09 +01:00
Xavier RENE-CORAIL
ad0592843c #582: add LGTM.com code quality badges 2019-01-18 19:32:15 +01:00
Andrey Prygunkov
8a09de775f #604: exclude third-party files from LGTM analysis 2019-01-18 00:45:00 +01:00
Andrey Prygunkov
adf7ec225b #362: save ParSetId into disk state
That’s needed to make direct rename work properly if the program was
reloaded in the middle of direct rename download process.
2019-01-17 19:45:59 +01:00
Andrey Prygunkov
d15722c72d #595: save original file name into disk state 2019-01-17 19:42:41 +01:00
Andrey Prygunkov
0776c6b057 #595: check original file names when looking for splitted fragments 2019-01-17 00:28:55 +01:00
Andrey Prygunkov
8f63eef312 #595: functional tests for par-join issue
Some tests are failing because the issue isn’t fixed yet.
2019-01-16 22:46:05 +01:00
fedux
8a59079627 #600: fixed deprecated OpenSSL calls
Since OpenSSL 1.1.0 we have:

 - ERR_remove_thread_state, ERR_remove_state: "They are now deprecated
   and do nothing".

 - ASN1_STRING_data: "This function is deprecated: applications should
   use ASN1_STRING_get0_data() instead".
2019-01-15 21:20:59 +01:00
Andrey Prygunkov
491d816bff #591: save only changed queue data during downloading
This is a speed optimisation for large queue.
2019-01-14 17:53:51 +01:00
Andrey Prygunkov
6dfe17c1d8 #597: c2b93c588b: slightly simplified code 2019-01-13 19:37:46 +01:00
Andrey Prygunkov
f3cf9317a6 #591: avoid superfluous savings of queue 2019-01-13 00:39:42 +01:00
Andrey Prygunkov
b0356d88d6 #591: use local buffer for formatting during saving disk state
This improves performance with large queue by avoiding many memory
allocations.
2019-01-12 21:47:56 +01:00
Andrey Prygunkov
c0d7a15afa #591: string format functions return new length 2019-01-12 21:43:33 +01:00
Andrey Prygunkov
fbfa793b20 #591: improved error reporting for queue disk state corruption 2019-01-12 19:48:44 +01:00
Andrey Prygunkov
a329c65eb3 #351: pause article cache loop when cache is empty
to improve CPU usage when idle
2019-01-11 21:52:45 +01:00
Andrey Prygunkov
b9c4c5b19e #597: static linking of std::thread in Linux installer 2019-01-07 19:11:52 +01:00
Andrey Prygunkov
a5f2c1c7c5 #597: 49e8fea0e2: use std::thread instead of platform implementation 2019-01-07 19:02:18 +01:00
Andrey Prygunkov
e2ea481799 #590: fixed compiler warning on MSVC 2019-01-07 18:53:49 +01:00
Andrey Prygunkov
c2b93c588b #597: use std::mutex and std::condition_variable on all platforms
wrapped them in custom classes for easier replacements, just in case.
2019-01-07 18:52:19 +01:00
Andrey Prygunkov
3934244a70 #597: use std::mutex and std::condition_variable on Windows
That’s the easiest way to get compatibility with Windows XP yet better
performance on Windows Vista and above.
2019-01-06 21:42:41 +01:00
Andrey Prygunkov
62ba9a5609 #597: implemented condition variable class for Windows
works on Windows Vista and newer.
2019-01-06 15:50:59 +01:00
Andrey Prygunkov
e7d4556f8b #590: 541a695e2f: fixed wrong type 2019-01-06 14:43:41 +01:00
Andrey Prygunkov
43c9bb78f3 #597: use ConditionVar instead of std:condition_variable 2019-01-06 13:14:56 +01:00
Andrey Prygunkov
e824c5b940 #597: implemented OS-specific condition variable class
Currently for POSIX only; Windows implementation follows.
2019-01-06 13:08:09 +01:00
Andrey Prygunkov
32a6bf18ad #597: reverted changes to Thread-unit
Due to compatibility issues on older platforms (issues discovered on
ARMv7 with GCC 5.2 but may not be limited to this platform) the usage
of C++11 thread- and synchronisation facilities has been reverted to
previous custom OS-specific implementation.
2019-01-06 12:50:28 +01:00
Andrey Prygunkov
2cb419691d #593: 794f240f48: fixed compiling error
in GCC 5 for armel. Promise/future are not supported there and were
replaced with condition_variable.
2019-01-04 19:38:46 +01:00
Andrey Prygunkov
a74722d8cc #590: 20036b73b8: fixed compiling error
when cross-compiling: PRId64 and others maybe undefined even if
<inttypes.h> exists
2019-01-03 23:17:20 +01:00
Andrey Prygunkov
0602e9d2f1 #593: use platform independent type 2019-01-03 15:54:02 +01:00
fedux
9713cbad5e #592: RemoteClient: Use strncpy instead of strcpy
Ensure that the aligned text is filled with zeroes to avoid any data
leak. Also fixed a typo.
2019-01-03 15:23:32 +01:00
hugbug
009cf9eee2 Merge pull request #593 from fedux/idle-pr
Idle CPU usage improvements
2019-01-03 15:20:29 +01:00
Federico Cuello
85995ad56f Pause FeedCoordination::Run thread for 1 sec using condition variables
Loop every second waiting on a condition variable to reduce the number
of CPU wake ups and keep responsiveness.
2019-01-03 12:30:34 +01:00
Federico Cuello
1f3067c1e3 Pause PrePostProcessor::Run thread using condition variables
Wait until new jobs or a Stop signal instead of looping, in a way that
doesn't reduce responsiveness.
2019-01-03 12:29:56 +01:00
Federico Cuello
794f240f48 Pause DoMainLoop until stop/reload signal in daemon mode
When in deamon mode, just wait for the Stop signal instead of looping
constantly, in a way that doesn't affect responsiveness.
2019-01-03 12:27:28 +01:00
Federico Cuello
49e8fea0e2 Use std::thread instead of platform implementation
Simplify thread handling by using std::thread.
2019-01-03 12:27:28 +01:00
Federico Cuello
fa8f8855f9 Use std::atomic for Thread class members
Before only m_threadCount was protected by a mutex but not the rest of
the bools that are read/written from different threads. This replaces
them by atomic values removing the need for the mutex and protecting the
previously unprotected bools, except for m_autoDestroy that it's only
set before starting the thread.
2019-01-03 12:27:26 +01:00
Federico Cuello
2c85def959 Replace custom Guard class by std::lock_guard and std::unique_lock
Use a typedef to maintain the name and use Guard for simple scoped lock
guards and UniqueLock when it needs to be movable.
2019-01-02 20:49:10 +01:00
Federico Cuello
fb3a27fde9 Replace m_threadMutex by static Mutex
Just use Mutex and remove the need to call Thread::Init()
2019-01-02 20:49:09 +01:00
Federico Cuello
b29131ffb8 Use std::mutex instead of custom class Mutex
Basically this is just removing the custom class and using a typedef to
keep the name. Most of the changes are just case for the lock/unlock
methods.
2019-01-02 20:46:48 +01:00
hugbug
31a34b58ea Merge pull request #590 from fedux/fix-warnings
Fix compile warnings
2018-12-29 14:00:53 +01:00
Federico Cuello
4a10fdb2df fix compile warning: -Wstringop-truncation / -Wstringop-overflow 2018-12-27 14:49:17 +01:00
Federico Cuello
541a695e2f fix compile warning: -Wsign-compare
Fix sign compare warning by improving casting choices.
2018-12-21 16:04:41 +01:00
Federico Cuello
07b7a766a2 fix compile warning: -Wclass-memaccess
Use value-initialization instead of clearing an object with memset.
2018-12-21 16:04:40 +01:00
Federico Cuello
1057e9194c fix compile warning: -Wmisleading-indentation 2018-12-21 15:12:34 +01:00
Federico Cuello
9eaf9fae9a fix compile warning: -Wmaybe-uninitialized
Initialize codepoint to avoid the warning.
2018-12-21 15:11:53 +01:00
Federico Cuello
34d157990d fix compile warning: -Wnonnull
Rewrite the intentional segafult to avoid the compile warning.
2018-12-21 15:07:43 +01:00
Federico Cuello
c93eb2087f fix compile warning: -Wreorder
Declare variables in the right order to avoid this warning.
2018-12-21 15:06:50 +01:00
Federico Cuello
1f89c037b9 fix compile warning: -Wunused-variable
Don't define variables only used for debuggin when debug is not enabled.
2018-12-21 15:01:00 +01:00
Federico Cuello
20036b73b8 fix compile warning: -Wformat
Use macros defined in inttypes.h for the proper format for 64 bit types
and fall-back to previously define format when inttypes.h is not
available.
2018-12-21 14:56:55 +01:00
Jurgen S
da3425af3f #585: proper UTF-8 encoding of email content
Updated script so the email content is encoded properly in UTF-8 to avoid "UnicodeEncodeErrors" when non ascii characters are used like german Umlauts etc.
2018-11-30 23:57:22 +01:00
Simon Chapman
4c482a91da #581: added python 3 compatibility to Logger.py script 2018-11-29 22:45:01 +01:00
Simon Chapman
458a1afb13 #578: added python 3 compatibility to EMail.py script 2018-11-19 08:20:57 +01:00
Andrey Prygunkov
3339a2c520 #538: android resolver workaround
isn’t necessary when building specifically for Android using Android
NDK.
2018-09-01 13:35:13 +02:00
Andrey Prygunkov
17c5a9cbc8 fixed #573: statistics for session download time and speed
may be way off on high load
2018-09-01 13:18:21 +02:00
Andrey Prygunkov
4db9ef2535 #541: fixed crash when adding many urls 2018-08-25 18:06:44 +02:00
Andrey Prygunkov
5a0eae7bf4 #541: fixed log messages printed twice 2018-08-25 17:54:28 +02:00
Sander
86ac23b6aa #567, #569: NextParamAsInt: Stop parsing at end-of-string
fixed potential crash in web-interface.
2018-07-30 19:18:22 +02:00
Andrey Prygunkov
fa1aa45fa7 #541: f3cb44e7b2: other case for DELETED/DUPE
queued URLs with lower dupescore have status DELETED/DUPE instead of
DELETED/MANUAL.
2018-07-27 17:19:16 +02:00
Andrey Prygunkov
f842a19544 #541: preserve logs for URL items
do not discard (cleanup) logs of URLs when loading disk-state
2018-07-24 22:47:39 +02:00
Andrey Prygunkov
f3cb44e7b2 #541: even better duplicate handling of urls
1) allow status DELETED/GOOD for URLs;
2) queued URLs with lower dupescore have status DELETED/DUPE instead of
DELETED/MANUAL.
2018-07-23 23:41:51 +02:00
Andrey Prygunkov
e54ffbaaaa #562: fixed: failures are being moved to DestDir
This only happenned for downloads without par2-files and without
archives.
2018-07-16 22:48:41 +02:00
Andrey Prygunkov
2d049f1904 #562: refactor: avoiding multiple dereferences 2018-07-16 21:22:58 +02:00
Andrey Prygunkov
5106979d5d #541: mark as good hides urls 2018-07-15 14:02:42 +02:00
Andrey Prygunkov
75d05bce4a #541: fixed url dupe handling for good/success status 2018-07-14 12:44:01 +02:00
Andrey Prygunkov
ea4ea2c901 #564: click on logo switches to downloads tab
Infos from about dialog moved onto settings page.
2018-07-13 17:17:50 +02:00
Andrey Prygunkov
5e15677218 #541: better duplicate handling of urls 2018-07-12 21:19:09 +02:00
Andrey Prygunkov
93ad31b9d8 #541: preserving age and size info for urls from feed
and showing it in history of webui
2018-07-10 20:12:51 +02:00
Andrey Prygunkov
14c5a1caf7 #541: delayed fetching of nzbs added via urls 2018-07-07 18:16:40 +02:00
Andrey Prygunkov
f52f5b5de9 #558: fixed test failures 2018-07-06 18:08:53 +02:00
Andrey Prygunkov
0916c2a908 #561: more deterministic cleanup of OpenSSL
to prevent crash when using OpenSSL-FIPS
2018-06-28 18:10:06 +02:00
Andrey Prygunkov
ab1238dde4 #558: added tests for unpack CRC error 2018-06-22 20:06:45 +02:00
Andrey Prygunkov
758ce4047b #525: force par-check for nzbs without archives 2018-06-19 22:37:45 +02:00
Andrey Prygunkov
c24bf0e8ce #538: added android toolchain script to distribution archive 2018-06-15 23:35:02 +02:00
Andrey Prygunkov
1264878a97 #538: added compatibility with Android Bionic C library 2018-06-13 21:03:34 +02:00
Andrey Prygunkov
a85ff314f3 #538: use precompiled headers for Android NDK builds 2018-06-13 21:01:34 +02:00
Andrey Prygunkov
a349ab08f7 #538: installer build script for Android NDK 2018-06-13 21:01:10 +02:00
Andrey Prygunkov
064de49edf #538: unpackers build script for Android NDK 2018-06-13 21:00:34 +02:00
Andrey Prygunkov
ae79c56c07 #538: toolchain build script for Android NDK 2018-06-13 21:00:11 +02:00
Andrey Prygunkov
d6353e9cee updated version string to "21.0-testing" 2018-06-12 23:09:04 +02:00
Andrey Prygunkov
d9d824631e updated version string to "20.0" 2018-06-06 21:34:34 +02:00
Andrey Prygunkov
2bd765b06f updated ChangeLog for v20.0 2018-06-06 20:09:37 +02:00
Andrey Prygunkov
f51c216417 #550: fixed SIMD status message for ARM CRC 2018-06-04 19:00:34 +02:00
Andrey Prygunkov
78b270d23e #550: workaround for GCC 7 bug on ARM
Fix for GCC 7 needing option “-fpermissive” to compile it’s own file
“arm_acle.h”.
2018-06-02 20:34:29 +02:00
Andrey Prygunkov
a4252a1e79 #549: force terminating remote processors
when terminating remote server to ensure all child threads are
terminated on reload/shutdown, even if connections were not closed as
expected
2018-06-01 23:38:10 +02:00
Andrey Prygunkov
1ac2be47d5 #549: force socket closing in remote server (Windows only)
to fix hanging connection to web-client
2018-06-01 23:27:55 +02:00
Andrey Prygunkov
9437a227ee #548: direct rename and direct unpack active by default
on new installations
2018-05-31 21:50:40 +02:00
Andrey Prygunkov
0d19722881 #547: improved duplicate detection for files with same subjects 2018-05-31 18:31:28 +02:00
Andrey Prygunkov
adfe5eef26 #542: fixed 7zip crashing
on newer Linux systems
2018-05-31 15:12:35 +02:00
Sander
321cddeeba #546: advice for letsencrypt in option descriptions 2018-05-30 23:49:50 +02:00
Andrey Prygunkov
44f08325f9 #438: proper program termination on Windows shutdown/logoff 2018-05-30 18:54:01 +02:00
Andrey Prygunkov
e601e77e5e #542: fixed unrar crashing
on newer Linux systems.
2018-05-28 21:38:10 +02:00
Sander
8e6ccfa8a7 #536: nshow IP address of incoming connection (#536) 2018-05-09 22:31:42 +02:00
Andrey Prygunkov
3eebee20aa #534: fixed logging of IPv6 addresses 2018-05-08 18:27:57 +02:00
Andrey Prygunkov
b83a9b9aff #533: detecting malformed articles and printing a warning 2018-05-06 22:46:25 +02:00
Andrey Prygunkov
05d7a8ede2 #533: fixed crash on malformed articles 2018-05-06 22:43:14 +02:00
Andrey Prygunkov
4d771036e2 #529: fixed missing file unlocking after direct rename
Also made locking more granular to avoid unnecessary locks for files
whose articles are not going to be discarded.
2018-05-01 21:22:34 +02:00
Andrey Prygunkov
7e659d8d97 #532: update make config 2018-04-15 18:34:14 +02:00
Andrey Prygunkov
137ac1a3ee fixed #532: wrong favicon used on Android
Dynamically activate icon targeted for iOS only when running on iOS.
2018-04-15 18:16:46 +02:00
Andrey Prygunkov
3a4e6623db fixed #529: crash when flushing article cache after direct rename 2018-04-03 13:02:57 +02:00
Andrey Prygunkov
c2669b359e fixed #527: deleting of active par-job may crash the program 2018-04-02 21:17:24 +02:00
Andrey Prygunkov
8bffb51974 fixed #528: tests may fails on Windows due to locked files 2018-04-02 20:12:54 +02:00
Andrey Prygunkov
81be21b540 #483: added new images to distribution archive 2018-03-25 18:14:57 +02:00
Andrey Prygunkov
222d6a1f6d #509: fixed incorrect renaming in rar-renamer
Further improved detection of rar-sets.
2018-02-22 00:19:51 +01:00
hatem zidi
cd6bf682f9 #483: optimized mobile touch icon (favicon)
Optimized for desktop and mobile platforms.
2018-02-02 18:40:45 +01:00
Andrey Prygunkov
d93769021a #501: fixed race condition in queue script coordinator 2018-01-29 23:54:30 +01:00
Andrey Prygunkov
cf0d086b57 #485: HttpOnly for cookies
to improve security
2018-01-26 00:08:51 +01:00
Andrey Prygunkov
bf53c6eaa6 #496: don't log passwords for incorrect login attempts 2018-01-26 00:00:41 +01:00
Andrey Prygunkov
9b50760006 #477: dupe check now case insensitive 2018-01-25 23:50:07 +01:00
Andrey Prygunkov
b7102894d7 #498: fixed pp-parameter initialization 2018-01-25 23:47:53 +01:00
Andrey Prygunkov
db102f5a15 #498: fixed wrong case in unpack password parameter 2018-01-25 17:39:18 +01:00
Andrey Prygunkov
d9cb0026bd #498: case insensitive pp-parameters 2018-01-25 17:38:35 +01:00
bket
5893d03f1b #497: added LibreSSL support 2018-01-20 18:17:38 +01:00
Andrey Prygunkov
18d138648b fixed #474: build fails on musl 2017-11-15 22:36:10 +01:00
Andrey Prygunkov
93a43e711f #471: more robust news server connection test
This fixes connection test errors with servers checking message id
format correctness.
2017-11-11 11:50:23 +01:00
Andrey Prygunkov
2b52dc5bfe #468: compatibility with Android 4 and older 2017-11-10 23:59:17 +01:00
Andrey Prygunkov
ce844367e7 #468: DNS resolving on Android 2017-11-07 00:21:58 +01:00
Andrey Prygunkov
64a5a78866 #467: print par2 creator packet
as INFO on par-check start and as part of ERROR message on par-repair
failure
2017-11-06 22:47:16 +01:00
Andrey Prygunkov
6f9fb29595 #466: removed less useful debug messages 2017-11-06 22:20:03 +01:00
Andrey Prygunkov
0ee60ab844 #466: keep log-file longer open
that improve logging performance especially in debug build
2017-11-06 22:18:40 +01:00
Andrey Prygunkov
8dfca2a542 #461, 7deb3c1b68: renamed "LogBuffer" in webui 2017-11-04 13:55:30 +01:00
Andrey Prygunkov
76bdd63e60 #465: fixed decoding errors on ARMv5 2017-11-03 17:37:10 +01:00
Andrey Prygunkov
ef78cbfc74 #464: fixed: x86_64 universal build fails on PaX kernel 2017-11-02 19:23:01 +01:00
Andrey Prygunkov
74768b2183 #454, 801bf1ae7c: reactivated simd-decoder
which got accidentally deactivated
2017-11-01 22:56:27 +01:00
Andrey Prygunkov
801bf1ae7c #454: fixed: missing data in raw article mode
- option “RawArticle”
2017-11-01 21:05:11 +01:00
Andrey Prygunkov
a901deff03 #463: stop static initialisation invasion 2017-10-31 11:30:54 +01:00
Andrey Prygunkov
67245d6ca8 #448, 186da63056: NServ memory cache switch
no longer has memory limit parameter. The parameter wasn’t respected
anyway.
2017-10-29 19:14:43 +01:00
Andrey Prygunkov
2d70e1de21 #462: fixed: backup servers not used on certain article decoding errors 2017-10-29 17:52:39 +01:00
Andrey Prygunkov
7deb3c1b68 #461: renamed option "LogBufferSize" to "LogBuffer"
and removed obsolete options “SaveQueue” and “ReloadQueue” from
config-file
2017-10-29 13:07:00 +01:00
Andrey Prygunkov
d4886ac7d1 #461: removed option "BrokenLog" 2017-10-29 12:51:49 +01:00
Andrey Prygunkov
5b3372107d #461: removed option "AccurateRate" 2017-10-29 12:43:06 +01:00
Andrey Prygunkov
07c54740a7 #461: removed option "TerminateTimeout"
No thread killing anymore. Hanging downloads are gracefully cancelled
after timeout set in “ArticleTimeout” or “UrlTimeout”.
2017-10-29 12:34:16 +01:00
Andrey Prygunkov
af111adbde #461: removed options "SaveQueue" and "ReloadQueue" 2017-10-28 16:17:45 +02:00
Andrey Prygunkov
d31a734a5c #460: better handling broken connections 2017-10-27 19:38:42 +02:00
Andrey Prygunkov
54f14f5efa #459: use glibc instead of uClibc
in universal installer builds for Linux
2017-10-27 00:40:12 +02:00
Andrey Prygunkov
18fbd12f2c #454: better target CPU detection in configure 2017-10-25 21:31:50 +02:00
Andrey Prygunkov
ff671e722d #455: changed default location of log-file 2017-10-23 23:29:40 +02:00
Andrey Prygunkov
15c292653e #458: compiling without libxml2 to test dev environment
new configure-parameter “--disable-libxml2”.
2017-10-22 23:52:13 +02:00
Andrey Prygunkov
c0aed9af48 #454: fixed compiling error on aarch64 2017-10-22 20:35:51 +02:00
Andrey Prygunkov
597e4fd034 #454: removed force-inline
since it’s no longer needed after moving loop into inner functions;
better compatibility with different compilers
2017-10-21 00:04:19 +02:00
Andrey Prygunkov
3c2575bc26 #454: removed option "RateBuffer"
since it’s no longer needed with raw decoder which works on 4KB buffer
already
2017-10-20 20:54:52 +02:00
Andrey Prygunkov
50c1ca588c #454: option "RawArticle" works again 2017-10-20 20:52:42 +02:00
Andrey Prygunkov
da9c8b1138 #454: fixed buffer overrun
and compiler warnings on VC++
2017-10-20 18:07:01 +02:00
Andrey Prygunkov
c59ab2d9dc #454: one-pass simd decoder
updated SIMD decoder, support for end-of-stream detection
2017-10-19 18:27:04 +02:00
Andrey Prygunkov
35fca1479c #454: fixed rare crash in stream end detection 2017-10-16 18:15:21 +02:00
Andrey Prygunkov
54c5a061c8 #454: fixed align issue on Windows 32 bit 2017-10-16 18:14:11 +02:00
Andrey Prygunkov
3a0489a4a9 #435: fixed warnings in 64 bit mode on Windows 2017-10-16 18:13:34 +02:00
Andrey Prygunkov
a31fb733a2 #454: SIMD CRC routines for Intel and ARM 2017-10-12 21:09:24 +02:00
Andrey Prygunkov
2691eff535 #448: speed optimisation in NServ
when using unlimited memory cache (command line switch “-m 0”)
2017-10-10 19:11:40 +02:00
Andrey Prygunkov
37b04c593a #448: don't try deleting files that don't exist
- a small optimisation to reduce disk activity
2017-10-08 21:11:05 +02:00
Andrey Prygunkov
b9b1c76ada #454: using raw-decoder from node-yencode library 2017-10-08 21:08:23 +02:00
Andrey Prygunkov
69a0db63f6 #454: integrated node-yencode library by Anime Tosho
1) integrated the library; 2) splitted units by CPU architecture; 3)
extended makefile and configure script to detect CPU architecture and
use appropriate compiler flags; 4) runtime CPU features detection for
x86 and ARM with dynamic code  dispatching; 5) temporary (for test
purposes) printing info about SIMD support to stdout on program
startup; 6) new SIMD routines are not yet used in the program
2017-10-08 20:49:13 +02:00
Andrey Prygunkov
e9926d92e0 fixed compiler warnings 2017-10-09 13:35:43 +02:00
Andrey Prygunkov
f5aa27979c #448, 186da63056: small speed optimisation in NServ 2017-10-09 13:35:10 +02:00
BernCarney
24a4542c14 #452, #453: proper URL encoding in example pp-scripts
Updated scripts to accept special characters in nzbget password and username.
2017-10-05 19:31:49 +02:00
Andrey Prygunkov
bb95e1f274 #448, 186da63056: corrected file mode 2017-09-29 21:45:52 +02:00
Andrey Prygunkov
186da63056 #448: memory cache in NServ
: new command line switch “-m”
2017-09-28 20:45:06 +02:00
Andrey Prygunkov
1facedb694 #451: speed control in NServ
: new command line switches “-w“ and “-r”
2017-09-28 17:45:14 +02:00
Andrey Prygunkov
54eb8e1291 #448: new option "SkipWrite"
replaces compiler define “SKIP_ARTICLE_WRITING”. 2) renamed option
“Decode” to “RawArticle”. 3) option “CrcCheck” moved from section
“Download Queue “ into section “Check and Repair”
2017-09-28 17:31:47 +02:00
Andrey Prygunkov
80b67383e3 #450: speed up yenc decoder
by optimising main decoding loop
2017-09-24 14:24:10 +02:00
Andrey Prygunkov
406a78218a #448, 71505340d0: allow CRC calculation even if
decoding is disabled via SKIP_ARTICLE_DECODING
2017-09-23 00:29:22 +02:00
Andrey Prygunkov
262df77f74 #449: new option "RateBuffer"
to configure speed meter update rate
2017-09-22 23:45:26 +02:00
Andrey Prygunkov
71505340d0 #448: disable article writing and decoding
Disabling is now possible for test purposes via defines
SKIP_ARTICLE_WRITING and SKIP_ARTICLE_DECODING (nzbget.h)
2017-09-22 20:25:05 +02:00
Andrey Prygunkov
bddb0bb26d #447: better optimisation settings (Windows)
Adjusted Visual Studio project to compile with more aggressive
optimisation settings.
2017-09-21 18:45:30 +02:00
Andrey Prygunkov
d90a40909b #446: faster CRC computation 2017-09-21 18:06:17 +02:00
Andrey Prygunkov
2bdc87c198 fixed #445: program hangs during update on Linux 2017-09-18 17:49:12 +02:00
Andrey Prygunkov
e97a0fde11 #443: functional tests on Travis CI 2017-09-16 09:54:41 +02:00
Andrey Prygunkov
eb18608522 #443: functional tests find 7z and par2 automatically 2017-09-15 20:25:40 +02:00
Andrey Prygunkov
481e7b3d2b refactor: removed updates from article writer 2017-09-13 19:19:01 +02:00
Andrey Prygunkov
8545cb3581 #426: show unpack password as plain field 2017-09-13 18:39:40 +02:00
Andrey Prygunkov
e422fea746 #440: merge branch '440-update-check' into develop 2017-09-11 21:18:53 +02:00
Andrey Prygunkov
2ce9f0df38 #440: notification control from main site
To not update all installations on release day but rather gradually
within several days (as defined in update-info file on web-site).
2017-09-11 21:17:51 +02:00
Andrey Prygunkov
36de095e51 #442: improved volume detection in rar-renamer 2017-09-11 20:56:03 +02:00
Andrey Prygunkov
9b05f779f6 #432, #421, b4bcc82abe: remote-server cleanup
Use “close(socket)” when “accept”-ing connections and use
“shutdown(socket)” otherwise.
2017-09-07 17:59:10 +02:00
Andrey Prygunkov
38efd4a4de #440: help screen for update dialog 2017-09-07 17:48:51 +02:00
Andrey Prygunkov
80b8ee8dfb #440: automatic update check
New option "UpdateCheck”.
2017-09-05 20:31:11 +02:00
Andrey Prygunkov
47b1c1a2dd #49, #260: 9dc2b8c71b: corrected formatting 2017-09-05 20:19:41 +02:00
Andrey Prygunkov
7417160da9 #435: thread memory cleanup when using OpenSSL 2017-09-05 19:51:17 +02:00
Andrey Prygunkov
a41e010165 #438: fixed propagation delay 2017-09-04 20:28:00 +02:00
Andrey Prygunkov
cbe7b1e051 #431: fixed broken SSL in built-in web-server 2017-09-04 20:26:19 +02:00
Andrey Prygunkov
00a5b68d84 #439: Authorized IP not working on IPv6 (fix for Windows) 2017-09-04 20:25:47 +02:00
Andrey Prygunkov
561713dbed #435: using "windows" instead of "win32" in setup file name 2017-09-03 11:27:55 +02:00
Andrey Prygunkov
cce9338909 #435: reorganised Windows setup files 2017-09-02 22:20:29 +02:00
Andrey Prygunkov
515fd9298d fixed #439: Authorized IP not working on IPv6 2017-09-02 19:29:33 +02:00
Andrey Prygunkov
0ee72d2dd7 #438: speed improvement in queue management
when downloading with very large queue (thousands of nzbs).
2017-09-02 11:37:57 +02:00
Andrey Prygunkov
57f932cfab #438: hibernating all file infos in one large disk state file
to greatly improve startup time
2017-09-02 11:36:38 +02:00
Andrey Prygunkov
8f803c2f21 #438: great speed optimization for queue-dir cleanup 2017-09-02 11:34:07 +02:00
Andrey Prygunkov
89bd5d6dfe #435: added build script for Windows into repository 2017-09-01 23:01:56 +02:00
Simon Nicolussi
49a0292053 #437: don't rely on sizes of externally generated files
Tests that try to match exact file sizes are prone to break for unexpected versions of p7zip and par2cmdline.
2017-08-31 22:38:42 +02:00
Andrey Prygunkov
a60d8d1273 #435: fixed compiling error with older OpenSSL versions 2017-08-31 21:03:58 +02:00
Andrey Prygunkov
44ea3d02ab #435: extended Windows setup for 64 bit binaries 2017-08-30 23:21:01 +02:00
Andrey Prygunkov
fe9f208f20 #435: better cleanup when using OpenSLL
To avoid memory leaks report when linking OpenSLL statically.
2017-08-30 23:20:09 +02:00
Andrey Prygunkov
20e8bb6ebc #435: project configuration for 64 bit (Windows)
Now using only static libraries.
2017-08-30 23:18:57 +02:00
Andrey Prygunkov
0709f248ee #435: fixed warnings in 64 bit mode on Windows 2017-08-30 22:22:29 +02:00
Andrey Prygunkov
35d8aa5fa7 #435: fixed compiling error if no regex.h 2017-08-28 21:06:45 +02:00
Andrey Prygunkov
9f80f45fb9 #435: compatibility with windows 64 bit 2017-08-28 21:05:52 +02:00
Andrey Prygunkov
763fe425d6 #433: better username/password validation
when testing connection on settings page
2017-08-27 20:28:15 +02:00
Andrey Prygunkov
9c86dc70bd #421, #434: fixed: filter buttons don't work in history 2017-08-27 17:37:44 +02:00
Andrey Prygunkov
1f6a360de5 #421: fixed status buttons on history tab 2017-08-26 18:10:54 +02:00
Andrey Prygunkov
dcdc41ca9a #421, #422: URL components should not be encoded as JSON 2017-08-26 11:34:36 +02:00
Andrey Prygunkov
6d307a05f8 #431: use remote address in error reporting
for incoming connections
2017-08-25 20:22:55 +02:00
Andrey Prygunkov
83c15b1f05 fixed #431: NServ terminates if client interrupts connection 2017-08-25 20:21:40 +02:00
Andrey Prygunkov
43fc121219 #430: article statistics for par-files after direct rename 2017-08-25 18:56:09 +02:00
Andrey Prygunkov
4b729eb0f0 fixed #430: pause and article statistics after direct rename 2017-08-24 14:53:25 +02:00
Andrey Prygunkov
0158614da2 #421: update downloads table even if no changes
when there are active downloads in order to recalculate estimated time
2017-08-24 13:46:37 +02:00
Andrey Prygunkov
ca7807fa92 #421: fixed missing semicolon in raphael.min.js 2017-08-23 18:07:59 +02:00
Andrey Prygunkov
97018ae102 #424: resume detected non-par2-files
when direct rename is active
2017-08-15 12:51:44 +03:00
Andrey Prygunkov
cbe6c6a340 fixed #426: options formatted as password fields when they shouldn't 2017-08-10 22:22:12 +02:00
Andrey Prygunkov
d84ec5685b #425: cross-compiling for FreeBSD using Clang 2017-08-07 12:31:44 +02:00
Andrey Prygunkov
557e0580a7 #421: 45b5727374: fixed messages filter buttons disappeared 2017-08-05 22:58:20 +02:00
Andrey Prygunkov
43c0bdd9d3 #423: Linux installer compatibility with FreeBSD 2017-08-04 23:57:57 +02:00
Andrey Prygunkov
86bcb7073c #420: support for redirect codes 303, 307 and 308
in web-client for fetching of rss feeds and nzb-files
2017-08-04 21:43:40 +02:00
Andrey Prygunkov
6cf0edd278 #421: added debug logging for etags 2017-08-01 21:36:10 +02:00
Andrey Prygunkov
b4bcc82abe #421: fixed crash when disconnecting web-clients on Windows 2017-08-01 21:35:48 +02:00
Andrey Prygunkov
6fb1ea1cff #421: support keep-alive in all responses 2017-07-31 22:50:26 +02:00
Andrey Prygunkov
3ee9125100 #421: better handling shutdown in remote server 2017-07-31 20:30:24 +02:00
Andrey Prygunkov
fad2be0e0f #421: new option "RemoteTimeout"
to define timeout for incoming connections including timeout for
keep-alive.
2017-07-31 20:24:02 +02:00
Andrey Prygunkov
2763f1a522 #421: support for keep-alive connections in built-in web-server 2017-07-31 19:47:17 +02:00
Andrey Prygunkov
1214c79eab correction in debug logging
which could cause crash on shutdown in debug mode
2017-07-31 18:02:24 +02:00
Andrey Prygunkov
7ee0b60361 #418: fixed variadic macros detection 2017-07-31 17:59:11 +02:00
Andrey Prygunkov
0135e605a8 #421, #422: do not parse json-response if it will not be used
… and small refactorings and fixes for error reporting
2017-07-30 23:40:54 +02:00
schnusch
546324d891 #421, #422: added detection of cached responses in WebUI's RPC 2017-07-30 23:40:47 +02:00
schnusch
43563e8dfb #421, #422: removed remnants of 412 error handling 2017-07-30 23:40:38 +02:00
schnusch
4f5d357e3c #421, #422: use GET requests for safe JsonRPC methods in WebUI 2017-07-30 23:40:30 +02:00
Andrey Prygunkov
a6c120bc82 #421, #422: avoid table updates if no changes 2017-07-30 23:40:29 +02:00
Andrey Prygunkov
18f673e6b3 #421, #422: allow caching for more API methods
1) All safe methods are now cacheable.
2) Corrected debug code, accidentally pushed in previous commit (#ifdef
DISABLE_PARCHECK).
2017-07-30 23:40:29 +02:00
Andrey Prygunkov
5ac7c0398e #421, #422: adjustments in ETag support
1) convert MD5 hash into string using standard method instead of base64;
2) if par2 isn’t available using another hash function from Util-unit;
3) avoid gzipping of response if it isn’t sent;
4) use BString class for header string formatting.
2017-07-30 23:40:29 +02:00
schnusch
0008f040b3 #421, 422: added support for Etag an If-None-Match HTTP headers
The web server now support Etag generation for static files and some RPC
methods. If If-None-Match is given in the request and matches with the Etag
generated for the response than no data is sent and 304 or 412 is returned.

The JavaScript RPC calls also support the new HTTP error code by buffering
Etags and responses and will reuse the previous response if 412 is returned.
2017-07-30 23:40:13 +02:00
Andrey Prygunkov
45b5727374 #421: call multiple API-methods simultaneously 2017-07-28 00:42:25 +02:00
Andrey Prygunkov
f001b0744b #421: reduce number of requests when loading webui
by combining all javascript-files into one and all css-files into one
2017-07-28 00:41:18 +02:00
Andrey Prygunkov
2124a886f8 #418: updated POSIX build files to newer autotools version
- compatibility with newer autotools;
- compatibility with newer platforms such as aarch64.
2017-07-26 23:40:09 +02:00
Andrey Prygunkov
7f4b15b4de #416: fixed wait interval 2017-07-26 18:50:07 +02:00
Andrey Prygunkov
68c74a5a30 #416: better error handling when fetching rss feeds 2017-07-24 21:33:48 +02:00
Stefaan Ghysels
01d4ebb800 #417: fixed linux installer failure on android emulator 2017-07-24 18:57:06 +02:00
Tobias Geerinckx-Rice
f56e01d200 #414: fixed compiler error when building using GnuTLS 2017-07-16 07:24:50 +02:00
Andrey Prygunkov
cdc5c5515f fixed #412: unpack using password file doesn't work on Windows
Also added more debug output for future use.
2017-07-12 20:48:59 +02:00
Andrey Prygunkov
67195e7683 #400: adjustments to unix domain sockets mode 2017-07-09 21:48:46 +02:00
Andrey Prygunkov
499e3d5d8f fixed typo in project file for Visual Studio 2017-07-09 20:11:12 +02:00
schnusch
0ee9083627 #400: support for file sockets (POSIX only)
Option "ControlIP" can be set to local file path to use file sockets instead of network sockets.
2017-07-09 19:52:22 +02:00
Andrey Prygunkov
726a6154be updated version string to "20.0-testing" 2017-07-09 19:53:09 +02:00
Andrey Prygunkov
c0eedc342b updated version string to "19.1" 2017-07-08 18:20:02 +02:00
Andrey Prygunkov
eb4b8b30e1 updated ChangeLog for v19.1 2017-07-08 18:13:06 +02:00
Andrey Prygunkov
593e29f163 updated version string to "19.1-testing" 2017-07-02 18:08:17 +02:00
Andrey Prygunkov
c4d29bc57f #408: abort direct unpack if destination path was changed
For example caused by a change of category during direct unpack.
2017-07-02 17:26:35 +02:00
Andrey Prygunkov
92db424ce0 #405: safety check for presence of unpacked files
Before trying to reuse them in default unpack module.
2017-07-01 22:32:11 +02:00
Andrey Prygunkov
17fbb795c8 #407: fixed: rar-rename may fail to read encrypted rar3-archives 2017-07-01 21:59:19 +02:00
Andrey Prygunkov
35e65e792b #406: fixed incorrect renaming in rar-renamer
Improved detection of rar-sets.
2017-06-30 19:09:34 +02:00
Andrey Prygunkov
486b9d7d2b #405: discard info about extracted archives
if found archive files not processed by direct unpack; to prevent
reusing on next unpack attempt
2017-06-30 17:36:19 +02:00
Andrey Prygunkov
3abaa0fb3f updated version string to "20.0-testing" 2017-06-29 21:53:50 +02:00
Andrey Prygunkov
810ddc8356 updated version string to "19.0" 2017-06-25 19:21:32 +02:00
Andrey Prygunkov
1a840f894e updated ChangeLog for v19.0 2017-06-25 19:05:04 +02:00
Andrey Prygunkov
928e0a6006 fixed #399: error when compiling without par-check 2017-06-23 23:22:49 +02:00
Andrey Prygunkov
a4cca673dc fixed #398: crash between post-processing steps 2017-06-16 17:51:28 +02:00
Andrey Prygunkov
fb9b84a23b #371: wait for direct rename completion before direct unpack
Redo accidentally undone 5df06a2626.
2017-06-16 16:45:55 +02:00
Andrey Prygunkov
bc2b9de6a9 #392: added link to wiki page 2017-06-10 18:07:31 +02:00
Simon Nicolussi
7793f64b77 #386: don't write beyond buffer when reading a signature 2017-06-10 18:04:30 +02:00
Andrey Prygunkov
cad3116b5b #392: Windows crash dump support
Also renamed option "DumpCore" to "CrashDump"; new option "CrashTrace".
2017-06-10 13:13:16 +02:00
Andrey Prygunkov
dd714355c4 nzbget/nzbget#388: updated wiki links to use new url format 2017-06-09 18:41:40 +02:00
Andrey Prygunkov
0a73a0c31f fixed #387: asterix passed as parameter to extension scripts
(Windows only)
2017-06-07 18:51:16 +02:00
Andrey Prygunkov
7f393d050c #362: removed unnecessary code 2017-06-07 17:25:24 +02:00
Andrey Prygunkov
5dcca7294f #371: respect PostStrategy when starting another direct unpack
to avoid running way too many direct unpack jobs at the same time.
2017-06-06 15:32:24 +02:00
Andrey Prygunkov
53da5725c2 #362, #382: articles yEncoded with less than 68 characters
are now correctly processed by direct rename.
2017-05-31 22:00:25 +02:00
Andrey Prygunkov
77cabd7bce #371: do not direct unpack if article decoding is disabled
via option Decode=no.
2017-05-30 21:19:38 +02:00
Andrey Prygunkov
2336d4bcfe #362: do not direct rename if article decoding is disabled
via option Decode=no.
2017-05-30 21:18:54 +02:00
Andrey Prygunkov
580e1974bc #362, #382: fixed crash during direct rename
which may happen if articles were yEncoded with less than 68 character
length.
2017-05-30 21:17:47 +02:00
Andrey Prygunkov
8790ee685f updated link to documentation 2017-05-26 00:36:51 +02:00
Andrey Prygunkov
32f0bbae58 #371: do not terminate unrar if it's not running
when cancelling direct unpack
2017-05-23 21:05:25 +02:00
Andrey Prygunkov
8ffd6c24fe #364: do not reorder files on nzb parse errors 2017-05-23 20:53:11 +02:00
Andrey Prygunkov
98cc4817fe #362, #382: fixed crash during direct rename
which may happen if download errors occurred.
2017-05-23 20:51:23 +02:00
Andrey Prygunkov
14b40d6712 #362: discard unneeded data after direct rename
Now also discarding data when download completes without direct rename
being able to process files (due to download errors).
2017-05-23 19:39:09 +02:00
Andrey Prygunkov
629640898d #362: prevent repeating of error messages
If a file got lost before cache flushing.
2017-05-23 19:21:07 +02:00
Andrey Prygunkov
5df06a2626 #371: wait for direct rename completion before direct unpack 2017-05-22 22:08:09 +02:00
Andrey Prygunkov
4ca95b2989 #371: reset direct unpack status on post-process again 2017-05-22 22:05:52 +02:00
Andrey Prygunkov
b3cc316092 #252: new option to force news servers to ipv4 or ipv6 2017-05-22 22:03:30 +02:00
Andrey Prygunkov
298fde6cd4 #379: compile unrar for Linux with fallocate 2017-05-20 14:05:24 +02:00
Andrey Prygunkov
015b3be461 #371: better cleanup on cancelling unpack
Deleting destination if it contains only hidden files.
2017-05-18 22:00:45 +02:00
Andrey Prygunkov
f6adbe848d #362: avoid unnecessary renaming of par-sets 2017-05-18 20:32:46 +02:00
Andrey Prygunkov
08de827d7b refactor: removed unneeded type casts 2017-05-18 18:43:08 +02:00
Andrey Prygunkov
d1bda91954 refactor: removed one override for FileExists 2017-05-17 21:26:27 +02:00
Andrey Prygunkov
e40e3178da #378: removed debug logging 2017-05-16 19:38:01 +02:00
Andrey Prygunkov
cf3985f228 #378: save nzb download statistics on idle or reload 2017-05-16 19:19:54 +02:00
Andrey Prygunkov
f2329dada4 #362: log statistics for direct rename 2017-05-16 18:57:32 +02:00
Andrey Prygunkov
a73d1ba56e #31: backup icon in history in phone theme 2017-05-12 19:14:02 +02:00
Andrey Prygunkov
8d5ce3ddc3 fixed #376: startup scheduler tasks can be executed again 2017-05-12 19:08:31 +02:00
Andrey Prygunkov
d3362f9280 #371: detect not unpacked archives
Detect not unpacked archives which may appear after rar-rename and
unpack them during post-processing.
2017-05-12 18:37:15 +02:00
Andrey Prygunkov
85dcde36c5 374: unrar tests for Travis CI 2017-05-09 21:46:15 +02:00
Andrey Prygunkov
716bb3130f #374: added gcc 4.8 to Travis CI test matrix 2017-05-09 21:45:32 +02:00
Andrey Prygunkov
725e2c7376 #371: fixed zombies after reload during unpack 2017-05-09 20:36:48 +02:00
Andrey Prygunkov
3a07dd378a #371: restart direct unpack after program reload
When program is reloaded during direct unpack the unpack restarts after
one (any) inner file is downloaded.
2017-05-08 23:27:49 +02:00
Andrey Prygunkov
ff02b53ed0 #371: better handling of cancelling direct unpack 2017-05-06 23:45:31 +02:00
Andrey Prygunkov
39089b6f2f #371: disable direct unpack tests in Travis CI
because it misses unrar 5 required for the tests
2017-05-05 22:49:03 +02:00
Andrey Prygunkov
61af2b3446 #371: integrated direct unpack into pp-workflow 2017-05-05 21:57:52 +02:00
Andrey Prygunkov
20f4f3020b #371: 6b546394b2: corrected stdin redirection 2017-05-05 21:56:54 +02:00
Andrey Prygunkov
df5a1fe959 #371: direct unpack volume handling
Archive parts are now unpacked as they arrive.
2017-05-03 21:42:59 +02:00
Andrey Prygunkov
6b546394b2 #371: added stdin-redirecting support 2017-05-03 21:39:57 +02:00
Andrey Prygunkov
160b274ce8 #371: new module "DirectUnpack"
without implementation and with a new failing unit test.
2017-05-03 21:30:35 +02:00
Andrey Prygunkov
4104a2357b #371: new option "DirectUnpack"
without implementation.
2017-05-03 21:08:21 +02:00
Andrey Prygunkov
ba1e51a8d8 #362: new queue event after direct rename
Queue-event NZB_NAMED, sent after the inner files are renamed
2017-05-01 17:48:20 +02:00
Andrey Prygunkov
9f7d6655b2 #364: logging for file reordering 2017-05-01 17:31:14 +02:00
Andrey Prygunkov
f6a9253a53 #364: fixed compiling error on Linux 2017-04-30 21:41:23 +02:00
Andrey Prygunkov
48020e8901 #361: fixed failing test 2017-04-30 21:36:35 +02:00
Andrey Prygunkov
5afa20d655 #364: better ordering for rar- and par-files 2017-04-30 21:27:51 +02:00
Andrey Prygunkov
197baf066a updated option descriptions in configuration file 2017-04-30 14:39:46 +02:00
Andrey Prygunkov
c873647aae #361: new option "FileNaming"
replace pp-parameter “*naming”.
2017-04-30 14:21:49 +02:00
Andrey Prygunkov
fc1847588d #362: save and restore disk state for direct rename 2017-04-30 00:10:17 +02:00
Andrey Prygunkov
62b3d47b43 #362: redesigned renaming
Spread RenameInfo into FileInfo and NzbInfo to make the state easier to
save on disk
2017-04-28 23:55:41 +02:00
Andrey Prygunkov
ddb9333ca6 #362: discard partially downloaded par2-files
to solve issues with par-checker, par-renamer and avoid saving state
for these files.
2017-04-28 18:36:27 +02:00
Andrey Prygunkov
830e0e4858 #362: proper rename on completion
Fixed: if the file was renamed during finalizing stage (completion) the
file may not be properly renamed.
2017-04-27 17:31:39 +02:00
Andrey Prygunkov
085c612f97 #24: fixed errors when adding many files at once 2017-04-25 21:36:53 +02:00
Andrey Prygunkov
dea9fb2090 #24: double click handler for windows 2017-04-25 19:33:31 +02:00
Andrey Prygunkov
5813c903eb #362: compatibility with gcc 4.8 2017-04-25 19:27:39 +02:00
Andrey Prygunkov
4f499e2c2e fixed #367: fatal-messages when compiling from sources 2017-04-25 19:26:53 +02:00
wtf911
3884328251 #365, #24: option for file association in windows installer
This will add a checkbox so after installation finishes there will be an option to associate .nzb files with NZBGet. It will also remove the association if NZBGet is uninstalled.
2017-04-25 00:40:19 +02:00
Andrey Prygunkov
7e9e2471ef #362: handling of download interruptions
if they happen during downloading of first articles
2017-04-24 23:10:14 +02:00
Andrey Prygunkov
2a433ee7fb #362: fixed inner sorting 2017-04-24 23:09:05 +02:00
Andrey Prygunkov
118f835385 #362: multiple par-sets and renaming par2-files 2017-04-23 20:20:59 +02:00
Andrey Prygunkov
9a6a42bd44 #362: unpause par2-file
as we need it to download first and ExtraPriority-flag doesn’t work on
paused files
2017-04-23 13:48:39 +02:00
Andrey Prygunkov
9434149842 #364: remote command for sorting of inner files
subcommand “SF” of remote command “-E“
2017-04-22 21:12:47 +02:00
Andrey Prygunkov
4107536c03 #364: implemented file reordering
- reordering inner files after adding nzb to queue;
- reordering inner files after adding direct renaming;
- new command “GroupSortFiles” in api-method “editqueue”.
2017-04-22 20:13:45 +02:00
Andrey Prygunkov
2aec782f58 #364: new option "ReorderFiles" 2017-04-22 20:10:00 +02:00
Andrey Prygunkov
eaaa943af3 #362: save new filenames into disk state 2017-04-21 20:33:23 +02:00
Andrey Prygunkov
964e8311a9 #362: renaming files 2017-04-20 21:28:06 +02:00
Andrey Prygunkov
895dd12e4d #362: another functional test for direct renamer 2017-04-19 18:59:09 +02:00
Andrey Prygunkov
d16036aa78 #362: loading hashes from par2-flle 2017-04-19 16:43:58 +02:00
Andrey Prygunkov
8b79a81eaf #362: functional tests for direct renamer
(new tests currently fail)
2017-04-18 19:58:12 +02:00
Andrey Prygunkov
231e94dd2e #362: detecting par2-files 2017-04-18 19:57:35 +02:00
Andrey Prygunkov
f7be22893d #362: computing 16k-hashes for downloaded files 2017-04-17 15:32:32 +02:00
Andrey Prygunkov
3ac91a4bb6 #362: fixed restoring of partial state
when direct write was disabled
2017-04-16 15:01:34 +02:00
Andrey Prygunkov
43441a8d55 #362: downloading first article of each file
, then other articles, when option “DirectRename” is active.
2017-04-16 14:58:54 +02:00
Andrey Prygunkov
b02059f196 #362: new option "DirectRename" 2017-04-16 14:57:10 +02:00
Andrey Prygunkov
f107802f0e #361: pp-param "*naming"
to define naming scheme for downloaded files: “nzb” - use file names
from nzb, “article” - use file names from article metadata (default).
2017-04-15 01:39:40 +02:00
Andrey Prygunkov
7faf1fe64b #361: prefer file names from nzb
to names from article body; to better handle obfuscated posts.
2017-04-14 20:36:00 +02:00
Andrey Prygunkov
bf2fea64e7 #360: removed unnecessary requests to news servers 2017-04-14 14:39:37 +02:00
Andrey Prygunkov
de3eb3de9d #352: parameters for api-method "servervolumes"
as a performance optimization measure to reduce amount of transferred
data.
2017-04-12 19:47:58 +02:00
Andrey Prygunkov
55d080ac96 #50: clear script execution log
before executing script
2017-04-12 19:41:28 +02:00
Andrey Prygunkov
795bacb4fe merge from master v18.1 2017-04-09 13:38:01 +02:00
Andrey Prygunkov
f7930b56a6 updated version string to "18.1" 2017-04-08 17:37:57 +02:00
Andrey Prygunkov
b2bf488d59 fixed #338: "undefined" in reorder extension scripts
When editing option “ScriptOrder” in web-interface.
2017-04-08 17:28:41 +02:00
Andrey Prygunkov
8e4b75b21e fixed #347: possible crash at the end of post-processing 2017-04-08 17:27:51 +02:00
Andrey Prygunkov
5b17bebbd6 fixed #348: queue was not saved after deleting of queued post-jobs 2017-04-08 17:27:22 +02:00
Andrey Prygunkov
bda1eaa192 fixed #350: sleep mode no longer works on Windows 2017-04-08 17:26:15 +02:00
Andrey Prygunkov
4bcdbbeb09 fixed #356: crash during download caused by a race condition 2017-04-08 17:25:29 +02:00
Andrey Prygunkov
f0ee12f92e #352: option "ServerX.Notes"
for user comments on news servers.
2017-04-07 20:43:06 +02:00
Andrey Prygunkov
0b14a2f869 #352: showing volume stats from settings page
New button “Volume Statistics” in section “News Servers” of settings
page. Shows the same volume data as in global statistics dialog.
2017-04-07 20:40:46 +02:00
Andrey Prygunkov
2579ce65b9 fixed #356: crash during download caused by a race condition 2017-03-31 17:46:02 +02:00
Andrey Prygunkov
77f86988cb #353: support for ECC certificates in built-in web-server 2017-03-21 23:29:48 +01:00
Andrey Prygunkov
b9b62dcd75 #353: better error reporting for TLS error in built-in web-server 2017-03-21 23:29:07 +01:00
Andrey Prygunkov
bfee7c55cd #50: confirmation for commands marked as dangerous 2017-03-21 23:22:54 +01:00
Andrey Prygunkov
34efa87699 #50: "send test e-mail" command in EMail.py 2017-03-20 21:03:40 +01:00
Andrey Prygunkov
e839db7107 #50: pass not yet saved options to script 2017-03-20 20:59:34 +01:00
Andrey Prygunkov
ffb16aa7bb #50: progress dialog for script execution log
New API-method “logscript”.
2017-03-20 20:30:19 +01:00
Andrey Prygunkov
a3b0b7675e #50: execute custom commands for scripts
New API-method “startscript”.
2017-03-17 20:24:14 +01:00
Andrey Prygunkov
602f62c17c #50: define custom buttons in extension scripts 2017-03-17 19:23:56 +01:00
Andrey Prygunkov
ace7a1968d #349: added host name to all error messages 2017-03-15 20:36:45 +01:00
Andrey Prygunkov
6efefe3780 fixed #350: sleep mode no longer works on Windows 2017-03-15 20:13:55 +01:00
Andrey Prygunkov
c02f708d74 #346: save changes before performing actions in history dialog 2017-03-13 19:53:54 +01:00
Andrey Prygunkov
e7c47f523a fixed #348: queue was not saved after deleting of queued post-jobs 2017-03-13 18:04:44 +01:00
Andrey Prygunkov
5c8be152f4 fixed #347: possible crash at the end of post-processing 2017-03-13 17:58:15 +01:00
Andrey Prygunkov
4cdbfc84dd #346: changing category via drop-down changes pp-parameters
accordingly to the new category.
2017-03-12 20:09:50 +01:00
Andrey Prygunkov
75ec856af3 #346: improved hover effect
Increased clickable area and made the carets (triangles) clickable too.
2017-03-11 23:21:50 +01:00
Andrey Prygunkov
88b5e16597 #346: multi-edit via drop-downs
If multiple items are selected and drop-down is shown for one of them
the menus work on all selected items.
2017-03-09 20:55:04 +01:00
Andrey Prygunkov
fff6fdd4eb #346: unified hover feedback for all drop-downs 2017-03-09 20:44:57 +01:00
Andrey Prygunkov
c4ff544459 #346: drop-down for status in downloads and history 2017-03-08 20:16:17 +01:00
Andrey Prygunkov
9c87942b43 #346: drop-down for category in history 2017-03-08 20:12:38 +01:00
Andrey Prygunkov
0e4c9275b0 #346: drop-down for category in downloads 2017-03-08 20:10:23 +01:00
Andrey Prygunkov
f11dce4269 #346: drop-down for priority in downloads 2017-03-08 20:07:25 +01:00
Andrey Prygunkov
f1340eb542 #263: improvements in RSS feed view in phone mode 2017-03-04 18:17:14 +01:00
Andrey Prygunkov
e89f5cbd8a #31: backup-badge for items in history
if backup news servers were used; that’s similar to backup-badge on
downloads tab.
2017-02-26 21:35:09 +01:00
Andrey Prygunkov
975016700e #23: proper exit code on client command success or failure 2017-02-26 20:40:55 +01:00
Andrey Prygunkov
a9017be606 #123: set name, password and dupe info when adding via URL
by click on a button near URL field in web-interface
2017-02-26 19:23:33 +01:00
Andrey Prygunkov
d81d6831dc #331: support for HTTP-header "X-Forwarded-For" in IP-logging 2017-02-26 12:40:37 +01:00
Andrey Prygunkov
1b94373c7e fixed #338: "undefined" in reorder extension scripts
When editing option “ScriptOrder” in web-interface.
2017-02-24 16:27:05 +01:00
Andrey Prygunkov
d5e881ce1a #330: optimisations for Safari 2017-02-24 14:55:05 +01:00
Andrey Prygunkov
a3f84aca0e #330: better session handling in form login 2017-02-23 20:49:50 +01:00
Andrey Prygunkov
0ab86b90f0 #330: authentication via form in web-interface
, new option “FormAuth”.
2017-02-22 17:41:25 +01:00
Andrey Prygunkov
b6a606db35 #339: extended error messages with a link to wiki-page 2017-02-20 18:24:30 +01:00
Andrey Prygunkov
578731f239 #339: integrated root certificate store file into Linux installer 2017-02-19 18:14:04 +01:00
Andrey Prygunkov
71db4ffe9c #192: adjusted linux build script 2017-02-19 18:12:23 +01:00
Andrey Prygunkov
7421ec7b1a #339: added missing "define" for Windows build 2017-02-19 15:29:14 +01:00
Andrey Prygunkov
958712663e fixed compile error under gcc 4.8 2017-02-18 20:14:48 +01:00
Andrey Prygunkov
d96fa66487 #339: 36ac548842: fixed compile error on gcc and clang 2017-02-18 20:12:53 +01:00
Andrey Prygunkov
36ac548842 #339: TLS certificate verification with GnuTLS 2017-02-18 19:47:20 +01:00
Andrey Prygunkov
fc44ab6128 #339: simplified verification code 2017-02-17 23:44:15 +01:00
Andrey Prygunkov
f0da3936e5 #339: prevent compilation failure on older OpenSSL versions 2017-02-17 13:22:07 +01:00
Andrey Prygunkov
04e694799d #339: TLS certificate verification with OpenSSL 2017-02-17 12:32:20 +01:00
Andrey Prygunkov
712cedb84f #339: new options "CertStore" and "CertCheck" 2017-02-17 11:27:49 +01:00
Andrey Prygunkov
4fb3ddcf84 updated version string to "19.0-testing" 2017-02-17 11:03:57 +01:00
Andrey Prygunkov
69d40c11fd Merge branch 'develop' for v18.0 2017-02-12 17:20:07 +01:00
Andrey Prygunkov
58893710d8 updated version string to "18.0" 2017-02-12 17:05:21 +01:00
Andrey Prygunkov
6c6f781510 updated ChangeLog for v18.0 2017-02-12 17:03:04 +01:00
Andrey Prygunkov
569ec22ee8 updated copyright string in Windows and OS X apps 2017-02-12 17:02:56 +01:00
Andrey Prygunkov
b06d3eca86 #327: printing only base file name in warnings 2017-02-12 14:35:44 +01:00
Andrey Prygunkov
dcf63b4db7 #277: allow control of what tab is shown when opening web-interface
Add “#downloads”, “#history”, “#messages” or “#settings” to the URL,
for example “http://localhost:6789/#history” or
“http://localhost:6789/index.html#history”
2017-01-27 20:10:57 +01:00
Andrey Prygunkov
06e7573572 #329: improved selecting of par2-file for repair 2017-01-19 21:48:35 +01:00
Andrey Prygunkov
639e0b6bfb #327: reverted non-strict par2-filename matching
To handle article subjects with non-parseable filenames. Also created a
functional test for this case.
2017-01-17 22:18:34 +01:00
Andrey Prygunkov
f2073ff920 #327: better handing of damaged par2-files in par-renamer
If par-renamer can’t load par2-file another par2-file is downloaded and
par-renamer tries again.
2017-01-17 00:23:29 +01:00
Sander
4ba2e07d94 #328: NServ: option -b to specify on which address to listen 2017-01-08 19:37:43 +01:00
Sander
91515b20e5 #324, #325: Nserv: Check if specified data-dir is an existing directory 2017-01-06 21:11:42 +01:00
Andrey Prygunkov
7226e1c186 #319: fixed automatic changing of pp-scripts when changing category in web-interface 2016-12-30 22:29:14 +01:00
Andrey Prygunkov
98c2dd46b7 #291: fixed "PostTotalTimeSec" in API-method "listgroups" 2016-12-27 17:21:06 +01:00
Andrey Prygunkov
f0b08dbd63 #315: reset feed search box 2016-12-22 20:24:00 +01:00
Sander
25ddfd5659 #320, #321: newlines added to 281 and 205 server answers 2016-12-21 19:46:55 +01:00
Andrey Prygunkov
7450b97871 #319: scheduler scripts in option "Extensions"
Scheduler scripts can now be selected in option “Extensions” if the
scripts provide default time definition.
2016-12-20 22:22:09 +01:00
Andrey Prygunkov
9f7e0ee972 #304: safer termination of scripts 2016-12-19 19:33:38 +01:00
Andrey Prygunkov
ae7719e948 #319: auto migration of old script settings 2016-12-16 21:03:59 +01:00
Andrey Prygunkov
9a92896678 #319: unified extension scripts settings
- new option “Extension” as replacement for options “PostScript”,
“QueueScript”, “ScanScript”, “FeedScript”;
- renamed “CategoryX.PostScript” to “CategoryX.Extensions”;
- renamed “FeedX.PostScript” to “FeedX.Extensions”.
2016-12-14 20:34:14 +01:00
Andrey Prygunkov
ab0723adda #288: rar-rename only if unpack is enabled 2016-12-14 18:50:14 +01:00
Andrey Prygunkov
13b98fca83 fixed incompatibility with macOS El Capitan and XCode 7/8 2016-12-13 21:52:32 +01:00
Andrey Prygunkov
5901998cb5 #242: fixed enter-key in feed filter dialog 2016-12-10 20:13:19 +01:00
Andrey Prygunkov
9ddd73368e #282: 0d67e322a3: fixed confirmation dialog 2016-12-07 21:02:55 +01:00
Andrey Prygunkov
12daa683e5 #313: 5f71c4a5a7: fixed test failure on Linux 2016-12-06 19:18:06 +01:00
Andrey Prygunkov
5f71c4a5a7 #313: better handling of renamed par-files 2016-12-06 18:49:12 +01:00
Andrey Prygunkov
13db817395 #313: better handling of obfuscated par-files in par-renamer
Avoid false detection of missing files.
2016-12-04 19:04:39 +01:00
Andrey Prygunkov
84f4cf2f33 #312: extended description of option "KeepHistory" 2016-12-03 12:54:49 +01:00
Andrey Prygunkov
0d67e322a3 #282: extra warning when deleting from history
An extra warning is shown when deleting 50 or more records from history
and there are selected records on other pages.
2016-11-29 18:56:00 +01:00
Andrey Prygunkov
374f706354 #282: visual indication of records selected on other pages 2016-11-22 21:36:20 +01:00
Andrey Prygunkov
f7cefadf33 use default SDK when building mac app 2016-11-20 19:31:06 +01:00
Andrey Prygunkov
c111a114b9 #242: keyboard support in web-interface 2016-11-20 14:24:27 +01:00
Andrey Prygunkov
2cd3f0fc68 #309: allow sending e-mails to multiple recipients
in the example pp-script “EMail.py”.
2016-11-20 13:12:50 +01:00
Andrey Prygunkov
92db59116e #301: showing drag grip
and extended the drag grip area to the full cell containing check box.
2016-11-18 19:59:33 +01:00
Andrey Prygunkov
760aed68fb #288: fixed incorrect renaming
of archives containing directory entries
2016-11-16 21:41:34 +01:00
Andrey Prygunkov
e612257c28 #304: graceful termination of scheduler scripts
If a script is running when the program must shutdown, the script
receives signal SIGINT (CTRL+BREAK on Windows) and has 10 seconds to
gracefully terminate until it is killed.
2016-11-15 19:22:52 +01:00
Andrey Prygunkov
31208a5816 #304: scheduler tasks can be started at program launch
Using asterisk as TaskX.Time.
2016-11-15 19:00:49 +01:00
Andrey Prygunkov
a03e7cd550 #295: disabled SSLv3 in built-in web-server 2016-11-15 18:47:30 +01:00
Andrey Prygunkov
02b33fb559 #306: splitted options "Retries" and "RetryInterval"
into four options: ArticleRetries, ArticleInterval, UrlRetries, and
UrlInterval.
2016-11-13 13:05:37 +01:00
Andrey Prygunkov
a9ed53daa8 fixed #303: rar tests fail with TLS disabled 2016-11-12 18:37:48 +01:00
Andrey Prygunkov
a3c460ed40 #301: showing row content while dragging
Implemented alternative UI for drag and drop.
2016-11-12 14:23:19 +01:00
Andrey Prygunkov
df11d1acb4 #301: functional tests for api-method "editqueue"
And a small fix in edit-commands GroupMoveAfter and GroupMoveBefore.
2016-11-08 21:02:36 +01:00
Andrey Prygunkov
e49d4c59af #301: auto scrolling during dragging 2016-11-06 21:16:19 +01:00
Andrey Prygunkov
5dc9c07a58 #301: moved drag-n-drop code into module fasttable.js
Ready for reuse on other places.
2016-11-04 22:02:26 +01:00
Andrey Prygunkov
3bb0751c86 #301: drag-n-drop for touch screens
; also no more dragging via priority-icon but only via check mark.
2016-11-03 19:20:03 +01:00
Andrey Prygunkov
a3b3921bea #301: API extension for drag-n-drop support
New actions “GroupMoveBefore” and “GroupMoveAfter” in API-method
“editqueue”; parameter “args” contains id of the target item to move
before/after.
2016-11-01 12:27:42 +01:00
Andrey Prygunkov
b19d26aee8 #301: drag and drop handling in web-interface 2016-11-01 12:23:33 +01:00
Andrey Prygunkov
7fae337360 fixed #300: sorting of selected items may give wrong results 2016-10-31 19:05:15 +01:00
Andrey Prygunkov
80debf521a #299: removed parameter "offset" from api-method "editqueue"
When needed the “offset” is now passed within parameter “Args” as
string.
2016-10-31 15:58:02 +01:00
Andrey Prygunkov
528133482e #298: added compatibility with openssl 1.1.0 2016-10-31 10:11:23 +01:00
Andrey Prygunkov
a98bbd7d0d #279: cache support in built-in nntp server 2016-10-29 16:55:12 +02:00
Andrey Prygunkov
2681fe187b #288: sooner detection of damaged rar-files 2016-10-22 15:05:19 +02:00
Andrey Prygunkov
e16cab67fb #288: ed4761db37: reverted std-input handling on Windows 2016-10-22 15:04:11 +02:00
Andrey Prygunkov
50e2e0cf37 #291: refactor: handling of shutdown/reload 2016-10-18 21:53:48 +02:00
Andrey Prygunkov
d2d20f29c1 #291: better handling of shutdown/reload
for post-processing jobs
2016-10-18 21:52:30 +02:00
Andrey Prygunkov
53f4992eeb #279: corrected generation of test files 2016-10-18 21:51:22 +02:00
Andrey Prygunkov
3ae1be1ca4 #294: handling of incomplete obfuscated rar-archives
and special file extensions; new option “UnpackIgnoreExt” honored by
rar-renamer and unpacker.
2016-10-16 13:21:58 +02:00
Andrey Prygunkov
ce1e1b61e7 #291: fixed status in api-method "listgroups" 2016-10-15 16:12:48 +02:00
Andrey Prygunkov
cac25ed290 #291: distinguishing pp-stages par-renaming and rar-renaming
to better handling temporary pause.
2016-10-15 13:37:34 +02:00
Andrey Prygunkov
8ed0b70df5 #291: eacfacc5a2: fixed compilation error on Windows 2016-10-15 13:11:54 +02:00
Andrey Prygunkov
d72071c8ed #291: temporary pause during post-processing
Options ParPauseQueue, UnpackPauseQueue, PostPauseQueue work properly.
2016-10-15 13:00:52 +02:00
Andrey Prygunkov
ba05dfc202 #291: tweaking balanced pp-startegy
Other job can be run only during repair stage but not during
verification stage.
2016-10-14 11:20:57 +02:00
Andrey Prygunkov
f3adab5690 #293: split section "Download Queue"
Many options moved into new section “Connection”.
2016-10-14 00:19:14 +02:00
Andrey Prygunkov
b1b5405809 #291: new option "PostStrategy" to configure multi-post-processing
instead of option “ParExclusive”.
2016-10-14 00:07:13 +02:00
Andrey Prygunkov
99fcd164ac #291: smoother transition between pp-stages
without intermediate “pp-queued” state shown in web-interface
2016-10-13 00:07:44 +02:00
Andrey Prygunkov
eacfacc5a2 #291: non-global cout/cerr in par2-module
Fixed global cout/cerr in par2-module to prevent crashes when executing
multiple par2-instances.
2016-10-12 16:00:08 +02:00
Andrey Prygunkov
3404b544de #291: removed not used unit of par2-module 2016-10-12 15:58:08 +02:00
Andrey Prygunkov
3604534850 #291: functional tests for multi-processing 2016-10-12 00:30:23 +02:00
Andrey Prygunkov
2e18021e08 #291: new option "ParExclusive"
to configure simultaneous post-processing of a second item if currently
performing par-check/repair for the first item in pp-queue
2016-10-12 00:22:34 +02:00
Andrey Prygunkov
2e19382ccc #291: multi post-processing
During par-check/repair another pp-item can be post-processed at the
same time, as long as it doesn’t require par-check/repair.
2016-10-12 00:13:50 +02:00
Andrey Prygunkov
a6dc20dc8e #291: refactor: made par-checker non-global
and more similar to other post-processing stages.
2016-10-11 20:43:11 +02:00
Andrey Prygunkov
a1bef9146e #286: corrected html in priority column 2016-10-08 15:33:04 +02:00
Andrey Prygunkov
61f18f81b7 #29, #278: small UI tweak 2016-10-08 15:32:26 +02:00
Andrey Prygunkov
129125faa1 #286: priority column and sorting
Also removed horizontal lines in tables, for better visuals.
2016-10-07 23:46:21 +02:00
Andrey Prygunkov
b97c987f66 #274: fixed error on history page
Fixed javascript error on history page when showing hidden items.
2016-10-04 22:11:32 +02:00
Andrey Prygunkov
53e504d974 fixed failing functional tests 2016-10-03 23:39:34 +02:00
Andrey Prygunkov
5885258c35 #288: reading encrypted archives using nettle library
when compiled with GnuTLS instead of OpenSSL.
2016-10-03 22:38:48 +02:00
Andrey Prygunkov
2034ea97d2 #288: fixed compilation error with GnuTLS
and added unit test for rar3 encrypted files
2016-10-03 15:05:29 +02:00
Andrey Prygunkov
2cc38a85df #288: fixed incompatibility with older compilers 2016-10-03 01:35:46 +02:00
Andrey Prygunkov
ccd509b70c #288: reading rar3 encrypted archives
Rar-renamer now supports renaming of rar3 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-10-02 23:10:31 +02:00
Andrey Prygunkov
bab43d8d30 #288: reading rar5 encrypted archives
Rar-renamer now supports renaming of rar5 archives with encrypted
headers (encrypted filenames). Only when compiled with OpenSSL as TLS
library.
2016-09-30 21:39:35 +02:00
Andrey Prygunkov
ed4761db37 #288: unit and functional tests for encrypted rar files 2016-09-30 19:32:55 +02:00
Andrey Prygunkov
db7bc4314f #288: rar-rename as a separate post-processing stage
Made rar-rename a separate post-processing stage which runs after
par-repair, in order to restore archive file names after possible
repair stage (which could rename files back to obfuscated names if
rar-rename were run before par-repair).
2016-09-28 22:22:18 +02:00
Andrey Prygunkov
8a21626bf6 #288: sophisticated renaming for rar-files 2016-09-27 23:34:42 +02:00
Andrey Prygunkov
804f8ab085 #288: integrated rar-renamer into post-processing
also implemented initial simple renaming for rar-files
2016-09-26 21:55:41 +02:00
Andrey Prygunkov
3593990dd6 #288: new module "Rename" to handle par-rename and rar-rename 2016-09-26 21:16:27 +02:00
Andrey Prygunkov
3a38a2c7c6 #288: refactor: moved parts of module "RarRenamer" into new module "RarReader"
Also added new unit test for module “RarReader”.
2016-09-26 21:01:15 +02:00
Andrey Prygunkov
16aef2e7a8 #288: functional tests for par-renamer and rar-renamer
Rar-renamer tests are currently failing as the feature isn’t completed
yet.
2016-09-25 21:08:45 +02:00
Andrey Prygunkov
bf992195e8 #288: fixed unit test for rar-renamer 2016-09-25 21:07:23 +02:00
Andrey Prygunkov
2fb0fd1113 #288: reading rar5-files 2016-09-24 22:43:20 +02:00
Andrey Prygunkov
709c9856c9 #288: reading rar3-files 2016-09-24 22:39:45 +02:00
Andrey Prygunkov
9e9ef3120f #288: added module "RarRenamer" with unit tests
The tests are failing at the moment as the module is not fully
implemented yet.
2016-09-24 18:37:02 +02:00
guaguasi
a7525ae6f9 #274, #285: password-badge for nzbs with passwords 2016-09-23 21:28:12 +02:00
Andrey Prygunkov
5e0e91f671 #249: do not cleanup if unpack disabled
If parameter "unpack" is disabled for an nzb-file the cleanup isn't
performed for it.
2016-09-19 23:40:00 +02:00
Andrey Prygunkov
09f863f3af #275: nZEDb attributes in rss feeds
Added support for nZEDb attributes in rss feeds.
2016-09-19 23:37:51 +02:00
Andrey Prygunkov
87e8d479d8 #281: improved error reporting on feed parse errors 2016-09-19 22:22:29 +02:00
Andrey Prygunkov
fb8c6b9cd3 #279: size of test files
can now be adjusted via pytest.ini; reduced default sizes to speed up
tests.
2016-09-19 22:04:49 +02:00
Andrey Prygunkov
bc98b0582c #279: corrected nntp error reporting 2016-09-19 21:44:38 +02:00
guaguasi
84388785e7 #276 , #280: highlight selected rows with alternative colors 2016-09-19 06:43:07 +02:00
Andrey Prygunkov
4c519cf646 #279: fixed compilation error on Windows 2016-09-17 23:45:03 +02:00
Andrey Prygunkov
f28c049c12 fixed #284: API-method "append" and dupe check
method “append” returns nzb-id also for items added straight to history.
2016-09-17 20:38:57 +02:00
Andrey Prygunkov
33dbbcb0b5 Merge branch '279-nserv' into develop 2016-09-17 13:49:45 +02:00
Andrey Prygunkov
7f6cbd137f #279: updated .gitignore 2016-09-17 13:49:09 +02:00
Andrey Prygunkov
facd8cd41f #279: fixed file permissions 2016-09-17 13:37:56 +02:00
guaguasi
e269d8f062 #29, #278: additional options in "custom pause dialog" 2016-09-16 06:40:55 +02:00
Andrey Prygunkov
dd141e4cf5 #279: initial collection of functional tests 2016-09-13 22:05:10 +02:00
Andrey Prygunkov
f18ee92a23 #279: built-in nntp server for functional testing
- implemented nserv - built-in simple nntp server to be used for
functional testing;
- nserv is part of nzbget executable and can be easily used on all
platforms;
- to start nzbget in nserv mode use command “nzbget --nserv”.
2016-09-13 21:31:20 +02:00
Andrey Prygunkov
b25a88683b #271: do not close files on daemonizing 2016-09-09 20:12:10 +02:00
guaguasi
9dc2b8c71b #49, #260: fields containing passwords are now password fields
- fields containing passwords are now password fields with option to view;
- excluding post-processing/archive password.
2016-09-09 19:43:46 +02:00
Andrey Prygunkov
2ef393531a updated version string to 18.0-testing 2016-09-09 19:45:46 +02:00
Andrey Prygunkov
0c1fce27a2 update version string to "17.1" 2016-09-05 19:36:02 +02:00
Andrey Prygunkov
606cf56150 Merge branch 'develop' for v17.1 2016-09-05 19:19:34 +02:00
Andrey Prygunkov
af8451e16e updated ChangeLog for v17.1 2016-09-05 19:05:52 +02:00
Andrey Prygunkov
203a828bbd #224: corrected text in history delete confirmation dialog 2016-08-28 15:00:48 +02:00
Andrey Prygunkov
a990078884 fixed #265: compilation error if configured without TLS 2016-08-21 22:44:48 +02:00
Andrey Prygunkov
3e5bd203f3 fixed #86: javascript error on Chrome for Linux
Renamed “Notification” to “PopupNotification” to avoid conflict with
browsers built-in interface with the same name.
2016-08-21 22:34:42 +02:00
Andrey Prygunkov
042979d122 #245: removed debug logging 2016-08-16 20:43:13 +02:00
Andrey Prygunkov
11c464ed46 fixed #262: crash on malformed articles 2016-08-12 23:45:57 +02:00
Andrey Prygunkov
070054a814 fixed #261: hanging after marking as BAD from queue script 2016-08-12 22:28:37 +02:00
Andrey Prygunkov
5841cf5002 updated version string to 17.1-testing 2016-08-09 00:14:31 +02:00
Andrey Prygunkov
6dda360986 #253: reset stats if file on disk is missing on retry
Fixed: if by retrying download an included file is missing on disk it
was downloaded again but its stats (remaining size, etc.) were not
correctly reset.
2016-08-09 00:01:55 +02:00
Andrey Prygunkov
12ff14b397 #253: park active file too when deleting
If by deleting download only one file remains and it is partially
downloaded it should be counted as remaining (parked) file for command
“Download remaining files” be available (RemaingFileCount must be > 0).
2016-08-08 23:52:58 +02:00
Andrey Prygunkov
20aa83e0f7 #253: new field "RetryData" in RPC-method "history"
to more carefully indicate if “Retry failed articles” is possible.
2016-08-08 23:42:18 +02:00
Andrey Prygunkov
4c8c7ef46b #253: do not park when deleting without keeping files 2016-08-08 22:57:29 +02:00
Andrey Prygunkov
43a6717394 #253: pause-state when unparking par-files
if option ParCheck=Force then all par-files must be unpaused, otherwise
one par-file must be unpaused.
2016-08-06 22:09:08 +02:00
Andrey Prygunkov
b4fc57977b #253: do not park if all articles failed 2016-08-06 21:28:07 +02:00
Andrey Prygunkov
4f9dbdadc3 #253: fixed: not parking if all articles were successful 2016-08-06 21:16:13 +02:00
Chris
418acb052f #256: compatibility with gcc 4.8 2016-08-05 07:51:10 +02:00
Gokturk Yuksek
c741297d84 #255: do not compile par-tests if parcheck is disabled 2016-08-04 22:38:02 +02:00
Andrey Prygunkov
55fb4df411 fixed #254: installing multiple versions on Windows 2016-08-03 20:37:35 +02:00
Andrey Prygunkov
35643b0207 fixed #253: delete-with-parking sometimes didn't park 2016-08-03 19:55:07 +02:00
Andrey Prygunkov
45753410df fixed #251: performance issue on certain Windows systems 2016-08-02 20:03:41 +02:00
Andrey Prygunkov
b58de541b3 #162: corrected option description 2016-07-30 12:35:19 +02:00
Andrey Prygunkov
3d577777bb #136: improved error reporting on certain file operations 2016-07-29 16:45:15 +02:00
Andrey Prygunkov
a0e9c537a3 fixed #247: root drive paths on Windows
.. could not be used (for example “NzbDir=N:\”).
2016-07-29 16:22:11 +02:00
Andrey Prygunkov
a2d87de7b9 updated version string to 18.0-testing 2016-07-29 16:18:54 +02:00
Andrey Prygunkov
032a7a4770 update version string to "17.0" 2016-07-27 14:23:43 +02:00
Andrey Prygunkov
b2dcc59845 Merge branch 'develop' for v17.0 2016-07-27 13:40:50 +02:00
Andrey Prygunkov
2ad86ee738 updated ChangeLog for v17.0 2016-07-26 21:08:45 +02:00
Andrey Prygunkov
dcf0318bea fixed #245: activity animation gets re-activated 2016-07-24 18:22:43 +02:00
Andrey Prygunkov
9973ec5f6c fixed #237: prevent errors when pressing ENTER
in web-interface.
2016-07-10 13:53:58 +02:00
Andrey Prygunkov
2b43695a01 #231: path to original nzb-file passed to scripts
Passed to queue-scripts in env. var “NZBNA_QUEUEDFILE” and to
post-processing-scripts as “NZBPP_QUEUEDFILE”.
2016-07-07 21:14:40 +02:00
Nathan Lee
19dd39d5e6 #236: fixed typo in webui
On the "Loading configuration failed" message. automatically was spelt incorrectly.
2016-07-03 21:03:27 +02:00
Andrey Prygunkov
e5eddbe7ce #223: SNI support with GnuTLS 2016-07-03 17:07:03 +02:00
Andrey Prygunkov
0f279aaf6e #223: SNI support with OpenSSL 2016-07-03 17:06:43 +02:00
Andrey Prygunkov
62cd5c776c #227: fixed check for reserved characters in file names (Windows) 2016-07-03 12:47:53 +02:00
Andrey Prygunkov
8c8f41ac33 #230: moved option "FeedX.Interval" upwards 2016-07-03 12:44:38 +02:00
Andrey Prygunkov
51880a60e9 fixed #235: very long log messages were not read properly from disk 2016-07-02 21:01:36 +02:00
Andrey Prygunkov
7dffe6f502 #210, #234: corrected file name cleanup 2016-06-28 21:00:43 +02:00
Andrey Prygunkov
f591e1680d #225: corrected event cleanup
events “NZB_MARKED” could be ignored when they shouldn’t
2016-06-02 19:40:05 +02:00
Andrey Prygunkov
0e0d7be16b #225: new queue script event NZB_MARKED
; new env var NZBNA_MARKSTATUS passed to queue scripts.
2016-05-30 23:46:37 +02:00
Andrey Prygunkov
8ad12646fb Merge branch '224-delete-files' into develop 2016-05-30 20:49:49 +02:00
Andrey Prygunkov
92ae10e338 #148: fixed crash in queue script handling
If multiple queue scripts were queued and the nzb-file was deleted
before all scripts were executed the program may crash.
2016-05-29 16:02:40 +02:00
Andrey Prygunkov
b2b3a2e281 #224: fixed: delete without history tracking did not work 2016-05-29 14:55:44 +02:00
Andrey Prygunkov
355e7a5ab1 #224: options to delete already downloaded files 2016-05-29 12:28:00 +02:00
Andrey Prygunkov
6f7be71a93 #205: fixed crash when "Download Again" URL 2016-05-28 20:06:45 +02:00
Andrey Prygunkov
4d5a4bb428 #206: fixed: string settings could not be saved empty 2016-05-26 19:36:04 +02:00
Andrey Prygunkov
717db2486a #206: fixed default settings were not properly set
All boolean settings were set to “off”.
2016-05-26 19:33:23 +02:00
JackDandy
43d450b8ae #222: the switch to disable animation on play/pause button didn't work 2016-05-25 23:56:26 +02:00
Travis La Marr
4501d129c2 #218: updated config description text 2016-05-21 20:45:35 +02:00
Andrey Prygunkov
d14dc599d9 #66: improved tray icon (Windows)
… to look better on a dark background.
2016-05-17 18:39:57 +02:00
Andrey Prygunkov
18d89435e4 #205: fixed: nzb-log was deleted
… just before adding to history
2016-05-16 12:41:57 +02:00
Andrey Prygunkov
a1e945661b #205: improved handling of queue cleanup
Also removed option “ParCleanupQueue” as it is not needed anymore with
reworked state handling.
2016-05-15 19:42:54 +02:00
Andrey Prygunkov
150857816d #197: various status info in browser window title
New option “WindowTitle” under web-interface settings.
2016-05-14 23:56:04 +02:00
Andrey Prygunkov
2190eec25a #121: do not delete completed state files 2016-05-14 13:00:32 +02:00
Andrey Prygunkov
b6dc2b6be9 Merge branch '205-retry-failed' into develop 2016-05-14 12:03:20 +02:00
Andrey Prygunkov
1809fa9228 #205: fixed state reset for completely failed files 2016-05-13 23:52:47 +02:00
Andrey Prygunkov
864fb92bc7 #205: new option "HistoryCleanupDisk"
… extracted from “DeleteCleanupDisk”.
2016-05-12 23:34:08 +02:00
Andrey Prygunkov
43f25587d2 #205: improved file state handling
Better handling of all cases: manual delete, health delete, retry
failed, download remaining with ContinuePartial=on/off and
DirectWrite=on/off.
2016-05-12 23:22:06 +02:00
Andrey Prygunkov
f08d3918dc #205: retry for deleted downloads too
Commands “Retry failed articles” and “Download remaining files“ now
work for deleted downloads too (including deleted by health check).
2016-05-09 19:17:17 +02:00
Will
89143bc19f #217: corrected option descriptions 2016-05-08 19:04:26 +02:00
Luis Flores III
7c2cac135d #212: corrected option descriptions 2016-05-04 20:05:27 +02:00
Andrey Prygunkov
11fc72e763 #206: 5376abc717: fixed edit-buttons in web-interface 2016-05-02 22:38:13 +02:00
Andrey Prygunkov
8c4d8cef1a #121: queue directory cleanup on program start
Automatically removing orphaned diskstate files from QueueDir:
- file info state;
- file progress state;
- file completion state;
- nzb-log.
2016-04-28 22:09:56 +02:00
Andrey Prygunkov
788d8d1f42 #210: validate and adjust file names after unpack
… and when moving from intermediate to final directory.
2016-04-26 23:56:50 +02:00
Andrey Prygunkov
a59b29c731 #210: auto adjusting file names with reserved words
… by prepending an underscore; with special support in par2-module.
2016-04-26 23:24:40 +02:00
Andrey Prygunkov
65398ac55f #210: support file names with reserved words on Windows
… using extra long path notation.
2016-04-26 19:47:50 +02:00
Andrey Prygunkov
8c3e70b1de #209: replace invalid characters in file names
- replace invalid characters in file names which were read from article
data;
- make necessary changes in par2-module to ensure par-repair works
correctly with changed file names;
- make necessary changes in par-renamer.
2016-04-25 19:11:14 +02:00
Andrey Prygunkov
8de5461759 #207: separate disk state files for queue and history 2016-04-23 21:33:06 +02:00
Andrey Prygunkov
cb20dfb547 #205: corrected file time info for retried files 2016-04-23 21:13:33 +02:00
Andrey Prygunkov
b82cc06789 #205: retry failed articles
- new action in history dialog: retry failed articles;
- new command “HistoryRetryFailed” of RPC-method “editqueue”;
- new subcommand “F” of command line switch “-E/--edit” for history.
2016-04-21 22:14:12 +02:00
Andrey Prygunkov
5376abc717 #206: UI for hidden web-interface settings 2016-04-21 21:37:58 +02:00
Andrey Prygunkov
6826410c6a #136: fixed crash on feed fetch failure (Windows)
Crash caused by nullptr passed to “FileSystem::DeleteFile”.
2016-04-18 20:41:09 +02:00
Andrey Prygunkov
85340831cc #203: exit code for feed scripts 2016-04-18 19:49:35 +02:00
Andrey Prygunkov
dd4df8b734 #99: separate quota reached flag
- using separate “quota reached”-flag instead of changing global pause
state;
 - new field “QuotaReached” in RPC-method “Status”.
2016-04-15 20:47:04 +02:00
Andrey Prygunkov
a4a3d1bc7a #99: indication of reached quota in web-interface
- remaining time is shown in orange when the quota is reached;
 - dialog “statistics and status” may show extra row “Download quota:
reached”.
2016-04-13 22:22:01 +02:00
Andrey Prygunkov
4f29804602 #136: fixed possible crash when canceling download
and having option “DirectWrite” disabled.
2016-04-12 19:03:47 +02:00
Andrey Prygunkov
a97cb4c61e #99: download volume quota
- new options “MonthlyQuota”, “QuotaStartDay”, “DailyQuota”;
- downloading is paused when quota is reached;
- new fields in RPC-method “status”: MonthSizeLo, MonthSizeHi,
MonthSizeMB, DaySizeLo, DaySizeHi, DaySizeMB. MonthSizes are related to
current billing month taking option “QuotaStartDay” into account;
- download volume for “this month” shown in web-interface in statistics
dialog shows data for current billing month (taking option
“QuotaStartDay” into account);
- scheduler task “UnpauseDownload” is ignored if the download was
paused due to quota.
2016-04-12 18:52:09 +02:00
Andrey Prygunkov
680171bd88 fixed #201: splitted files were not always joined 2016-04-12 18:30:25 +02:00
Andrey Prygunkov
e3c976406d #145: install included scripts into default MainDir on Windows
… instead of program’s directory.
2016-04-10 11:50:03 +02:00
Andrey Prygunkov
17dda0b0fc #155: unit tests for ServerPool 2016-04-02 17:43:47 +02:00
Andrey Prygunkov
298178b2dc #155: added handling of optional news servers 2016-03-30 22:10:57 +02:00
Andrey Prygunkov
3e919c0cb4 #196: badges in readme 2016-03-29 22:50:04 +02:00
Andrey Prygunkov
f3814e4e48 #196: continuous integration via Travis CI 2016-03-29 22:26:52 +02:00
Andrey Prygunkov
3278544d46 #195: preallocate files without sparse on Windows
Reduce disk fragmentation in direct write mode on Windows when using
article cache.
2016-03-29 18:51:08 +02:00
Andrey Prygunkov
44cf69b093 107: refactor: removed function "SetScript()" from Script-class
use SetArgs() instead.
2016-03-27 18:24:13 +02:00
Andrey Prygunkov
e6344d36a7 #107: new option "ShellOverride"
allows to configure path to python (bash, etc.); useful on systems with
non-standard paths; eliminating the need to change shebang for every
script; also makes it possible to put scripts on non-exec file systems.
2016-03-27 16:19:08 +02:00
Andrey Prygunkov
6b879e0c75 #172: 232c1a5597: fixed: segment alloc not always properly handled
and may cause program hanging on shutdown, with high cpu usage.
2016-03-26 18:41:27 +01:00
Andrey Prygunkov
0b5168ed23 #194: reworked script startup sequence
to use only async-signal-safe functions during forking.
2016-03-26 13:03:49 +01:00
Andrey Prygunkov
698edf1185 improved one info-message useful for troubleshooting 2016-03-25 21:53:19 +01:00
Andrey Prygunkov
737f059b3a #193: fixed for-range loops on guarded pointers
Range expression must be GuardedPtr. Dereferencing this object means
loosing the lock on pointer and therefore can’t be part of range
expression.
2016-03-25 20:29:25 +01:00
Andrey Prygunkov
cb8df8495b #172: 232c1a5597: fixed: segment realloc not properly handled
and may cause program hanging on shutdown, with high cpu usage.
2016-03-24 21:34:36 +01:00
Andrey Prygunkov
d348929ff0 #162: fixed: unpack destination path were not correct
if multiple unpack attempts were needed.
2016-03-24 21:16:42 +01:00
Andrey Prygunkov
c0d29d88b9 #193: corrected lock scope 2016-03-24 17:47:42 +01:00
Gunnar Lilleaasen
181a395515 Updated names of guarded pointers used directly in foreach loops. 2016-03-24 13:01:15 +01:00
Gunnar Lilleaasen
4f7849fbc1 Fixed guard usage in foreach loops, where the guards would unlock before the iteration was done. 2016-03-24 02:44:12 +01:00
Andrey Prygunkov
857ada54ea #121: unified disk state management
… to use StateDiskFile for all operations.
2016-03-23 19:29:57 +01:00
Andrey Prygunkov
01ef1c4024 #192: use pkg-config for all required libraries 2016-03-23 00:08:20 +01:00
Andrey Prygunkov
1e59b9976a #136, #127: forcing extended paths with unrar (Windows)
Instead of using extended (extra long) path notation only if the path
is really long, we now always use extended notation since we don’t know
how long the paths inside archive are.
2016-03-22 18:25:42 +01:00
Andrey Prygunkov
7d36881b29 Merge pull request #190 from heksesang/develop
merge pull request #190 from heksesang/develop
2016-03-20 22:58:53 +01:00
Gunnar Lilleaasen
8378ef8437 Replaced semantically incorrect in-class member initializers with correct
ones.
2016-03-20 21:33:17 +01:00
Andrey Prygunkov
1f83fb8a39 #189: added include for new macro file 2016-03-20 20:01:55 +01:00
Louis Sautier
67612d8dd5 Add the ax_cxx_compile_stdcxx macro locally 2016-03-20 18:13:44 +01:00
Andrey Prygunkov
a42df761fb #135: don't install doc-files coming with par2-module 2016-03-20 12:19:59 +01:00
Andrey Prygunkov
d37a9364e0 #125: linux build script works without system wide C++14 compiler
Since the script uses cross-compiling tools, it’s now possible to build
the targets without having a C++14 compiler installed on the host
system.
2016-03-20 12:17:52 +01:00
Andrey Prygunkov
caca0abb75 #168: 9da07e1e54: fixed: daemon lock-file was not properly created 2016-03-19 20:36:50 +01:00
Andrey Prygunkov
b71c990c2e updated copyright string in about box 2016-03-19 19:09:20 +01:00
Andrey Prygunkov
5948729b87 #185: guarded containers
Using guarded lists to automatically unlock containers.
2016-03-18 21:45:29 +01:00
Andrey Prygunkov
f973388879 #185: use guards with private mutexes
Use guard objects to automatically unlock private mutexes when leaving
current scope.
2016-03-16 22:37:19 +01:00
Andrey Prygunkov
65e0238aa2 #181: show first script directory in Windows app
If option “ScriptDir” contains multiple paths the menu command “Show in
Windows Explorer -> Scripts” of tray icon shows the first path
contained in option (instead of failing).
2016-03-14 18:43:27 +01:00
Andrey Prygunkov
ca088ef2e6 #163: also saving messages for nzbs in history
If an nzb was already removed from queue (put into history), the
messages were not added to it’s log. Fixed.
2016-03-13 19:42:05 +01:00
Andrey Prygunkov
1a48feba72 #178: 1d728996b2: fixed button "Choose" in settings 2016-03-13 18:18:57 +01:00
Andrey Prygunkov
1d728996b2 closes #178: workaround for a bug in Chrome on iOS 2016-03-13 00:24:45 +01:00
Andrey Prygunkov
22b1ae55e5 #148: unique smart pointers for queue scripts 2016-03-13 00:08:31 +01:00
Andrey Prygunkov
d7f5d8dea2 #168: smart pointers in par-checker 2016-03-13 00:08:31 +01:00
Andrey Prygunkov
39cf412938 #184: easier use of Debuggable class 2016-03-13 00:08:31 +01:00
Andrey Prygunkov
a5ac6d594f #148: unique smart pointers for other containers
: feeds, servers, scheduler.
2016-03-13 00:08:16 +01:00
Andrey Prygunkov
1a74695126 #148: unique smart pointers for download containers 2016-03-11 22:49:14 +01:00
Andrey Prygunkov
35049d436e #183: dropped support for old queue disk state formats
(v12 or older)
2016-03-10 21:47:38 +01:00
Andrey Prygunkov
069350eaa3 #168: unique smart pointers for download objects
… before they are put into containers.
2016-03-09 23:28:37 +01:00
Andrey Prygunkov
e3cce71329 #175: in-class member initializers (more) 2016-03-06 15:37:21 +01:00
Andrey Prygunkov
6bae67412a #168: unique smart pointers in download-info
… only a little bit yet.
2016-03-06 15:10:57 +01:00
Andrey Prygunkov
9461678a19 #168: unique smart pointers in remote server 2016-03-06 15:10:24 +01:00
Andrey Prygunkov
c22c5f6c7b #168: unique smart pointers in feed filter helper 2016-03-06 14:13:17 +01:00
Andrey Prygunkov
e5501cfa15 #182: use std::shared_ptr for feed items cache
… instead of a custom reference counting cache.
2016-03-05 19:59:33 +01:00
Andrey Prygunkov
14d1906320 #175: bfa5027bf9: fixed curses-mode initialization 2016-03-04 21:29:40 +01:00
Andrey Prygunkov
82ca298902 #181: show first script directory in OS X app
If option “ScriptDir” contains multiple paths the menu command “Show in
Finder -> Scripts” of OS X application shows the first path contained
in option (instead of failing).
2016-03-03 23:38:34 +01:00
Andrey Prygunkov
27fc3b594b #181: multiple script directories
Option “ScriptDir” now accepts multiple directories (separated with
comma or semicolon).
2016-03-03 19:09:22 +01:00
Andrey Prygunkov
b4a6b3954d fixed #180: could not delete intermediate directory (Windows) 2016-03-03 17:51:03 +01:00
Andrey Prygunkov
b6f4c9f704 #172: improved memory management in TlsSocket 2016-03-02 19:04:28 +01:00
Andrey Prygunkov
ee5cec4b16 #176: fixed compilation error with Visual Studio
A workaround for a bug in Visual Studio actually.
2016-03-01 19:49:46 +01:00
Andrey Prygunkov
f3f7fbd0de #176: updated copyright notice in source files
- added link to http://nzbget.net;
- replaced FSF Post address with a web link;
- removed unusable subversion-tags;
- updated year.
2016-03-01 19:45:07 +01:00
Andrey Prygunkov
8a344c97c9 #176: changed order of member declarations 2016-03-01 00:20:14 +01:00
Andrey Prygunkov
831c73d28d #176: removed aligning of class member names 2016-02-29 19:31:40 +01:00
Andrey Prygunkov
bfa5027bf9 #175: in-class member initializers 2016-02-28 19:53:37 +01:00
Andrey Prygunkov
194a4a66e5 closes #158: corrected option descriptions 2016-02-27 14:24:21 +01:00
Andrey Prygunkov
ceb66387eb #172: b7f01fc58c: fixed compilation error with GCC 2016-02-27 14:01:45 +01:00
Andrey Prygunkov
b18bc04606 #172: use vector of Mutex in TlsSocket
instead of dynamic array of raw pointers.
2016-02-26 23:40:47 +01:00
Andrey Prygunkov
b7f01fc58c #172: use CString in Tokenzier
instead of raw C-style string.
2016-02-26 23:34:54 +01:00
Andrey Prygunkov
7fc6d9e99e #172: moved class WString to NString.cpp 2016-02-26 23:18:35 +01:00
Andrey Prygunkov
232c1a5597 #172: new class for cached segments
1) improve memory management; 2) removed dependency of module
“DownloadInfo.cpp” from article cache module “ArticleWriter.h”
2016-02-26 22:59:00 +01:00
Andrey Prygunkov
1414a4976e fixed #174: command "--printconfig" segfaults 2016-02-26 22:30:00 +01:00
Andrey Prygunkov
048add1dcf #172: vectors and smart pointers in script controller
instead of raw pointers
2016-02-25 22:44:33 +01:00
Andrey Prygunkov
1ae8132be2 #168: unique smart pointers in gzip; #172: use system objects directly 2016-02-25 21:52:39 +01:00
Andrey Prygunkov
72cd89d5f4 #172: use vectors in curses frontend on Windows
Replaced raw dynamic arrays with vectors in curses frontend on Windows.
2016-02-25 19:35:21 +01:00
Andrey Prygunkov
b414526d02 #172: using vector of strings in script controller
instead of dynamic array of c-style strings
2016-02-25 19:27:24 +01:00
Andrey Prygunkov
27eb78faab #168: unique smart pointers in regex
instead of new/delete.
2016-02-25 18:50:00 +01:00
Andrey Prygunkov
6bd5223b92 fixed: not all unit tests were active on POSIX 2016-02-25 18:34:57 +01:00
Andrey Prygunkov
e8afb4e331 #172: use system objects directly
instead of dynamic creation, which were used to avoid system includes;
that’s become unimportant after using of precompiled headers.
2016-02-23 22:31:27 +01:00
Andrey Prygunkov
390481f814 #172: using CString in Maintenance-class
instead of c-style string.
2016-02-23 22:09:11 +01:00
Andrey Prygunkov
4eb0e82f75 #172: using vector of strings in command line parser
instead of dynamic array of c-style strings.
2016-02-23 22:08:20 +01:00
Andrey Prygunkov
548753aa69 #172: using vector of strings in SplitCommandLine
instead of dynamic array of c-style strings.
2016-02-23 22:07:30 +01:00
Andrey Prygunkov
cdf6068db5 #172: using vector instead of raw dynamic array in queue manager 2016-02-23 22:03:30 +01:00
Andrey Prygunkov
6aead41e6f #126: using CharBuffer instead of raw pointers in LoadFileIntoBuffer 2016-02-23 21:59:10 +01:00
Andrey Prygunkov
e48cc0257b #126: 9e2d8544da: fixed cleanup in par-renamer 2016-02-20 21:20:27 +01:00
Andrey Prygunkov
1d05477729 #168: c96d2259ce: fixed: remote mode didn't work 2016-02-19 22:47:03 +01:00
Andrey Prygunkov
2b840315b0 #126: d87d6ac2ac: fixed: exe path could be determined incorrectly 2016-02-19 22:17:12 +01:00
Andrey Prygunkov
177be1571e #168: c96d2259ce: fixed feed parse error 2016-02-19 22:11:44 +01:00
Andrey Prygunkov
b9076fc21d #168: 9da07e1e54: adjusted finalization
of global objects
2016-02-19 19:08:00 +01:00
Andrey Prygunkov
9dfa66ac3e fixed #171: queue-scripts not called for failed URLs
if the scripts were set in category’s option “PostScript”.
2016-02-18 23:03:52 +01:00
Andrey Prygunkov
b5dae56c7e #168: 9da07e1e54: fixed segfault
when starting daemon (nzbget -D).
2016-02-18 22:28:02 +01:00
Andrey Prygunkov
4a08fdb586 #168: c96d2259ce: fixed compiling error on Windows 2016-02-18 22:11:28 +01:00
Andrey Prygunkov
b910d123eb #143: store EditItem directly in container
instead of pointer to object.
2016-02-17 22:03:33 +01:00
Andrey Prygunkov
9da07e1e54 #168: unique smart pointers for global variables
and restructured main module “nzbget.cpp”
2016-02-17 22:03:33 +01:00
Andrey Prygunkov
c96d2259ce #168: unique smart pointers for local variables 2016-02-17 22:03:33 +01:00
Andrey Prygunkov
36fe905bd9 #168: unique smart pointers for member fields (2) 2016-02-17 22:03:33 +01:00
Andrey Prygunkov
40eb5c4e1e #168: unique smart pointers for static members 2016-02-17 22:03:32 +01:00
Andrey Prygunkov
c9b883f909 #168: unique smart pointers for member fields 2016-02-17 22:03:32 +01:00
Andrey Prygunkov
d803330e3f #167: more stack objects 2016-02-17 22:03:20 +01:00
Andrey Prygunkov
8f4718fb9d #167: prefer stack objects in tests 2016-02-17 22:03:06 +01:00
Andrey Prygunkov
1162c89e77 #167: prefer stack objects 2016-02-17 22:02:41 +01:00
Andrey Prygunkov
b55190e52f #162: change destination directory from queue-script
Queue-scripts can now change destination after download is completed
and before unpack.
2016-02-02 23:31:36 +01:00
Andrey Prygunkov
74f2ab8963 #162: removed support for multiple paths
in options “DestDir” and “CategoryX.DestDir”.
2016-02-02 21:38:45 +01:00
Andrey Prygunkov
4fbff8cd25 closes #163: queue scripts save messages into nzb-log 2016-02-01 22:22:56 +01:00
Andrey Prygunkov
c1f58220c2 #126: 6d33d83d20: removed incorrect attribute 2016-02-01 22:19:22 +01:00
Andrey Prygunkov
624944b82a #162: fixed compilation error in VC++ 2016-02-01 19:18:55 +01:00
Andrey Prygunkov
00d81795e9 #162: combining free space on all paths of "DestDir"
1) When checking free space (option “DiskSpace”) the total free size of
directories in “DestDir” is checked;
2) Web-interface shows the total free size of all directories.
2016-02-01 00:36:59 +01:00
Andrey Prygunkov
ec87c3c1f8 #162: multiple paths in global option "DestDir"
are now supported too.
2016-02-01 00:21:36 +01:00
Andrey Prygunkov
c1ac38d10b #162: multiple destination paths for categories
in option “CategoryX.DestDir”.
2016-01-30 18:32:29 +01:00
Andrey Prygunkov
b7d785797f #136: fixed: root drive paths sometimes unusable on Windows
Paths like “C:\” were sometimes not usable.
2016-01-29 22:34:37 +01:00
Andrey Prygunkov
2b2b1e1539 213eb2c7c1, 0c3ce58ffa: restored target detection in configure script
which is required to proper configure on OS X.
2016-01-25 19:15:40 +01:00
Andrey Prygunkov
3ae8b5ac95 #136: fixed: scripts were not found 2016-01-24 19:07:25 +01:00
Andrey Prygunkov
05a1926ebc #161: printing low-level messages from par2-module 2016-01-24 17:58:04 +01:00
Andrey Prygunkov
ce64eeeb1d #160: status "FAILURE" for failed par-repairs
If par-repair fails the par-status is now set to “FAILURE” (instead of
“REPAIRABLE”).
2016-01-23 22:56:10 +01:00
Andrey Prygunkov
6e763f3573 #126: small optimization 2016-01-23 18:41:58 +01:00
Andrey Prygunkov
b448fddfe6 #136: corrected slashes and simpilified code 2016-01-23 18:29:55 +01:00
Andrey Prygunkov
69eb079851 #136: ff69fbbeb9: fixed compilation error under GCC 2016-01-23 16:58:37 +01:00
Andrey Prygunkov
ff69fbbeb9 #136: avoid double slashes in paths
Extra long path names are not normalized automatically by Windows and
therefore must contain paths in canonical form.
2016-01-23 14:23:53 +01:00
Andrey Prygunkov
26fea301e0 #136: fixed crash on Windows 2016-01-22 00:37:49 +01:00
Andrey Prygunkov
d95e389583 #156: fixed memory access error 2016-01-22 00:36:40 +01:00
Andrey Prygunkov
72370c9c71 #126: fixed memory access error 2016-01-22 00:36:04 +01:00
Andrey Prygunkov
d04e6a53da #156: fixed compilation error with certain compilers 2016-01-20 20:46:39 +01:00
Andrey Prygunkov
1aa59ded15 #126: another place for CString 2016-01-20 20:45:36 +01:00
Andrey Prygunkov
080bd22d77 #141: ignore hidden files and directories in NzbDir
The files and directories whose names start with dot are now ignored by
the scanner of incoming nzb directory.
2016-01-17 12:58:43 +01:00
Andrey Prygunkov
8f84132218 #156: new class "CharBuffer" for temporary buffers
Replaced everywhere plain “char”-buffers with new class. Avoid using
“malloc/free”.
2016-01-17 00:06:27 +01:00
Andrey Prygunkov
34771792ac #126: removed "CString::Capacity()" as it was error source 2016-01-16 19:02:27 +01:00
Andrey Prygunkov
17024eb0e5 #126: replaced "char*" with "CString" at few more places 2016-01-16 16:31:45 +01:00
Andrey Prygunkov
da0a0f3105 refactor: removed unneeded code 2016-01-14 19:47:12 +01:00
Andrey Prygunkov
98cd5a8dbc refactor: removed unneeded checks 2016-01-14 19:46:49 +01:00
Andrey Prygunkov
421de1013f #154: use lambdas when searching in containers 2016-01-14 19:43:33 +01:00
Andrey Prygunkov
0ee644d252 #154: use lambdas when deleting elements from containers 2016-01-14 19:42:49 +01:00
Andrey Prygunkov
ac1bd3d07c #154: use lambdas when sorting containers 2016-01-13 20:09:36 +01:00
Andrey Prygunkov
ef4a72d383 #152: eliminated dereferences in for-range loops using “begin()” and “end()” template functions in “nzbget.h” 2016-01-13 19:44:40 +01:00
Andrey Prygunkov
b32b4c0691 #152: for-range loops with iterators 2016-01-12 00:30:17 +01:00
Andrey Prygunkov
0732cb4b8b closes #147: par-check doesn't ignore files from option "ExtCleanupDisk"
Only files listed in option ParIgnoreExt are ignored.
2016-01-09 21:44:01 +01:00
Andrey Prygunkov
362ee700c5 #125: check for C++14 features in configure-script
- configure now requires a C++14 compliant compiler;
- special exception: GCC 4.9 is also recognized and supported (it
offers only partial support for C++14);
- an error message and a link to wiki-page is printed if no C++14
compiler was found.
2016-01-08 22:49:00 +01:00
Andrey Prygunkov
93835ea2af #146: improved error reporting on DNS lookup errors
Previously printed error messages were not correct as errors must be
read via special function, not via default “errno”.
2016-01-08 22:45:34 +01:00
Andrey Prygunkov
32400a810f #143: store download objects directly in containers
…where possible.
2016-01-08 22:29:44 +01:00
Andrey Prygunkov
17999fb96d #143: store feed cache objects directly in container 2016-01-08 21:52:16 +01:00
Andrey Prygunkov
4f8370400f #143: store Segment and DupeSource directly in containers 2016-01-07 00:35:05 +01:00
Andrey Prygunkov
09dde2b82f #143: store Scanner objects directly in container 2016-01-07 00:07:06 +01:00
Andrey Prygunkov
f6587d3299 #143: store Script objects and templates directly in container 2016-01-05 23:47:59 +01:00
Andrey Prygunkov
a70b86abd0 #143: store Block directly in container 2016-01-05 22:15:26 +01:00
Andrey Prygunkov
6885082299 #143: store volume stat objects directly in container 2016-01-05 18:57:15 +01:00
Andrey Prygunkov
37c126e24d #143: fixed file permissions in repository 2016-01-04 22:44:34 +01:00
Andrey Prygunkov
476dae8c1e #143: store feed objects directly in containers 2016-01-04 22:43:56 +01:00
Andrey Prygunkov
c949b27468 #143: store Term and Rule directly in containers 2016-01-04 19:54:51 +01:00
Andrey Prygunkov
28897e4e79 #143: store OptEntry and Category directly in containers 2016-01-04 19:42:47 +01:00
Andrey Prygunkov
81f2c7825d #143: store Message directly in container
…instead of pointer to object.
2016-01-03 20:49:30 +01:00
Andrey Prygunkov
5d4b56b40b #143: store FileHash directly in container
instead of pointer to object.
2016-01-03 20:40:01 +01:00
Andrey Prygunkov
dc8803d6a3 #143: passing ids to "editqueue" as vector
… instead of pointer to array. And names as vector<CString> instead of
vector<char*>.
2016-01-03 20:38:52 +01:00
Andrey Prygunkov
be852d0a9b #143: using <CString> instead of <char*> in containers 2016-01-03 19:58:11 +01:00
Andrey Prygunkov
c8c86f00ef #142: deactivated timestamp check of autotools files
This makes it easier for users to build from git, eliminating the need
for “touch <certain files>“-command.
2016-01-03 16:19:43 +01:00
Andrey Prygunkov
13fef5ce96 Merge pull request #140 from parnic/develop
Fixed compilation error when using GnuTLS
2016-01-03 00:53:36 +01:00
Andrey Prygunkov
69c995359b #126: time formatting functions 2016-01-01 15:56:53 +01:00
Andrey Prygunkov
3e89638b39 refactor: replaced "time(NULL)" with an utility function 2016-01-01 14:57:48 +01:00
parnic
8dc9b4e396 Fixed compilation error with g++ 5.3
daemon/connect/TlsSocket.cpp: In member function ‘bool TlsSocket::Start()’:
daemon/connect/TlsSocket.cpp:322:56: error: use of deleted function ‘CString::CString(CString&)’
  const char* priority = !m_cipher.Empty() ? m_cipher : "NORMAL";
                                                        ^
In file included from daemon/connect/TlsSocket.h:30:0,
                 from daemon/connect/TlsSocket.cpp:30:
./daemon/util/NString.h:73:2: note: declared here
  CString(CString& other) = delete;
  ^
2015-12-31 22:39:50 -06:00
Andrey Prygunkov
28ec8a8ec3 #132: tvdbid and tvmazeid in web-interface
updated descriptions and other references.
2015-12-31 15:42:11 +01:00
Andrey Prygunkov
1b457bceb7 #132: tvdbid and tvmazeid in rss feeds
- new fields “tvdbid” and “tvmazeid” in filter;
- new fields are used when automatically generating dupe-keys;
- new options “tvdbid:xxx” and “tvmazeid:xxx” of command “Accept” to
generate dupe-keys based on these fields.
2015-12-31 15:35:35 +01:00
Andrey Prygunkov
2c152b51b6 #126: fixed memory leak 2015-12-30 23:23:33 +01:00
Andrey Prygunkov
9771d7f53f #125: check for C++11 features in configure-script 2015-12-30 17:26:14 +01:00
Andrey Prygunkov
94b7ef8a37 #138: fixed compilation error on Windows 2015-12-30 16:41:28 +01:00
Andrey Prygunkov
04c3e0d263 #138: use "nullptr" instead of "NULL" 2015-12-30 16:35:07 +01:00
Andrey Prygunkov
74c0e8804f #136: transcoding of names in par2-module
when running on POSIX the file names must be transcoded as well, not
only on Windows.
2015-12-29 23:49:37 +01:00
Andrey Prygunkov
3aa97e44e7 #136: adjustment in par2-module
using built-in latin1-to-utf8 conversion routine instead of Windows
function (which depend on current code page).
2015-12-29 23:44:22 +01:00
Andrey Prygunkov
5df8f73b20 #137: transcode file names read from articles
File names which were read from downloaded articles (yEnc) are now
transcoded from Latin1 to UTF8.
2015-12-29 23:33:39 +01:00
Andrey Prygunkov
a9181d8a56 #136: support for extra long UNC paths 2015-12-29 20:47:30 +01:00
Andrey Prygunkov
df68fd52c8 #131: better support for UNC paths on Windows
fixed: automatic creation of directories didn't work if the last existed
directory in the path was the network share itself.
2015-12-29 20:34:14 +01:00
Andrey Prygunkov
65f2a0afe3 #136: support for Unicode and extra long paths in par2-module
- par-renamer and par-checker both support Unicode paths and extra long
paths;
2015-12-29 01:54:19 +01:00
Andrey Prygunkov
6292ad5394 #131: fixed incorrect return value of "Seek" 2015-12-29 01:17:12 +01:00
Andrey Prygunkov
b8e14faefe #136: removed unneeded function
deleted functions for converting ANSI <-> UTF which are not used
anymore.
2015-12-28 18:35:24 +01:00
Andrey Prygunkov
d8a2d79240 #136: handling of Unicode paths with MSXML
- when parsing nzb-files (option NzbDir);
- when parsing rss feeds (option TempDir).
2015-12-28 18:34:47 +01:00
Andrey Prygunkov
449a048304 #136: made class WString public for use from other modules 2015-12-28 14:45:01 +01:00
Andrey Prygunkov
f347f1aed1 #136: avoid crash in par-renamer
if the directory content could not be read.
2015-12-28 11:05:52 +01:00
Andrey Prygunkov
03ba670d36 #136: Unicode in script environment
- pass all data to scripts in Unicode environment;
- script which doesn’t support Unicode can access ANSI-version of
environment, which is provided by Windows automatically from the
Unicode-version.
2015-12-28 11:04:59 +01:00
Andrey Prygunkov
a9847831a5 #136: extra long paths with unrar
- pass extra long paths to unrar using “\\?\” prefix (and in Unicode).
2015-12-28 11:02:20 +01:00
Andrey Prygunkov
920507c163 #136: Unicode Windows-API when calling other programs
- use CreateProcessW;
- pass command-line in Unicode;
- pass environment in Unicode;
- if current directory is too long convert it to short path (8.3
notation); because CreateProcessW doesn’t support extra long path
(prefixed with “\\?\”) for current directory.
2015-12-28 11:00:32 +01:00
Andrey Prygunkov
e11dfb62d0 #136: Unicode-Windows-API for file operations
- internally all paths are handled in UTF8;
- all paths are stored in config-file in UTF8;
- when calling file access Windows API functions the paths are
converted to wide-chars and Unicode-API is used;
- extra long paths are prefixed with “\\?\” (extended path format).
2015-12-28 10:56:11 +01:00
Andrey Prygunkov
c68ba306fe #131: using enum for file mode in function DiskFile::Open 2015-12-26 17:19:12 +01:00
Andrey Prygunkov
6840ef4439 #131: routing a few more remaining function calls through module "FileSystem"
“rename”, “remove” and “rmdir”.
2015-12-25 22:55:46 +01:00
Andrey Prygunkov
dce6f0c9a3 #131: new class DiskFile to wrap "FILE*"-functions 2015-12-25 18:11:50 +01:00
Andrey Prygunkov
8fb9fb7b2d #133: put all par2-modules into namespace "Par2"
To avoid conflicts with similarly named classes of NZBGet.
2015-12-24 21:25:38 +01:00
Andrey Prygunkov
eae6fd5d91 #131: wrapped function "remove" into module FileSystem
under name “DeleteFile”.
2015-12-24 18:34:15 +01:00
Andrey Prygunkov
321c7efa41 #130: moved parts from module "Util" into new module "FileSystem" 2015-12-22 22:01:03 +01:00
Andrey Prygunkov
19ce3bf69b #130: improved class DirBrowser
It doesn’t return filenames ‘.’ and ‘..’ (for current and parent
directories) anymore, eliminating the need to check and ignore these
names on each usage of the class.
2015-12-21 20:54:21 +01:00
Andrey Prygunkov
c170d6a180 Merge branch 'develop' into 126-managed-strings 2015-12-21 19:12:16 +01:00
Andrey Prygunkov
80f614a54c #126: fixed compilation errors under GCC 2015-12-21 18:58:35 +01:00
Andrey Prygunkov
99d8cee0db #126: class BString supports initialization via assignment
which makes the code clearer.
2015-12-20 23:46:22 +01:00
Andrey Prygunkov
d87d6ac2ac #126: using CString for ref-parameters and return values
1) for parameters use references to CString instead of pointers to
buffers;
2) when returning strings use CString instead of pointer to char buffer
which caller needs to deallocate;
3) use BString even more.
2015-12-20 16:27:01 +01:00
Andrey Prygunkov
4f4a289309 Merge pull request #129 from fjeldsoe/patch-2
removing leading and trailing spaces from URL when adding from web-interface.
2015-12-19 22:44:25 +01:00
fjeldsoe
44aaf77e5d Added trim method to url string value
Adding a NZB URL fails if there is a space at the start of the string, this should fix it. Often when you copy and paste a link, spaces in front of the string can occur.
2015-12-19 22:37:42 +01:00
Andrey Prygunkov
9e2d8544da #126: full use of class BString
1) replaced characters arrays with class BString through the whole
program. The string formatting code has become much cleaner.
2) class Util returns error message via CString instead of character
buffers.
3) few more places to use CString.
2015-12-19 18:43:52 +01:00
Andrey Prygunkov
8394759527 #126: reworked string classes
1) classes CString and BString now have no memory overhead compared to
C-style null-terminated strings and character arrays respectively.
2) class StringBuilder is back and should be used when often Append’s
are needed.
2015-12-19 00:09:12 +01:00
Andrey Prygunkov
4ba432dee3 #126: fixed: corrupted files when article cache was active 2015-12-18 23:47:18 +01:00
Andrey Prygunkov
5ea439c8a0 #125: 94e55e1b5a: fixed accidental deactivation of compiler optimization 2015-12-13 23:39:55 +01:00
Andrey Prygunkov
6d33d83d20 #126: using GCC attributes to detect formatting errors
also eliminated warnings found with activated detection.
2015-12-13 18:28:48 +01:00
Andrey Prygunkov
17d3e05e16 #126: update project file for Windows
and fixed a few warnings.
2015-12-13 17:09:52 +01:00
Andrey Prygunkov
ecc7fc9695 #126: extended string classes
- Append/AppendFmt-methods;
- CString stores length internally for faster appends;
- removed class StringBuilder, replaced usages with CString;
- binding to existing C-style null-terminated strings with methods
Bind/Unbind.
2015-12-13 16:02:57 +01:00
Andrey Prygunkov
558fce9b47 #126: replaced C-style strings with class "CString"
: replaced all data members.
2015-12-12 16:36:25 +01:00
Andrey Prygunkov
6b0fdc881e #126: new string classes
which can be easily used as replacement for C-style null-terminated
strings and C-style char arrays.
2015-12-12 16:34:45 +01:00
Andrey Prygunkov
94e55e1b5a #125: activated C++14 in Linux build script 2015-12-10 19:59:43 +01:00
Andrey Prygunkov
d6e05430bf #124: updated build instructions for Linux and FreeBSD toolchains 2015-12-08 18:50:13 +01:00
Andrey Prygunkov
fc50dc871a #124: added FreebSD toolchain script to Makefile 2015-12-08 18:34:09 +01:00
Andrey Prygunkov
7e6c3f435a #124: program update script works on FreeBSD too 2015-12-08 00:09:38 +01:00
Andrey Prygunkov
9512ec7d74 #124: installer-script now supports FreeBSD 2015-12-08 00:09:02 +01:00
Andrey Prygunkov
202200c74a #124: unpack build scripts support FreeBSD toolchain 2015-12-08 00:08:20 +01:00
Andrey Prygunkov
fe4e9d1a94 #124: supporting FreeBSD in the installer build script 2015-12-08 00:07:52 +01:00
Andrey Prygunkov
c330704c9f #124: toolchain creation script for FreeBSD
the script creates a cross-compiling toolchain, which works on Linux
and produces binaries for FreeBSD.
2015-12-08 00:04:14 +01:00
Andrey Prygunkov
1d77852bea updated version string to "16.4" 2015-12-05 12:24:20 +01:00
Andrey Prygunkov
15a5d056ed #118: merge 80653a8dad: fixed resource (socket) leak
, which may cause “too many open files” errors with a possible crash.
2015-12-05 11:51:30 +01:00
Andrey Prygunkov
32009c5691 closes #120: better error reporting when using GnuTLS 2015-11-30 19:44:53 +01:00
Andrey Prygunkov
41c62dc413 #117: better handling of command line when calling external programs (Windows)
Trailing slashes must be doubled. This in particular improves
compatibility with user-compiled “unrar”.
2015-11-30 19:38:59 +01:00
Andrey Prygunkov
9c3d6fadca #117: refactor: restructured Script-module 2015-11-30 19:33:13 +01:00
Andrey Prygunkov
726542b698 #108: 51172e1c96: fixed file attributes 2015-11-30 19:32:00 +01:00
Andrey Prygunkov
51172e1c96 closes #108: save nzb-log into a file directly from web-ui
In download details dialog or history details dialog -> button “Log” ->
button “Save to file”.
2015-11-29 23:41:41 +01:00
Andrey Prygunkov
f8049a81e1 #119: do not print warnings for certain missing web files
Built-in web-server doesn’t print warnings to log for certain files
which are or can be missing but that’s OK for them:
- package-info.json - update information file, which is available only
with binary packages supporting automatic updates;
- favicon.ico - the file is located in img-subdirectory; web-clients
requesting the file from the root directory are doing this wrong;
- apple-touch-icon*.png - iOS safari asks for these files, but we don’t
have nice icons.
2015-11-28 21:50:44 +01:00
Andrey Prygunkov
80653a8dad closes #118: fixed resource (socket) leak,
which may cause “too many open files” errors with a possible crash
2015-11-27 22:45:58 +01:00
Andrey Prygunkov
4e4816c3c8 #116: use size specific integer types 2015-11-21 00:02:22 +01:00
Andrey Prygunkov
b5dd49dbc4 fixed a compiler warning in VS2015 (Windows only) 2015-11-20 23:17:56 +01:00
Andrey Prygunkov
1f406a391a #115: using precompiled header in Linux build script
… to speed up compilation. Another new feature in the build script -
multicore compilation via new option “corex”.
2015-11-20 00:02:09 +01:00
Andrey Prygunkov
87d2c1a7f4 #115: using precompiled header on Windows 2015-11-19 23:54:38 +01:00
Andrey Prygunkov
ec17d119a1 #115: put all external headers together
into “nzbget.h”
2015-11-19 23:51:02 +01:00
Andrey Prygunkov
8630f1365c merged #100: fixed: downloads may sometimes fail due to incorrect decoding of articles 2015-11-16 22:56:44 +01:00
Andrey Prygunkov
322b08e4b2 #98: showing number of selected items in confirmation box
…when deleting or performing other actions on multiple items in
web-interface.
2015-11-15 00:42:09 +01:00
Andrey Prygunkov
fc3c90605c #110: accepting certificate chains in option SecureCert
The built-in web-server can now use certificate chain files through
option “SecureCert”, when compiled using OpenSSL.
2015-11-07 00:03:02 +01:00
hugbug
afb310ab9b a72f787a40: renamed missing file 2015-11-06 23:57:40 +01:00
Andrey Prygunkov
580df4d361 closes #97: cleanup of dupe-badges
allowing character “=“ in dupe-badges.
2015-11-06 00:07:08 +01:00
Andrey Prygunkov
70ccfd9802 normalized whitespace formatting
1) removed trailing spaces and tabs;
2) replaced occasional leading spaces with tabs.
2015-11-05 23:45:19 +01:00
Andrey Prygunkov
98bc1ebd37 #103: corrected whitespace formatting 2015-11-04 00:14:58 +01:00
Andrey Prygunkov
a9a6f1e2d4 #103: manual corrections of variable names 2015-11-03 23:33:21 +01:00
Andrey Prygunkov
bf49f16d7c #103: renamed global variables 2015-11-01 21:42:35 +01:00
Andrey Prygunkov
c46e6953e1 #103: updated references for renamed modules 2015-10-31 19:57:41 +01:00
Andrey Prygunkov
a72f787a40 #103: renamed modules to normalize acronyms 2015-10-31 19:15:54 +01:00
Andrey Prygunkov
1fb21b330e #103: normalized (renamed) acronyms 2015-10-30 23:54:37 +01:00
Andrey Prygunkov
af85bb91fa #103: few manual renames 2015-10-30 22:08:21 +01:00
Andrey Prygunkov
57bee2447e #103: in "tests": renamed local, member variables and function parameters 2015-10-30 21:50:26 +01:00
Andrey Prygunkov
050dc8d55f updated version string to "16.3" 2015-10-29 20:59:27 +01:00
Andrey Prygunkov
4a5063c14e #100: fixed: downloads may sometimes fail due to incorrect decoding of articles 2015-10-28 18:09:29 +01:00
Andrey Prygunkov
5adb50274e #103: renamed local, member variables and function parameters 2015-10-27 22:37:23 +01:00
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
739925ecc8 #104: improved error reporting when creating sparse files 2015-10-22 21:36:23 +02:00
Andrey Prygunkov
c8f4712e77 #101: upgraded Linux build machine to use GCC 5.2 2015-10-21 23:35:12 +02:00
Andrey Prygunkov
13f5ab7388 #102: updated compiling instructions (Windows only)
…for Visual Studio 2015.
2015-10-21 23:03:17 +02:00
Andrey Prygunkov
7d2ee607a1 #102: upgraded VC++ project to Visual Studio 2015 2015-10-21 22:57:18 +02:00
Andrey Prygunkov
68a87c775c fixed #96: failed to load nzb if path contains certain characters (Windows only)
although the path is URL-encoded the MSXML still fails to parse it
properly sometimes.
2015-10-20 23:20:08 +02:00
Andrey Prygunkov
50d344cb91 fixed #100: workaround to deal with malformed responses
…which still may contain useful data.
2015-10-20 21:09:01 +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
0c5dc22a93 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-18 19:14:45 +02:00
Andrey Prygunkov
e81b42f8dc #77: fixed issues with reverse proxies (3)
when very long headers were sent from the proxy, in particular if
htdigest authorization were used.
2015-10-17 01:01:06 +02:00
Andrey Prygunkov
2a302b3f0d #77: reverted e7562b6470
restored header name back to “X-Auth-Token” since it wasn’t the source
of the problem.
2015-10-17 00:54:20 +02:00
Andrey Prygunkov
eb78bdcf06 #77: fixed issues with reverse proxies (again)
removing of authorization-header wasn’t such a good idea.
2015-10-14 08:05:17 +02:00
Andrey Prygunkov
e7562b6470 #77: fixed issues with reverse proxies
renamed header “X-Auth-Token” to “X-Private-Auth-Token” to avoid
conflicts with apache and nginx.
2015-10-14 00:00:59 +02:00
Andrey Prygunkov
01e672c7e2 #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-13 23:29:15 +02:00
Andrey Prygunkov
3c638c5ca2 fixed file permissions in repository 2015-10-13 23:22:32 +02:00
Andrey Prygunkov
5356f5aafc updated version string to 17.0-testing 2015-10-13 23:22:19 +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
Andrey Prygunkov
7acd2ad884 updated version string (preparing to release 13.0) 2014-07-14 20:22:36 +00:00
Andrey Prygunkov
1f474c3097 updated ChangeLog 2014-07-05 21:56:20 +00:00
Andrey Prygunkov
c8b4f6e985 removed libpar2-patches from NZBGet source tree; the documentation now suggests to use the libpar2 version maintained by Debian/Ubuntu team, which already includes all necessary patches; also removed patches to create libpar2 and libsigc++ project files for Visual Studio on Windows, no one needed them anyway 2014-07-04 21:01:13 +00:00
Andrey Prygunkov
fc20bcca91 pp-script "EMail.py" now takes the status of previous pp-scripts into account and report a failure if any of the scripts has failed 2014-07-04 19:50:11 +00:00
Andrey Prygunkov
702b635826 improved RPC-API: history items now preserve "NZBID" from queue items; that makes the tracking of items across queue and history easier for third-party apps; field "NZBID" returned by RPC-method "history" is now available for history items of all kinds (NZB, URL, DUP); field "ID" is deprecated and should not be used 2014-07-04 19:07:51 +00:00
Andrey Prygunkov
990c5f67e4 fixed: current download could be damaged if the program was restarted during download and the option "ContinuePartial" was active (bug introduced in v13) 2014-07-03 20:45:53 +00:00
Andrey Prygunkov
8ef4ca2ce8 fixed: port number was not sent in headers when downloading from URLs which could cause issues with RSS for web-sites using non-standard http ports 2014-06-30 20:42:50 +00:00
Andrey Prygunkov
b105ce6698 fixed: queued nzb-files was not deleted from disk when deleting download without history tracking 2014-06-29 21:24:58 +00:00
Andrey Prygunkov
6c93b836f5 fixed: check for file or directory existense could fail sometimes (Windows only, bug introduced in v13) 2014-06-26 18:29:55 +00:00
Andrey Prygunkov
f0e60ee577 improvement in RPC-API: method "append" now returns id of added nzb-file or "0" on an error; this makes it easier for third-party apps to track added nzb-files; for backward compatibility with older software expecting a boolean result the old version of method "append" is still supported; the new version of method "append" has a different signature (order of parameters); parameter "content" can now be either nzb-file content (encoded in base 64) or an URL; this makes the method "appendurl" obsolete (still supported for compatibility); if an URL was added to queue the queue entry created for fetched nzb-file has the same "NZBID" for easier tracking 2014-06-19 15:00:46 +00:00
Andrey Prygunkov
2cfbb2373a fixed: scheduler command "FetchFeed" did not work properly with parameter "0" (fetch all feeds) 2014-06-17 21:31:09 +00:00
Andrey Prygunkov
d26d04d92b when changing category in web-interface the post-processing parameters are now automatically updated according to new category settings; only parameters which are different in old and new category are changed; parameters which present in both or in neither categories are not changed; that ensures that only the relevant parameters are updated and parameters which were manually changed by user remain they settings when it make sense; in the "download details dialog" the new parameters are updated on the postprocess-tab directly after changing of category and can be controlled before saving; in the "edit multiple downloads dialog" the parameters are updated individually for each download on saving; new action "CP" of remote command "--edit/-E" for groups to set category and apply parameters; new action "GroupApplyCategory of RPC-method "editqueue" for the same purpose 2014-06-13 21:53:27 +00:00
Andrey Prygunkov
0d6fe32246 to detect daylight saving activation/deactivation the time zone information is now checked every minute if a download is active or once in 3 hours if the program is in stand-by; these delays should work well with hibernation mode on synology) 2014-06-12 20:57:00 +00:00
Andrey Prygunkov
36de8073f2 apostrophe is not considered an invalid file name character anymore 2014-06-11 21:15:36 +00:00
Andrey Prygunkov
5aaaa1e6a7 fixed: the program could crash during cleanup if files with invalid timestamps were found in the directory (windows only) 2014-06-09 20:52:23 +00:00
Andrey Prygunkov
a4126a52ce fixed: par-rename initiated unnecessary par-check if option "InterDir" were not active (bug introduced in r1030) 2014-06-06 21:49:11 +00:00
Andrey Prygunkov
076017128e added support for power management on windows to avoid pc going into sleep mode during download or post-processing 2014-06-06 19:25:02 +00:00
Andrey Prygunkov
7240147418 added option "ParIgnoreExt" which lists files which do not trigger par-repair if they are missing 2014-06-03 20:47:28 +00:00
Andrey Prygunkov
0923f2bb5c added new choice "Always" for option "ParCheck"; it forces the par-check for every (even undamaged) download but in contrast to choice "Force" only one par2-file is downloaded first; additional files are downloaded if needed 2014-06-02 20:43:37 +00:00
Andrey Prygunkov
5ce0b9985a corrected a typing error in a month name 2014-06-02 20:28:36 +00:00
Andrey Prygunkov
cd76375d8e post-processing scripts which move the whole download into a new location can inform the program about new location using command "[NZB] DIRECTORY=/new/path", allowing other scripts to process files further 2014-05-30 22:09:50 +00:00
Andrey Prygunkov
df2ef01494 when checking for missing files the files whose extensions match with option "ExtCleanupDisk" are ignored now (to avoid time consuming restoring of files which will be deleted later anyway) 2014-05-30 21:35:30 +00:00
Andrey Prygunkov
1d3d875f3d refactor: created new class "Tokenizer" and replaced all usages of function "strtok_r" with new class; also created new function "MatchFileExt" for the similar code used in two places 2014-05-29 21:38:27 +00:00
Andrey Prygunkov
48446367f4 post-processing scripts now have two new parameters: env. var "NZBPP_STATUS" indicates the status of download including the total status (SUCCESS, FAILURE, etc.) and the detail field (for example in case of failures: PAR, UNPACK, etc.); env. var "NZBPP_TOTALSTATUS" is equal to the total status of parameter "NZBPP_STATUS" and is provided for convenience (to avoid parsing of "NZBPP_STATUS"); the new parameters provide a simple way for pp-scripts to determine download status without a guess work needed in previous versions; parameters "NZBPP_PARSTATUS" and "NZBPP_UNPACKSTATUS" are now considered deprecated (still passed for compatibility); updated script "EMail.py" to use new parameters "NZBPP_TOTALSTATUS" and "NZBPP_STATUS" instead of "NZBPP_PARSTATUS" and "NZBPP_UNPACKSTATUS" 2014-05-28 22:19:39 +00:00
Andrey Prygunkov
fb1f293a17 improved fast par-renamer: it can now detect missing files (files listed in par2-files but not present on disk) 2014-05-28 21:50:15 +00:00
Andrey Prygunkov
f85533d608 fixed: some nzb-file data were not calculated for history items loaded from disk state; this may cause problems for commands "Post-process again" and "Download remaining files" (bug introduced in v13) 2014-05-28 21:37:44 +00:00
Andrey Prygunkov
e32faf6053 better error reporting if deleting of directories fails 2014-05-25 20:42:50 +00:00
Andrey Prygunkov
a429ea4679 windows version is now configured to use OpenSSL instead of GnuTLS 2014-05-24 17:37:42 +00:00
Andrey Prygunkov
9112d2277e fixed: incorrect number of paused files were shown in curses output mode 2014-05-24 12:25:24 +00:00
Andrey Prygunkov
a9050045f3 renamed section "SCRIPTS" to "EXTENSION SCRIPTS" in the settings 2014-05-24 12:25:08 +00:00
Andrey Prygunkov
ed3cad6e9c when building nzbget if both OpenSSL and GnuTLS are available now using OpenSSL by default (the preferred library can still be selected with configure-parameter --with-tlslib=OpenSSL/GnuTLS) 2014-05-23 18:12:57 +00:00
Andrey Prygunkov
deee5aff00 rolled back changes made in r1019 (not necessary anymore) 2014-05-22 17:09:34 +00:00
Andrey Prygunkov
8c36a4d4c6 fixed: renaming or deleting of temporary files could fail, especially when options "UnpackPauseQueue" and "ScriptPauseQueue" were not active (windows only) 2014-05-22 16:58:16 +00:00
Andrey Prygunkov
157074db29 fixed small memory leak (bug introduced in r1012) 2014-05-22 15:50:12 +00:00
Andrey Prygunkov
14ff04d2e3 improved error reporting: added error check when closing article file for writing 2014-05-21 21:27:51 +00:00
Andrey Prygunkov
d0e2d439aa if renaming of files fails, few more attempts are made; this should improve compatibility with virus scanner or sync software; better error reporting if renaming still fails 2014-05-20 21:20:49 +00:00
Andrey Prygunkov
159340a396 fixed: remaining size and time were not printed in remote console mode (bug introduced somewhere in v13) 2014-05-19 21:28:35 +00:00
Andrey Prygunkov
de6625bcaf updated links to doc-article "Extensions scripts" 2014-05-18 21:24:34 +00:00
Andrey Prygunkov
0d7ed691e6 fixed: program could hang when adding nzb-files from fetched RSS feed (bug introduced in r966) 2014-05-17 21:39:49 +00:00
Andrey Prygunkov
0721f723be fixed: nzb-files were sometimes not deleted from NzbDir (option "NzbCleanupDisk") 2014-05-15 17:02:10 +00:00
Andrey Prygunkov
2da7239ac6 fixed: if post-processing step "move" failed, the command "post-process again" did not try to move again 2014-05-08 19:54:25 +00:00
Andrey Prygunkov
7b4c07c837 refactor: better handling of completed URL downloads 2014-05-07 19:58:47 +00:00
Andrey Prygunkov
169c56f105 implemented general scripts concept which is an extension of the post-processing scripts concept initially introduced in v11; the general scripts concept applies to all scripts used in the program: scan-script, queue-script and scheduler-script (in addition to post-processing scripts); option "NzbProcess" renamed to "ScanScript"; option "NzbAddedProcess" renamed to "QueueScript"; option "DefScript" and "CategoryX.DefScript" renamed to "PostScript" and "CategoryX.PostScript" (options with old names are recognized and automatically converted on first settings saving); new option "TaskX.Script"; old option "TaskX.Process" kept for scheduling of external programs not related to nzbget (to avoid writing of intermediate proxy scripts); scan-script, queue-script and scheduler-script now work similar to post-processing scripts: -scripts must be put into scripts-directory; -scripts can be configured via web-interface and can have options; -multiple scripts can be chosen for each scripts-option, all chosen scripts are executed; -program and script options are passed to the script as env. variables;; renamed default directory with scripts from "ppscripts" to "scripts"; script signature indicates the type of script (post-processing, scan, queue or scheduler); one script can have mixed signature allowing it to be used for multiple purposes (for example a notification script can send a notification on both events: after adding to queue and after post-processing); result of RPC-method "configtemplates" has new fields "PostScript", "ScanScript", "QueueScript", "SchedulerScript" to indicate the purpose of the script; queue-script (formerly NzbAddedProcess) has new parameter "NZBNA_EVENT" indicating the reason of calling the script; currently the script is called only after adding of files to download queue and therefore the parameter is always set to "NZB_ADDED" but the queue-script can be called on other events in the future too 2014-05-06 15:36:15 +00:00
Andrey Prygunkov
d51cdfd7c4 icreased few wait intervals which were unnecessary too small 2014-05-04 13:18:28 +00:00
Andrey Prygunkov
3c02b139e8 eliminated loop waiting time in queue coordinator on certain conditions - may improve performance on very high speed connections 2014-05-03 11:28:39 +00:00
Andrey Prygunkov
9d660b9d4e extended info printed by remote command "nzbget -B dump" 2014-05-02 19:36:02 +00:00
Andrey Prygunkov
eaf7c61f01 fixed: for downloads with force priority the status was shown orange (instead of green) and the progress info was not shown during post-processing if the program was paused 2014-04-27 12:05:56 +00:00
Andrey Prygunkov
1234c05690 small adjustment in speed formatting 2014-04-26 21:40:18 +00:00
Andrey Prygunkov
fd5b6769fa small fix: data sizes exactly equal to 10, 100, 1000 MB or GB were formatted using 4 digits instead of 3 (one digit after decimal point too much) 2014-04-26 21:29:49 +00:00
Andrey Prygunkov
63db34070e data sizes above 1000 GB are now shown as TB in web-interface (instead of GB) 2014-04-26 21:21:29 +00:00
Andrey Prygunkov
b41cd3ff97 additon to r945: adjusted modules initialization to avoid possible bugs due to delayed thread starts 2014-04-25 23:02:51 +00:00
Andrey Prygunkov
f2406ee0e4 fixed: queue was not locked during loading on program start and that could cause problems 2014-04-25 22:56:33 +00:00
Andrey Prygunkov
4712c6a372 fixed: errors during loading of queue from disk state may render the already loaded parts useless too; now at least these parts of queue are used 2014-04-25 22:51:22 +00:00
Andrey Prygunkov
56dc1b2b6c fixed: the program could crash during parsing of malformed nzb-files 2014-04-25 21:37:47 +00:00
Andrey Prygunkov
cb13d00844 added force-priorities; downloads with priorities equal to or greater than 900 are downloaded and post-processed even if the program is in paused state (force mode); in web-interface the combo for choosing priority has new entry "force" (priority value 900); new fields "ForcedSizeLo", "ForcedSizeHi" and "ForcedSizeMB" returned by RPC-method "status"; 2014-04-22 20:26:29 +00:00
Andrey Prygunkov
7a11e8eb19 splitted files are now joined automatically (again) 2014-04-17 16:33:20 +00:00
Andrey Prygunkov
482af25c90 fixed: field "STATUS" was not set correctly for par-checked downloads without unpack (bug introduced in r992) 2014-04-16 17:51:28 +00:00
Andrey Prygunkov
0c17e21b85 fixed: par-check could hang on renamed and splitted files 2014-04-16 17:49:41 +00:00
Andrey Prygunkov
0acb6ac548 fixed: cancelling of active par-job sometimes didn't work 2014-04-16 17:48:44 +00:00
Andrey Prygunkov
7f339860ad fixed: command "Pause" was not shown in actions menu in download details dialog (bug introduced in r987) 2014-04-15 19:11:56 +00:00
Andrey Prygunkov
67cf38a291 experimental: download speeds above 1024 KB/s are now indicated in MB/s 2014-04-15 16:09:35 +00:00
Andrey Prygunkov
cad28b9fd5 addition to r990: fixed: download speeds above approx. 70 MB/s were not indicated correctly in web-interface and by RPC-method "status" 2014-04-15 16:04:18 +00:00
Andrey Prygunkov
80ceca6e28 new field "STATUS" in RPC-method "history" to allow third-party apps easier determine the status of an item without inspecting status-fields of every processing step; web-interface uses new field "STATUS" 2014-04-14 22:06:23 +00:00
Andrey Prygunkov
bdcb8864fb fixed: history status "SKIPPED" and "SCAN" for URL-items were not properly read from disk state 2014-04-14 20:55:04 +00:00
Andrey Prygunkov
ced444282f fixed: download speeds above approx. 70 MB/s were not indicated correctly in web-interface and by RPC-method "status" 2014-04-14 19:47:18 +00:00
Andrey Prygunkov
5c7c11e3f4 fixed: status "PP-QUEUED" was shown in green/orange instead of gray (bug introduced in r987) 2014-04-13 08:22:27 +00:00
Andrey Prygunkov
2b4628fb43 fixed: estimated time was not shown during download (bug introduced in r987) 2014-04-13 08:13:11 +00:00
Andrey Prygunkov
a0dbd75f35 RPC-method "listgroups" now returns new field "Status" making it easier for third-party apps to determine the status of download entry; added prefix "Post" to new post-processing fields added in r984; changed web-interface to use new field "Status"; fixed: progress-label during post-processing did not show output of the pp-scripts (bug introduced in r984); fixed: button "Log" were not shown in the download details dialog for active post-processing download (bug introduced in r984) 2014-04-12 21:30:19 +00:00
Andrey Prygunkov
a20877ea80 corrected html formatting for statistics data in details dialog 2014-04-12 21:21:05 +00:00
Andrey Prygunkov
e151691711 fixed: files state were not fully reloaded when option "ContinuePartial" was used (bug introduced in r982) 2014-04-12 08:14:01 +00:00
Andrey Prygunkov
f42db27eaa RPC-method "listgroups" now returns info about post-processing similar to info returned by method "postqueue"; RPC-method "postqueue" is obsolete now; web-interface requires less requests to NZBGet on each page update and it is now easier for third-party developers to obtain the info about download and post-processing status (no need to merge download queue and post queue) 2014-04-10 20:45:46 +00:00
Andrey Prygunkov
724eab69d8 per-server/per-nzb article completion statistics are now available for active downloads in details dialog (not only for history); the info on that page is constantly updated as long as the page is active (unless refresh is disabled); download age info removed from details dialog to save place (it is shown in the download list anyway) ;if backup news-servers start to be used for nzb-file a badge appears in the download list showing the percentage of articles downloaded from backup servers; click on the badge opens download details dialog directly on the completion page 2014-04-10 20:24:28 +00:00
Andrey Prygunkov
a83dbccc6c changed the way option "ContinuePartial" works: now the information about completed articles is stored in a special file in QueueDir; when option "DirectWrite" is active no separate flag-files per article are created in TempDir; the file contains additional information, which were not stored/available before; fixed: per-server/per-nzb article completion statistics could be inaccurate for nzb-files whose download were interrupted by reload/restart; per-server/per-nzb article completion statistics are now available via RPC-method "listgroups" for active downloads (not only for "history") 2014-04-10 20:06:55 +00:00
Andrey Prygunkov
178e987650 fixed: seconds/minutes/hours slots of volume statistics could be incorrectly cleared on program start due to time zone offset not yet initialized at the time the volume data was loaded 2014-04-09 21:09:45 +00:00
Andrey Prygunkov
3cd126f08d fixed: after deleting servers from config file the program could crash on start when loading server volume statistics data from disk 2014-04-09 20:09:08 +00:00
Andrey Prygunkov
b109123a43 fixed: data volume dialoge may show wrong current date due to incorrect time zone calculation 2014-04-05 23:29:52 +00:00
Andrey Prygunkov
c97e97d2cc updated all links to go to new domain (nzbget.net) 2014-04-04 21:45:48 +00:00
Andrey Prygunkov
160d098510 extended data volume statistics dialog with numbers for current day, month, all-time total and custom counter; the custom counter can be manually reset; new fields in the result of RPC-method "servervolumes"; new RPC-method "resetservervolume" 2014-04-04 20:44:46 +00:00
Andrey Prygunkov
a72e1924ca updated options descriptions in template config file 2014-04-03 20:34:27 +00:00
Andrey Prygunkov
fd7508f152 better handling of backwards system clock changes in data volume meter 2014-04-03 20:21:59 +00:00
Andrey Prygunkov
1de995f9d5 better handling of incorrect system clock date (such as 01-01-2000) in data volume meter 2014-04-02 20:56:11 +00:00
Andrey Prygunkov
47fbe6423e added collecting of download volume statistics data per news server; in web-interface the data is shown as chart in "Statistics and Status" dialog; new RPC-method "servervolumes" returns the collected data 2014-04-01 21:06:31 +00:00
Andrey Prygunkov
461c2a38a5 fixed: sometimes URLs were removed too early from the feed history causing them to be detected as "new" and fetched again; if duplicate check was not active the same nzb-files could be downloaded again 2014-03-28 21:32:55 +00:00
Andrey Prygunkov
d89036f9f3 1) the current time zone is now determined once on program start and if a clock adjustment is detected using system function "localtime"; the function "localtime" is no longer constantly used by the scheduler; this should solve the hibernation problem on synology NAS, even when task scheduler is used; 2) fixed: RSS feed preview dialog displayed slightly incorrect post ages because of the wrong time zone conversion 2014-03-21 21:35:32 +00:00
Andrey Prygunkov
998cb16bfa added new files 2014-03-20 21:44:00 +00:00
Andrey Prygunkov
4c2a8c2892 refactor: moved speed meter code from "QueueCoordinator" into new module "StatMeter" 2014-03-20 21:37:32 +00:00
Andrey Prygunkov
8d3afa0bb6 remote command "-B dump" now can be used also in release (non-debug) versions and prints useful debug data as "INFO" instead of "DEBUG" 2014-03-20 21:18:27 +00:00
Andrey Prygunkov
3fd7bbc0a3 refactor: reducing dependencies between modules 2014-03-20 21:14:39 +00:00
Andrey Prygunkov
bf66500aac reworking queue (continued): merged url queue into main download queue: urls added to queue are now immediately shown in web-interface; urls can be reordered and deleted; when urls are fetched the downloaded nzb-files are put into queue at the positions of their urls; this solves the problem with fetched nzb-files ordered differently than the urls if the fetching of upper (position wise) urls were completed after of the lower urls; removed options "ReloadUrlQueue" and "ReloadPostQueue" since there are no separate url- and post-queues anymore; nzb-files added via urls have new field "URL" which can be accessed via RPC-methods "listgroups" and "history"; new env. var. "NZBNP_URL", "NZBNA_URL" and "NZBPP_URL" passed to NzbProcess, NzbAddedProcess and PostProcess-scripts; removed remote command "--list U", urls are now shown as groups by command "--list G"; RPC-method "urlqueue" is still supported for compatibility but should not be used since the urls are now returned by method "listgroups", the entries have new field "Kind" which can be "NZB" or "URL" 2014-03-18 22:35:58 +00:00
Andrey Prygunkov
e28da0d7fd added new option "PropagationDelay", which sets the minimum post age to download; newer posts are kept on hold in download queue until they get older than the defined delay, after that they are downloaded 2014-03-11 22:05:27 +00:00
Andrey Prygunkov
f10bc886c4 column "age" in web-interface now shows minutes for recent posts (instead of "0 h") 2014-03-11 21:42:54 +00:00
Andrey Prygunkov
df578ac78b updated MSVC project file 2014-03-09 21:51:07 +00:00
Andrey Prygunkov
18e1557cf3 fixed: if during par-repair the downloaded extra par-files were damaged and the repair was terminated with failure status the post-processing scripts were executed twice sometimes 2014-03-09 21:18:57 +00:00
Andrey Prygunkov
30e6131cd7 improved par-check for damaged collections with multiple par-sets and having missing files: only orphaned files (not belonging to any par-set) are scanned when looking for missing files; this greatly decrease the par-check time for big collections 2014-03-05 23:46:29 +00:00
Andrey Prygunkov
44310fda20 impoved error reporting if par-renamer fails to rename files 2014-03-04 21:29:27 +00:00
Andrey Prygunkov
fb7431abb5 impoved error reporting if unpacker fails to move files 2014-03-04 18:24:16 +00:00
Andrey Prygunkov
5b109ea3ce fixed missing includes (bug introduced in r957) 2014-02-26 21:34:55 +00:00
Andrey Prygunkov
a671e9f925 refactor: splitted unit ScriptController.cpp into three units: Script.cpp, QueueScript.cpp, PostScript.cpp 2014-02-26 21:28:15 +00:00
Andrey Prygunkov
8168804f05 reorganized source code directory structure: created directory 'daemon' with several subdirectories and put all source code files there 2014-02-24 22:11:14 +00:00
Andrey Prygunkov
ec576ad0a9 fixed: damaged nzb-files containing multiple par-sets and not having enough par-blocks could cause a crash during par-check 2014-02-22 23:23:45 +00:00
Andrey Prygunkov
fa3abcfdec reworking queue (continued): refactor: download queue can now be accessed without QueueCoordinator; edit and save functions can now be called directly on download queue without accessing global objects QueueEditor and DiskState (the calls are rerouted to these objects internally) 2014-02-22 23:21:20 +00:00
Andrey Prygunkov
33864614e7 eliminated the distinction between manual pause and soft-pause; there is only one pause register now; options "ParPauseQueue", "UnpackPauseQueue" and "ScriptPauseQueue" do not change the state of the pause but instead are respected directly; RPC-methods "pausedownload2" and "resumedownload2" are aliases to "pausedownload" and "resumedownload" (kept for compatibility); field "Download2Paused" of RPC-method "status" is an alias to "DownloadPaused" (kept for compatibility); action "D2" of remote commands "--pause/-P" and "--unpause/-U" is not supported anymore 2014-02-19 21:45:56 +00:00
Andrey Prygunkov
f4bf68ee59 refactor: moved parts from unit "PrePostProcessor.cpp" into new unit "HistoryCoordinator.cpp" 2014-02-19 21:17:24 +00:00
Andrey Prygunkov
2b3d6f976d refactor: removed unneded parameter in one function 2014-02-14 21:04:20 +00:00
Andrey Prygunkov
641a3313ea fixed: health check action (pause or delete) didn't work properly (bug introduced in r949) 2014-02-14 20:55:21 +00:00
Andrey Prygunkov
08e6665ffc reworking queue (continued): remote command "-E/--edit" and RPC-method "editqueue" now use NZBIDs of groups to edit groups (instead of using ID of any file in the group as in older versions); remote command "-L/--list" for groups (G) and group-view in curses-frontend now print NZBIDs instead of "FirstID-LastID"; RPC-method "listgroups" returns NZBIDs in fields "FirstID" and "LastID", which are usually used as arguments to "editqueue" (for compatibility with existing third-party software); items queued for post-processing and not having any remaining files now can be edited (to cancel post-processing), which was not possibly before due to lack of "LastID" in empty groups; edit commands for download queue and post-processing queue are now both use the same IDs (NZBIDs) 2014-02-12 21:24:46 +00:00
Andrey Prygunkov
e6a7af4ab3 fixed: crash during post-processing if history was disabled (bug introduced in r943) 2014-02-10 20:56:54 +00:00
Andrey Prygunkov
a0030a7909 fixed: strange (damaged?) par2-files could cause a crash during par-renaming 2014-02-10 20:54:12 +00:00
Andrey Prygunkov
13c7a7986e fixed: when splitting paused downloads the destination download has shown icorrect paused size (bug introduced in r934) 2014-02-09 21:01:52 +00:00
Andrey Prygunkov
bd1ea872be adjusted modules initialization to avoid possible bugs due to delayed thread starts 2014-02-08 22:09:44 +00:00
Andrey Prygunkov
dfcd595bc1 fixed a locking issue happen in non-daemon mode (bug introduced in r943) 2014-02-05 22:06:43 +00:00
Andrey Prygunkov
f77c97c66a reworking queue (continued): merged post-processing queue into main download queue; changing the order of (pp-queued) items in the download queue now also means changing the order of post-processing jobs; priorities of downloads are now respected when picking the next queued post-processing job; ; the moving of download items in web-interface is now allowed for downloads queued for post-processing; removed actions of remote command "--edit/-E" and of RPC-method "editqueue" used to move post-processing jobs in the post-processing queue (the moving of download items should be used instead) 2014-02-04 22:30:52 +00:00
Andrey Prygunkov
ca53391bdb reworked download queue (continued): removed few (not more necessary) checks from duplicate manager 2014-02-03 20:50:53 +00:00
Andrey Prygunkov
d01dd904da reworking queue (continued): field "Priority" was removed from individual files; instead nzb-files (collections) now have field "Priority"; nzb-files now also have new fields "MinTime" and "MaxTime", which are set when nzb-file is parsed and then kept; this eliminates the need of recalculation file statistics (min and max priority, min and max time); removed action "FileSetPriority" from RPC-command "editqueue"; removed action "I" from remote command "--edit/-E" for individual files (now it is allowed for groups only) 2014-01-31 20:51:14 +00:00
Andrey Prygunkov
bb885bddd4 for downloads not having any (obviously named) par2-files the critical health is assumed 85% instead of 100% as the absense of par2-files suggests; this avoids the possibly false triggering of health-check action (detele or pause) for downloads having misnamed (obfuscated) par2-files; combined with improved fast par-renamer this provides proper processing of downloads with misnamed (obfuscated) par2-files 2014-01-28 22:14:50 +00:00
Andrey Prygunkov
3d8f2c62ea improved fast par-renamer: it now automatically detects and renames misnamed (obfuscated) par2-files 2014-01-28 22:06:56 +00:00
Andrey Prygunkov
7cdb5e86c6 reworked download queue (continued): removed fields FirstID and LastID from internal nzb-file file data; RPC-method "listgroups" returns ID of the last file in the group for both FirstID and LastID fields; the only usage for these fields were in RPC-method "editqueue" where LastID was preferred anyway; remote command "--list/-L" for groups now shows only LastID; curses-interface shows only LastID 2014-01-26 21:33:26 +00:00
Andrey Prygunkov
255b2b464d fixed: download priority was not shown correctly in web-interface (and via RPC) (bug introduced in r934) 2014-01-25 10:30:04 +00:00
Andrey Prygunkov
0ef771ca15 avoiding unnecessary calls to system function "localtime" from scheduler if no tasks are defined; this solves hibernation issues on synology NAS (but requires no usage of scheduler) 2014-01-23 21:03:04 +00:00
Andrey Prygunkov
a3207496b6 fixed: post-processing scripts were not executed in standalone mode ("nzbget /path/to/file.nzb") 2014-01-23 20:46:37 +00:00
Andrey Prygunkov
741724973c reworked download queue (continued): 1) current download data such as remained size or size of paused files is now internally automatically updated on related events (download of article is completed, queue edited, etc.); 2) this eliminates the need of calculating this data upon each RPC-request (from web-interface) and greatly decrease CPU load of processing RPC-requests when having large download queue (and/or large nzb-files in queue) 2014-01-21 21:56:43 +00:00
Andrey Prygunkov
3375c91b56 reworked download queue: 1) queue now holds nzb-jobs instead of individual files (contained within nzbs); 2) this drastically improves performance when managing queue containing big nzb-files on operations such as pause/unpause/move items; 3) tested with queue of 30 nzb-files each 40-100GB size (total queue size 1.5TB) - queue managing is fast even on slow device; 4) limitation: individual files (contained within nzbs) now cannot be moved beyond nzb borders (in older version it was possible to move individual files freely and mix files from different nzbs, although this feature was not supported in web-interface and therefore was not much known); 5) this change opens doors for further speed optimizations and integration of download queue with post-processing queue and possibly url-queue; 6) NOTE: make backup of your queue-directory before trying this (devel) version 2014-01-21 21:45:47 +00:00
Andrey Prygunkov
67da9d7233 updated version string to 13.0-testing 2014-01-21 20:51:58 +00:00
Andrey Prygunkov
4b228b32f0 OSX-app: restarting via troubleshooting-menu could result in an error message "Could not establish connection to background process" although the process was successfully restarted 2014-01-02 21:06:03 +00:00
Andrey Prygunkov
f66189de6b updated version string (preparing to release 12.0) 2014-01-02 20:32:11 +00:00
Andrey Prygunkov
743805ecd5 updated ChangeLog 2014-01-01 21:27:29 +00:00
Andrey Prygunkov
6c9dcea08c fixed: for rar-files with old naming scheme only files with extensions rxx and sxx were deleted during cleanup leaving the files with extensions txx, uxx, etc. 2013-12-25 22:43:58 +00:00
Andrey Prygunkov
fa1944090d fixed wrong size of progress wheel during adding of URL in add dialog 2013-12-24 21:00:17 +00:00
Andrey Prygunkov
04cf428619 fixed: duplicate mode was not saved from history dialog 2013-12-24 18:38:10 +00:00
Andrey Prygunkov
86d6b00886 added new command "Download again" for history items; new action "HistoryRedownload" of RPC-method "editqueue"; for controlling via command line: new action "A" of subcommand "H" of command "--edit/-E" 2013-12-21 21:39:49 +00:00
Andrey Prygunkov
38429d98df fixed a potential problem in incorrect using of one library function 2013-12-19 21:28:45 +00:00
Andrey Prygunkov
0c9667fe58 fixed memory leak in RSS feed parser (Posix only) 2013-12-19 20:28:50 +00:00
Andrey Prygunkov
3c6bb7be4c 1) NZBIDs are now generated with more care avoiding numbering holes possible with previous versions; 2) fixed: new NZBIDs were generated on each refresh of web-ui (bug introduced in r811); 3) for queue disk state written by versions r811-r920 the NZBIDs are renumbered starting from 1 2013-12-18 20:19:42 +00:00
Andrey Prygunkov
40fa732122 do not closing rss filter dialog if a communication error occurs during first fetching of rss and the filter was already edited by user (this allows to save the filter) 2013-12-16 21:15:49 +00:00
Andrey Prygunkov
01e2e25bce fixed potential segfault 2013-12-12 23:43:38 +00:00
Andrey Prygunkov
29e916dcdd NZBGet.app for OSX: fixed: one text message was not properly shown 2013-12-10 20:44:58 +00:00
Andrey Prygunkov
1bfa7610ae improved error reporting when creation of temporary output file fails 2013-12-10 20:37:02 +00:00
Andrey Prygunkov
fb94c32bb4 fixed: when deleting download, if all remaining queued files are par2-files the disk cleanup should not be performed, but it was sometimes 2013-12-10 20:25:07 +00:00
Andrey Prygunkov
94ad26d818 fixed: RSS feed filter fields "age" and "size" did not work (bug introduced in r908) 2013-12-03 21:39:18 +00:00
Andrey Prygunkov
f323addc1c added new option "TimeCorrection" to adjust conversion from system time to local local (solves issues with scheduler when using a binary compiled for other platform) 2013-11-28 21:03:01 +00:00
Andrey Prygunkov
5559c91c0e do not closing rss filter dialog if a communication error occurs during editing of rss filter (this allows to save the filter into clipboard at least) 2013-11-28 20:46:32 +00:00
Andrey Prygunkov
6cc5eab94b fixed: some of actions for remote command "--edit/-E" did not work properly (bug introduced in r900) 2013-11-24 20:15:41 +00:00
Andrey Prygunkov
c2a3450c8f refactor: removed many unneeded pointer-not-null-safety-checks 2013-11-24 19:29:52 +00:00
Andrey Prygunkov
01e1ec0794 fixed line endings in one source file 2013-11-24 13:47:36 +00:00
Andrey Prygunkov
ea381cde90 fixed encoding issue for non-ASCII characters in DNZB-Headers 2013-11-18 20:37:20 +00:00
Andrey Prygunkov
26074c67c6 extended RSS filters: 1) added search field "description"; 2) any newznab attribute can now be used as search field with prefix "attr-" (for example "attr-genre"); 3) removed search fields "genre" and "rating" (use "attr-genre" and "attr-rating" instead 2013-11-17 21:57:32 +00:00
Andrey Prygunkov
e2b13fcda5 fixed: for downloads deleted by health-check status was shown as "DELETED-HEALTH" instead of "FAILURE" 2013-11-14 20:17:45 +00:00
Andrey Prygunkov
0130852d9a added scheduler command "FetchFeed"; renamed RPC-method "fetchfeeds" to "fetchfeed" and added parameter "ID" 2013-11-12 20:54:45 +00:00
Andrey Prygunkov
a027af9e84 if unpack fails with write error (usually because of not enough space on disk) this is shown as status "Unpack: space" in web-interface; this unpack-status is handled as "success" by duplicate handling (no download of other duplicate); also added new unpack-status "wrong password" (only for rar5-archives); env.var. NZBPP_UNPACKSTATUS has two new possible values: 3 (write error) and 4 (wrong password); updated pp-script "EMail.py" to support new unpack-statuses 2013-11-08 21:54:44 +00:00
Andrey Prygunkov
ce81b3d4da added status filter buttons to history page 2013-11-07 21:01:44 +00:00
Andrey Prygunkov
96d8ff3cb7 added new scheduler commands "ActivateServer" and "DeactivateServer"; combined options "TaskX.DownloadRate" and "TaskX.Process" into one option "TaskX.Param", also used by two new commands 2013-11-07 20:55:33 +00:00
Andrey Prygunkov
b67c354fdb better handling of obfuscated nzb-files containing multiple files with same names; removed option "StrictParName" which was not working good with obfuscated files; if more par-files are required for repair the files with strict names are tried first and then other par-files 2013-11-07 20:14:23 +00:00
Andrey Prygunkov
9a610197ea when a duplicate backup is returned from history to download queue its paused-state is now correctly restored 2013-11-05 21:11:47 +00:00
Andrey Prygunkov
1109c3423c reworked duplicate handling: 1) when a duplicate is added to queue it is now moved to history as dupe-backup instead of being paused in download queue; 2) if download fails the best duplicate is moved from history back to queue for download (if there are no duplicates in queue); this makes it easier to manage download queue without worrying about properly pausing/unpausing duplicates); 3) badges with duplicate info are not shown in the list of downloads and history anymore; if necessary they can be activated by manually editing setting "dupeBadges" in index.js; 4) when deleting downloads from queue there are three options now: "move to history", "move to history as duplicate" and "delete without history tracking"; 5) new actions "GroupDupeDelete" and "GroupFinalDelete" in addition to "GroupDelete" in RPC-method "editqueue"; 6) DUPE-mark for history records can now be set or cleared via history dialog; 7) new action "HistorySetDupeBackup" in RPC-method "editqueue"; 8) when deleting downloads from queue the messages about deleted individual files are now printed as "detail" instead of "info"; 9) removed command "Mark as duplicates" from edit dialog for multiple selected downloads and from RPC-method "editqueue"; the command is not needed anymore since all duplicate properties are now changeable 2013-11-04 20:59:20 +00:00
Andrey Prygunkov
18fcb8d0ad if unpack did not find archive files the par-check is not requested anymore if par-rename was already done 2013-10-30 20:15:07 +00:00
Andrey Prygunkov
a5845ed0d9 improved par-check: if main par2-file is corrupted and can not be loaded other par2-files are downloaded and then used as replacement for main par2-file 2013-10-30 20:06:18 +00:00
Andrey Prygunkov
3392fa59fe improved support of non-ascii characters in file names on windows (again) 2013-10-25 20:09:25 +00:00
Andrey Prygunkov
95e816572a small restructure in settings order: 1) combined sections "REMOTE CONTROL" and "PERMISSIONS" into one section with name "SECURITY"; 2) moved sections "CATEGORIES" and "RSS FEEDS" higher in the section list 2013-10-24 20:33:40 +00:00
Andrey Prygunkov
3acb3aab9f addition to r894: commited missing changes in another file 2013-10-24 20:24:43 +00:00
Andrey Prygunkov
509860d890 fixed: if unpack fails the created destination directory was not automatically removed (only if option "InterDir" was active) 2013-10-24 20:17:51 +00:00
Andrey Prygunkov
61dcc467ee added support for rar5-format when checking signatures of archives with non-standard file extensions 2013-10-24 20:15:59 +00:00
Andrey Prygunkov
ae6601d9e3 improved handling of non-ascii characters in file names on windows 2013-10-23 19:22:02 +00:00
Andrey Prygunkov
33733af3c5 when option "UnpackCleanupDisk" is active all archive files are now deleted from download directory without relying on output printed by unrar; this solves issues with non-ascii-characters in archive file names on some platforms and especially in combination with rar5 2013-10-23 19:16:20 +00:00
Andrey Prygunkov
da7c0ab7d6 variable substitution now works for all options, including "FeedX.Filter"; if variable could not be found no error is printed anymore because character sequence used to define variable referenece can be part of feed filter and therefore should not be reported as error 2013-10-22 21:34:36 +00:00
Andrey Prygunkov
afd156b51f when option "InterDir" is used the intermediate destination directory names now include unique number to avoid several downloads with same name to use the same directory and interfere with each other 2013-10-22 21:17:02 +00:00
Andrey Prygunkov
5d549b7c60 option "InterDir" is now active by default 2013-10-22 21:05:13 +00:00
Andrey Prygunkov
1b5671dc87 increased width of Update-dialog to accommodate 80 character columns; this improves output of console tools like wget relying on standard terminal width 2013-10-22 21:04:22 +00:00
Andrey Prygunkov
87e2893505 for external script exec-permissions are now added automatically; this makes installation of pp-scripts and other scripts easier 2013-10-21 20:24:02 +00:00
Andrey Prygunkov
89443e342f if option "ParRename" is disabled (not recommended) unpacker does not initiate par-rename anymore, instead the full par-verify is performed then; refactor: simplified the code requesting par-rename after unpack 2013-10-20 20:59:49 +00:00
Andrey Prygunkov
de5ed803ed for archives including par-files for renaming of extracted files the par-renaming now works for extracted sub-directories too 2013-10-19 21:16:53 +00:00
Andrey Prygunkov
528f9a7ec4 added new files to VC project 2013-10-18 20:38:41 +00:00
Andrey Prygunkov
a1f7656fe4 addition to r879: removed check if download has downloaded files 2013-10-17 22:00:37 +00:00
Andrey Prygunkov
a5703a55eb added automatic updates: new button "Check for updates" on settings tab of web-interface, in section "SYSTEM", initiates check and shows dialog allowing to install new version; it is possible to choose between stable, testing and development branches; this feature is for end-users using binary packages created and updated by maintainers, who need to write an update script specific for platform; the script is then called by NZBGet when user clicks on install-button; the script must download and install new version; for more info visit http://nzbget.sourceforge.net/Packaging 2013-10-17 19:35:43 +00:00
Andrey Prygunkov
1275b85465 option "DiskSpace" now checks space on "InterDir" in addition to "DestDir" 2013-10-17 19:11:46 +00:00
Andrey Prygunkov
52dc2738a1 when removing duplicates from queue after successful download now removing only unpaused items which does not have any downloaded articles; this prevents deletion of higher score duplicates 2013-10-17 19:09:40 +00:00
Andrey Prygunkov
7c0f7cbdc2 addition to r877: commited missing changes in header-file 2013-10-17 18:56:26 +00:00
Andrey Prygunkov
c14ef8bd13 fixed: in RSS filters when using substitution variables referring to matches a wrong variable could be substituted if substring search did not start with star-character 2013-10-17 18:52:16 +00:00
Andrey Prygunkov
ce62ae9f50 fixed: superfluous spaces in RSS filter caused non-matching 2013-10-14 20:33:52 +00:00
Andrey Prygunkov
133347f884 support for rar-archives with non-standard extensions is now limited to file extensions consisting of digits; this is to avoid extracting of rar-archives having non-rar extensions on purpose (example: .cbr) 2013-10-09 19:47:11 +00:00
Andrey Prygunkov
ca4f56cb04 fixed: detection of par-files inside archives did not work properly 2013-10-09 19:45:35 +00:00
Andrey Prygunkov
f512ae973d history records with failed script status are now shown as "PP-FAILURE" in history list (instead of just "FAILURE") 2013-10-09 19:43:53 +00:00
Andrey Prygunkov
04cceb314e updated descriptions of few options 2013-10-09 19:38:49 +00:00
Andrey Prygunkov
4138e10788 removed option "Also delete downloaded files" from history delete confirmation dialog; for failed downloads cleanup is now performed if option "DeleteCleanupDisk" is active; in RPC-Method "editqueue" removed actions "HistoryDeleteCleanup" and "HistoryFinalDeleteCleanup" 2013-10-09 19:37:44 +00:00
Andrey Prygunkov
5d11b9aa97 added special handling for files ".AppleDouble" and ".DS_Store" during unpack to avoid problems on NAS having support for AFP protocol (used on Mac OSX) 2013-10-08 19:44:06 +00:00
Andrey Prygunkov
6033c2b3ce fixed: invalid "Offset" passed to RPC-method "editqueue" or command line action "-E/--edit" could crash the program 2013-10-08 19:41:26 +00:00
Andrey Prygunkov
dfb28dc155 history records can now be either permanently deleted or just hidden from history list; hiding (instead of deleting) is recommended for proper work of duplicate handling; in addition it is now possible to delete downloaded files; RPC-method "editqueue" has now four actions to delete history records: "HistoryDelete", "HistoryDeleteCleanup", "HistoryFinalDelete", "HistoryFinalDeleteCleanup"; action "HistoryDelete" which has existed before now hides records, already hidden records are ignored; button "Old" in web-interface on history tab renamed to "Hidden"; badge "DUP", used to distinguish old history records changed to "hidden" 2013-10-08 19:35:59 +00:00
Andrey Prygunkov
756b29d9ed fixed a potential seg. fault in a commonly used function 2013-10-07 21:15:46 +00:00
Andrey Prygunkov
dc7a3af768 destination directory for option "DestDir" is not checked/created on program start anymore (only when a download starts); this helps when DestDir is mounted to a network drive which is not available on program start 2013-10-07 19:29:36 +00:00
Andrey Prygunkov
baf3a2d915 addition to r863: fixed: option "UrlForce" did not really worked 2013-10-07 16:15:38 +00:00
Andrey Prygunkov
25832ab2ea option "HealthCheck" is now set to "Delete" by default; the previously default setting "Pause" did not work well with automatic duplicate handling 2013-10-07 16:14:14 +00:00
Andrey Prygunkov
c95c0401eb added new option "UrlForce" to allow URL-downloads (including fetching of RSS feeds and nzb-files from feeds) even if download is paused; the option is active by default 2013-10-06 18:55:09 +00:00
Andrey Prygunkov
936580a924 moved command "Duplicate properties" from actions-menu into button "Dupe" near "PP-Parameters"; renamed button "PP-Parameters" to "Postprocess" and button "PP-Messages" (visible only during post-processing) to "Log" to free space for new Dupe-button 2013-10-05 13:03:52 +00:00
Andrey Prygunkov
00df147688 fixed: NZBGet.app (OSX): when the option "Show web-interface on start" was active, it took too long to initialize and could result in an error message "Could not establish connection to background process" 2013-10-03 20:35:01 +00:00
Andrey Prygunkov
8273dcfdfc fixed: error reading diskstate when upgrading from r808 or an older version 2013-10-03 13:34:33 +00:00
Andrey Prygunkov
81e2dc3635 improved parsing of multi-episodes from titles when generating dupekeys using item-options "rageid" or "series" and season/episode numbers 2013-10-01 16:52:23 +00:00
Andrey Prygunkov
94611cd80b fixed: when generating dupekeys with item-options "rageid" or "series" the season/episode numbers were not parsed from title if they were not used in the filter string 2013-10-01 16:49:12 +00:00
Andrey Prygunkov
28af81142f added two new item-options for RSS filter rules "Accept" and "Options": option "rageid" generates duplicate key using custom rageid and season/episode numbers; option "series" generates duplicate key using custom series name (any unique string) and season/episode numbers 2013-09-30 19:38:26 +00:00
Andrey Prygunkov
b3fd3ec0ba hiding badges for dupekeys in downloads/history lists if option "DupeCheck" is disabled 2013-09-30 19:31:27 +00:00
Andrey Prygunkov
44518d5b33 fixed: if download failed an existing queued duplicate was not automatically unpaused 2013-09-30 19:29:53 +00:00
Andrey Prygunkov
323e74f50f fixed: space character was not separator in word search mode in RSS filter 2013-09-29 10:47:25 +00:00
Andrey Prygunkov
a972e755d1 changes in duplicate handling: 1) when comparing two items if the both have dupekey only dupekeys ae compared (names not checked); 2) when a new item without dupekey is added and there is another items with the same name having dupekey its dupekey was copied to new item, this is disabled now; 3) fixed: command "Mark as Good" in history removed all duplicates but should remove only records with status "DUPE" 2013-09-28 21:16:16 +00:00
Andrey Prygunkov
49b6292f7f changes in duplicate handling: removed internal field "DupeMark" showing the item having duplicates; this flag was not always in sync with reality and it was used only to show (or not) badges with duplicate key in web-interface; now badges are always shown for items having non-empty duplicate keys; the badges becomes red if duplicate mode is set to "force" 2013-09-28 19:53:20 +00:00
Andrey Prygunkov
dd27dc1503 removed command "Unmark Duplicate" from actions menu and from command line syntax; the duplicate mark is removed automatically once the duplicate mode is set to "force"; otherwise manually removing duplicate mark does not make much sense since the titles are checked for duplicates anyway 2013-09-27 20:45:24 +00:00
Andrey Prygunkov
547ed1fd26 fixed compiler warning 2013-09-27 20:25:10 +00:00
Andrey Prygunkov
c387b0d069 duplicate properties (dupekey, dupescore and dupemode) can now be viewed and changed in download-edit-dialog and history-edit-dialog via new command "Duplicate properties" in actions menu 2013-09-27 19:56:16 +00:00
Andrey Prygunkov
ba2af4d84d fixed: in rss feed filter the substitution variable did not work 2013-09-26 21:06:43 +00:00
Andrey Prygunkov
20b7c6a823 do not showing dupemode in the build-filter-dialog if the mode is set to default value "score" 2013-09-26 20:22:28 +00:00
Andrey Prygunkov
b2edab0452 1) refactor: moved feed related classes from unit "DownloadInfo" to new unit "FeedInfo"; 2) rss filter fields "season" and "episode" are now available for all feeds (not restricted to newznab); if the feed does not have the fields, they are automatically parsed from feed title; 3) fields "season" and "episode" can now be used as substitution variables in option "dupekey" of rss filter command "Options" 2013-09-26 19:37:25 +00:00
Andrey Prygunkov
a72dc67268 added new search fields to RSS feed filter: imdbid, priority, dupekey and dupescore 2013-09-26 19:15:59 +00:00
Andrey Prygunkov
11b9745268 added OR-operator and groups (braces) to RSS filter syntax 2013-09-26 19:10:26 +00:00
Andrey Prygunkov
1d1d49a3c9 addition to r755: fixed: passwords containing special characters such as TAB were not properly read from nzb-files metatag 2013-09-24 20:42:32 +00:00
Andrey Prygunkov
31a4e7a22c by adding nzb-file to queue via RPC-methods "append" and "appendurl" the actual format of the file is checked and if nzb-format is detected the file is added even if it does not have .nzb extension 2013-09-24 19:35:54 +00:00
Andrey Prygunkov
dca13f0749 better handling of queued duplicates in command "Mark as bad" 2013-09-24 19:33:35 +00:00
Andrey Prygunkov
d377eee11c when adding nzb-file in in dupemode "score" the file is skipped if dup-history has a success-item with the same or higher score and recent history does not have a success-duplicate 2013-09-24 19:31:56 +00:00
Andrey Prygunkov
196052efed removed prefix "dupe" from badges in download and history lists 2013-09-24 19:28:36 +00:00
Andrey Prygunkov
694c5104fa fixed: incorrect health calculation for downloads consisting of only par-files 2013-09-23 20:26:29 +00:00
Andrey Prygunkov
acbf765851 fixed compiler warning 2013-09-23 20:24:31 +00:00
Andrey Prygunkov
ef06dfb7b3 replaced parameter "NoDupeCheck (yes, no)" with "DupeMode (score, all, force)" when adding nzb-files to queue using RPC-methods "append" and "appendurl"; changed option "nodupe" to "dupemode" in RSS filter commands "Append" and "Options" 2013-09-23 20:18:54 +00:00
Andrey Prygunkov
613432ef2c tuned algorithm calculating maximum threads limit to allow more threads for backup server connections (related to option "TreadLimit" removed in v11); this may sometimes increase speed when backup servers were used 2013-09-22 13:39:43 +00:00
Andrey Prygunkov
512dc87b3f fixed: items were removed from history too soon (option "KeepHistory") 2013-09-22 12:59:30 +00:00
Andrey Prygunkov
480f034301 fixed few compiler warnings 2013-09-20 21:55:47 +00:00
Andrey Prygunkov
39275ce133 improved smart duplicates features: added functions "Mark as Bad" and "Mark as Good" for history items; when a history item having success-status is marked as bad: 1) it is considered as failure by any duplicate check performed later; 2) if history has duplicates with dupe-status (dupe-backups) they are all moved (as paused) to download queue and one of them (with the highest duplicate score) is unpaused (downloaded); when a history item is marked as good: 1) it is considered as success by any duplicate check performed later; 2) no other duplicates will be added to history as dupe-backups anymore; 3) if history has duplicates with dupe-status (dupe-backups) they are all removed from recent history (moved to dup-history); new actions "HistoryMarkBad" and "HistoryMarkGood" in RPC-method "editqueue"; new actions "B" and "G" of command "--edit/-E" for history items (subcommand "H") 2013-09-20 20:45:07 +00:00
Andrey Prygunkov
c73fa0b42d addition to r817: fixed: error by reading dup-history from disk state 2013-09-19 19:45:31 +00:00
Andrey Prygunkov
3d4ed43337 changed format of generated dupekey for tv shows; now using season and episode exactly as they are passed by rss feed 2013-09-18 20:47:46 +00:00
Andrey Prygunkov
74067fd515 source nzb-files are now deleted when download-item leaves queue and history (option "NzbCleanupDisk") 2013-09-18 20:27:47 +00:00
Andrey Prygunkov
9538771eef refactor: addition to r825: small optimization 2013-09-18 20:09:32 +00:00
Andrey Prygunkov
7ecb968e23 if download was deleted by duplicate check its status in the history is now shown as "DUPE" instead of just "DELETED" 2013-09-18 19:49:59 +00:00
Andrey Prygunkov
c9365732d9 option values in RSS filter command "Options" can now refer to pattern groups in regular expressions 2013-09-17 19:33:56 +00:00
Andrey Prygunkov
d41c13ac29 fixed: if duplicate check has prevented adding file to queue the unneeded disk state files were not deleted from queue directory 2013-09-17 19:18:33 +00:00
Andrey Prygunkov
1b2fa2b2e8 extended word/substring search in RSS feed filters with pattern character "#" which matches one digit 2013-09-17 19:06:11 +00:00
Andrey Prygunkov
b9a9113abe addition to r820: fixed: when old disk state was converted the content hashes were not initialized (bug introduced in r816) 2013-09-17 19:02:59 +00:00
Andrey Prygunkov
1dd9dbec6c option values in RSS filter command "Options" can now refer to pattern groups in search string 2013-09-16 20:18:58 +00:00
Andrey Prygunkov
84efe5447b fixed compiler warning 2013-09-16 19:51:27 +00:00
Andrey Prygunkov
61bab55d11 fixed: when old disk state was converted the content hashes were not initialized (bug introduced in r816) 2013-09-16 19:49:45 +00:00
Andrey Prygunkov
85d05153f7 changed syntax of option "dupekey" of command "Option" in RSS filter: option "dupekey" now sets duplicate key (overrides existing key); option "dupekey+" adds to existing duplicate key 2013-09-15 14:02:35 +00:00
Andrey Prygunkov
f9bc316c98 rss filter command "Options" can now increase priority and dupe scrore using new option names "priority+" and "dupecore+" 2013-09-15 08:50:06 +00:00
Andrey Prygunkov
c4adc8d9be improved detection of same nzb-files acquired from different sources (nzb-sites): 1) the order of individual files as well as the order of articles in nzb-files do not matter; 2) individual files having extensions listed in option "ExtCleanupDisk" are now excluded from content comparison (unless these are par2-files, which are never excluded) 2013-09-14 21:20:31 +00:00
Andrey Prygunkov
169719c62d improved duplicate check: when history item expires (as defined by option "KeepHistory") and the duplicate check is active (option "DupeCheck") the item is not completely deleted from history; instead the amount of stored data reduces to minimum required for duplicate check (about 150 bytes vs 2000 bytes for full history item); such old history items are not shown in web-interface by default (to avoid transferring of large amount of history items); new button "Old" in web-interface to show old history items; the items are marked with badge "DUP"; 2013-09-13 20:13:09 +00:00
Andrey Prygunkov
a509a491af improved duplicate check: 1) added check for nzb-file content to avoid queueing of exactly same files (even with different names); 2) when nzb-file is added with option "Disable duplicate check" the option now works only during adding, it does not exclude the file from later checks (when adding other files) 2013-09-12 15:54:14 +00:00
Andrey Prygunkov
aed8e26062 extended add-dialog with options "Add paused" and "Disable duplicate check" 2013-09-11 20:21:49 +00:00
Andrey Prygunkov
cec414126d improved smart duplicates: nzb-files now have field "DupeScore" which can be set from rss filter using command "Options"; items with higher duplicate scores are downloaded even if the history already has successfully downloaded item (with lower score); changed the syntax of rss filter command "Options" to allow use of more options (and easy add new options in the future); new options "DupeScore", "DupeKey" and "NoDupe" to fine tune duplicate handling; updated description of option "DupeCheck" 2013-09-11 20:18:52 +00:00
Andrey Prygunkov
6bf96ab808 addition to r811: fixed: items added from feed view dialog were not marked as duplicates 2013-09-09 20:52:51 +00:00
Andrey Prygunkov
d8d9f72985 added smart duplicates feature: similar nzb-files are automatically marked as duplicates; queue items can be also manually marked as duplicates using new commands in multi-edit-dialog (action menu); duplicate-mark be manually removed using using new command in multi-edit-dialog and edit-dialog (action menu); duplicates are added in paused state; if download of first duplicates fail, another duplicate is unpaused; if download succeeds all remaining duplicates are removed from queue; an item marked as duplicate has field "DupeKey" which has the same value for all duplicates of the title; this field is shown in web-interface near nzb-name (in a short form to save screen place); new actions "GroupMarkDupe" and "GroupUnMarkDupe" of RPC-command "editqueue" to manually mark/unmark duplicates; new subcommands "DM" and "DU" of command "--edit/-E" to manually mark/unmark duplicates;;; if url-download results in a file without nzb-extension a history item with status "Scan: skipped" is created to inform user about this fact; RPC-commands "listgroups", "postqueue" and "history" now return more info about nzb-item (many new fields); removed option "MergeNzb" because it conflicts with duplicate handling, items can be merged manually if necessary 2013-09-09 20:31:17 +00:00
Andrey Prygunkov
cecb2e8f4c failed article downloads are now logged as "detail" instead of "warning" to reduce number of warnings for downloads removed from server (DMCA); one warning is printed for a file with a summary of number of failed downloads for the file 2013-09-08 20:34:53 +00:00
Andrey Prygunkov
d9ae28d3ed fixed compiler errors when configured with switch "--disable-parcheck" 2013-09-03 20:51:51 +00:00
Andrey Prygunkov
761078625e addition to r807: corrected makefile 2013-09-01 09:28:06 +00:00
Andrey Prygunkov
be37a75928 set svn keywords 2013-08-31 21:14:39 +00:00
Andrey Prygunkov
884e9fb3c9 created NZBGet.app - NZBGet is now a user friendly Mac OSX application with easy installation and seamless integration into OS UI: works in background, accessible via menubar icon 2013-08-31 21:05:31 +00:00
Andrey Prygunkov
8821502a81 updated support for DNZB-Headers: removed "X-DNZB-Link", added "X-DNZB-Details" 2013-08-29 19:56:29 +00:00
Andrey Prygunkov
f66c012df6 pp-scripts can now set post-processing parameters by printing command "[NZB] NZBPR_varname=value"; this allows scripts which are executed sooner to pass data for scripts executed later 2013-08-28 22:27:50 +00:00
Andrey Prygunkov
baf996fc06 new env-var "NZBPP_FINALDIR" passed to pp-scripts 2013-08-28 21:59:02 +00:00
Andrey Prygunkov
e56a1d3274 refactor: small code rework in passing parameters to post-processing scripts 2013-08-28 20:11:05 +00:00
Andrey Prygunkov
b04af33fb5 addition to r794: removed mistakenly added pp-parameter "NZBPP_DELETED"; since post-processing is not performed for deleted downloads, this parameter has no use 2013-08-28 19:17:41 +00:00
Andrey Prygunkov
1f53d32a62 new option "ParRename" to force par-renaming as a first post-processing step (active by default); this saves an unpack attempt and is even more useful if unpack is disabled 2013-08-28 15:08:37 +00:00
Andrey Prygunkov
534aeb3d07 addition to r795: renamed option "ApprovedIP" to "AuthorizedIP" 2013-08-26 22:07:43 +00:00
Andrey Prygunkov
a3693d0a45 refactor: fixed compiler warnings regarding "printf" 2013-08-26 16:05:29 +00:00
Andrey Prygunkov
967a6bd4a4 fixed potential buffer overflow in remote client 2013-08-26 10:02:45 +00:00
Andrey Prygunkov
c589c9b9ec addition to r795: removed a (wrong) tip about router from option decription 2013-08-24 18:56:47 +00:00
Andrey Prygunkov
d10ad4835b added new option "ApprovedIP" to set the list of IP-addresses which may connect without authorization 2013-08-23 22:05:29 +00:00
Andrey Prygunkov
38a273b195 added collecting of server usage statistical data for each download: number of successful and failed article downloads per news server; new page in history dialog shows collected statistics; new fields in RPC-method "history": ServerStats (array), TotalArticles, SuccessArticles, FailedArticles; new env. vars passed to pp-scripts: NZBPP_TOTALARTICLES, NZBPP_SUCCESSARTICLES, NZBPP_FAILEDARTICLES and per used news server: NZBPP_SERVERX_SUCCESSARTICLES, NZBPP_SERVERX_FAILEDARTICLES; also new env.vars: DELETED, HEALTHDELETED 2013-08-16 21:53:32 +00:00
Andrey Prygunkov
9618f46188 fixed scrolling to the top of page happening by clicking on items in downloads/history lists and on action-buttons in edit-download and history dialogs 2013-08-15 17:21:01 +00:00
Andrey Prygunkov
2562b384bc addition to r779: fixed: health were not shown for items with status "PP-QUEUED" 2013-08-14 22:00:39 +00:00
Andrey Prygunkov
bc8d133b69 post-processing progress label is now automatically trimmed if it doesn't fill into one line; this avoids layout breaking if the text is too long 2013-08-14 21:10:02 +00:00
Andrey Prygunkov
beb9967ad0 addition to r775: fixed: the confirmation by leaving settings page could be sometimes shown even if there were no changes made 2013-08-14 21:07:00 +00:00
Andrey Prygunkov
433baf0923 added support for http redirects when fetching URLs 2013-08-13 17:22:45 +00:00
Andrey Prygunkov
423da8b785 addition to r782: fixed: when adding files to queue the info about category and priority could get lost for some files 2013-08-12 18:39:17 +00:00
Andrey Prygunkov
f4708d2b1a fixed: final directory were not properly shown (Windows only) (bug introduced in r765) 2013-08-12 18:35:28 +00:00
Andrey Prygunkov
b00b7f7c31 critical health was sometimes not calculated properly on certain CPU architectures (mipsel) 2013-08-11 14:58:19 +00:00
Andrey Prygunkov
ec4110cb2c 1) when a duplicate file was detected in collection it was automatically deleted (if option DupeCheck is active) but the total size of collection was not updated; 2) when deleting individual files the total count of files in collection was not updated 2013-08-10 20:30:33 +00:00
Andrey Prygunkov
b2f02c7fa6 addition to r779: added calculation of critical health for old items in download queue (added to queue with program versions older than r779) 2013-08-10 20:03:14 +00:00
Andrey Prygunkov
aeb561c240 added automatic par-renaming of extracted files if archive includes par-files 2013-08-10 09:04:33 +00:00
Andrey Prygunkov
97a6abca0e fixed: when multiple nzb-files were added via URL (rss including) at the same time the info about category and priority could get lost for some of files 2013-08-09 20:37:41 +00:00
Andrey Prygunkov
5aa3a29288 addition to r779: added missing include to avoid compilation error on some systems 2013-08-08 21:46:42 +00:00
Andrey Prygunkov
bc49e7c48e all table columns except "Name" now have fixed widths to avoid annoying layout changes especially during post-processing when long status messages are displayed in the name-column 2013-08-08 21:23:29 +00:00
Andrey Prygunkov
9ba10446e9 added download health monitoring: health indicates download status, whether the file is damaged and how much; new option "HealthCheck" to define what to do with bad downloads (pause, delete, none); par-check is now automatically started for downloads having health below 100%, this works independently of unpack (even if unpack is disabled); for downloads having health less than critical health no par-check is performed (it would fail); new fields "Health" and "CriticalHealth" are returned by RPC-Method "listgroups"; new fields "Health", "CriticalHealth", "Deleted" and "HealthDeleted" are returned by RPC-Method "history"; new parameters "NZBPP_HEALTH" and "NZBPP_CRITICALHEALTH" are passed to pp-scripts; manually deleted downloads now have history status "deleted" (instead of "unknown") 2013-08-08 21:09:36 +00:00
Andrey Prygunkov
a4c686876f added filter buttons to messages tab (info, warning, etc.); also changed the color of filter buttons in feed view and feed filter dialogs (from blue to black) 2013-08-07 20:09:43 +00:00
Andrey Prygunkov
f31ba7dea3 small correction in help text 2013-08-05 20:49:28 +00:00
Andrey Prygunkov
897946c1ce added fields "rageid", "season", "episode" and command "=" to rss filters 2013-08-05 18:41:02 +00:00
Andrey Prygunkov
802266e3aa added confirmation dialog by leaving settings page if there are unsaved changes 2013-08-05 18:09:10 +00:00
Andrey Prygunkov
d9f89f7457 added menu "View" to settings page which allows to switch to "Compact Mode" when option descriptions are hidden 2013-08-05 18:05:04 +00:00
Andrey Prygunkov
b871d84379 added support for fields "rating" and "genre" in rss filters 2013-08-04 21:41:50 +00:00
Andrey Prygunkov
0375309060 fixed: rss filter commands "<=" and ">=" did not work 2013-08-04 15:35:07 +00:00
Andrey Prygunkov
a5ca653df8 fixed: crash on certain invalid rss filter strings 2013-08-03 21:06:35 +00:00
Andrey Prygunkov
ec194a15fb fixed: colons in regular expressins could cause incorrect parsing of rss filters 2013-08-03 09:56:40 +00:00
Andrey Prygunkov
eaf5d71b40 small changes in button captions: edit dialogs called from settings page (choose script, choose order, build rss filter) now have buttons "Discard/Apply" instead of "Close/Save"; in all other dialogs button "Close" renamed to "Cancel" unless it was the only button in dialog 2013-08-02 21:03:58 +00:00
Andrey Prygunkov
7a9ee279ed reversed the order of priorities in comboboxes in dialogs: the highest priority - at the top, the lowest - at the bottom 2013-08-02 16:46:24 +00:00
Andrey Prygunkov
827acdadea 1) added multiline filters for RSS feeds; new dialog to build filters in web-interface; 2) refactor: the length of configuration option values is now unlimited (previously was limited to 1000 characters; unlimited needed for long feed filters) 2013-08-02 15:54:11 +00:00
Andrey Prygunkov
ef99b2057a addition to r765: fixed small memory leak 2013-07-29 15:58:59 +00:00
Andrey Prygunkov
c938714b70 pp-scripts which move files can now inform the program about new location by printing text "[NZB] FINALDIR=/path/to/files"; the final path is then shown in history dialog instead of download path 2013-07-28 21:27:12 +00:00
Andrey Prygunkov
21e3dd30fd fixed: URLs for nzb-files were not properly read from RSS feeds of certains sites 2013-07-28 17:47:56 +00:00
Andrey Prygunkov
b271ab4721 addition to r757: fixed: statistic dialog had a scroll bar 2013-07-27 21:57:40 +00:00
Andrey Prygunkov
3abe382f44 program can now be stopped via web-interface: new button "shutdown" in section "SYSTEM" 2013-07-27 16:19:27 +00:00
Andrey Prygunkov
88a6b702d2 updated VC project file 2013-07-26 21:34:52 +00:00
Andrey Prygunkov
497d1af8bf fixed: download could hang if there were defined active news servers with 0 connections (ServerX.Active=yes, ServerX.Connections=0) (bug introduced in r743) 2013-07-26 20:38:57 +00:00
Andrey Prygunkov
cc4b6acd14 options "DeleteCleanupDisk" and "NzbCleanupDisk" are now active by default (in the example config file) 2013-07-26 20:14:32 +00:00
Andrey Prygunkov
1714e2331c combined rss filter commands @ and " into one command to make filters more intuitive 2013-07-26 20:09:36 +00:00
Andrey Prygunkov
4e419ec16d small change in css: slightly reduced the max height of modal dialogs to better work on notebooks 2013-07-25 20:13:26 +00:00
Andrey Prygunkov
5e0f214a8f fixed: malformed nzb-file could cause a memory leak 2013-07-25 19:20:06 +00:00
Andrey Prygunkov
da1727e5e4 added support for metatag "password" in nzb-files 2013-07-25 18:32:07 +00:00
Andrey Prygunkov
101be2eeb1 added confirmation by deleting download or history item from edit-dialog 2013-07-25 18:25:13 +00:00
Andrey Prygunkov
e69015204a when saving/restoring the feed status (last update time) the feeds are identified by url and filter (previously only by url) 2013-07-25 18:22:56 +00:00
Andrey Prygunkov
1b203e3292 fully implemented feed filters 2013-07-24 21:32:15 +00:00
Andrey Prygunkov
1ad8bd212c refactor: small rework of NZBParameterList-class 2013-07-24 21:09:56 +00:00
Andrey Prygunkov
3711f30d01 new logo (thanks dogzipp for the logo) 2013-07-24 21:01:27 +00:00
Andrey Prygunkov
85d59d25df 1) DirectNZB headers X-DNZB-MoreInfo, X-DNZB-Report and X-DNZB-Link are now processed when downloading URLs and the links "More Info", "External Link" and "Report This NZB" are shown in download-edit-dialog and in history-dialog; 2) combined all footer buttons into one button "Actions" with menu: in download-edit-dialog: "Pause/Resume", "Delete" and "Cancel Post-Processing", in history-dialog: "Delete", "Post-Process Again" and "Download Remaining Files (Return to Queue)" 2013-07-23 21:21:14 +00:00
Andrey Prygunkov
6d7f55a435 added missing svn-keywords 2013-07-22 20:39:49 +00:00
Andrey Prygunkov
c22ca18a82 added filtering for RSS feeds via new option "FeedX.Filter" (not all filter commands are implemented yet but this is mentioned in the option description) 2013-07-22 20:38:21 +00:00
Andrey Prygunkov
ec48959600 changed the way how option "Unpack" works: instead of enabling/disabling the unpacker as a whole, it now defines the initial value of post-processing parameter "Unpack" for nzb-file when it is added to queue; this makes it now possible to disable Unpack globally but still enable it for selected nzb-files; new option "CategoryX.Unpack" to set unpack on a per category basis 2013-07-21 20:44:13 +00:00
Andrey Prygunkov
67634c4a71 fixed compilation error on Linux (bug introduced in r743) 2013-07-20 14:05:21 +00:00
Andrey Prygunkov
c31d38a562 fixed: certain characters printed by pp-scripts could crash the program 2013-07-20 14:02:26 +00:00
Andrey Prygunkov
6b0499b82e news servers can now be temporarily disabled via speed limit dialog without reloading of the program; new option "ServerX.Active" to disable servers via settings; new option "ServerX.Name" to use for logging and in UI 2013-07-20 07:15:21 +00:00
Andrey Prygunkov
046364283f fixed: choosing local files didn't work in Opera 2013-07-18 19:21:38 +00:00
Andrey Prygunkov
85880f9bd1 in RPC-Method "appendurl" parameter "addtop" adds nzb to the top of the main download queue (not only to the top of the URL queue) 2013-07-17 19:12:23 +00:00
Andrey Prygunkov
5fd436e5c5 added switch "Titles/Filenames" to feed view dialog for rss feeds with "bad" titles 2013-07-17 19:11:35 +00:00
Andrey Prygunkov
01533cbf9f better parsing of rss feeds of certain nzb-sites (now using enclosure-tag if possible) (Windows only) 2013-07-17 19:02:41 +00:00
Andrey Prygunkov
f5c8276fdc addition to r734: fixed possible matching bug 2013-07-16 22:18:09 +00:00
Andrey Prygunkov
05f2b81025 better parsing of rss feeds of certain nzb-sites (now using enclosure-tag if possible) (POSIX only) 2013-07-16 21:41:53 +00:00
Andrey Prygunkov
9dfd6cecad added filter buttons (all, new, fetched, backlog) to feed view dialog 2013-07-16 21:11:08 +00:00
Andrey Prygunkov
2febf837e5 fixed: restoring of settings didn't work for multi-sections (servers, categories, etc.) if they were empty 2013-07-16 18:47:52 +00:00
Andrey Prygunkov
ac954bba11 refactor: small speed/memory optimization in aliases support for categories 2013-07-16 18:46:41 +00:00
Andrey Prygunkov
2bda4fef5b made alias-matching case-insensitive 2013-07-15 22:27:14 +00:00
Andrey Prygunkov
5a815592dc added new option "CategoryX.Aliases" to configure category name matching with nzb-sites; especially useful with rss feeds 2013-07-15 21:28:55 +00:00
Andrey Prygunkov
3f4c6ce144 added more debug logging to feed manager 2013-07-15 21:28:26 +00:00
Andrey Prygunkov
19e0c53d1e destination directory for option "CategoryX.DestDir" is not checked/created on program start anymore (only when a download starts for that category); this helps when certain categories are configured for external disks, which are not always connected 2013-07-15 20:07:50 +00:00
Andrey Prygunkov
95963b2289 download queue is now saved in a more secure way to avoid potential loss of queue if the program crashes during saving of queue 2013-07-15 19:53:01 +00:00
Andrey Prygunkov
4cd21cad9c fixed: crash after downloading of an URL (happen only on certain systems) 2013-07-15 19:13:36 +00:00
Andrey Prygunkov
fcf1d7d502 improved compatibility with certain nzb-sites when fetching nzb-files (original nzb-filenames were sometimes not detected properly) 2013-07-14 21:27:13 +00:00
Andrey Prygunkov
e92d04771d toolbar button "Rss Feeds" is now visible only if there are feeds defined in settings 2013-07-14 18:39:07 +00:00
Andrey Prygunkov
ce43190ca6 fixed: crash after executing of remote commands (bug introduced in r722) 2013-07-14 15:47:49 +00:00
Andrey Prygunkov
284dbbad24 addition to r722: added missing file to Makefile 2013-07-14 07:08:10 +00:00
Andrey Prygunkov
1e115a5eab addition to r722: added missing file 2013-07-14 06:39:36 +00:00
Andrey Prygunkov
f18a355469 added rss feeds support: 1) new options "FeedX.Name", "FeedX.URL", "FeedX.Interval", "FeedX.PauseNzb", "FeedX.Category", "FeedX.Priority" (section "Rss Feeds"); 2) new option "FeedHistory" (section "Download Queue"); 3) Button "Preview Feed" on settings tab near each feed definition; 4) new toolbar button "Feeds" on downloads tab with menu to view feeds or fetch new nzbs from all feeds (the button is visible only if there are feeds defined in settings); 5) new dialog to see feed content showing status of each item (new, fetched, backlog) with ability to manually fetch selected items 2013-07-13 22:00:49 +00:00
Andrey Prygunkov
8ba95bb82a updated version string to 12.0-testing 2013-07-12 21:16:55 +00:00
Andrey Prygunkov
ee5a2a320e updated version string (preparing to release 11.0) 2013-07-01 19:18:41 +00:00
Andrey Prygunkov
738fd3da58 updated ChangeLog 2013-07-01 18:48:07 +00:00
Andrey Prygunkov
decc08934c removed a superfluous page scroll after clicking on option in web-interface settings 2013-06-26 20:52:10 +00:00
Andrey Prygunkov
5f5b7f92cf improved configure-script: defining of symbol "FILE_OFFSET_BITS=64", required on some systems, is not necessary anymore 2013-06-20 18:18:05 +00:00
Andrey Prygunkov
20fa280171 imporved error message in web-interface displayed when the template configuration file could not be found 2013-06-19 18:24:50 +00:00
Andrey Prygunkov
e5fa2ef750 fixed: support for splitted files (.001, .002, etc.) were broken 2013-06-18 21:56:23 +00:00
Andrey Prygunkov
bd31e25757 configure-script now defines "SIGCHLD_HANDLER" by default on all systems including BSD; this eliminates the need of configure-parameter "--enable-sigchld-handler" on 64-Bit BSD; the trade-off: 32-Bit BSD now requires "--disable-sigchld-handler" 2013-06-18 19:06:11 +00:00
Andrey Prygunkov
5b3113d96b 1) when a nzb-file is added via web-interface or via remote call the file is now put into incoming nzb-directory (option "NzbDir") and then scanned; this has two advantages over the old behavior when the file was parsed directly in memory: the file serves as a backup for troubleshootings and the file is processed by nzbprocess-script (if defined in option "NzbProcess") making the pre-processing much easier; 2) new env-var parameters are passed to NzbProcess-script: NZBNP_NZBNAME, NZBNP_CATEGORY, NZBNP_PRIORITY, NZBNP_TOP, NZBNP_PAUSED; 3) new commands for use in NzbProcess-scripts: "[NZB] TOP=1" to add nzb to the top of queue and "[NZB] PAUSED=1" to add nzb-file in paused state 2013-06-17 20:39:46 +00:00
Andrey Prygunkov
84b4f7695b when a nzb-file whose name ends with ".queued" is added via web-interface the ".queued"-part is automatically removed 2013-06-16 13:00:57 +00:00
Andrey Prygunkov
fc8ea3bcd0 fixed: if an error occurs when a RPC-client or web-browser communicates with nzbget the program could crash 2013-06-15 15:07:11 +00:00
Andrey Prygunkov
9051a4df4d fixed: if the last file of collection was detected as duplicate after the download of the first article the file was deleted from queue (that's OK) but the post-processing was not triggered (that's a bug) 2013-06-13 20:51:38 +00:00
Andrey Prygunkov
a7b42b6c97 fixed: pp-scripts "Logger.py" and "EMail.py" failed trying to get post-processing log from nzbget if option "ControlUsername" were set to a non-default value 2013-06-12 20:25:06 +00:00
Andrey Prygunkov
f7675b1e46 addition to r693: fixed: unicode characters were not properly encoded in JSON-RPC response 2013-06-12 20:14:45 +00:00
Andrey Prygunkov
db1117d892 new option "ControlUsername" to define login user name (if you don't like default username "nzbget") 2013-06-05 21:09:28 +00:00
Andrey Prygunkov
4b14e19229 removed option "RenameBroken"; it caused problems in par-checker (the option existed since early program versions before the par-check was added) 2013-06-04 21:04:00 +00:00
Andrey Prygunkov
606021fb8a removed option "AppendNzbDir"; if it was disabled that caused problems in par-checker and unpacker; the option is now assumed always active 2013-06-04 20:56:17 +00:00
Andrey Prygunkov
b15499c1dd removed option "ProcessLogKind"; scripts should use prefixes ([INFO], [DETAIL], etc); messages printed without prefixes are added as [INFO] 2013-06-03 20:56:56 +00:00
Andrey Prygunkov
950588cb65 addition to r698: if options of the section "Terminal" were missed in the config file, they were written with empty values causing warnings on program start 2013-06-03 20:35:18 +00:00
Andrey Prygunkov
7991c06543 fixed: in the option "NzbAddedProcess" the env-var parameter with nzb-name was passed in "NZBNA_NAME", should be "NZBNA_NZBNAME"; the old parameter name "NZBNA_NAME" is still supported for compatibility 2013-06-02 21:11:09 +00:00
Andrey Prygunkov
b335c4ca05 updated svn log URL 2013-06-02 21:10:14 +00:00
Andrey Prygunkov
cf3773dd28 added functions to backup and restore settings from web-interface; when restoring it's possible to choose what sections to restore (for example only news servers settings or only settings of a certain pp-script) or restore the whole configuration 2013-06-02 19:20:31 +00:00
Andrey Prygunkov
2a3740e49f added check for directory existence in pp-script <Logger> to avoid script failure if the directory was deleted by one of the previous scripts 2013-05-28 20:22:59 +00:00
Andrey Prygunkov
bcbd30ff6e addition to r694: fixed: a directory check/creation could fail if the directory was just created in another thread 2013-05-26 21:10:08 +00:00
Andrey Prygunkov
571ab9602f 1) additional comment to r693 (ArticleDownloader.cpp, line 632): fixed: the program could hang if the destination file could not be created; 2) improved thread synchronisation to avoid (short-time) lockings of the program during creation of destination files 2013-05-26 20:42:15 +00:00
Andrey Prygunkov
cfab6a3bb6 more detailed error message if a directory could not be created (<DstDir>, <NzbDir>, etc.); the message includes error text reported by OS such as <permission denied> or similar 2013-05-26 13:47:23 +00:00
Andrey Prygunkov
7c9ab59aff addition to r649 (unicode support in XML-RPC and JSON-RPC): fixed a typo which could prevent the filtering of invalid xml-characters 2013-05-23 20:47:42 +00:00
Andrey Prygunkov
7b1d1129a8 when unpacking the unpack start time is now measured after receiving of unrar copyright message; this provides better unpack time estimation in a case when user uses unpack-script to do some things before executing unrar (for example sending Wake-On-Lan message to the destination NAS); it works with unrar only, it's not possible with 7-Zip because it buffers printed messages 2013-05-23 20:40:46 +00:00
Andrey Prygunkov
bf3e8fe3a9 the maximum number of download threads are now managed automatically taking into account the number of allowed connections to news servers; removed option <ThreadLimit> 2013-05-22 20:25:19 +00:00
Andrey Prygunkov
baeac17d5b when the program is reloaded, a message with version number is printed like on start 2013-05-22 20:09:21 +00:00
Andrey Prygunkov
68ce6dea4b if a communication error occurs in web-interface, it retries multiple times before giving up with an error message 2013-05-21 20:41:08 +00:00
Andrey Prygunkov
00df4b8920 small correction in a log-message: removed <Request:> from message <Request: Queue collection...> 2013-05-21 20:35:42 +00:00
Andrey Prygunkov
36814514b7 new parameter (env. var) <NZBPP_NZBID> is passed to pp_scripts and contains an internal ID of NZB-file 2013-05-21 20:34:42 +00:00
Andrey Prygunkov
381a9a28b0 pp-scripts terminated with unknown status are now considered failed (status=FAILURE instead of status=UNKNOWN) 2013-05-21 20:32:41 +00:00
Andrey Prygunkov
5c364896d3 added support for rar-files with non-standard extensions (such as .001, etc.) 2013-05-21 20:21:52 +00:00
Andrey Prygunkov
07ce1d44a9 fixed: remote command <--list> for history items may fail with segfault on certain par-status 2013-05-16 20:57:04 +00:00
Andrey Prygunkov
1348ac86f7 added setting of post-processing parameters for history items; pp-parameters can now be viewed and changed in history dialog in web-interface; useful before post-processing again; new action <HistorySetParameter> in RPC-method <editqueue>; new action <O> in remote command <--edit/-E> for history items (subcommand <H>) 2013-05-16 20:54:13 +00:00
Andrey Prygunkov
b4c4855a9b option <ControlPassword> can now be set to en empty value to disable authentication; useful if nzbget works behind other web-server with its own authentication 2013-05-15 19:45:48 +00:00
Andrey Prygunkov
7a1001a70b fixed: error in IE when loading web-interface (bug introduced in r673) 2013-05-15 16:51:13 +00:00
Andrey Prygunkov
340a8130e9 addition to r677: added missing headers, causing compilation error in newer gcc versions 2013-05-15 16:45:36 +00:00
Andrey Prygunkov
9eb8de27d2 addition to r676: fixed crash on Linux with uClibc 2013-05-14 20:52:39 +00:00
Andrey Prygunkov
476a43a5bf configuration can now be saved in web-interface even if there were no changes made but if obsolete or invalid options were detected in the config file; the saving removes invalid entries from config file 2013-05-14 20:27:23 +00:00
Andrey Prygunkov
9ab955d026 refactor: more consistent using of c-headers 2013-05-14 20:20:52 +00:00
Andrey Prygunkov
bcedb32cf0 1) fixed compilation error on Linux (bug introduced in r675); 2) refactor: small corrections in class <ParCoordinator> 2013-05-14 20:18:17 +00:00
Andrey Prygunkov
6bc266f13c Par-checker and renamer now add messages into the log of pp-item (like unpack- and pp-scripts-messages); these message now appear in the log created by scripts Logger.py and EMail.py 2013-05-13 21:00:08 +00:00
Andrey Prygunkov
ed36feeb0a fixed: by deleting of a partially downloaded nzb-file from queue, when the option <DeleteCleanupDisk> was active, the file <_brokenlog.txt> was not deleted preventing the directory from automatic deletion 2013-05-13 20:13:28 +00:00
Andrey Prygunkov
85400cd8f6 fixed: for pp-scripts saved using windows line endings (CR,LF) the descriptions of options were not displayed correctly on settings page 2013-05-08 21:53:18 +00:00
Andrey Prygunkov
2866697b32 fixed: crash when adding malformed nzb-files with certain structure (Windows only) 2013-05-08 21:51:48 +00:00
Andrey Prygunkov
f1a99e1194 when deleting downloads via web-interface a proper hint regarding deleting of already downloaded files from disk depending on option <DeleteCleanupDisk> is displayed 2013-05-08 21:50:47 +00:00
Andrey Prygunkov
db47ddf3dc when download is resumed in web-interface the option <ParCheck=Force> is respected and all par2-files are resumed (not only main par2-file) 2013-05-08 21:45:22 +00:00
Andrey Prygunkov
2cc4dbd2ba made pp-scripts EMail.py and Logger.py compatible with python3 (python2 is OK too) 2013-05-07 18:02:15 +00:00
Andrey Prygunkov
a38eef2971 fixed: symbol <DISABLE_TLS> must be defined in project settings, defining it in <win32.h> didn't work properly (Windows only) 2013-05-06 19:52:54 +00:00
Andrey Prygunkov
e3e197a917 1) option <ExtCleanupDisk> now checks not only file extensions but any substring at the end of file name (in particular this allows to delete file _brokenlog.txt); 2) fixed: when option <InterDir> was used the files extracted from archives were not processed/deleted by option <ExtCleanupDisk> 2013-05-06 18:58:18 +00:00
Andrey Prygunkov
361d0befb6 addition to r665: fixed: crash on starting of download 2013-05-06 18:32:16 +00:00
Andrey Prygunkov
ba35c662ea small improvement in multithread synchronisation: do not create mutexex for each file-object but instead only for active objects (which are being downloaded at the moment) 2013-05-05 21:17:23 +00:00
Andrey Prygunkov
45ce763c71 small improvement in multithread synchronisation of download queue 2013-05-05 21:16:02 +00:00
Andrey Prygunkov
73c85a0013 fixed: when a duplicate file was detected during download the program could hang 2013-05-05 20:59:22 +00:00
Andrey Prygunkov
26361630c2 addition to r660: fixed: downloads were always checked when option <ParCheck> was set to <Auto> 2013-05-04 07:45:00 +00:00
Andrey Prygunkov
96c30c509b fixed: spaces in option <ExtCleanupDisk> prevented its correct operation 2013-05-02 20:48:32 +00:00
Andrey Prygunkov
d9b9786486 improved par-check: added support for manual par-check; if option <ParCheck> is set to <Manual> and a damaged download is detected the program downloads all par2-files but doesn't perform par-check; the user must perform par-check/repair manually then (possibly on another, faster computer); old values <yes/no> of option <ParCheck> renamed to <Force> and <Auto> respectively; when set to <Force> all par2-files are always downloaded; removed option <LoadPars> since its functionality is now covered by option <ParCheck>; Result of par-check can now have new value <Manual repair necessary>; field <ParStatus> in RPC-method <history> can have new value <MANUAL>; parameter <NZBPP_PARSTATUS> for pp-script can have new value <4 = manual repair necessary>; extended pp-script <EMail.py> to handle ParStatus=4 (manual) 2013-05-02 20:40:36 +00:00
Andrey Prygunkov
bb9cea260d small improvements in formatting of option descriptions in web-interface 2013-05-02 20:03:51 +00:00
Andrey Prygunkov
958c2f97ec refactor: discarding of download queue is now less complicated and not depend on diskstate version 2013-04-30 20:12:07 +00:00
Andrey Prygunkov
27651f17bf improvement in JSON-/XML-RPC: all ID fields including NZBID are now persistent and remain their values after restart; this allows for third-party software to identify nzb-files by ID; method <history> now returns ID of NZB-file in the field <NZBID>; in versions up to 0.8.0 the field <NZBID> was used to identify history items in the edit-commands <HistoryDelete>, <HistoryReturn>, <HistoryProcess>; since version 9 field <ID> is used for this purpose; in versions 9-10 field <NZBID> still existed and had the same value as field <ID> for compatibility with version 0.8.0; the compatibility is not provided anymore; this change was needed to provide a consistent using of field <NZBID> across all RPC-methods 2013-04-30 20:10:10 +00:00
Andrey Prygunkov
71621f7bb5 eliminated a compiler warning 2013-04-30 20:06:54 +00:00
Andrey Prygunkov
d8add46215 automatic deletion of backup-source files after successful par-repair; important when repairing renamed rar-files since this could cause failure during unpack 2013-04-29 20:46:09 +00:00
Andrey Prygunkov
e459f570d5 improved unicode support in pp-script Logger.py 2013-04-29 19:25:59 +00:00
Andrey Prygunkov
5b5057dee0 fixed: failed to read download queue from disk if post-processing queue was not empty 2013-04-29 19:24:44 +00:00
Andrey Prygunkov
f21becb37d added link to catalog of pp-scripts to web-interface 2013-04-29 18:21:06 +00:00
Andrey Prygunkov
9d03eb1ad4 fixed: when option <InterDir> was active and the download after unpack contained rar-file with the same name as one of original files (sometimes happen with included subtitles) the original rar-file was kept with name <.rar_duplicate1> even if the option <UnpackCleanupDisk> was active 2013-04-28 20:29:19 +00:00
Andrey Prygunkov
cb90c5e616 when logging messages from a post-processing script, a short name of the script is now used as prefix if possible; a short name doesn't include subdirectory name or file extension; RPC-method <configtemplates> returns new field <DisplayName> representing the short name of the script which is recommended for using in UI 2013-04-26 20:01:53 +00:00
Andrey Prygunkov
77059f2db0 improved unicode support in XML-RPC and JSON-RPC 2013-04-26 19:56:41 +00:00
Andrey Prygunkov
fb72c36a48 if a news-server returns empty or bad article (this may be caused by errors on the news server), the program tries again from the same or other servers (in previous versions the article was marked as failed without other download attempts) 2013-04-26 19:55:30 +00:00
Andrey Prygunkov
b2b215a061 updated ChangeLog 2013-04-24 20:26:05 +00:00
Andrey Prygunkov
8d313e4cf8 removed pp-script Cleanup.sh (its functionality is now part of the main program) 2013-04-24 20:24:30 +00:00
Andrey Prygunkov
6bb760375e added option <ExtCleanupDisk> to automatically delete unwanted files (with specified extensions) after successful par-check or unpack 2013-04-24 20:16:04 +00:00
Andrey Prygunkov
025cd043d3 history dialog now shows status of every script 2013-04-23 18:20:52 +00:00
Andrey Prygunkov
3f368d4a8e fixed: download time in statistics were incorrect if the computer were put into standby (thanks Frank Kuypers for the patch) 2013-04-21 20:23:32 +00:00
Andrey Prygunkov
449e41e435 removed unused file from repository 2013-04-21 19:56:28 +00:00
Andrey Prygunkov
bf0062be52 addition to r639: eliminated a compiler warning 2013-04-21 19:43:14 +00:00
Andrey Prygunkov
3c025c8b52 updated forum URL in about dialog in web-interface 2013-04-19 18:53:56 +00:00
Andrey Prygunkov
6dc3d954c5 fixed: authorization to news-server was forced even when username/password were empty (bug introduced in r634) 2013-04-19 18:44:32 +00:00
Andrey Prygunkov
c9b7a11a89 fixed: by adding nzb-files with assigned category and empty option <CategoryX.DefScript> the global option <DefScript> should be used but it wasn't 2013-04-19 17:34:17 +00:00
Andrey Prygunkov
33cb2d108e fixed: if a download didn't have any par-files and the option <ParCheck> was active, the par-check was started anyway and then failed 2013-04-18 21:04:48 +00:00
Andrey Prygunkov
e053e74b58 fixed: scripts containg spaces in their names were not assigned to nzb-files by adding to queue (when defined in option <DefScript>) 2013-04-18 20:28:31 +00:00
Andrey Prygunkov
4e35fc2fbe improved multiscripts: 1) first level subfolders in the ppscripts-directory (option <ScriptDir>) are now scanned for scripts too; 2) only files containing script definition signature are considered scripts and are shown in web-interface; 3) these changes allows to easily install collections of scripts (scripts bundles by just putting a folder with multiple scripts into ppscripts-directory); 2013-04-18 20:24:30 +00:00
Andrey Prygunkov
4768d8e459 if username and password are defined for a news-server the authentication is now forced (in previous versions the authentication was performed only if requested by server); needed for servers supporting both anonimous (restricted) and authorized (full access) accounts 2013-04-17 20:34:15 +00:00
Andrey Prygunkov
3598cc1d85 refactor: restructured class <Connection> 2013-04-17 20:21:46 +00:00
Andrey Prygunkov
e3a895b88c updated README 2013-04-15 20:22:16 +00:00
Andrey Prygunkov
61eff3ddf0 removed old example post-processing script 2013-04-15 20:17:24 +00:00
Andrey Prygunkov
e9268984ae added post-processing scripts EMail.py and Logger.py 2013-04-15 20:16:06 +00:00
Andrey Prygunkov
f28b35bd28 reworked concept of post-processing scripts: multiple scripts can be assigned to each nzb-file; all assigned scripts are executed after the nzb-file is downloaded and internally processed (unpack, repair); option <PostProcess> is obsolete; new option <ScriptDir> sets directory where all pp-scripts must be stored; new option <DefScript> sets the default list of pp-scripts to be assigned to nzb-file when it's added to queue; new option <CategoryX.DefScript> to set the default list of pp-scripts on a category basis; the execution order of pp-scripts can be set using new option <ScriptOrder>; there are no separate configuration files for pp-scripts; configuration options and pp-parameters are defined in the pp-scripts; script configuration options are saved in nzbget configuration file (nzbget.conf); changed parameters list of RPC-methods <loadconfig> and <saveconfig>; new RPC-method <configtemplates> returns configuration descriptions for the program and for all pp-scripts; configuration of all scripts can be done in web-interface; the pp-scripts assigned to a particular nzb-file can be viewed and changed in web-interface on page <pp-parameters> in the edit download dialog; option <PostPauseQueue> renamed to <ScriptPauseQueue> (the old name is still recognized); new option <ConfigTemplate> to define the location of template configuration file (in previous versions it must be always stored in <WebDir>) 2013-04-15 20:06:05 +00:00
Andrey Prygunkov
a86618c2c2 fixed: when options <DirectWrite> and <ContinuePartial> were both active, a restart or reload of the program during download may cause damaged files in the active download 2013-04-08 20:19:05 +00:00
Andrey Prygunkov
1f1a4b8fb8 fixed potential segfault which could happen with file paths longer than 1024 characters 2013-04-07 15:14:20 +00:00
Andrey Prygunkov
a1d0be34c2 updated README 2013-04-07 15:10:42 +00:00
Andrey Prygunkov
ef0a04cc1c improved unicode (utf8) support: non-ascii characters are now correctly transferred via JSON-RPC; correct displaying of nzb-names and paths in web-interface; it is now possible to use non-ascii characters on settings page for option values (such as paths or category names) 2013-04-06 21:03:05 +00:00
Andrey Prygunkov
284262b7da added new feature <split download> which creates new download from selected files of source download; new command <Split> in web-interface in edit download dialog on page <Files>; new action <S> in remote command <--edit/-E>; new action <FileSplit> in JSON-/XML-RPC method <editqueue> 2013-04-06 20:54:00 +00:00
Andrey Prygunkov
58b0a17986 reworked post-processor queue: 1) only one job is created for each nzb-file; no more separate jobs are created for par-collections within one nzb-file; 2) option <AllowReProcess> removed; a post-processing script is called only once per nzb-file, this behavior cannot be altered anymore; 3) with a new feature <Split> (see next commits) individual par-collections can be processed separately in a more effective way than before 2013-04-06 20:25:07 +00:00
Andrey Prygunkov
57abe00c62 updated version string to 11.0-testing 2013-04-06 12:58:50 +00:00
Andrey Prygunkov
d014407ba4 updated ChangeLog 2013-03-31 14:37:57 +00:00
Andrey Prygunkov
2e8bfa16f9 fixed: articles with decoding errors (incomplete or damaged posts) caused infinite retry-loop in downloader 2013-03-31 14:36:15 +00:00
Andrey Prygunkov
bf34713b0c updated version string to 10.1 2013-03-31 14:10:57 +00:00
Andrey Prygunkov
c46c1a96cd updated version string (preparing to release 10.0) 2013-03-29 21:07:24 +00:00
Andrey Prygunkov
2693b62de4 if an obsolete option is found in the config file a warning is printed instead of an error and the program is not paused anymore 2013-03-23 15:07:25 +00:00
Andrey Prygunkov
18387f6d98 fixed: when the option <ContinuePartial> is active and there are partially downloaded files in queue, after reloading/restarting of the program the file may stuck with status <downloading>; trying to reload or quit the program in this state resulted in a crash (bug introduced in r599) 2013-03-22 22:07:23 +00:00
Andrey Prygunkov
08b7356184 adding of local files via web-interface now works in IE10 2013-03-18 21:43:01 +00:00
Andrey Prygunkov
e30cdfc176 addition to r602: fixed: if news servers from different levels were defined with the same group (bad config actually), download could hang when waiting for a free connection to a higher level server 2013-03-17 20:24:25 +00:00
Andrey Prygunkov
cc0ed38e68 refactor: removed dead code 2013-03-17 12:43:11 +00:00
Andrey Prygunkov
5ec0d20286 improvement in news-server/connection management: new option <ServerX.Group> allows more flexible configuration of news servers when using multiple accounts on the same server; with this option it's also possible to imitate the old server management behavior regarding levels as it was before r599 2013-03-17 12:21:46 +00:00
Andrey Prygunkov
e0aa69f605 improvement in news-server/connection management: do not reconnect on <article/group not found> errors since this doesn't help but unnecessary increases CPU load and network traffic 2013-03-16 15:24:01 +00:00
Andrey Prygunkov
c859f39036 addition to r599: fixed: download could be cancelled when waiting for a free connection to news server 2013-03-16 13:32:35 +00:00
Andrey Prygunkov
5251f62665 major improvement in news-server/connection management (main and fill servers): if download of article fails, the program tries all servers of the same level before trying higher level servers; this ensures that fill servers are used only if all main servers fail; this makes the configuring of multiple servers much easier than before: in most cases the simple configuration of level 0 for all main servers and level 1 for all fill servers suffices; in previous versions the level was increased immediately after the first tried server of the level failed; to make sure all main servers were tried before downloading from fill servers it was required to create complex server configurations with duplicates; these configurations were still not as effective as now 2013-03-14 22:30:59 +00:00
Andrey Prygunkov
2b87e2b221 fixed: download could be cancelled when waiting for a free connection on a second-(or higher)-level news server 2013-03-12 22:24:29 +00:00
Andrey Prygunkov
c185e9b487 removed unneeded code from configure-script 2013-03-12 21:33:45 +00:00
Andrey Prygunkov
b5d9a99f10 fixed: special characters (quotation marks, etc.) in unpack password and in configuration options were not displayed properly and could be discarded on saving 2013-03-12 20:28:03 +00:00
Andrey Prygunkov
fec67fe0ea fixed: some characters with codes below 32 were not properly encoded in JSON-RPC; sometimes output from unrar contained such characters and could break web-interface 2013-03-12 20:22:08 +00:00
Andrey Prygunkov
987997a986 fixed: when option <UnpackCleanupDisk> is active the unpacked archive-files (second level archives) could be deleled too (mostly affected 7-Zip archives but sometimes also rar-archives if the second level rar-files had same names as the first level rars) 2013-03-11 20:03:47 +00:00
Andrey Prygunkov
27ef79ca27 immediately clearing post-process progress label after unpack to avoid status update lag in web-interface 2013-03-11 20:00:49 +00:00
Andrey Prygunkov
bcc1932a37 addition to r586/r591: added automatic speed meter recalibration to recover after possible synchronisation errors which can occur when the option <AccurateRate> is not active; this makes the default (less accurate but fast) speed meter almost as good as the accurate one; important when speed throttling is active 2013-03-11 19:54:14 +00:00
Andrey Prygunkov
2e163e9986 reverted r586 <automatic recovery after synchronisation errors>: needs more testing 2013-03-10 22:50:34 +00:00
Andrey Prygunkov
5473e57b10 fixed: if an external program (unrar, pp-script, etc.) could not be started, the execute-function has returned code 255 although the code -1 were expected in this case; this could break designed post-processing flow 2013-03-10 22:19:46 +00:00
Andrey Prygunkov
b970bad058 imporved configure-script: 1) libs which are added via pkgconfig are now put into LIBS instead of LDFLAGS - improves compatibility with newer Linux linkers; 2) OpenSSL libs/includes are now added using pkgconfig to better handle dependencies; 3) additional check for libcrypto (part of OpenSSL) ensures the library is added to linker command even if pkgconfig is not used 2013-03-10 15:20:41 +00:00
Andrey Prygunkov
bea1814cb9 removed a line of unused code (introduced in r579) 2013-03-09 22:29:18 +00:00
Andrey Prygunkov
45e58f29b4 corrections in code formatting; no actual code changes 2013-03-09 22:23:58 +00:00
Andrey Prygunkov
f7a3df635a added automatic recovery after synchronisation errors which can occur in speed meter when the option <AccurateRate> is not active; this makes the <inaccurate/fast/default> speed meter much more reliable; important when speed throttling is active; a warning is printed to log to indicate detected error and reset of speed meter 2013-03-09 22:21:44 +00:00
Andrey Prygunkov
3f67984929 changed default value for option <ServerX.JoinGroup> to <no>; most news servers nowadays do not require joining the group and many servers do not keep headers for many groups making the join-command fail even if the articles still can be successfully downloaded 2013-03-09 14:51:48 +00:00
Andrey Prygunkov
bf82171baa removed hint <Post-processing script may have moved files elsewhere> from history dialog since it caused more questions than helped 2013-03-08 19:21:43 +00:00
Andrey Prygunkov
4c49a7f003 added link to wiki-article <Performance tips> to settings tab on web-interface 2013-03-08 19:10:34 +00:00
Andrey Prygunkov
57b5d40851 when post-processing-parameters are passed to the post-processing script a second version of each parameter with a normalized parameter-name is passed in addition to the original parameter name; in the normalized name the special characters <*> and <:> are replaced with <_> and all characters are passed in upper case; this is important for internal post-processing-parameters (*Unpack:=yes/no) which includes special characters 2013-03-07 16:45:41 +00:00
Andrey Prygunkov
ecde2d1627 improved post-processing script: better handling of nzb-files not having archive files 2013-03-07 16:45:08 +00:00
Andrey Prygunkov
b48e9a31f6 updated description of option <ServerX.Cipher> and added link to wiki article <Choosing a cipher> 2013-03-06 21:52:15 +00:00
Andrey Prygunkov
f64e5241ed improved the handling of hanging connections: if a connection hangs longer than defined by option <ConnectionTimeout> the program tries to gracefully close connection first (this is new); if it still hangs after <TerminateTimeout> the download thread is terminated as a last resort (as in previous versions) 2013-03-06 21:36:09 +00:00
Andrey Prygunkov
4e9d01055a news servers configuration is now less error-prone: 1) the option <ServerX.Level> is not required to start from <0> and when several news servers are configured the Levels can be any integers - the program sorts the servers and corrects the Levels to 0,1,2,etc. automatically if needed; 2) when option <ServerX.Connections> is set to <0> the server is ignored (in previous version such a server could cause hanging when the program was trying to go to the next level); 3) if no news servers are defined (or all definitions are invalid) a warning is printed to inform that the download is not possible 2013-03-06 21:35:31 +00:00
Andrey Prygunkov
9d4ca25499 updated VC++ project file 2013-03-04 22:16:25 +00:00
Andrey Prygunkov
f1ddf9dc2b addition to r568: corrected saving of diskstate for post-processor queue 2013-03-04 22:14:15 +00:00
Andrey Prygunkov
e99b790d58 added new option <ServerX.Cipher> to manually select cipher for encrypted communication with news server; manually choosing a faster cipher (such as <RC4>) can significantly improve performance (if CPU is a limiting factor) 2013-03-04 22:01:14 +00:00
Andrey Prygunkov
5e4a99c1ad addition to r572: changed the log-messages for deleting of 7-zip-files to <info> too 2013-03-04 21:28:37 +00:00
Andrey Prygunkov
c89824bf25 the log-messages <deleting file *file*> (when option <UnpackCleanupDisk> is active) and <moving file *file* to *destination*> are now printed as <info> instead of <detail> (since <detail> is for article related messages whereas <info> is more suitable for file related messages) 2013-03-04 20:28:27 +00:00
Andrey Prygunkov
1230d9cdd4 added fast renaming of intentionally misnamed (rar-) files; the new renaming algorithm doesn't require full par-scan and restores original filenames in just a few seconds, even on very slow computers (NAS, media players, etc.); the fast renaming is performed automatically when requested by the built-in unpacker (option <Unpack> must be active) 2013-03-04 19:55:36 +00:00
Andrey Prygunkov
d3dd8dc686 fixed a compilation warning 2013-03-03 20:48:14 +00:00
Andrey Prygunkov
382faa49cb added new option <InterDir> to put intermediate files during download into a separate directory (instead of storing them directly in destination directory (option <DestDir>); when nzb-file is completely (successfully) downloaded, repaired (if neccessary) and unpacked the files are moved to destination directory (option <DestDir> or <CategoryX.DestDir>); intermediate directory can significantly improve unpack performance if it is located on a separate physical hard drive 2013-03-01 20:32:17 +00:00
Andrey Prygunkov
5cf4b4663f addition to r568: corrected diskstate version check 2013-02-28 20:47:30 +00:00
Andrey Prygunkov
749b4d3083 when a history item is post-processed again and the archive files were previously deleted because of option <UnpackCleanupDisk> the post-processing goes directly to script stage; if the archive files were kept, the full post-processing including unpack is performed instead 2013-02-28 20:23:50 +00:00
Andrey Prygunkov
02835d057e fixed: remote commands <--list/-L> and <--connect/-C> showed download speed and speed limit in Bytes instead of KiloBytes (bug introduced in r544) 2013-02-15 08:04:23 +00:00
Andrey Prygunkov
f5aaaecc48 fixed: RPC-method <history> returned incorrect Par-Status (bug introduced in r563) 2013-02-14 15:06:10 +00:00
Andrey Prygunkov
184ff84f92 small change in example post-processing script: message <Deleting source ts-files> are now printed only if ts-files really existed 2013-02-13 19:47:48 +00:00
Andrey Prygunkov
ef56bc1f55 fixed: par-status <FAILED> was not correctly checked in the example post-processing script (bug introduced in r558) 2013-02-13 17:05:26 +00:00
Andrey Prygunkov
9846f7509e fixed: parameter <NZBPP_PARSTATUS> was not correctly passed to post-processing script when the download was repaired 2013-02-13 16:54:13 +00:00
Andrey Prygunkov
1cf7cefe83 updated VC++ project file 2013-02-12 22:12:43 +00:00
Andrey Prygunkov
b5f1dbc47b changed formatting of remaining time for post-processing to short format (as used for remaining download time) 2013-02-12 14:10:44 +00:00
Andrey Prygunkov
37b85491c3 fixed: RPC-method <history> returned bad results for URLs; impacts history tab in web-interface (bug introduced in r555) 2013-02-10 21:35:46 +00:00
Andrey Prygunkov
30d792b35b when running external programs (such as unrar or post-processing script) the full path to the program is not neccessary since the search in system PATH is now performed 2013-02-09 10:58:14 +00:00
Andrey Prygunkov
ac29412b2f updated example post-processing script: added check for nzbget version (at least 10.0) and option <Unpack>, small other corrections 2013-02-08 13:16:29 +00:00
Andrey Prygunkov
01c170afaf fixed a compilation warning 2013-02-07 19:32:07 +00:00
Andrey Prygunkov
539e0811c9 fixed compilation error on Linux 2013-02-07 19:30:11 +00:00
Andrey Prygunkov
940448ffae added built-in unpack: 1) rar and 7-zip formats are supported (via external Unrar and 7-Zip executables); 2) new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>, <UnrarCmd>, <SevenZipCmd>; 3) web-interface now shows progress and estimated time during unpack (rar only; for 7-Zip progress is not available due to limitations of 7-Zip) 4) when built-in unpack is enabled, the post-processing script is called after unpack and possibly par-check/repair (if needed); 5) for nzb-files containing multiple collections (par-sets) the post-processing script is called only once, after the last par-set; 6) new parameter <NZBPP_UNPACKSTATUS> passed to post-processing script; 7) if the option <AllowReProcess> is enabled the post-processing-script is called after each par-set (as in previous versions); 8) example post-processing script updated: removed unrar-code, added check for unpack status; 9) new field <UnpackStatus> in result of RPC-method <history>; 10) history-dialog in web-interface shows three status: par-status, unpack-status, script-status; 11) with two built-in special post-processing parameters <*Unpack:> and <*Unpack:Password> the unpack can be disabled for individual nzb-file or the password can be set; 12) built-in special post-processing parameters can be set via web-interface on page <PP-Parameters> (when built-in unpack is enabled). 2013-02-06 22:04:50 +00:00
Andrey Prygunkov
68a73f96c4 warning <Non-nzbget request received> now is not printed when the connection was aborted before the request signature was read 2013-01-31 20:49:52 +00:00
Andrey Prygunkov
87a93745cb when the par-checked requests more par-files, they get an extra priority and are downloaded before other files regardless of their priorities; this is needed to avoid hanging of par-checker-job if a file with a higher priority gets added to queue during par-check 2013-01-28 23:22:15 +00:00
Andrey Prygunkov
3b08abca10 refactor: extracted par-related code from module <PrePostProcessor> into new module <ParCoordinator> 2013-01-23 21:32:36 +00:00
Andrey Prygunkov
5e68096a2e added validation for option <CategoryX.DestDir>; removed a superfluous slash in the generated destination path 2013-01-22 22:16:18 +00:00
Andrey Prygunkov
e3ef11ceae improved error reporting for connection errors (especially on Windows) 2013-01-21 21:29:45 +00:00
Andrey Prygunkov
575fe8379f improved error reporting for connection errors when using OpenSSL 2013-01-21 21:28:51 +00:00
Andrey Prygunkov
60feae7e5b new feature <Pause for X Minutes> in web-interface; new XML-/JSON-RPC method <scheduleresume> 2013-01-21 21:19:04 +00:00
Andrey Prygunkov
2b45ecaea1 fixed: some XML-/JSON-RPC methods may return negative values for file sizes between 2-4GB; this had also influence on web-interface 2013-01-21 20:44:30 +00:00
Andrey Prygunkov
7a3a430137 fixed warning <file glyphicons-halflings.png not found> 2013-01-20 11:19:10 +00:00
Andrey Prygunkov
11c0563fe5 refactor: download speed and speed limit are now internally integers (Bytes) instead of floats (KB) 2013-01-18 21:36:17 +00:00
Andrey Prygunkov
00018b3e89 improved the automatic par-scan (option <ParScan=auto>) to significantly reduce the verify-time in some common cases with renamed rar-files: 1) the extra files are scanned in an optimized order; 2) the scan stops when all missings files are found 2013-01-17 22:08:01 +00:00
Andrey Prygunkov
de787a069d added support for HTTPS to the built-in web-server (web-interface and XML/JSON-RPC); new options <SecureControl>, <SecurePort>, <SecureCert> and <SecureKey>; Module <TLS.c/h> completely rewritten with support for servers-side sockets, newer versions of GnuTLS, proper thread lockings in OpenSSL 2013-01-17 19:07:13 +00:00
Andrey Prygunkov
5f33ea6013 merge from 9-branch: improved the post-processing script to better handle renamed rar-files 2013-01-15 22:00:44 +00:00
Andrey Prygunkov
ee74c4c17f refactor: reformatted TLS.c/.h 2013-01-13 17:46:11 +00:00
Andrey Prygunkov
b031f52ee2 replaced a browser error message when trying to add local files in IE9 with a better message dialog 2013-01-06 19:50:30 +00:00
Andrey Prygunkov
77bb01b18c small changes in libpar2-patches for compatibility with optware 2013-01-06 13:00:13 +00:00
Andrey Prygunkov
d2fdc28c85 refactor: reworked Connection-class: fully encapsulted sockets; better read/write methods 2012-12-30 15:27:38 +00:00
Andrey Prygunkov
d34e985a92 addition to r525: updated config.h.in 2012-12-20 22:04:24 +00:00
Andrey Prygunkov
f0c2c834c3 fixed: segfault if a category didn't have a destination directory defined (bug introduced in r524) 2012-12-20 22:00:46 +00:00
Andrey Prygunkov
ca0cce9401 addition to r525: corrected and updated configure-script 2012-12-19 21:52:24 +00:00
Andrey Prygunkov
2a0e211daf fixed: the reported line numbers for configuration errors were sometimes inaccurate 2012-12-19 20:37:07 +00:00
Andrey Prygunkov
ea89983e45 added full par-scan feature needed to par-check/repair files which were renamed after creation of par-files; new option <ParScan> to activate full par-scan (always or automatic); the automatic full par-scan activates if missing files are detected during par-check, this avoids unnecessary full scan for normal (not renamed) par sets 2012-12-19 20:16:17 +00:00
Andrey Prygunkov
e4ed1c8fd7 categories can now have their own destination directories 2012-12-18 22:29:24 +00:00
Andrey Prygunkov
d46155bf32 updated version string to 10.0-testing 2012-12-18 22:09:09 +00:00
Andrey Prygunkov
9bdf0d8937 updated version string in windows version 2012-12-18 21:45:45 +00:00
Andrey Prygunkov
f56c1226b6 corrected file properties 2012-12-09 13:21:18 +00:00
Andrey Prygunkov
bc3e4742f0 updated version string (preparing to release 9.0) 2012-12-09 13:15:23 +00:00
Andrey Prygunkov
891b16ac76 fixed: saving of file properties (priority or category) failed if a post-processing script having post-processing parameters were not used (bug introduced in r476) 2012-11-25 19:38:09 +00:00
Andrey Prygunkov
11a32c3537 updated Changelog 2012-11-25 15:57:28 +00:00
Andrey Prygunkov
8afc96e1ad updated Changelog 2012-11-23 22:28:48 +00:00
Andrey Prygunkov
55d2c9e49c updated README 2012-11-23 21:34:38 +00:00
Andrey Prygunkov
9baabee3fd fixed an issue on mobile safari where the click on time-label (which should bring the statistics dialog) was often registered as a click on speed-label (and showed the time limit dialog instead) 2012-11-21 21:05:45 +00:00
Andrey Prygunkov
ad20cb6644 improved the startup script <nzbgetd> so it can be directly used in </etc/init.d> without modifications 2012-11-21 20:24:17 +00:00
Andrey Prygunkov
04d2d92524 implemented function <Clear messages> in web-interface; added RPC-method <clearlog> 2012-11-20 20:42:00 +00:00
Andrey Prygunkov
6630a8c2a5 renamed subcommand <K> of command <--edit/-E> to <C> (the old subcommand is still supported for compatibility) 2012-11-16 21:27:37 +00:00
Andrey Prygunkov
67ee86eaeb made all command-line subcommands case insensitive (like it already was in <--edit/-E>-command) (example: <nzbget -L g> and <nzbget -L G> is the same) 2012-11-16 21:12:28 +00:00
Andrey Prygunkov
2fcfbc2e1a added new option <NzbAddedProcess> to setup a script called after a nzb-file is added to queue 2012-11-16 20:50:56 +00:00
Andrey Prygunkov
2bb1162adf corrected the help screen (nzbget --help) 2012-11-12 21:21:26 +00:00
Andrey Prygunkov
f5e0b67305 extended remote command <--append/-A> with optional parameters: <T> - adds the file/URL to the top of queue; <P> - pauses added files; <C category-name> - sets category for added nzb-file/URL; <N nzb-name> - sets nzb filename for added URL; the old switches <--category/-K> and <--top/-T> are deprecated but still supported for compatibility 2012-11-12 21:11:42 +00:00
Andrey Prygunkov
0b25fb5771 fixed: if the loading of settings tab were cancelled (by clicking on other tab) an error could appear 2012-11-12 19:22:15 +00:00
Andrey Prygunkov
27ff29329f added debug messages for speed meter 2012-11-12 19:21:34 +00:00
Andrey Prygunkov
b52cfbb602 fixed: version number wasn't displayed in about dialog 2012-11-12 19:21:01 +00:00
Andrey Prygunkov
c5a1c64a35 fixed: switching between tabs didn't work in IE10 2012-11-12 19:20:34 +00:00
Andrey Prygunkov
cbedd9bec5 extended browser check for IE<9 with a tip about compatibility mode 2012-11-12 19:19:52 +00:00
Andrey Prygunkov
7a90844970 fixed: edit commands for group/post/history didn't work properly (bug introduced in r487) 2012-11-12 19:18:55 +00:00
Andrey Prygunkov
45dcb72178 fixed compilation error on windows (bug introduced in r499) 2012-11-12 19:18:32 +00:00
Andrey Prygunkov
d23b5bb58b addition: fixed: the loading of configuration in web-interface failed if the program was started with parameter <-c> using relative path to config filename 2012-11-11 13:35:38 +00:00
Andrey Prygunkov
bf7de99182 fixed: the loading of configuration in web-interface failed if the program was started with parameter <-c> using relative path to config filename 2012-11-09 16:38:22 +00:00
Andrey Prygunkov
8ddfab4b47 now using minified versions of libraries for better performance and smaller size 2012-11-07 14:34:14 +00:00
Andrey Prygunkov
57c2dc2d65 updated README 2012-11-07 14:30:37 +00:00
Andrey Prygunkov
62236a38f3 added javascript error reporting - should help users to easily see browser compatibility issues 2012-11-07 14:28:14 +00:00
Andrey Prygunkov
25ab7bba02 small improvements in speed meter: 1) eliminated unneeded calls of time-function in standby mode (might help with hibernation issue on Synology NAS); 2) better speed metering on high CPU load caused by other programs (if nzbget has less CPU time) 2012-11-07 14:25:34 +00:00
Andrey Prygunkov
fe14f3ee0e fixed: RPC-method <listfiles> didn't work correctly if the parameter <NZBID> was set to <0> (bug introduced in r487) 2012-11-07 03:16:54 +00:00
Andrey Prygunkov
425120de94 fixed: trailing spaces were not discarded when the config file were loaded in web-interface 2012-11-07 03:12:25 +00:00
Andrey Prygunkov
2520c8d173 fixed compilation warning 2012-11-07 03:07:35 +00:00
Andrey Prygunkov
7b4ee1c44b fixed compilation error on older systems (bug introduced in r411) 2012-11-07 03:01:27 +00:00
Andrey Prygunkov
58a1dcd141 fixed compilation error on recent linux versions 2012-11-04 20:36:12 +00:00
Andrey Prygunkov
3b4f44f276 refactor: restructured the entire web-interface code 2012-11-03 07:41:44 +00:00
Andrey Prygunkov
ebcc06686c added editing of individual files of the group in web-interface (pause/resume/delete/reorder); added new command <FileReorder> to RPC-method <editqueue> to set the order of individual files in the group 2012-10-20 11:06:45 +00:00
Andrey Prygunkov
45b3a7dbcd temporary pausing the play animation if any modal dialog is shown (to avoid artifacts in safari) 2012-10-18 20:37:15 +00:00
Andrey Prygunkov
16f04f2255 fixed: the lockfile (option <LockFile>) was deleted after reloading (bug introduced in r463) 2012-10-18 20:31:32 +00:00
Andrey Prygunkov
a23fcbd095 addition: added processing of URLs starting with path <nzbget> (e.g. <http://localhost:6789/nzbget/>) as alias to the root path (e.g. <http://localhost:6789/>) in internal web-server to support reverse proxies lacking the ability to rewrite URL 2012-10-16 15:49:18 +00:00
Andrey Prygunkov
7491c0f7c4 added processing of URLs starting with path <nzbget> (e.g. <http://localhost:6789/nzbget/>) as alias to the root path (e.g. <http://localhost:6789/>) in internal web-server to support reverse proxies lacking the ability to rewrite URL 2012-10-15 20:40:32 +00:00
Andrey Prygunkov
83da75a5e5 fixed: error in GnuTLS-support on certain systems (bug introduced in r463) 2012-10-15 18:04:25 +00:00
Andrey Prygunkov
2474c32f60 addition: added indication of soft-pause state via orange border on play/pause button 2012-10-14 08:18:01 +00:00
Andrey Prygunkov
6910f1f0b7 added indication of soft-pause state via orange border on play/pause button 2012-10-14 07:32:37 +00:00
Andrey Prygunkov
c426aeac6a fixed: the web-interface was trying to load the post-processing configuration template file even if no post-processing script was used or when the script doesn't have a config file at all; this lead to warnings in the log (although harmless) (bug introduced in r476) 2012-10-08 03:51:45 +00:00
Andrey Prygunkov
0e2716ba31 fixed: the settings page failed to load when a post-procesing script with a config file was used and the post-processing configuration template file was not present in webui-directory (bug introduced in r476) 2012-10-07 16:40:41 +00:00
Andrey Prygunkov
011239d45c renamed options <ServerIP>, <ServerPort> and <ServerPassword> to <ControlIP>, <ControlPort> and <ControlPassword> to avoid confusion with news-server options <ServerX.Host>, <ServerX.Port> and <ServerX.Password>; the old option names are still recognized and are automatically renamed when the configuration is saved from web-interface; also renamed option <> to <MainDir> 2012-10-05 18:57:09 +00:00
Andrey Prygunkov
5bb3d1a9e1 added support for post-processing parameters in web-interface 2012-10-05 18:13:13 +00:00
Andrey Prygunkov
43766c7ab9 fixed: the status of active post-processing download was displayed as <PP-QUEUED> if the download has multiple par-sets 2012-10-02 15:23:33 +00:00
Andrey Prygunkov
e12eeed65d fixed: <make install> failed on BSD due to different syntax in <sed>-command 2012-10-01 20:15:05 +00:00
Andrey Prygunkov
711ecb4025 fixed: the size of small downloads (less than 100 MB) was not printed properly in web-interface 2012-10-01 19:50:19 +00:00
Andrey Prygunkov
88957699c5 fixed: unrar failure was not always properly detected causing the post-processing to delete not yet unpacked rar-files 2012-10-01 19:41:13 +00:00
Andrey Prygunkov
fcd6c51d55 categories available in web-interface are now configured in program configuration file (nzbget.conf) instead of a separate file <webui/categories.txt> and can therefore be added and changed via web-interface on settings page 2012-09-30 19:58:58 +00:00
Andrey Prygunkov
adda02dd0d updated descriptions in example configuration file 2012-09-29 19:56:19 +00:00
Andrey Prygunkov
ab1cff2a7d added <free disk space> to dialog <statistics and status> in web-interface 2012-09-29 19:54:54 +00:00
Andrey Prygunkov
0716a743d8 fixed: free disk space reported incorrectly on some OSes 2012-09-29 19:52:18 +00:00
Andrey Prygunkov
d0e17fde77 the status of post-processing and directory scan is now displayed as <disabled> if the related options in config file are not set 2012-09-28 19:29:03 +00:00
Andrey Prygunkov
d6c0aa8a80 the priority of nzb-file can now be set when adding local-file via web-interface; JSON/XML-RPC method <append> extended with parameter <priority> 2012-09-28 19:21:34 +00:00
Andrey Prygunkov
815bf9b390 fixed: added workaround for bug in iOS 6 safari caching POST-requests 2012-09-28 19:04:11 +00:00
Andrey Prygunkov
0aa6e0a8b2 all images are now provided with HiDPI versions in addition to standard versions; the HiDPI images are activated automatically on retina displays (requires webkit browser) 2012-09-27 20:59:47 +00:00
Andrey Prygunkov
8b1aff33fe added remote command <--reload/-O> and JSON/XML-RPC method <reload> to reload configuration from disk and reintialize the program; the reload can be performed from web-interface 2012-09-27 20:13:25 +00:00
Andrey Prygunkov
fdc9464576 added subcommand <W> to remote command <-S/--scan> to scan syncronously (wait until scan completed); added parameter <SyncMode> to XML/JSON-RPC method <scan>; the command <Scan> in web-interface now waits for completing of scan before reporting the status 2012-09-19 18:42:13 +00:00
Andrey Prygunkov
dc6c1a0fe1 with active option <AllowReProcess> the NZB considered completed even if there are paused non-par-files (the paused non-par-files are treated the same way as paused par-files): as a result the reprocessable script is called 2012-09-18 02:47:44 +00:00
Andrey Prygunkov
78a73ac15f added missing turtle icon 2012-09-18 02:32:39 +00:00
Andrey Prygunkov
48891ed7c7 many improvements in web-interface UI: main tabs are better distinguishable; separate tab headers removed; handbrake button moved to navbar and renamed to pause/resume-button; animation on pause/resume-button better shows current state; two other important info-elements <current speed> and <remaining time> moved to the navbar as well; the search-edit moved to navbar too; the refresh-button has animation; the navbar is now fixed to the top on big screens; the speed limit is now set via click on <current speed> info; <statistics and status> are accessible via click on <remaining time>; the scan-button moved to add-dialog; due to reduced number of toolbar buttons on the downloads-tab the ability to hide buttons on the toolbar were removed (not neccessary anymore); the phone-theme is now less cluttered; added editing of nzbget and post-processing script settings; the settings-tab is searchable like other tabs; added new XML/JSON-RPC methods <config>, <loadconfig> and <saveconfig>; 2012-09-16 11:38:44 +00:00
Andrey Prygunkov
d3fd5ba9ac fixed: url-downloads could fail when compiled with gzip-support (bug introduced in r440) 2012-09-08 06:40:59 +00:00
Andrey Prygunkov
2b6f575802 set svn keywords 2012-08-11 10:37:14 +00:00
Andrey Prygunkov
f604460d56 set svn keywords 2012-08-06 20:32:48 +00:00
Andrey Prygunkov
7472893e8e added built-in web-interface; new option <WebDir> 2012-08-04 13:13:49 +00:00
Andrey Prygunkov
eff074faae <index.html> is now returned by web-server for every directory-request, not only for the root one (</>) 2012-07-31 18:34:50 +00:00
Andrey Prygunkov
07c04b40b1 </index.html> is now returned by web-server when the root path </> is requested 2012-07-30 20:46:35 +00:00
Andrey Prygunkov
754adb545e eliminated few compiler warnings 2012-07-28 13:37:37 +00:00
Andrey Prygunkov
5fc04277c1 updated VC-project 2012-07-28 13:36:44 +00:00
Andrey Prygunkov
78f5fd3f71 fixed compilation error on linux (bug introduced in r449) 2012-07-18 21:27:03 +00:00
Andrey Prygunkov
0277c6b9bd improved handling of configuration errors: the program now does not terminate on errors but rather logs all of them and uses default option values 2012-07-16 20:42:33 +00:00
Andrey Prygunkov
e34b4b8ae7 fixed: RPC-method <log(0, IdFrom)> could return wrong results if the log was filtered with options <XXXTarget> 2012-07-16 20:10:21 +00:00
Andrey Prygunkov
c0de18f3aa fixed line endings 2012-07-16 19:41:33 +00:00
Andrey Prygunkov
4b78918347 fixed incompatibility with OpenSLL 1.0 (thanks to OpenWRT team for the patch) 2012-07-15 12:10:19 +00:00
Andrey Prygunkov
6606a883c5 improved the automatic installation (<make install>) to install all necessary files (not only the binary as it was before) 2012-07-14 20:04:11 +00:00
Andrey Prygunkov
30c1a64d31 renamed example configuration file and postprocessing script to make the installation easier 2012-07-14 13:53:28 +00:00
Andrey Prygunkov
91dbcc40aa fixed: remote command <-E/--edit> with option <GN> or <FN> did not work 2012-07-11 19:48:58 +00:00
Andrey Prygunkov
b0f5119ec0 added authorization via URL in RPC-server (example: http://localhost:6789/username:password/jsonrpc) 2012-07-10 20:04:13 +00:00
Andrey Prygunkov
ed9aba18b8 added processing of http-request <OPTIONS> in RPC-server for better support of cross domain requests 2012-07-10 20:00:13 +00:00
Andrey Prygunkov
d507325378 added gzip-support to URL-downloader 2012-07-09 20:56:28 +00:00
Andrey Prygunkov
0a0546168b fixed memory leak in gzip-support (bug introduced in r436) 2012-07-05 19:09:28 +00:00
Andrey Prygunkov
5abbbe80d1 refactor: reordered classes 2012-07-05 16:47:29 +00:00
Andrey Prygunkov
4a6413f654 fixed error in configure script (bug introduced in r436) 2012-07-04 18:12:51 +00:00
Andrey Prygunkov
6c60244b26 added gzip-support to built-in web-server 2012-07-03 20:34:36 +00:00
Andrey Prygunkov
a384f0e6e9 prevent duplicate nzb-entries in the history 2012-07-03 20:21:11 +00:00
Andrey Prygunkov
2a56410543 improved performance of RPC-command <listgroups> 2012-07-02 20:04:35 +00:00
Andrey Prygunkov
7ef22fc1e0 fixed compilation error on linux (bug introduced in r432) 2012-07-01 19:54:45 +00:00
Andrey Prygunkov
5051d698c0 implemented built-in web-server 2012-07-01 19:31:06 +00:00
Andrey Prygunkov
1a87c08bc2 changed version naming scheme by removing the leading zero: current version is now called 9.0 instead of 0.9.0 (it's really the 9th major version of the program) 2012-07-01 17:23:08 +00:00
Andrey Prygunkov
ed0c5908ce fixed few compiler warnings 2012-07-01 14:56:28 +00:00
Andrey Prygunkov
f571ced9c5 when adding url via RPC the supplied filename (if not empty) has precedence over the original file name 2012-06-24 17:00:51 +00:00
Andrey Prygunkov
3110181a9f fixed: when adding url the nzb name was not set properly (bug introduced in r419) 2012-06-24 16:07:16 +00:00
Andrey Prygunkov
fcb7966f70 fixed: segfault in remote command <--list/-L> when used without subcommands (bug introduced in r422) 2012-06-23 21:18:47 +00:00
Andrey Prygunkov
be2945a16f fixed a resource leak (socket) which could occur when an active download was deleted from queue 2012-06-23 19:29:43 +00:00
Andrey Prygunkov
6b3326ad42 restored accidental change of Connection.cpp in r423 (should be commited as a separate changeset) 2012-06-23 19:27:36 +00:00
Andrey Prygunkov
3e81a03087 refactor: corrected inconsistent include of <config.h> 2012-06-23 18:58:56 +00:00
Andrey Prygunkov
3778430ead in remote command <--list/-l> with subcommands <GR> and <FR> the regex-matching is now performed on the server; that ensures the list-command selects the same records as the edit-command (when server and client have different implementations of POSIX ERE) 2012-06-22 18:30:14 +00:00
Andrey Prygunkov
31bd251f37 added support for regular expressions (POSIX ERE Syntax) in remote commands <--list/-L> and <--edit/-E> using new subcommands <GR> and <FR> 2012-06-20 22:53:03 +00:00
Andrey Prygunkov
f49f01ec85 refactor: splitted class <Util> into <Util> and <Webtil> 2012-06-20 22:15:09 +00:00
Andrey Prygunkov
1bd6721af9 added options <GN> and <FN> for remote command <--edit/-E>. With these options the name of group or file can be used in edit-command instead of file ID 2012-06-19 15:08:23 +00:00
Andrey Prygunkov
4a069266d8 added new field <name> to nzb-info-object. It is initially set to the cleaned up name of the nzb-file. The renaming of the group changes this field. All RPC-methods related to nzb-object return the new field, the old field <NZBNicename> is now deprecated. The option <MergeNZB> now checks the <name>-field instead of <nzbfilename> (the latter is not changed when the nzb is renamed). New env-var-parameter <NZBPP_NZBNAME> for post-processing script. 2012-06-11 15:09:03 +00:00
Andrey Prygunkov
d10d7f3f02 fixed: RPC-Command <history> were not returning the UrlStatus correctly in JSON-RPC (bug introduced in r414) 2012-06-08 11:06:00 +00:00
Andrey Prygunkov
05adbb1325 fixed: by adding a failed URL to the history it was added to the end instead of the top of the history (bug introduced in r414) 2012-06-08 11:04:23 +00:00
Andrey Prygunkov
ca8719b42b fixed: after renaming of a group, the new name was not displayed by remote commands <-L G> and <-C in curses mode> 2012-05-27 12:59:51 +00:00
Andrey Prygunkov
ec80850e76 improved error reporting when trying to download a HTTPS-URL and the program was compiled without TLS/SSL support 2012-05-04 14:55:49 +00:00
Andrey Prygunkov
ab75a8b3e5 added the ability to queue URLs. The program automatically downloads nzb-files from given URLs and put them to download queue. When multiple URLs are added in a short time, they are put into a special URL-queue. The number of simultaneous URL-downloads are controlled via new option UrlConnections. With the new option ReloadUrlQueue can be controlled if the URL-queue should be reloaded after the program is restarted (if the URL-queue was not empty). New switch <-U> for remote-command <--append/-A> to queue an URL. New subcommand <-U> in the remote command <--list/-L> prints the current URL-queue. If URL-download fails, the URL is moved into history. With subcommand <-R> of command <--edit> the failed URL can be returned to URL-queue for redownload. The remote command <--list/-L> for history can now print the infos for URL history items. New XML/JSON-RPC command <appendurl> to add an URL or multiple URLs for download. New XML/JSON-RPC command <urlqueue> returns the items from the URL-queue. The XML/JSON-RPC command <history> was exteneded to provide infos about URL history items. The URL-queue obeys the pause-state of download queue. The URL-downloads support HTTP and HTTPS protocols. 2012-05-03 13:47:44 +00:00
Andrey Prygunkov
d00c8119fa removed references to <NetAddress.c/h> from VS-Project 2012-05-03 11:00:54 +00:00
Andrey Prygunkov
2b7d188677 removed NetAddress.cpp/h 2012-05-03 10:53:18 +00:00
Andrey Prygunkov
12c09693bd refactoring: removed class <NetAddress>. That makes <Connection>-class more transparent and easier to use. The TLS-initializing moved from <NNTPConnection> to <Connection> 2012-05-03 10:51:13 +00:00
Andrey Prygunkov
87793b3dc3 updated version string to 0.9.0-testing 2012-05-03 10:23:00 +00:00
Andrey Prygunkov
7ce8c0b966 updated version string, ChangeLog and README (preparing to release 0.8.0) 2012-04-29 16:15:25 +00:00
Andrey Prygunkov
a831944b14 added the automatic configuring of required signal handling logic to better support BSD without breaking the compatibility with certain Linux systems 2012-03-16 22:03:55 +00:00
Andrey Prygunkov
17533d2c61 fixed a compatibility issue with OpenBSD (and possibly other BSD based systems) 2012-01-14 20:06:53 +00:00
Andrey Prygunkov
bee1d6beed fixed the incorrect displaying of sizes bigger than 4 GB on 64 bit systems 2012-01-12 21:25:08 +00:00
Andrey Prygunkov
a49ae076a5 improved the parsing of filename from article subject 2012-01-01 20:51:23 +00:00
Andrey Prygunkov
4856503a33 fixed: par-repair could fail when the filenames were not correctly parsed from article subjects 2011-12-29 21:30:25 +00:00
Andrey Prygunkov
0d4560f54e fixed a compilation error on some windows versions 2011-12-29 14:04:11 +00:00
Andrey Prygunkov
c5f7c2ace7 fixed spelling errors in the example configuration file 2011-12-29 14:02:10 +00:00
Andrey Prygunkov
f8c03fa48c fixed a bug causing error on decoding of input data in JSON-RPC 2011-11-18 20:46:48 +00:00
Andrey Prygunkov
8b6b34d7c0 fixed incorrect displaying of group sizes between 2GB and 4GB on many 64-bit OSes 2011-06-15 07:27:44 +00:00
Andrey Prygunkov
1fd7001e0c corrected a spelling error 2011-05-24 12:57:47 +00:00
Andrey Prygunkov
2631550c2f corrected the address of Free Software Foundation in copyright notice; corrected the spelling of authors name (caused by new rules for translating of cyrillic names to latin alphabet / english spelling) 2011-05-24 12:52:41 +00:00
Andrey Prygunkov
01734fadcf fixed incorrect displaying of group sizes bigger than 4GB on many 64-bit OSes 2011-03-30 21:04:58 +00:00
Andrey Prygunkov
33cead0b03 updated descriptions in example config file 2011-03-24 08:36:24 +00:00
Andrey Prygunkov
08f86cbcf9 added priorities; new action <I> for remote command <--edit/-E> to set priorities for groups or individual files; new actions <SetGroupPriority> and <SetFilePriority> of RPC-command <EditQueue>; remote command <--list/-L> prints priorities and indicates files or groups being downloaded; ncurses-frontend prints priorities and indicates files or groups being download; new command <PRIORITY> to set priority of nzb-file from nzbprocess-script; RPC-commands <ListGroups> and <ListFiles> return priorities and indicate files or groups being downloaded 2011-03-12 11:48:13 +00:00
Andrey Prygunkov
a83b96c74d fixed compilation error on Posix (bug introduced in r391) 2011-03-04 11:11:35 +00:00
Andrey Prygunkov
7cf0ddc81b eliminated small memory leak (bug introduced in r391) 2011-03-01 11:56:26 +00:00
Andrey Prygunkov
41640b5215 added new option <AccurateRate>, which enables syncronisation in speed meter; that makes the indicated speed more accurate by eliminating measurement errors possible due thread conflicts; thanks to anonymous nzbget user for the patch 2011-02-05 15:48:59 +00:00
Andrey Prygunkov
e1abedbfa4 fixed: article IDs containing special xml-characters were not parsed correctly (bug introduced in r386) 2010-12-07 16:22:31 +00:00
Andrey Prygunkov
a1482b9781 added renaming of groups; new subcommand <N> for command <--edit/-E>; new action <SetName> for RPC-method <editqueue> 2010-08-11 13:30:34 +00:00
Andrey Prygunkov
8a6d6ae771 option <DirectWrite> is now efficiently works on Windows with NTFS partitions 2010-07-23 14:42:16 +00:00
Andrey Prygunkov
a67e8314af added URL-based-authentication as alternative to HTTP-header authentication for XML- and JSON-RPC 2010-07-18 12:46:28 +00:00
Andrey Prygunkov
f5e7497913 fixed: nzb-files containing umlauts and other special characters could not be parsed - replaced XML-Reader with SAX-Parser; the issue was fixed only on POSIX 2010-07-05 14:52:42 +00:00
Andrey Prygunkov
e4c7c601d7 updated version string to 0.8.0-testing 2010-06-10 15:23:42 +00:00
Andrey Prygunkov
02fdc78066 updated version string in other file (preparing to release 0.7.0) 2010-04-26 21:04:08 +00:00
Andrey Prygunkov
d1a4521396 updated version string, ChangeLog and README (preparing to release 0.7.0) 2010-04-25 13:38:08 +00:00
Andrey Prygunkov
646ddb4ddb fixed: elapsed time for active post-processing job was calculated incorrectly if the post-processor queue was paused; that could cause the interruption of active par-job when using the option <ParTimeLimit> 2010-02-08 16:08:10 +00:00
Andrey Prygunkov
8578078f7c added second pause register, which is independent of main pause-state and therfore is intended for usage from external scripts; that allows to pause download without interferring with options <ParPauseQueue> and <PostPauseQueue> and scheduler tasks <PauseDownload> and <UnpauseDownload> - they all work with first (default) pause register; new subcommand <D2> for commands <--pause/-P> and <--unpause/-U>; new RPC-command <pausedownload2> and <resumedownload2>; existing RPC-commands <pause> und <resume> renamed to <pausedownload> and <resumedownload>; new field <Download2Paused> in result struct for RPC-command <status>; existing fields <ServerPaused> and <ParJobCount> renamed to <DownloadPaused> and <PostJobCount>; old RPC-commands and fields still exist for compatibility; the status output of command <--list/-L> indicates the state of second pause register; key <P> in curses-frontend can unpause second pause-register 2010-01-30 14:43:58 +00:00
Andrey Prygunkov
ab204281ed fixed: script-status was not reset by <post-process again> 2010-01-29 09:53:57 +00:00
Andrey Prygunkov
31940d8f58 nzbprocess-script (option <NZBProcess>) can now set category and post-processing parameters for nzb-file 2010-01-29 09:34:44 +00:00
Andrey Prygunkov
98874790fc corrected line endings in one source file 2010-01-29 08:54:20 +00:00
Andrey Prygunkov
5cd476687e fixed: command <list history> (nzbget -L H) didn't work (bug introduced in r373) 2010-01-29 08:53:04 +00:00
Andrey Prygunkov
73449e4407 updated README 2010-01-28 19:11:17 +00:00
Andrey Prygunkov
eae06a4145 fixed: nothing was downloaded when the option <Retries> was set to <0> 2010-01-18 10:59:26 +00:00
Andrey Prygunkov
5a8d56c2b4 fixed: when option <DaemonUserName> was specified and nzbget was started as root, the lockfile was not removed 2009-12-29 13:13:12 +00:00
Andrey Prygunkov
da6ccb6310 added files <nzbgetd> and <nzbget-shell.bat> to makefile 2009-12-27 21:11:38 +00:00
Andrey Prygunkov
b0a8d04f97 added debian style init script; thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script 2009-12-27 21:07:04 +00:00
Andrey Prygunkov
99acfc9641 added shell batch file for windows (thanks to orbisvicis <orbisvicis@users.sourceforge.net>) for the script 2009-12-26 22:48:12 +00:00
Andrey Prygunkov
a731b606c5 fixed: RPC-method <append> did not work properly on Posix systems (it worked only on Windows) 2009-12-10 22:43:54 +00:00
Andrey Prygunkov
c99b369b56 fixed line endings 2009-12-08 22:26:21 +00:00
Andrey Prygunkov
0f41e6053d fixed: base64 decoding function used by RPC-method <append> sometimes failed, in particular when called from Ruby-language 2009-12-08 22:25:23 +00:00
Andrey Prygunkov
b2ffddd84d changed the sleep-time during the throttling of bandwidth from 200ms to 10ms in order to achieve better uniformity 2009-12-08 21:55:39 +00:00
Andrey Prygunkov
1a9451fe61 command <pause post-processing> now not only pauses the post-processing queue but also pauses the current post-processing job (par-job or script-job); however the script-job can be paused only after the next line printed to screen 2009-12-08 21:50:53 +00:00
Andrey Prygunkov
0859eef869 fixed line endings in two source files 2009-10-16 11:55:47 +00:00
Andrey Prygunkov
1ad28cb9ac added the returning of a proper HTTP error code if the authorization was failed on RPC-calls (thanks to jdembski <jdembski@users.sourceforge.net> for the patch) 2009-10-16 11:54:15 +00:00
Andrey Prygunkov
0f8aa5d7e7 added support for JSON-P (extension of JSON-RPC) 2009-10-16 11:26:45 +00:00
Andrey Prygunkov
5bf680a1cf eliminated few compiler warnings on GCC 2009-10-09 20:18:30 +00:00
Andrey Prygunkov
e9d599ab26 fixed: JSON-RPC-commands failed, if parameters were placed before method name in the request 2009-10-09 20:14:33 +00:00
Andrey Prygunkov
dc83e66f72 improvement in example post-processing script: added check for existence of <unrar> and command <wc> 2009-10-09 19:22:31 +00:00
Andrey Prygunkov
5cf16dc9fd in <curses> and <colored> output-modes the download speed is now printed with one decimal digit when the speed is lower than 10 KB/s 2009-09-30 20:26:47 +00:00
Andrey Prygunkov
e076ac1a9c improved example post-processing script: added the check for existence of destination directory to return a proper ERROR-code (important for reprocessing of history items) 2009-09-27 11:24:56 +00:00
Andrey Prygunkov
7a28932569 fixed: RPC-commands <HistoryReturn> and <HistoryProcess> were processed as <HistoryDelete> 2009-09-26 20:44:11 +00:00
Andrey Prygunkov
5567c5abf6 fixed: reprocessing of a history item, which doesn't have any files left, caused a seg. fault 2009-09-26 19:54:53 +00:00
Andrey Prygunkov
b2e2c5c173 clearing the list of completeded files when adding a nzb-file to history if the cleaning up of queue (option <ParCleanupQueue>) is enabled (since there no more files in the nzb-file left, it cannot be returned to download queue; the list of completed files is not needed anymore) 2009-09-26 16:01:44 +00:00
Andrey Prygunkov
e41731ce23 by saving the queue to disk now using relative paths for the list of compeled files to reduce the file's size 2009-09-26 15:21:11 +00:00
Andrey Prygunkov
b1b6d6ace8 added field <PostTime> to result of RPC-Command <listfiles> and fields <MinPostTime> and <MaxPostTime> for command <listgroups> 2009-09-22 20:40:40 +00:00
Andrey Prygunkov
97b5a4c304 corrected few spelling errors in the example configuration file 2009-09-20 15:57:11 +00:00
Andrey Prygunkov
0722417e0b added a new return code <95/POSTPROCESS_NONE> for post-processing scripts for cases when pp-script skips all post-processing work (typically upon a user's request via a pp-parameter); modified the example post-processing script to return the new code instead of a error code when a pp-parameter <PostProcess> was set to <no> 2009-09-20 15:24:19 +00:00
Andrey Prygunkov
dc9e938510 Added message log to history items; new field <Log> is returned by RPC-command <history> for each item 2009-09-19 15:49:08 +00:00
Andrey Prygunkov
c37b8f37e2 fixed: the discarding of download queue did not work (bug introduced in r346) 2009-09-19 14:33:41 +00:00
Andrey Prygunkov
80116b6687 renamed ParStatus constant <FAILED> to <FAILURE> for a consistence with ScriptStatus constant <FAILURE>, that also affects the results of RPC-command <history> 2009-08-23 12:32:46 +00:00
Andrey Prygunkov
7d5225b2ba added field <RemainingFileCount> to history items returned via RPC-command <history> 2009-08-23 12:18:44 +00:00
Andrey Prygunkov
f2f318b11e fixed: returning the postprocessing exit code <92 - par-check all collections> when there were no par-files results in endless calling of postprocessing script 2009-08-22 13:31:03 +00:00
Andrey Prygunkov
6bfe1b0cfd added actions <R> (return history item) and <P> (postprocess history item) for subcommand <H> of command <-E/--edit>; added actions <HistoryReturn> and <HistoryProcess> for remote RPC-command <editqueue> 2009-08-22 12:05:25 +00:00
Andrey Prygunkov
f807cca8c7 modified example postprocessing script to not use the command <dirname>, which is not always available (thanks to Ger Teunis for the patch) 2009-07-26 11:35:38 +00:00
Andrey Prygunkov
feadf59fa0 added history: new option <KeepHistory>, new remote subcommand <H> for commands <L> (list history entries) and <E> (delete history entries), new RPC-command <History> and subcommand <HistoryDelete> for command <EditQueue>; changed the result code returning status <ERROR> for postprocessing script from <1> to <94> 2009-06-30 16:08:41 +00:00
Andrey Prygunkov
10e64e04fe fixed: option <ContinuePartial> did not work 2009-06-24 17:09:19 +00:00
Andrey Prygunkov
c2cbd502ea fixed: remote command <-S/--scan> caused the processing of already scanned files (bug introduced in r328) 2009-06-20 20:58:22 +00:00
Andrey Prygunkov
cf5fd8064b debug-messages generated on early stages during initializing are now printed to screen/log-file; messages about obsolete options are now printed to screen/log-file 2009-06-14 15:57:01 +00:00
Andrey Prygunkov
1a417b9d63 fixed: discarding of download queue (option <reloadqueue=no>) didn't work (bug introduced in r320 2009-06-13 16:47:26 +00:00
Andrey Prygunkov
123cfe6a38 added <binary> flag by opening of all files to explicitly set binary mode for compatibility with OSes which do not do that by default (like Posix does) 2009-06-11 21:13:37 +00:00
Andrey Prygunkov
43c0681d35 added the detection of <socklen_t> type to configure script (to remove OS2-specific code from <nzbget.h>); changed configure script to avoid unneded tests when debugging is disabled 2009-06-11 12:39:06 +00:00
Andrey Prygunkov
2ec5784a44 improvements in example postprocessing script: 1) if download contains only par2-files the script do not delete them during cleanup; 2) if download contains only nzb-files the script moves them to incoming nzb-directory for further download 2009-06-10 16:28:54 +00:00
Andrey Prygunkov
d59f4229d6 set the default compiler in configure script to <C++> instead of <C> for better error detection and because nzbget does not need C-compiler 2009-06-09 19:29:39 +00:00
Andrey Prygunkov
69c8ac3942 fixed compatibility issues with OS/2 2009-06-07 18:32:24 +00:00
Andrey Prygunkov
acbc9370f5 added support for platforms without IPv6 (they do not have <getaddrinfo>) 2009-06-07 16:53:32 +00:00
Andrey Prygunkov
f3563e8b4a improved configure script for better detection of libpar2 2009-06-05 17:31:47 +00:00
Andrey Prygunkov
5484ccc0f0 added field <NZBID> to NZBInfo; the field is now also returned by XML-/JSON-RPC methods <listfiles>, <listgroups> and <postqueue> 2009-06-03 16:07:00 +00:00
Andrey Prygunkov
34fe0f1077 refactoring: NZBInfo-objects now holds the status of completed post-process jobs; that made the list of completed post jobs unnecessary 2009-06-03 16:00:35 +00:00
Andrey Prygunkov
5039cbb529 improved the detection of new files in incoming nzb directory: now the scanner does not rely on system datum, but tracks the changing of file sizes during a last few (<NzbDirFileAge>) seconds instead 2009-05-31 10:11:19 +00:00
Andrey Prygunkov
93a9876de0 removed unneeded time functions 2009-05-30 22:18:31 +00:00
Andrey Prygunkov
ae311718da corrected includes for <time.h> 2009-05-30 22:09:41 +00:00
Andrey Prygunkov
441945d7e6 improved formatting of groups and added time info in curses output mode 2009-05-30 16:36:49 +00:00
Andrey Prygunkov
419b7fcb2c refactoring: moved the code for checking the incoming nzb directory from <PrePostProcessor> into the new module <Scanner> 2009-05-29 22:37:51 +00:00
Andrey Prygunkov
0692547440 redesigned server pool and par-checker to avoid using of semaphores; updated makefile to include postprocess-example.conf 2009-05-27 21:09:08 +00:00
Andrey Prygunkov
6471928f91 added subcommand <S> to remote commands <--pause/-P> and <--unpause/-U> to pause/unpause the scanning of incoming nzb-directory; added commands <PauseScan> and <UnpauseScan> for scheduler option <TaskX.Command>; added remote commands <PauseScan> and <ResumeScan> for XML-/JSON-RPC 2009-05-25 20:18:20 +00:00
Andrey Prygunkov
1866295d5d refactoring: removed references to OS-specific includes and types from <Thread.h> in order to isolate other units from these specifics 2009-05-24 17:03:34 +00:00
Andrey Prygunkov
4b538b419a refactoring: extended the meaning of DowloadQueue-object, which now contains file queue, post-job queue, list of completed post-jobs and list of nzb-infos; post-jobs now have references to nzb-infos, what eiliminates duplicate infos; since file queue and post-job queue are now accessed via DownloadQueue only one locking mechanism for both queues is needed 2009-05-24 14:26:53 +00:00
Andrey Prygunkov
72c19c08b8 improved error reporting while parsing nzb-files 2009-05-18 22:38:56 +00:00
Andrey Prygunkov
57b5afa676 fixed: environment block was not passed correctly to child process, what could result in seg faults (windows only) 2009-05-16 18:46:51 +00:00
Andrey Prygunkov
a5b8dfc1a3 changed the command line syntax for requesting of post-processor queue from <-O> to <-L O> for consistency with other post-queue related commands (<-P O>, <-U O> and <-E O>) 2009-05-15 20:22:03 +00:00
Andrey Prygunkov
9b87a3b755 imporved example postprocessing script: added support for external configuration file, postprocessing parameters and configuration via web-interface 2009-05-15 16:45:16 +00:00
Andrey Prygunkov
191b8531be option <TaskX.Process> now can contain parameters which must be passed to the script 2009-05-08 16:57:12 +00:00
Andrey Prygunkov
b1f6735e87 added pausing/resuming for post-processor queue; added new modifier <O> to remote commands <--pause/-P> and <--unpause/-U>; added new commands <postpause> and <postresume> to XML-/JSON-RPC; extended output of remote command <--list/-L> to indicate paused state of post-processor queue; extended command <status> of XML-/JSON-RPC with field <PostPause> 2009-05-06 19:28:54 +00:00
Andrey Prygunkov
900968a91c corrected output redirections in example post-processing script 2009-05-04 18:09:54 +00:00
Andrey Prygunkov
1e25d93a05 fixed: seg. fault in service mode on program start (windows only) 2009-05-04 16:24:17 +00:00
Andrey Prygunkov
bbdfb2b4f2 added modifier <O> to command <-E/--edit> for editing of post-processor-queue; following subcommands are supported: <+/-offset>, <T>, <B>, <D>; subcommand <D> supports deletion of queued post-jobs and active job as well; deletion of active job means the cancelling of par-check/repair or terminating of post-processing-script (including child processes of the script); updated remote-server to support new edit-subcommands in XML/JSON-RPC 2009-05-03 17:14:52 +00:00
Andrey Prygunkov
93ebcbac6c made example post-processing script a little bit simpler and more readable 2009-04-27 17:23:38 +00:00
Andrey Prygunkov
8c261215d7 extended the syntax of option <TaskX.Time> in two ways: 1) it now accepts multiple comma-separated values; 2) an asterix as hours-part means <every hour> 2009-04-26 16:33:21 +00:00
Andrey Prygunkov
8059c4f1d5 improved example post-processing script: added support for delayed par-check (try unrar first, par-repair if unrar failed) 2009-04-25 12:05:30 +00:00
Andrey Prygunkov
e9f5cf2259 added check to prevent the changing of read-only options (ConfigFile, AppBin, AppDir, Version) 2009-04-24 16:49:37 +00:00
Andrey Prygunkov
86bec2943b added readonly option <Version> for usage in processing scripts (option is avaialble as environment variable <NZBOP_VERSION>) 2009-04-24 16:30:47 +00:00
Andrey Prygunkov
f812646267 updated ChangeLog 2009-04-24 16:21:53 +00:00
Andrey Prygunkov
390582e3f2 added readonly options <AppBin> and <ConfigFile> for usage in processing scripts (options are avaialble as environment variables <NZBOP_APPBIN> and <NZBOP_CONFIGFILE) 2009-04-24 16:10:58 +00:00
Andrey Prygunkov
5d5c6b06b9 commited changes missed in previous revision 2009-04-24 05:48:55 +00:00
Andrey Prygunkov
10269dc779 added new return code (93) for post-processing script to indicate successful processing; that results in cleaning up of download queue if option <ParCleanupQueue> is active 2009-04-23 20:39:07 +00:00
Andrey Prygunkov
b28c2ae735 added estimated remaining time and better distinguishing of server state in command <--list/-L> 2009-04-19 19:54:45 +00:00
Andrey Prygunkov
8810d18020 fixed compilation error on windows, caused by changes in revision 297 2009-04-19 19:00:17 +00:00
Andrey Prygunkov
9b8841174e fixed linking error on OpenSolaris when using GnuTLS 2009-04-19 17:27:58 +00:00
Andrey Prygunkov
a3a56b2ee9 fixed compilation warning on certain versions of gcc 2009-04-19 17:26:41 +00:00
Andrey Prygunkov
41db56c093 fixed compilation error when using native curses library on OpenSolaris 2009-04-19 16:44:03 +00:00
Andrey Prygunkov
c8e7cc856f added svn revision number to version string (commands <-v> and <-V>, startup log entry); svn revision is automatically read from svn-repository on each build 2009-04-17 21:53:00 +00:00
Andrey Prygunkov
18d3e0db42 updated version string (0.7.0-testing) 2009-04-13 19:06:37 +00:00
483 changed files with 160209 additions and 37324 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

74
.gitignore vendored Normal file
View File

@@ -0,0 +1,74 @@
# 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.h.in~
config.log
config.status
Makefile
stamp-h1
autom4te.cache/
.dirstamp
*.o-*
# 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
.vscode/
# macOS
.DS_Store
# NZBGet specific
nzbget
code_revision.cpp
*.temp
*.pyc
pytest.ini
.cache

17
.lgtm.yml Normal file
View File

@@ -0,0 +1,17 @@
# Configuration file for integration with http://lgtm.com
path_classifiers:
library:
# exclude these directories from default alerts report:
- lib
- webui/lib
extraction:
cpp:
configure:
command:
# compile with tests to activate scanning of C++ sources for tests
- ./configure --enable-tests
queries:
- exclude: js/incomplete-sanitization # this one gives false positives only and nothing useful

67
.travis.yml Normal file
View File

@@ -0,0 +1,67 @@
sudo: required
dist: trusty
language: cpp
matrix:
include:
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5
- unrar
- p7zip-full
- par2
env:
- COMPILER=g++-5
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
- unrar
- p7zip-full
- par2
env:
- COMPILER=g++-4.9
- compiler: gcc
addons:
apt:
packages:
- unrar
- p7zip-full
- par2
env:
- COMPILER=g++-4.8
- CXXFLAGS="-std=c++11 -O2 -s"
- CONFIGUREOPTS="--disable-cpp-check"
- compiler: clang
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.6
packages:
- clang-3.6
- unrar
- p7zip-full
- par2
env:
- COMPILER=clang++-3.6
install:
- sudo pip install -U pytest
script:
- $COMPILER --version
- CXX=$COMPILER ./configure $CONFIGUREOPTS --enable-tests && make
- ./nzbget --tests
- cd tests/functional && pytest -v

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)
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 and later)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,129 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef ARTICLEDOWNLOADER_H
#define ARTICLEDOWNLOADER_H
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#include "Observer.h"
#include "DownloadInfo.h"
#include "Thread.h"
#include "NNTPConnection.h"
#include "Decoder.h"
class ArticleDownloader : public Thread, public Subject
{
public:
enum EStatus
{
adUndefined,
adRunning,
adFinished,
adFailed,
adRetry,
adDecodeError,
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
private:
FileInfo* m_pFileInfo;
ArticleInfo* m_pArticleInfo;
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;
time_t m_tLastUpdateTime;
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
public:
ArticleDownloader();
~ArticleDownloader();
void SetFileInfo(FileInfo* pFileInfo) { m_pFileInfo = pFileInfo; }
FileInfo* GetFileInfo() { return m_pFileInfo; }
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
virtual void Run();
virtual void Stop();
bool Terminate();
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void CompleteFileParts();
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
void LogDebugInfo();
};
class DownloadSpeedMeter
{
public:
virtual ~DownloadSpeedMeter() {};
virtual float CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};
class NZBInfoLocker
{
public:
virtual ~NZBInfoLocker() {};
virtual void LockNZBInfo(NZBInfo* pNZBInfo) = 0;
virtual void UnlockNZBInfo(NZBInfo* pNZBInfo) = 0;
};
#endif

View File

@@ -1,903 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "BinRpc.h"
#include "Log.h"
#include "Options.h"
#include "QueueCoordinator.h"
#include "QueueEditor.h"
#include "PrePostProcessor.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern void ExitProc();
const char* g_szMessageRequestNames[] =
{ "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug",
"Edit queue", "Log", "Quit", "Version", "Post-queue", "Write log", "Scan" };
const unsigned int g_iMessageRequestSizes[] =
{ 0,
sizeof(SNZBDownloadRequest),
sizeof(SNZBPauseUnpauseRequest),
sizeof(SNZBListRequest),
sizeof(SNZBSetDownloadRateRequest),
sizeof(SNZBDumpDebugRequest),
sizeof(SNZBEditQueueRequest),
sizeof(SNZBLogRequest),
sizeof(SNZBShutdownRequest),
sizeof(SNZBVersionRequest),
sizeof(SNZBPostQueueRequest),
sizeof(SNZBWriteLogRequest),
sizeof(SNZBScanRequest)
};
//*****************************************************************
// BinProcessor
void BinRpcProcessor::Execute()
{
// Read the first package which needs to be a request
int iBytesReceived = recv(m_iSocket, ((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature), 0);
if (iBytesReceived < 0)
{
return;
}
// Make sure this is a nzbget request from a client
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), m_szClientIP);
return;
}
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
{
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetServerPort(), m_szClientIP);
return;
}
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_szClientIP);
Dispatch();
}
void BinRpcProcessor::Dispatch()
{
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestScan &&
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
{
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
return;
}
BinCommand* command = NULL;
switch (ntohl(m_MessageBase.m_iType))
{
case eRemoteRequestDownload:
{
command = new DownloadBinCommand();
break;
}
case eRemoteRequestList:
{
command = new ListBinCommand();
break;
}
case eRemoteRequestLog:
{
command = new LogBinCommand();
break;
}
case eRemoteRequestPauseUnpause:
{
command = new PauseUnpauseBinCommand();
break;
}
case eRemoteRequestEditQueue:
{
command = new EditQueueBinCommand();
break;
}
case eRemoteRequestSetDownloadRate:
{
command = new SetDownloadRateBinCommand();
break;
}
case eRemoteRequestDumpDebug:
{
command = new DumpDebugBinCommand();
break;
}
case eRemoteRequestShutdown:
{
command = new ShutdownBinCommand();
break;
}
case eRemoteRequestVersion:
{
command = new VersionBinCommand();
break;
}
case eRemoteRequestPostQueue:
{
command = new PostQueueBinCommand();
break;
}
case eRemoteRequestWriteLog:
{
command = new WriteLogBinCommand();
break;
}
case eRemoteRequestScan:
{
command = new ScanBinCommand();
break;
}
default:
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
break;
}
if (command)
{
command->SetSocket(m_iSocket);
command->SetMessageBase(&m_MessageBase);
command->Execute();
delete command;
}
}
//*****************************************************************
// Commands
void BinCommand::SendBoolResponse(bool bSuccess, const char* szText)
{
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
SNZBDownloadResponse BoolResponse;
memset(&BoolResponse, 0, sizeof(BoolResponse));
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
BoolResponse.m_bSuccess = htonl(bSuccess);
int iTextLen = strlen(szText) + 1;
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
// Send the request answer
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
send(m_iSocket, (char*)szText, iTextLen, 0);
}
bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
{
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
iSize -= sizeof(SNZBRequestBase);
if (iSize > 0)
{
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
if (iBytesReceived != iSize)
{
error("invalid request");
return false;
}
}
return true;
}
void PauseUnpauseBinCommand::Execute()
{
SNZBPauseUnpauseRequest PauseUnpauseRequest;
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
{
return;
}
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
}
void SetDownloadRateBinCommand::Execute()
{
SNZBSetDownloadRateRequest SetDownloadRequest;
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
{
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
SendBoolResponse(true, "Rate-Command completed successfully");
}
void DumpDebugBinCommand::Execute()
{
SNZBDumpDebugRequest DumpDebugRequest;
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
{
return;
}
g_pQueueCoordinator->LogDebugInfo();
SendBoolResponse(true, "Debug-Command completed successfully");
}
void ShutdownBinCommand::Execute()
{
SNZBShutdownRequest ShutdownRequest;
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
{
return;
}
SendBoolResponse(true, "Stopping server");
ExitProc();
}
void VersionBinCommand::Execute()
{
SNZBVersionRequest VersionRequest;
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
{
return;
}
SendBoolResponse(true, VERSION);
}
void DownloadBinCommand::Execute()
{
SNZBDownloadRequest DownloadRequest;
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
{
return;
}
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
if (NeedBytes == 0)
{
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
if (pNZBFile)
{
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
delete pNZBFile;
char tmp[1024];
snprintf(tmp, 1024, "Collection %s added to queue", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(false, tmp);
}
}
free(pRecvBuffer);
}
void ListBinCommand::Execute()
{
SNZBListRequest ListRequest;
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
{
return;
}
SNZBListResponse ListResponse;
memset(&ListResponse, 0, sizeof(ListResponse));
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseFileEntry));
char* buf = NULL;
int bufsize = 0;
if (ntohl(ListRequest.m_bFileList))
{
// Make a data structure and copy all the elements of the list into it
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// prepare list of nzb-infos
NZBQueue cNZBQueue;
NZBInfo::BuildNZBList(pDownloadQueue, &cNZBQueue);
// calculate required buffer size for nzbs
int iNrNZBEntries = cNZBQueue.size();
int iNrPPPEntries = 0;
bufsize += iNrNZBEntries * sizeof(SNZBListResponseNZBEntry);
for (NZBQueue::iterator it = cNZBQueue.begin(); it != cNZBQueue.end(); it++)
{
NZBInfo* pNZBInfo = *it;
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
// calculate required buffer size for pp-parameters
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
bufsize += sizeof(SNZBListResponsePPPEntry);
bufsize += strlen(pNZBParameter->GetName()) + 1;
bufsize += strlen(pNZBParameter->GetValue()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
iNrPPPEntries++;
}
}
// calculate required buffer size for files
int iNrFileEntries = pDownloadQueue->size();
bufsize += iNrFileEntries * sizeof(SNZBListResponseFileEntry);
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bufsize += strlen(pFileInfo->GetSubject()) + 1;
bufsize += strlen(pFileInfo->GetFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
buf = (char*) malloc(bufsize);
char* bufptr = buf;
// write nzb entries
for (NZBQueue::iterator it = cNZBQueue.begin(); it != cNZBQueue.end(); it++)
{
unsigned long iSizeHi, iSizeLo;
NZBInfo* pNZBInfo = *it;
SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) bufptr;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1);
pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1);
pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1);
bufptr += sizeof(SNZBListResponseNZBEntry);
strcpy(bufptr, pNZBInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pNZBInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
strcpy(bufptr, pNZBInfo->GetCategory());
bufptr += ntohl(pListAnswer->m_iCategoryLen);
strcpy(bufptr, pNZBInfo->GetQueuedFilename());
bufptr += ntohl(pListAnswer->m_iQueuedFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
// write ppp entries
int iNZBIndex = 1;
for (NZBQueue::iterator it = cNZBQueue.begin(); it != cNZBQueue.end(); it++, iNZBIndex++)
{
NZBInfo* pNZBInfo = *it;
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
SNZBListResponsePPPEntry* pListAnswer = (SNZBListResponsePPPEntry*) bufptr;
pListAnswer->m_iNZBIndex = htonl(iNZBIndex);
pListAnswer->m_iNameLen = htonl(strlen(pNZBParameter->GetName()) + 1);
pListAnswer->m_iValueLen = htonl(strlen(pNZBParameter->GetValue()) + 1);
bufptr += sizeof(SNZBListResponsePPPEntry);
strcpy(bufptr, pNZBParameter->GetName());
bufptr += ntohl(pListAnswer->m_iNameLen);
strcpy(bufptr, pNZBParameter->GetValue());
bufptr += ntohl(pListAnswer->m_iValueLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iValueLen = htonl(ntohl(pListAnswer->m_iValueLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
}
// write file entries
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
unsigned long iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseFileEntry* pListAnswer = (SNZBListResponseFileEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
int iNZBIndex = 0;
for (unsigned int i = 0; i < cNZBQueue.size(); i++)
{
iNZBIndex++;
if (cNZBQueue[i] == pFileInfo->GetNZBInfo())
{
break;
}
}
pListAnswer->m_iNZBIndex = htonl(iNZBIndex);
Util::SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
Util::SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
bufptr += sizeof(SNZBListResponseFileEntry);
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += ntohl(pListAnswer->m_iSubjectLen);
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iFilenameLen = htonl(ntohl(pListAnswer->m_iFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries);
ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries);
ListResponse.m_iNrTrailingFileEntries = htonl(iNrFileEntries);
ListResponse.m_iTrailingDataLength = htonl(bufsize);
}
if (htonl(ListRequest.m_bServerState))
{
unsigned long iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
Util::SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
ListResponse.m_iPostJobCount = htonl(pPostQueue->size());
g_pPrePostProcessor->UnlockPostQueue();
int iUpTimeSec, iDnTimeSec;
long long iAllBytes;
bool bStandBy;
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
ListResponse.m_bServerStandBy = htonl(bStandBy);
Util::SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
}
// Send the request answer
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
if (buf)
{
free(buf);
}
}
void LogBinCommand::Execute()
{
SNZBLogRequest LogRequest;
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
{
return;
}
Log::Messages* pMessages = g_pLog->LockMessages();
int iNrEntries = ntohl(LogRequest.m_iLines);
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
int iStart = pMessages->size();
if (iNrEntries > 0)
{
if (iNrEntries > (int)pMessages->size())
{
iNrEntries = pMessages->size();
}
iStart = pMessages->size() - iNrEntries;
}
if (iIDFrom > 0 && !pMessages->empty())
{
iStart = iIDFrom - pMessages->front()->GetID();
if (iStart < 0)
{
iStart = 0;
}
iNrEntries = pMessages->size() - iStart;
if (iNrEntries < 0)
{
iNrEntries = 0;
}
}
// calculate required buffer size
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
bufsize += strlen(pMessage->GetText()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
char* buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
pLogAnswer->m_iID = htonl(pMessage->GetID());
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
pLogAnswer->m_tTime = htonl((int)pMessage->GetTime());
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
bufptr += sizeof(SNZBLogResponseEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += ntohl(pLogAnswer->m_iTextLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pLogAnswer->m_iTextLen = htonl(ntohl(pLogAnswer->m_iTextLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pLog->UnlockMessages();
SNZBLogResponse LogResponse;
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
LogResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}
void EditQueueBinCommand::Execute()
{
SNZBEditQueueRequest EditQueueRequest;
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
{
return;
}
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
int iAction = ntohl(EditQueueRequest.m_iAction);
int iOffset = ntohl(EditQueueRequest.m_iOffset);
int iTextLen = ntohl(EditQueueRequest.m_iTextLen);
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
if (iNrEntries * sizeof(int32_t) + iTextLen != iBufLength)
{
error("Invalid struct size");
return;
}
if (iNrEntries <= 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
return;
}
char* pBuf = (char*)malloc(iBufLength);
char* szText = NULL;
int32_t* pIDs = NULL;
// Read from the socket until nothing remains
char* pBufPtr = pBuf;
int NeedBytes = iBufLength;
int iResult = 0;
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
bool bOK = NeedBytes == 0;
if (bOK)
{
szText = iTextLen > 0 ? pBuf : NULL;
pIDs = (int32_t*)(pBuf + iTextLen);
}
QueueEditor::IDList cIDList;
cIDList.reserve(iNrEntries);
for (int i = 0; i < iNrEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
free(pBuf);
if (bOK)
{
SendBoolResponse(true, "Edit-Command completed successfully");
}
else
{
SendBoolResponse(false, "Edit-Command failed");
}
}
void PostQueueBinCommand::Execute()
{
SNZBPostQueueRequest PostQueueRequest;
if (!ReceiveRequest(&PostQueueRequest, sizeof(PostQueueRequest)))
{
return;
}
SNZBPostQueueResponse PostQueueResponse;
memset(&PostQueueResponse, 0, sizeof(PostQueueResponse));
PostQueueResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
PostQueueResponse.m_MessageBase.m_iStructSize = htonl(sizeof(PostQueueResponse));
PostQueueResponse.m_iEntrySize = htonl(sizeof(SNZBPostQueueResponseEntry));
char* buf = NULL;
int bufsize = 0;
// Make a data structure and copy all the elements of the list into it
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
int NrEntries = pPostQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBPostQueueResponseEntry);
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
{
PostInfo* pPostInfo = *it;
bufsize += strlen(pPostInfo->GetNZBFilename()) + 1;
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
bufsize += strlen(pPostInfo->GetDestDir()) + 1;
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
time_t tCurTime = time(NULL);
buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
{
PostInfo* pPostInfo = *it;
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) bufptr;
pPostQueueAnswer->m_iID = htonl(pPostInfo->GetID());
pPostQueueAnswer->m_iStage = htonl(pPostInfo->GetStage());
pPostQueueAnswer->m_iStageProgress = htonl(pPostInfo->GetStageProgress());
pPostQueueAnswer->m_iFileProgress = htonl(pPostInfo->GetFileProgress());
pPostQueueAnswer->m_iTotalTimeSec = htonl((int)(pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0));
pPostQueueAnswer->m_iStageTimeSec = htonl((int)(pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0));
pPostQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pPostInfo->GetNZBFilename()) + 1);
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetDestDir()) + 1);
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
bufptr += sizeof(SNZBPostQueueResponseEntry);
strcpy(bufptr, pPostInfo->GetNZBFilename());
bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
strcpy(bufptr, pPostInfo->GetParFilename());
bufptr += ntohl(pPostQueueAnswer->m_iParFilename);
strcpy(bufptr, pPostInfo->GetInfoName());
bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen);
strcpy(bufptr, pPostInfo->GetDestDir());
bufptr += ntohl(pPostQueueAnswer->m_iDestDirLen);
strcpy(bufptr, pPostInfo->GetProgressLabel());
bufptr += ntohl(pPostQueueAnswer->m_iProgressLabelLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pPostQueueAnswer->m_iProgressLabelLen = htonl(ntohl(pPostQueueAnswer->m_iProgressLabelLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pPrePostProcessor->UnlockPostQueue();
PostQueueResponse.m_iNrTrailingEntries = htonl(NrEntries);
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
if (buf)
{
free(buf);
}
}
void WriteLogBinCommand::Execute()
{
SNZBWriteLogRequest WriteLogRequest;
if (!ReceiveRequest(&WriteLogRequest, sizeof(WriteLogRequest)))
{
return;
}
char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(WriteLogRequest.m_iTrailingDataLength);
pRecvBuffer[NeedBytes] = '\0';
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
if (NeedBytes == 0)
{
bool OK = true;
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
{
case Message::mkDetail:
detail(pRecvBuffer);
break;
case Message::mkInfo:
info(pRecvBuffer);
break;
case Message::mkWarning:
warn(pRecvBuffer);
break;
case Message::mkError:
error(pRecvBuffer);
break;
case Message::mkDebug:
debug(pRecvBuffer);
break;
default:
OK = false;
}
SendBoolResponse(OK, OK ? "Message added to log" : "Invalid message-kind");
}
free(pRecvBuffer);
}
void ScanBinCommand::Execute()
{
SNZBScanRequest ScanRequest;
if (!ReceiveRequest(&ScanRequest, sizeof(ScanRequest)))
{
return;
}
g_pPrePostProcessor->ScanNZBDir();
SendBoolResponse(true, "Scan-Command scheduled successfully");
}

137
BinRpc.h
View File

@@ -1,137 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef BINRPC_H
#define BINRPC_H
#include "Connection.h"
#include "MessageBase.h"
class BinRpcProcessor
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
const char* m_szClientIP;
void Dispatch();
public:
void Execute();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
void SetSignature(int iSignature) { m_MessageBase.m_iSignature = iSignature; }
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
class BinCommand
{
protected:
SOCKET m_iSocket;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};
class DownloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ListBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class LogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PauseUnpauseBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class EditQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class SetDownloadRateBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class DumpDebugBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ShutdownBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class VersionBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class PostQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class WriteLogBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class ScanBinCommand: public BinCommand
{
public:
virtual void Execute();
};
#endif

41
COPYING
View File

@@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@@ -303,10 +303,9 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
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.
Also add information on how to contact you by electronic and paper mail.
@@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

2651
ChangeLog
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,192 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "ColoredFrontend.h"
#include "Util.h"
ColoredFrontend::ColoredFrontend()
{
m_bSummary = true;
m_bNeedGoBack = false;
#ifdef WIN32
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
#endif
}
void ColoredFrontend::BeforePrint()
{
if (m_bNeedGoBack)
{
// go back one line
#ifdef WIN32
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
GetConsoleScreenBufferInfo(m_hConsole, &BufInfo);
BufInfo.dwCursorPosition.Y--;
SetConsoleCursorPosition(m_hConsole, BufInfo.dwCursorPosition);
#else
printf("\r\033[1A");
#endif
m_bNeedGoBack = false;
}
}
void ColoredFrontend::PrintStatus()
{
char tmp[1024];
char timeString[100];
timeString[0] = '\0';
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
{
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
}
char szDownloadLimit[128];
if (m_fDownloadLimit > 0.0f)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
}
else
{
szDownloadLimit[0] = 0;
}
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
szPostStatus[0] = 0;
}
#ifdef WIN32
char* szControlSeq = "";
#else
printf("\033[s");
const char* szControlSeq = "\033[K";
#endif
snprintf(tmp, 1024, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, fCurrentDownloadSpeed, (float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0),
timeString, szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
tmp[1024-1] = '\0';
printf("%s", tmp);
m_bNeedGoBack = true;
}
void ColoredFrontend::PrintMessage(Message * pMessage)
{
#ifdef WIN32
switch (pMessage->GetKind())
{
case Message::mkDebug:
SetConsoleTextAttribute(m_hConsole, 8);
printf("[DEBUG]");
break;
case Message::mkError:
SetConsoleTextAttribute(m_hConsole, 4);
printf("[ERROR]");
break;
case Message::mkWarning:
SetConsoleTextAttribute(m_hConsole, 5);
printf("[WARNING]");
break;
case Message::mkInfo:
SetConsoleTextAttribute(m_hConsole, 2);
printf("[INFO]");
break;
case Message::mkDetail:
SetConsoleTextAttribute(m_hConsole, 2);
printf("[DETAIL]");
break;
}
SetConsoleTextAttribute(m_hConsole, 7);
char* msg = strdup(pMessage->GetText());
CharToOem(msg, msg);
printf(" %s\n", msg);
free(msg);
#else
const char* msg = pMessage->GetText();
switch (pMessage->GetKind())
{
case Message::mkDebug:
printf("[DEBUG] %s\033[K\n", msg);
break;
case Message::mkError:
printf("\033[31m[ERROR]\033[39m %s\033[K\n", msg);
break;
case Message::mkWarning:
printf("\033[35m[WARNING]\033[39m %s\033[K\n", msg);
break;
case Message::mkInfo:
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
break;
case Message::mkDetail:
printf("\033[32m[DETAIL]\033[39m %s\033[K\n", msg);
break;
}
#endif
}
void ColoredFrontend::PrintSkip()
{
#ifdef WIN32
printf(".....\n");
#else
printf(".....\033[K\n");
#endif
}
void ColoredFrontend::BeforeExit()
{
if (IsRemoteMode())
{
printf("\n");
}
}

View File

@@ -1,728 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
// SKIP_DEFAULT_WINDOWS_HEADERS prevents the including of <windows.h>, which includes "winsock.h",
// but we need "winsock2.h" here (they conflicts with each other)
#define SKIP_DEFAULT_WINDOWS_HEADERS
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "Connection.h"
#include "Log.h"
#include "TLS.h"
static const int CONNECTION_READBUFFER_SIZE = 1024;
#ifndef DISABLE_TLS
bool Connection::bTLSLibInitialized = false;
#endif
void Connection::Init(bool bTLS)
{
debug("Initializing global connection data");
#ifdef WIN32
WSADATA wsaData;
int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
if (err != 0)
{
error("Could not initialize socket library");
return;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE( wsaData.wVersion ) != 0)
{
error("Could not initialize socket library");
WSACleanup();
return;
}
#endif
#ifndef DISABLE_TLS
if (bTLS)
{
debug("Initializing TLS library");
char* szErrStr;
int iRes = tls_lib_init(&szErrStr);
bTLSLibInitialized = iRes == TLS_EOK;
if (!bTLSLibInitialized)
{
error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error");
if (szErrStr)
{
free(szErrStr);
}
}
}
#endif
}
void Connection::Final()
{
debug("Finalizing global connection data");
#ifdef WIN32
WSACleanup();
#endif
#ifndef DISABLE_TLS
if (bTLSLibInitialized)
{
debug("Finalizing TLS library");
tls_lib_deinit();
}
#endif
}
Connection::Connection(NetAddress* pNetAddress)
{
debug("Creating Connection");
m_pNetAddress = pNetAddress;
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = true;
#ifndef DISABLE_TLS
m_pTLS = NULL;
m_bTLSError = false;
#endif
}
Connection::Connection(SOCKET iSocket, bool bAutoClose)
{
debug("Creating Connection");
m_pNetAddress = NULL;
m_eStatus = csConnected;
m_iSocket = iSocket;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = bAutoClose;
#ifndef DISABLE_TLS
m_pTLS = NULL;
#endif
}
Connection::~Connection()
{
debug("Destroying Connection");
if (m_eStatus == csConnected && m_bAutoClose)
{
Disconnect();
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
if (m_pTLS)
{
free(m_pTLS);
}
#endif
}
bool Connection::Connect()
{
debug("Connecting");
if (m_eStatus == csConnected)
return true;
bool bRes = DoConnect();
if (bRes)
m_eStatus = csConnected;
else
Connection::DoDisconnect();
return bRes;
}
bool Connection::Disconnect()
{
debug("Disconnecting");
if (m_eStatus == csDisconnected)
return true;
bool bRes = DoDisconnect();
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
return bRes;
}
int Connection::Bind()
{
debug("Binding");
if (m_eStatus == csListening)
{
return 0;
}
int iRes = DoBind();
if (iRes == 0)
{
m_eStatus = csListening;
}
return iRes;
}
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
if (m_eStatus != csConnected)
{
return -1;
}
int iRes = DoWriteLine(pBuffer);
return iRes;
}
int Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
if (m_eStatus != csConnected)
{
return -1;
}
int iRes = send(m_iSocket, pBuffer, iSize, 0);
return iRes;
}
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
if (m_eStatus != csConnected)
{
return NULL;
}
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
}
SOCKET Connection::Accept()
{
debug("Accepting connection");
if (m_eStatus != csListening)
{
return INVALID_SOCKET;
}
SOCKET iRes = DoAccept();
return iRes;
}
int Connection::Recv(char* pBuffer, int iSize)
{
debug("Receiving data");
memset(pBuffer, 0, iSize);
int iReceived = recv(m_iSocket, pBuffer, iSize, 0);
if (iReceived < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
}
return iReceived;
}
bool Connection::RecvAll(char * pBuffer, int iSize)
{
debug("Receiving data (full buffer)");
memset(pBuffer, 0, iSize);
char* pBufPtr = (char*)pBuffer;
int NeedBytes = iSize;
if (m_iBufAvail > 0)
{
int len = iSize > m_iBufAvail ? m_iBufAvail : iSize;
memcpy(pBufPtr, m_szBufPtr, len);
pBufPtr += len;
m_szBufPtr += len;
m_iBufAvail -= len;
NeedBytes -= len;
}
// Read from the socket until nothing remains
while (NeedBytes > 0)
{
int iReceived = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iReceived <= 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
return false;
}
pBufPtr += iReceived;
NeedBytes -= iReceived;
}
return true;
}
bool Connection::DoConnect()
{
debug("Do connecting");
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; //is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
addr_hints.ai_socktype = SOCK_STREAM,
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
ReportError("Could not resolve hostname %s", m_pNetAddress->GetHost(), true, 0);
return false;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
res = connect(m_iSocket , addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), true, 0);
return false;
}
#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)
{
ReportError("setsockopt failed", NULL, true, 0);
}
return true;
}
bool Connection::DoDisconnect()
{
debug("Do disconnecting");
if (m_iSocket > 0)
{
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
#ifndef DISABLE_TLS
if (m_pTLS)
{
CloseTLS();
}
#endif
}
m_eStatus = csDisconnected;
return true;
}
int Connection::DoWriteLine(const char* pBuffer)
{
//debug("Connection::doWrite()");
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
//debug( "Connection::DoReadLine()" );
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
}
return pBuffer;
}
int Connection::DoBind()
{
debug("Do binding");
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error( "Could not resolve hostname %s", m_pNetAddress->GetHost() );
return -1;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
if (listen(m_iSocket, 10) < 0)
{
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
return 0;
}
SOCKET Connection::DoAccept()
{
SOCKET iSocket = accept(GetSocket(), NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
}
void Connection::Cancel()
{
debug("Cancelling connection");
if (m_iSocket != INVALID_SOCKET)
{
m_eStatus = csCancelled;
int r = shutdown(m_iSocket, SHUT_RDWR);
if (r == -1)
{
ReportError("Could not shutdown connection", NULL, true, 0);
}
}
}
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int ErrCode)
{
#ifndef DISABLE_TLS
if (m_bTLSError)
{
// TLS-Error was already reported
m_bTLSError = false;
return;
}
#endif
char szErrPrefix[1024];
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
szErrPrefix[1024-1] = '\0';
if (PrintErrCode)
{
if (ErrCode == 0)
{
#ifdef WIN32
ErrCode = WSAGetLastError();
#else
ErrCode = errno;
#endif
}
#ifdef WIN32
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
#else
const char* szErrMsg = hstrerror(ErrCode);
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
#endif
}
else
{
if (m_bSuppressErrors)
{
debug(szErrPrefix);
}
else
{
error(szErrPrefix);
}
}
}
#ifndef DISABLE_TLS
bool Connection::CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix)
{
bool bOK = iResultCode == TLS_EOK;
if (!bOK)
{
ReportError(szErrMsgPrefix, szErrStr ? szErrStr : "unknown error", false, 0);
if (szErrStr)
{
free(szErrStr);
}
}
return bOK;
}
bool Connection::StartTLS()
{
debug("Starting TLS");
if (m_pTLS)
{
free(m_pTLS);
}
m_pTLS = malloc(sizeof(tls_t));
tls_t* pTLS = (tls_t*)m_pTLS;
memset(pTLS, 0, sizeof(tls_t));
tls_clear(pTLS);
char* szErrStr;
int iRes;
iRes = tls_init(pTLS, NULL, NULL, NULL, 0, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not initialize TLS-object: %s"))
{
return false;
}
debug("tls_start...");
iRes = tls_start(pTLS, (int)m_iSocket, NULL, 1, NULL, &szErrStr);
debug("tls_start...%i", iRes);
if (!CheckTLSResult(iRes, szErrStr, "Could not establish secure connection: %s"))
{
return false;
}
return true;
}
void Connection::CloseTLS()
{
tls_close((tls_t*)m_pTLS);
free(m_pTLS);
m_pTLS = NULL;
}
int Connection::recv(SOCKET s, char* buf, int len, int flags)
{
size_t iReceived = 0;
if (m_pTLS)
{
m_bTLSError = false;
char* szErrStr;
int iRes = tls_getbuf((tls_t*)m_pTLS, buf, len, &iReceived, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not read from TLS-socket: %s"))
{
m_bTLSError = true;
return -1;
}
}
else
{
iReceived = ::recv(s, buf, len, flags);
}
return iReceived;
}
int Connection::send(SOCKET s, const char* buf, int len, int flags)
{
if (m_pTLS)
{
m_bTLSError = false;
char* szErrStr;
int iRes = tls_putbuf((tls_t*)m_pTLS, buf, len, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not send to TLS-socket: %s"))
{
m_bTLSError = true;
return -1;
}
return 0;
}
else
{
int iRet = ::send(s, buf, len, flags);
return iRet;
}
}
#endif

View File

@@ -1,100 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include "NetAddress.h"
class Connection
{
public:
enum EStatus
{
csConnected,
csDisconnected,
csListening,
csCancelled
};
protected:
NetAddress* m_pNetAddress;
SOCKET m_iSocket;
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
int m_iTimeout;
bool m_bSuppressErrors;
bool m_bAutoClose;
#ifndef DISABLE_TLS
void* m_pTLS;
static bool bTLSLibInitialized;
bool m_bTLSError;
#endif
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int ErrCode);
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
#ifndef DISABLE_TLS
bool CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix);
int recv(SOCKET s, char* buf, int len, int flags);
int send(SOCKET s, const char* buf, int len, int flags);
void CloseTLS();
#endif
public:
Connection(NetAddress* pNetAddress);
Connection(SOCKET iSocket, bool bAutoClose);
virtual ~Connection();
static void Init(bool bTLS);
static void Final();
bool Connect();
bool Disconnect();
int Bind();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
int WriteLine(const char* pBuffer);
SOCKET Accept();
void Cancel();
NetAddress* GetServer() { return m_pNetAddress; }
SOCKET GetSocket() { return m_iSocket; }
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
bool GetSuppressErrors() { return m_bSuppressErrors; }
#ifndef DISABLE_TLS
bool StartTLS();
#endif
};
#endif

View File

@@ -1,485 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "Decoder.h"
#include "Log.h"
#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;
}
Decoder::~ Decoder()
{
debug("Destroying Decoder");
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
}
void Decoder::Clear()
{
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
{
if (!strncmp(buffer, "=ybegin ", 8))
{
return efYenc;
}
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
return efUx;
}
if (!strncmp(buffer, "begin ", 6))
{
bool bOK = true;
buffer += 6; //strlen("begin ")
while (*buffer && *buffer != ' ')
{
char ch = *buffer++;
if (ch < '0' || ch > '7')
{
bOK = false;
break;
}
}
if (bOK)
{
return efUx;
}
}
return efUnknown;
}
/**
* YDecoder: fast implementation of yEnc-Decoder
*/
void YDecoder::Init()
{
debug("Initializing global decoder");
crc32gentab();
}
void YDecoder::Final()
{
debug("Finalizing global Decoder");
}
YDecoder::YDecoder()
{
Clear();
}
void YDecoder::Clear()
{
Decoder::Clear();
m_bBody = false;
m_bBegin = false;
m_bPart = false;
m_bEnd = false;
m_bCrc = false;
m_lExpectedCRC = 0;
m_lCalculatedCRC = 0xFFFFFFFF;
m_iBegin = 0;
m_iEnd = 0xFFFFFFFF;
m_iSize = 0;
m_iEndSize = 0;
m_bAutoSeek = false;
m_bNeedSetPos = false;
m_bCrcCheck = false;
}
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
*
* (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)
{
if (m_bBody && !m_bEnd)
{
if (!strncmp(buffer, "=yend ", 6))
{
m_bEnd = true;
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
if (pb)
{
m_bCrc = true;
pb += 7 + (int)m_bPart; //=strlen(" crc32=") or strlen(" pcrc32=")
m_lExpectedCRC = strtoul(pb, NULL, 16);
}
pb = strstr(buffer, " size=");
if (pb)
{
pb += 6; //=strlen(" size=")
m_iEndSize = (int)atoi(pb);
}
return 0;
}
char* iptr = buffer;
char* optr = buffer;
while (true)
{
switch (*iptr)
{
case '=': //escape-sequence
iptr++;
*optr = *iptr - 64 - 42;
optr++;
break;
case '\n': // ignored char
case '\r': // ignored char
break;
case '\0':
goto BreakLoop;
default: // normal char
*optr = *iptr - 42;
optr++;
break;
}
iptr++;
}
BreakLoop:
if (m_bCrcCheck)
{
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
}
return (unsigned int)(optr - buffer);
}
else
{
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
{
m_bBegin = true;
char* pb = strstr(buffer, " name=");
if (pb)
{
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
}
pb = strstr(buffer, " size=");
if (pb)
{
pb += 6; //=strlen(" size=")
m_iSize = (int)atoi(pb);
}
m_bPart = strstr(buffer, " part=");
if (!m_bPart)
{
m_bBody = true;
m_iBegin = 1;
m_iEnd = m_iSize;
}
}
else if (m_bPart && !strncmp(buffer, "=ypart ", 7))
{
m_bPart = true;
m_bBody = true;
char* pb = strstr(buffer, " begin=");
if (pb)
{
pb += 7; //=strlen(" begin=")
m_iBegin = (int)atoi(pb);
}
pb = strstr(buffer, " end=");
if (pb)
{
pb += 5; //=strlen(" end=")
m_iEnd = (int)atoi(pb);
}
}
}
return 0;
}
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer);
if (wcnt > 0)
{
if (m_bNeedSetPos)
{
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
{
return false;
}
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
{
return false;
}
m_bNeedSetPos = false;
}
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus YDecoder::Check()
{
m_lCalculatedCRC ^= 0xFFFFFFFF;
debug("Expected crc32=%x", m_lExpectedCRC);
debug("Calculated crc32=%x", m_lCalculatedCRC);
if (!m_bBegin)
{
return eNoBinaryData;
}
else if (!m_bEnd)
{
return eArticleIncomplete;
}
else if (!m_bPart && m_iSize != m_iEndSize)
{
return eInvalidSize;
}
else if (m_bCrcCheck && m_bCrc && (m_lExpectedCRC != m_lCalculatedCRC))
{
return eCrcError;
}
return eFinished;
}
/**
* UDecoder: supports UU encoding formats
*/
UDecoder::UDecoder()
{
}
void UDecoder::Clear()
{
Decoder::Clear();
m_bBody = false;
m_bEnd = false;
}
/* DecodeBuffer-function uses portions of code from tool UUDECODE by Clem Dye
* UUDECODE.c (http://www.bastet.com/uue.zip)
* Copyright (C) 1998 Clem Dye
*
* Released under GPL (thanks)
*/
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
{
if (!m_bBody)
{
if (!strncmp(buffer, "begin ", 6))
{
char* pb = buffer;
pb += 6; //strlen("begin ")
// skip file-permissions
for (; *pb != ' ' && *pb != '\0' && *pb != '\n' && *pb != '\r'; pb++) ;
pb++;
// extracting filename
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
m_bBody = true;
return 0;
}
else if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
m_bBody = true;
}
}
if (m_bBody && (!strncmp(buffer, "end ", 4) || *buffer == '`'))
{
m_bEnd = true;
}
if (m_bBody && !m_bEnd)
{
int iEffLen = UU_DECODE_CHAR(buffer[0]);
if (iEffLen > len)
{
// error;
return 0;
}
char* iptr = buffer;
char* optr = buffer;
for (++iptr; iEffLen > 0; iptr += 4, iEffLen -= 3)
{
if (iEffLen >= 3)
{
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
*optr++ = UU_DECODE_CHAR (iptr[2]) << 6 | UU_DECODE_CHAR (iptr[3]);
}
else
{
if (iEffLen >= 1)
{
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
}
if (iEffLen >= 2)
{
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
}
}
}
return (unsigned int)(optr - buffer);
}
return 0;
}
bool UDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer, len);
if (wcnt > 0)
{
fwrite(buffer, 1, wcnt, outfile);
}
return true;
}
Decoder::EStatus UDecoder::Check()
{
if (!m_bBody)
{
return eNoBinaryData;
}
return eFinished;
}

119
Decoder.h
View File

@@ -1,119 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef DECODER_H
#define DECODER_H
class Decoder
{
public:
enum EStatus
{
eUnknownError,
eFinished,
eArticleIncomplete,
eCrcError,
eInvalidSize,
eNoBinaryData
};
enum EFormat
{
efUnknown,
efYenc,
efUx,
};
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
public:
Decoder();
virtual ~Decoder();
virtual EStatus Check() = 0;
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
const char* GetArticleFilename() { return m_szArticleFilename; }
static EFormat DetectFormat(const char* buffer, int len);
};
class YDecoder: public Decoder
{
protected:
static unsigned int crc_tab[256];
bool m_bBegin;
bool m_bPart;
bool m_bBody;
bool m_bEnd;
bool m_bCrc;
unsigned long m_lExpectedCRC;
unsigned long m_lCalculatedCRC;
unsigned long m_iBegin;
unsigned long m_iEnd;
unsigned long m_iSize;
unsigned long m_iEndSize;
bool m_bAutoSeek;
bool m_bNeedSetPos;
bool m_bCrcCheck;
unsigned int DecodeBuffer(char* buffer);
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
public:
YDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
static void Init();
static void Final();
};
class UDecoder: public Decoder
{
private:
bool m_bBody;
bool m_bEnd;
unsigned int DecodeBuffer(char* buffer, int len);
public:
UDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
};
#endif

View File

@@ -1,814 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include "nzbget.h"
#include "DiskState.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
static const char* FORMATVERSION_SIGNATURE_V3 = "nzbget diskstate file version 3\n";
static const char* FORMATVERSION_SIGNATURE_V4 = "nzbget diskstate file version 4\n";
static const char* FORMATVERSION_SIGNATURE_V5 = "nzbget diskstate file version 5\n";
static const char* FORMATVERSION_SIGNATURE_V6 = "nzbget diskstate file version 6\n";
static const char* FORMATVERSION_SIGNATURE_V7 = "nzbget diskstate file version 7\n";
/* Parse signature and return format version number
*/
int DiskState::ParseFormatVersion(const char* szFormatSignature)
{
if (!strcmp(szFormatSignature, FORMATVERSION_SIGNATURE_V3))
{
return 3;
}
else if (!strcmp(szFormatSignature, FORMATVERSION_SIGNATURE_V4))
{
return 4;
}
else if (!strcmp(szFormatSignature, FORMATVERSION_SIGNATURE_V5))
{
return 5;
}
else if (!strcmp(szFormatSignature, FORMATVERSION_SIGNATURE_V6))
{
return 6;
}
else if (!strcmp(szFormatSignature, FORMATVERSION_SIGNATURE_V7))
{
return 7;
}
else
{
return 0;
}
}
/* Save Download Queue to Disk.
* The Disk State consists of file "queue", which contains the order of files
* and of one diskstate-file for each file in download queue.
* This function saves file "queue" and files with NZB-info. It does not
* save file-infos.
*/
bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
{
debug("Saving queue to disk");
// prepare list of nzb-infos
NZBQueue cNZBQueue;
NZBInfo::BuildNZBList(pDownloadQueue, &cNZBQueue);
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
FILE* outfile = fopen(fileName, "w");
if (!outfile)
{
error("Could not create file %s", fileName);
perror(fileName);
return false;
}
fprintf(outfile, FORMATVERSION_SIGNATURE_V6);
// save nzb-infos
fprintf(outfile, "%i\n", cNZBQueue.size());
for (NZBQueue::iterator it = cNZBQueue.begin(); it != cNZBQueue.end(); it++)
{
NZBInfo* pNZBInfo = *it;
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
fprintf(outfile, "%s\n", pNZBInfo->GetQueuedFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
fprintf(outfile, "%i\n", pNZBInfo->GetPostProcess() ? 1 : 0);
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
unsigned long High, Low;
Util::SplitInt64(pNZBInfo->GetSize(), &High, &Low);
fprintf(outfile, "%lu,%lu\n", High, Low);
fprintf(outfile, "%i\n", pNZBInfo->GetCompletedFiles()->size());
for (NZBInfo::Files::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
{
char* szFilename = *it;
fprintf(outfile, "%s\n", szFilename);
}
fprintf(outfile, "%i\n", pNZBInfo->GetParameters()->size());
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue());
}
}
// save file-infos
fprintf(outfile, "%i\n", pDownloadQueue->size());
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
if (!pFileInfo->GetDeleted())
{
// find index of nzb-info
int iNZBIndex = 0;
for (unsigned int i = 0; i < cNZBQueue.size(); i++)
{
iNZBIndex++;
if (cNZBQueue[i] == pFileInfo->GetNZBInfo())
{
break;
}
}
fprintf(outfile, "%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused());
}
}
fclose(outfile);
if (pDownloadQueue->empty())
{
remove(fileName);
}
return true;
}
bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
{
debug("Loading queue from disk");
bool bOK = false;
NZBQueue cNZBQueue;
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
FILE* infile = fopen(fileName, "r");
if (!infile)
{
error("Could not open file %s", fileName);
return false;
}
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 6)
{
error("Could not load diskstate due file version mismatch");
fclose(infile);
return false;
}
int size;
char buf[10240];
// load nzb-infos
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->AddReference();
cNZBQueue.push_back(pNZBInfo);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetDestDir(buf);
if (iFormatVersion >= 5)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetQueuedFilename(buf);
}
if (iFormatVersion >= 4)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetCategory(buf);
int iPostProcess;
if (fscanf(infile, "%i\n", &iPostProcess) != 1) goto error;
pNZBInfo->SetPostProcess(iPostProcess == 1);
}
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
pNZBInfo->SetFileCount(iFileCount);
unsigned long High, Low;
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
pNZBInfo->SetSize(Util::JoinInt64(High, Low));
if (iFormatVersion >= 4)
{
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
for (int i = 0; i < iFileCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->GetCompletedFiles()->push_back(strdup(buf));
}
}
if (iFormatVersion >= 6)
{
int iParameterCount;
if (fscanf(infile, "%i\n", &iParameterCount) != 1) goto error;
for (int i = 0; i < iParameterCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
char* szValue = strchr(buf, '=');
if (szValue)
{
*szValue = '\0';
szValue++;
pNZBInfo->SetParameter(buf, szValue);
}
}
}
}
// load file-infos
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
unsigned int id, iNZBIndex, paused;
if (fscanf(infile, "%i,%i,%i\n", &id, &iNZBIndex, &paused) != 3) goto error;
if (iNZBIndex < 0 || iNZBIndex > cNZBQueue.size()) goto error;
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
fileName[1024-1] = '\0';
FileInfo* pFileInfo = new FileInfo();
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
if (res)
{
pFileInfo->SetID(id);
pFileInfo->SetPaused(paused);
pFileInfo->SetNZBInfo(cNZBQueue[iNZBIndex - 1]);
pDownloadQueue->push_back(pFileInfo);
}
else
{
warn("Could not load diskstate for file %s", fileName);
delete pFileInfo;
}
}
bOK = true;
error:
fclose(infile);
if (!bOK)
{
error("Error reading diskstate for file %s", fileName);
}
for (NZBQueue::iterator it = cNZBQueue.begin(); it != cNZBQueue.end(); it++)
{
NZBInfo* pNZBInfo = *it;
pNZBInfo->Release();
}
return bOK;
}
bool DiskState::SaveFile(FileInfo* pFileInfo)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
fileName[1024-1] = '\0';
return SaveFileInfo(pFileInfo, fileName);
}
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
{
debug("Saving FileInfo to disk");
FILE* outfile = fopen(szFilename, "w");
if (!outfile)
{
error("Could not create file %s", szFilename);
return false;
}
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
unsigned long High, Low;
Util::SplitInt64(pFileInfo->GetSize(), &High, &Low);
fprintf(outfile, "%lu,%lu\n", High, Low);
fprintf(outfile, "%i\n", pFileInfo->GetGroups()->size());
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
{
fprintf(outfile, "%s\n", *it);
}
fprintf(outfile, "%i\n", pFileInfo->GetArticles()->size());
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pArticleInfo = *it;
fprintf(outfile, "%i,%i\n", pArticleInfo->GetPartNumber(), pArticleInfo->GetSize());
fprintf(outfile, "%s\n", pArticleInfo->GetMessageID());
}
fclose(outfile);
return true;
}
bool DiskState::LoadArticles(FileInfo* pFileInfo)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
fileName[1024-1] = '\0';
return LoadFileInfo(pFileInfo, fileName, false, true);
}
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
{
debug("Loading FileInfo from disk");
FILE* infile = fopen(szFilename, "r");
if (!infile)
{
error("Could not open file %s", szFilename);
return false;
}
char buf[1024];
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetSubject(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetFilename(buf);
int iFilenameConfirmed;
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
unsigned long High, Low;
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
if (bFileSummary) pFileInfo->SetSize(Util::JoinInt64(High, Low));
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
int size;
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
}
if (bArticles)
{
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
int PartNumber, PartSize;
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
ArticleInfo* pArticleInfo = new ArticleInfo();
pArticleInfo->SetPartNumber(PartNumber);
pArticleInfo->SetSize(PartSize);
pArticleInfo->SetMessageID(buf);
pFileInfo->GetArticles()->push_back(pArticleInfo);
}
}
fclose(infile);
return true;
error:
fclose(infile);
error("Error reading diskstate for file %s", szFilename);
return false;
}
bool DiskState::SavePostQueue(PostQueue* pPostQueue, bool bCompleted)
{
debug("Saving post-queue to disk");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
fileName[1024-1] = '\0';
FILE* outfile = fopen(fileName, "w");
if (!outfile)
{
error("Could not create file %s", fileName);
perror(fileName);
return false;
}
fprintf(outfile, FORMATVERSION_SIGNATURE_V7);
fprintf(outfile, "%i\n", pPostQueue->size());
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
{
PostInfo* pPostInfo = *it;
fprintf(outfile, "%s\n", pPostInfo->GetNZBFilename());
fprintf(outfile, "%s\n", pPostInfo->GetDestDir());
fprintf(outfile, "%s\n", pPostInfo->GetParFilename());
fprintf(outfile, "%s\n", pPostInfo->GetInfoName());
fprintf(outfile, "%s\n", pPostInfo->GetCategory());
fprintf(outfile, "%s\n", pPostInfo->GetQueuedFilename());
fprintf(outfile, "%i\n", (int)pPostInfo->GetParCheck());
fprintf(outfile, "%i\n", (int)pPostInfo->GetParStatus());
fprintf(outfile, "%i\n", (int)pPostInfo->GetStage());
fprintf(outfile, "%i\n", pPostInfo->GetParameters()->size());
for (NZBParameterList::iterator it = pPostInfo->GetParameters()->begin(); it != pPostInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue());
}
}
fclose(outfile);
if (pPostQueue->empty())
{
remove(fileName);
}
return true;
}
bool DiskState::LoadPostQueue(PostQueue* pPostQueue, bool bCompleted)
{
debug("Loading post-queue from disk");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
fileName[1024-1] = '\0';
FILE* infile = fopen(fileName, "r");
if (!infile)
{
error("Could not open file %s", fileName);
return false;
}
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 7)
{
error("Could not load diskstate due file version mismatch");
fclose(infile);
return false;
}
int size;
char buf[10240];
int iIntValue;
// load file-infos
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
PostInfo* pPostInfo = new PostInfo();
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetNZBFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetDestDir(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetParFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetInfoName(buf);
if (iFormatVersion >= 4)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetCategory(buf);
}
else
{
pPostInfo->SetCategory("");
}
if (iFormatVersion >= 5)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetQueuedFilename(buf);
}
else
{
pPostInfo->SetQueuedFilename("");
}
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParCheck(iIntValue);
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParStatus(iIntValue);
if (iFormatVersion < 7)
{
// skip old field ParFailed, not used anymore
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
}
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetStage((PostInfo::EStage)iIntValue);
if (iFormatVersion >= 6)
{
int iParameterCount;
if (fscanf(infile, "%i\n", &iParameterCount) != 1) goto error;
for (int i = 0; i < iParameterCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
char* szValue = strchr(buf, '=');
if (szValue)
{
*szValue = '\0';
szValue++;
pPostInfo->AddParameter(buf, szValue);
}
}
}
pPostQueue->push_back(pPostInfo);
}
fclose(infile);
return true;
error:
fclose(infile);
error("Error reading diskstate for file %s", fileName);
return false;
}
/*
* Delete all files from Queue.
* Returns true if successful, false if not
*/
bool DiskState::DiscardDownloadQueue()
{
debug("Discarding queue");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
FILE* infile = fopen(fileName, "r");
if (!infile)
{
error("Could not open file %s", fileName);
return false;
}
bool res = false;
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (3 <= iFormatVersion && iFormatVersion <= 6)
{
// skip nzb-infos
int size = 0;
char buf[1024];
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break; // filename
if (!fgets(buf, sizeof(buf), infile)) break; // destdir
if (iFormatVersion >= 4)
{
if (!fgets(buf, sizeof(buf), infile)) break; // category
if (!fgets(buf, sizeof(buf), infile)) break; // postprocess
}
if (iFormatVersion >= 5)
{
if (!fgets(buf, sizeof(buf), infile)) break; // localfile
}
if (!fgets(buf, sizeof(buf), infile)) break; // file count
if (!fgets(buf, sizeof(buf), infile)) break; // file size
if (iFormatVersion >= 4)
{
// completed files
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) break;
for (int i = 0; i < iFileCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break; // filename
}
}
if (iFormatVersion >= 6)
{
// postprocess-parameters
int iParameterCount;
if (fscanf(infile, "%i\n", &iParameterCount) != 1) break;
for (int i = 0; i < iParameterCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break;
}
}
}
// file-infos
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
{
int id, group, paused;
if (fscanf(infile, "%i,%i,%i\n", &id, &group, &paused) == 3)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
fileName[1024-1] = '\0';
remove(fileName);
}
}
res = true;
}
else
{
error("Could not discard diskstate due file version mismatch");
res = false;
}
fclose(infile);
if (res)
{
remove(fileName);
}
return res;
}
/*
* Delete all files from Queue.
* Returns true if successful, false if not
*/
bool DiskState::DiscardPostQueue()
{
debug("Discarding post-queue");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postq");
fileName[1024-1] = '\0';
remove(fileName);
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postc");
fileName[1024-1] = '\0';
remove(fileName);
return true;
}
bool DiskState::DownloadQueueExists()
{
debug("Checking if a saved queue exists on disk");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
return Util::FileExists(fileName);
}
bool DiskState::PostQueueExists(bool bCompleted)
{
debug("Checking if a saved queue exists on disk");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
fileName[1024-1] = '\0';
return Util::FileExists(fileName);
}
bool DiskState::DiscardFile(FileInfo* pFileInfo)
{
// delete diskstate-file for file-info
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
fileName[1024-1] = '\0';
remove(fileName);
return true;
}
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
{
// build array of IDs of files in queue for faster access
int* ids = (int*)malloc(sizeof(int) * (pDownloadQueue->size() + 1));
int* ptr = ids;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
*ptr++ = pFileInfo->GetID();
}
*ptr = 0;
// read directory
DirBrowser dir(g_pOptions->GetTempDir());
while (const char* filename = dir.Next())
{
int id, part;
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec") ||
((sscanf(filename, "%i.out", &id) == 1) &&
!(g_pOptions->GetContinuePartial() && g_pOptions->GetDirectWrite()));
if (!del)
{
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
(sscanf(filename, "%i.out", &id) == 1))
{
del = true;
ptr = ids;
while (*ptr)
{
if (*ptr == id)
{
del = false;
break;
}
ptr++;
}
}
}
if (del)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetTempDir(), filename);
szFullFilename[1024-1] = '\0';
remove(szFullFilename);
}
}
free(ids);
}

View File

@@ -1,53 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef DISKSTATE_H
#define DISKSTATE_H
#include "DownloadInfo.h"
class DiskState
{
private:
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
int ParseFormatVersion(const char* szFormatSignature);
public:
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
bool SavePostQueue(PostQueue* pPostQueue, bool bCompleted);
bool LoadPostQueue(PostQueue* pPostQueue, bool bCompleted);
bool DiscardDownloadQueue();
bool DiscardPostQueue();
bool DiscardFile(FileInfo* pFileInfo);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};
#endif

View File

@@ -1,711 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#include "nzbget.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
int PostInfo::m_iIDGen = 0;
NZBParameter::NZBParameter(const char* szName)
{
m_szName = strdup(szName);
m_szValue = NULL;
}
NZBParameter::~NZBParameter()
{
if (m_szName)
{
free(m_szName);
}
if (m_szValue)
{
free(m_szValue);
}
}
void NZBParameter::SetValue(const char* szValue)
{
if (m_szValue)
{
free(m_szValue);
}
m_szValue = strdup(szValue);
}
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
m_szFilename = NULL;
m_szDestDir = NULL;
m_szCategory = strdup("");
m_iFileCount = 0;
m_lSize = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
m_szQueuedFilename = strdup("");
}
NZBInfo::~NZBInfo()
{
debug("Destroying NZBInfo");
if (m_szFilename)
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szCategory)
{
free(m_szCategory);
}
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
{
free(*it);
}
m_completedFiles.clear();
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
{
delete *it;
}
m_ppParameters.clear();
}
void NZBInfo::AddReference()
{
m_iRefCount++;
}
void NZBInfo::Release()
{
m_iRefCount--;
if (m_iRefCount <= 0)
{
delete this;
}
}
void NZBInfo::SetDestDir(const char* szDestDir)
{
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void NZBInfo::SetFilename(const char * szFilename)
{
if (m_szFilename)
{
free(m_szFilename);
}
m_szFilename = strdup(szFilename);
}
void NZBInfo::SetCategory(const char* szCategory)
{
if (m_szCategory)
{
free(m_szCategory);
}
m_szCategory = strdup(szCategory);
}
void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
{
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
m_szQueuedFilename = strdup(szQueuedFilename);
}
void NZBInfo::GetNiceNZBName(char* szBuffer, int iSize)
{
MakeNiceNZBName(m_szFilename, szBuffer, iSize);
}
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
{
char postname[1024];
const char* szBaseName = Util::BaseFileName(szNZBFilename);
// if .nzb file has a certain structure, try to strip out certain elements
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
{
// OK, using stripped name
}
else
{
// using complete filename
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
}
// wipe out ".nzb"
if (char* p = strrchr(postname, '.')) *p = '\0';
Util::MakeValidFilename(postname, '_', false);
// if the resulting name is empty, use basename without cleaning up "msgid_"
if (strlen(postname) == 0)
{
// using complete filename
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
// wipe out ".nzb"
if (char* p = strrchr(postname, '.')) *p = '\0';
Util::MakeValidFilename(postname, '_', false);
// if the resulting name is STILL empty, use "noname"
if (strlen(postname) == 0)
{
strncpy(postname, "noname", 1024);
}
}
strncpy(szBuffer, postname, iSize);
szBuffer[iSize-1] = '\0';
}
void NZBInfo::BuildDestDirName()
{
char szBuffer[1024];
char szCategory[1024];
bool bHasCategory = m_szCategory && m_szCategory[0] != '\0';
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
strncpy(szCategory, m_szCategory, 1024);
szCategory[1024 - 1] = '\0';
Util::MakeValidFilename(szCategory, '_', true);
}
if (g_pOptions->GetAppendNZBDir())
{
char szNiceNZBName[1024];
GetNiceNZBName(szNiceNZBName, 1024);
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, szNiceNZBName);
}
else
{
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
}
szBuffer[1024-1] = '\0';
}
else
{
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
{
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szCategory);
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
}
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
SetDestDir(szBuffer);
}
void NZBInfo::SetParameter(const char* szName, const char* szValue)
{
NZBParameter* pParameter = NULL;
bool bDelete = !szValue || !*szValue;
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
{
NZBParameter* pLookupParameter = *it;
if (!strcmp(pLookupParameter->GetName(), szName))
{
if (bDelete)
{
delete pLookupParameter;
m_ppParameters.erase(it);
return;
}
pParameter = pLookupParameter;
break;
}
}
if (bDelete)
{
return;
}
if (!pParameter)
{
pParameter = new NZBParameter(szName);
m_ppParameters.push_back(pParameter);
}
pParameter->SetValue(szValue);
}
void NZBInfo::BuildNZBList(DownloadQueue* pDownloadQueue, NZBQueue* pNZBQueue)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bool inlist = false;
for (NZBQueue::iterator it = pNZBQueue->begin(); it != pNZBQueue->end(); it++)
{
NZBInfo* pNZBInfo = *it;
if (pNZBInfo == pFileInfo->GetNZBInfo())
{
inlist = true;
break;
}
}
if (!inlist)
{
pNZBQueue->push_back(pFileInfo->GetNZBInfo());
}
}
}
ArticleInfo::ArticleInfo()
{
//debug("Creating ArticleInfo");
m_szMessageID = NULL;
m_iSize = 0;
m_eStatus = aiUndefined;
m_szResultFilename = NULL;
}
ArticleInfo::~ ArticleInfo()
{
//debug("Destroying ArticleInfo");
if (m_szMessageID)
{
free(m_szMessageID);
}
if (m_szResultFilename)
{
free(m_szResultFilename);
}
}
void ArticleInfo::SetMessageID(const char * szMessageID)
{
m_szMessageID = strdup(szMessageID);
}
void ArticleInfo::SetResultFilename(const char * v)
{
if (m_szResultFilename)
{
free(m_szResultFilename);
}
m_szResultFilename = strdup(v);
}
FileInfo::FileInfo()
{
debug("Creating FileInfo");
m_Articles.clear();
m_Groups.clear();
m_szSubject = NULL;
m_szFilename = NULL;
m_bFilenameConfirmed = false;
m_lSize = 0;
m_lRemainingSize = 0;
m_bPaused = false;
m_bDeleted = false;
m_iCompleted = 0;
m_bOutputInitialized = false;
m_pNZBInfo = NULL;
m_iIDGen++;
m_iID = m_iIDGen;
}
FileInfo::~ FileInfo()
{
debug("Destroying FileInfo");
if (m_szSubject)
{
free(m_szSubject);
}
if (m_szFilename)
{
free(m_szFilename);
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
free(*it);
}
m_Groups.clear();
ClearArticles();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void FileInfo::ClearArticles()
{
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
{
delete *it;
}
m_Articles.clear();
}
void FileInfo::SetID(int s)
{
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
}
}
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->AddReference();
}
void FileInfo::SetSubject(const char* szSubject)
{
m_szSubject = strdup(szSubject);
}
void FileInfo::SetFilename(const char* szFilename)
{
if (m_szFilename)
{
free(m_szFilename);
}
m_szFilename = strdup(szFilename);
}
void FileInfo::MakeValidFilename()
{
Util::MakeValidFilename(m_szFilename, '_', false);
}
void FileInfo::LockOutputFile()
{
m_mutexOutputFile.Lock();
}
void FileInfo::UnlockOutputFile()
{
m_mutexOutputFile.Unlock();
}
bool FileInfo::IsDupe(const char* szFilename)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
return false;
}
GroupInfo::GroupInfo()
{
m_iFirstID = 0;
m_iLastID = 0;
m_iRemainingFileCount = 0;
m_lRemainingSize = 0;
m_lPausedSize = 0;
m_iRemainingParCount = 0;
}
GroupInfo::~GroupInfo()
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void GroupInfo::BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo* pGroupInfo = NULL;
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
{
GroupInfo* pGroupInfo1 = *itg;
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupInfo = new GroupInfo();
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
pGroupInfo->m_pNZBInfo->AddReference();
pGroupInfo->m_iFirstID = pFileInfo->GetID();
pGroupInfo->m_iLastID = pFileInfo->GetID();
pGroupQueue->push_back(pGroupInfo);
}
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
{
pGroupInfo->m_iFirstID = pFileInfo->GetID();
}
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
{
pGroupInfo->m_iLastID = pFileInfo->GetID();
}
pGroupInfo->m_iRemainingFileCount++;
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
if (pFileInfo->GetPaused())
{
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
}
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (strstr(szLoFileName, ".par2"))
{
pGroupInfo->m_iRemainingParCount++;
}
}
}
PostInfo::PostInfo()
{
debug("Creating PostInfo");
m_szNZBFilename = NULL;
m_szDestDir = NULL;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_szCategory = NULL;
m_szQueuedFilename = NULL;
m_bWorking = false;
m_bParCheck = false;
m_iParStatus = 0;
m_eRequestParCheck = rpNone;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
m_tStartTime = 0;
m_tStageTime = 0;
m_eStage = ptQueued;
m_pScriptThread = NULL;
m_Messages.clear();
m_iIDGen++;
m_iID = m_iIDGen;
}
PostInfo::~ PostInfo()
{
debug("Destroying PostInfo");
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szCategory)
{
free(m_szCategory);
}
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
{
delete *it;
}
m_ppParameters.clear();
}
void PostInfo::SetNZBFilename(const char* szNZBFilename)
{
m_szNZBFilename = strdup(szNZBFilename);
}
void PostInfo::SetDestDir(const char* szDestDir)
{
m_szDestDir = strdup(szDestDir);
}
void PostInfo::SetParFilename(const char* szParFilename)
{
m_szParFilename = strdup(szParFilename);
}
void PostInfo::SetInfoName(const char* szInfoName)
{
m_szInfoName = strdup(szInfoName);
}
void PostInfo::SetCategory(const char* szCategory)
{
m_szCategory = strdup(szCategory);
}
void PostInfo::SetQueuedFilename(const char * szQueuedFilename)
{
m_szQueuedFilename = strdup(szQueuedFilename);
}
void PostInfo::SetProgressLabel(const char* szProgressLabel)
{
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
m_szProgressLabel = strdup(szProgressLabel);
}
PostInfo::Messages* PostInfo::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void PostInfo::UnlockMessages()
{
m_mutexLog.Unlock();
}
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
m_mutexLog.Lock();
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
m_mutexLog.Unlock();
}
void PostInfo::AddParameter(const char* szName, const char* szValue)
{
NZBParameter* pParameter = new NZBParameter(szName);
pParameter->SetValue(szValue);
m_ppParameters.push_back(pParameter);
}
void PostInfo::AssignParameter(NZBParameterList* pSrcParameters)
{
for (NZBParameterList::iterator it = pSrcParameters->begin(); it != pSrcParameters->end(); it++)
{
NZBParameter* pParameter = *it;
AddParameter(pParameter->GetName(), pParameter->GetValue());
}
}

View File

@@ -1,333 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef DOWNLOADINFO_H
#define DOWNLOADINFO_H
#include <vector>
#include <deque>
#include "Log.h"
#include "Thread.h"
class NZBInfo;
class ArticleInfo
{
public:
enum EStatus
{
aiUndefined,
aiRunning,
aiFinished,
aiFailed
};
private:
int m_iPartNumber;
char* m_szMessageID;
int m_iSize;
EStatus m_eStatus;
char* m_szResultFilename;
public:
ArticleInfo();
~ArticleInfo();
void SetPartNumber(int s) { m_iPartNumber = s; }
int GetPartNumber() { return m_iPartNumber; }
const char* GetMessageID() { return m_szMessageID; }
void SetMessageID(const char* szMessageID);
void SetSize(int s) { m_iSize = s; }
int GetSize() { return m_iSize; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
const char* GetResultFilename() { return m_szResultFilename; }
void SetResultFilename(const char* v);
};
class FileInfo
{
public:
typedef std::vector<ArticleInfo*> Articles;
typedef std::vector<char*> Groups;
private:
int m_iID;
NZBInfo* m_pNZBInfo;
Articles m_Articles;
Groups m_Groups;
char* m_szSubject;
char* m_szFilename;
long long m_lSize;
long long m_lRemainingSize;
bool m_bPaused;
bool m_bDeleted;
bool m_bFilenameConfirmed;
int m_iCompleted;
bool m_bOutputInitialized;
Mutex m_mutexOutputFile;
static int m_iIDGen;
public:
FileInfo();
~FileInfo();
int GetID() { return m_iID; }
void SetID(int s);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
Groups* GetGroups() { return &m_Groups; }
const char* GetSubject() { return m_szSubject; }
void SetSubject(const char* szSubject);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
void MakeValidFilename();
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
long long GetSize() { return m_lSize; }
long long GetRemainingSize() { return m_lRemainingSize; }
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
bool GetPaused() { return m_bPaused; }
void SetPaused(bool Paused) { m_bPaused = Paused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int s) { m_iCompleted = s; }
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
};
typedef std::deque<FileInfo*> DownloadQueue;
class GroupInfo;
typedef std::deque<GroupInfo*> GroupQueue;
class GroupInfo
{
private:
NZBInfo* m_pNZBInfo;
int m_iFirstID;
int m_iLastID;
int m_iRemainingFileCount;
long long m_lRemainingSize;
long long m_lPausedSize;
int m_iRemainingParCount;
public:
GroupInfo();
~GroupInfo();
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
int GetFirstID() { return m_iFirstID; }
int GetLastID() { return m_iLastID; }
long long GetRemainingSize() { return m_lRemainingSize; }
long long GetPausedSize() { return m_lPausedSize; }
int GetRemainingFileCount() { return m_iRemainingFileCount; }
int GetRemainingParCount() { return m_iRemainingParCount; }
static void BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue);
};
class NZBParameter
{
private:
char* m_szName;
char* m_szValue;
void SetValue(const char* szValue);
friend class NZBInfo;
friend class PostInfo;
public:
NZBParameter(const char* szName);
~NZBParameter();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
typedef std::deque<NZBParameter*> NZBParameterList;
typedef std::deque<NZBInfo*> NZBQueue;
class NZBInfo
{
public:
typedef std::vector<char*> Files;
private:
int m_iRefCount;
char* m_szFilename;
char* m_szDestDir;
char* m_szCategory;
int m_iFileCount;
long long m_lSize;
Files m_completedFiles;
bool m_bPostProcess;
char* m_szQueuedFilename;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bCleanupDisk;
NZBParameterList m_ppParameters;
public:
NZBInfo();
~NZBInfo();
void AddReference();
void Release();
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
void GetNiceNZBName(char* szBuffer, int iSize);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
int GetFileCount() { return m_iFileCount; }
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
void BuildDestDirName();
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
bool GetPostProcess() { return m_bPostProcess; }
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetParCleanup() { return m_bParCleanup; }
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
bool GetCleanupDisk() { return m_bCleanupDisk; }
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
static void BuildNZBList(DownloadQueue* pDownloadQueue, NZBQueue* pNZBQueue);
};
class PostInfo
{
public:
enum EStage
{
ptQueued,
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptExecutingScript,
ptFinished
};
enum ERequestParCheck
{
rpNone,
rpCurrent,
rpAll
};
typedef std::deque<Message*> Messages;
private:
int m_iID;
char* m_szNZBFilename;
char* m_szDestDir;
char* m_szParFilename;
char* m_szInfoName;
char* m_szCategory;
char* m_szQueuedFilename;
bool m_bWorking;
bool m_bParCheck;
int m_iParStatus;
ERequestParCheck m_eRequestParCheck;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pScriptThread;
NZBParameterList m_ppParameters;
Mutex m_mutexLog;
Messages m_Messages;
static int m_iIDGen;
public:
PostInfo();
~PostInfo();
int GetID() { return m_iID; }
const char* GetNZBFilename() { return m_szNZBFilename; }
void SetNZBFilename(const char* szNZBFilename);
const char* GetDestDir() { return m_szDestDir; }
void SetDestDir(const char* szDestDir);
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
const char* GetCategory() { return m_szCategory; }
void SetCategory(const char* szCategory);
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
EStage GetStage() { return m_eStage; }
void SetStage(EStage eStage) { m_eStage = eStage; }
void SetProgressLabel(const char* szProgressLabel);
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
int GetStageProgress() { return m_iStageProgress; }
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
time_t GetStartTime() { return m_tStartTime; }
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
time_t GetStageTime() { return m_tStageTime; }
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
bool GetWorking() { return m_bWorking; }
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
bool GetParCheck() { return m_bParCheck; }
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
int GetParStatus() { return m_iParStatus; }
void SetParStatus(int iParStatus) { m_iParStatus = iParStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetScriptThread() { return m_pScriptThread; }
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
NZBParameterList* GetParameters() { return &m_ppParameters; }
void AddParameter(const char* szName, const char* szValue);
void AssignParameter(NZBParameterList* pSrcParameters);
Messages* LockMessages();
void UnlockMessages();
};
typedef std::deque<PostInfo*> PostQueue;
#endif

View File

@@ -1,408 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "Options.h"
#include "Frontend.h"
#include "Log.h"
#include "Connection.h"
#include "MessageBase.h"
#include "QueueCoordinator.h"
#include "PrePostProcessor.h"
#include "RemoteClient.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Options* g_pOptions;
Frontend::Frontend()
{
debug("Creating Frontend");
m_iNeededLogFirstID = 0;
m_iNeededLogEntries = 0;
m_bSummary = false;
m_bFileList = false;
m_fCurrentDownloadSpeed = 0;
m_lRemainingSize = 0;
m_bPause = false;
m_fDownloadLimit = 0;
m_iThreadCount = 0;
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
m_iDnTimeSec = 0;
m_iAllBytes = 0;
m_bStandBy = 0;
m_RemoteMessages.clear();
m_RemoteQueue.clear();
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
}
bool Frontend::PrepareData()
{
if (IsRemoteMode())
{
if (IsStopped())
{
return false;
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Stop();
return false;
}
}
else
{
if (m_bSummary)
{
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
m_bPause = g_pOptions->GetPause();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
m_iPostJobCount = pPostQueue->size();
g_pPrePostProcessor->UnlockPostQueue();
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
}
}
return true;
}
void Frontend::FreeData()
{
if (IsRemoteMode())
{
for (Log::Messages::iterator it = m_RemoteMessages.begin(); it != m_RemoteMessages.end(); it++)
{
delete *it;
}
m_RemoteMessages.clear();
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
{
delete *it;
}
m_RemoteQueue.clear();
}
}
Log::Messages * Frontend::LockMessages()
{
if (IsRemoteMode())
{
return &m_RemoteMessages;
}
else
{
return g_pLog->LockMessages();
}
}
void Frontend::UnlockMessages()
{
if (!IsRemoteMode())
{
g_pLog->UnlockMessages();
}
}
DownloadQueue * Frontend::LockQueue()
{
if (IsRemoteMode())
{
return &m_RemoteQueue;
}
else
{
return g_pQueueCoordinator->LockQueue();
}
}
void Frontend::UnlockQueue()
{
if (!IsRemoteMode())
{
g_pQueueCoordinator->UnlockQueue();
}
}
bool Frontend::IsRemoteMode()
{
return g_pOptions->GetRemoteClientMode();
}
void Frontend::ServerPauseUnpause(bool bPause)
{
if (IsRemoteMode())
{
RequestPauseUnpause(bPause);
}
else
{
g_pOptions->SetPause(bPause);
}
}
void Frontend::ServerSetDownloadRate(float fRate)
{
if (IsRemoteMode())
{
RequestSetDownloadRate(fRate);
}
else
{
g_pOptions->SetDownloadRate(fRate);
}
}
void Frontend::ServerDumpDebug()
{
if (IsRemoteMode())
{
RequestDumpDebug();
}
else
{
g_pQueueCoordinator->LogDebugInfo();
}
}
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
{
if (IsRemoteMode())
{
return RequestEditQueue(eAction, iOffset, iID);
}
else
{
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset, NULL);
}
return false;
}
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
{
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
bool Frontend::RequestMessages()
{
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect();
if (!OK)
{
return false;
}
SNZBLogRequest LogRequest;
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
if (m_iNeededLogEntries == 0)
{
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
}
else
{
LogRequest.m_iIDFrom = 0;
}
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
{
return false;
}
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
return false;
}
char* pBuf = NULL;
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
connection.Disconnect();
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
{
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
m_RemoteMessages.push_back(pMessage);
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
}
free(pBuf);
}
return true;
}
bool Frontend::RequestFileList()
{
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect();
if (!OK)
{
return false;
}
SNZBListRequest ListRequest;
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
return false;
}
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
return false;
}
char* pBuf = NULL;
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
connection.Disconnect();
if (m_bSummary)
{
m_bPause = ntohl(ListResponse.m_bServerPaused);
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
}
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
RemoteClient client;
client.SetVerbose(false);
client.BuildFileList(&ListResponse, pBuf, &m_RemoteQueue);
}
if (pBuf)
{
free(pBuf);
}
return true;
}
bool Frontend::RequestPauseUnpause(bool bPause)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerPauseUnpause(bPause);
}
bool Frontend::RequestSetDownloadRate(float fRate)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerSetDownloadRate(fRate);
}
bool Frontend::RequestDumpDebug()
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerDumpDebug();
}
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, false);
}

View File

@@ -1,85 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FRONTEND_H
#define FRONTEND_H
#include "Thread.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "MessageBase.h"
#include "QueueEditor.h"
class Frontend : public Thread
{
private:
Log::Messages m_RemoteMessages;
DownloadQueue m_RemoteQueue;
bool RequestMessages();
bool RequestFileList();
protected:
bool m_bSummary;
bool m_bFileList;
unsigned int m_iNeededLogEntries;
unsigned int m_iNeededLogFirstID;
int m_iUpdateInterval;
// summary
float m_fCurrentDownloadSpeed;
long long m_lRemainingSize;
bool m_bPause;
float m_fDownloadLimit;
int m_iThreadCount;
int m_iPostJobCount;
int m_iUpTimeSec;
int m_iDnTimeSec;
long long m_iAllBytes;
bool m_bStandBy;
bool PrepareData();
void FreeData();
Log::Messages* LockMessages();
void UnlockMessages();
DownloadQueue* LockQueue();
void UnlockQueue();
bool IsRemoteMode();
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
void ServerPauseUnpause(bool bPause);
bool RequestPauseUnpause(bool bPause);
void ServerSetDownloadRate(float fRate);
bool RequestSetDownloadRate(float fRate);
void ServerDumpDebug();
bool RequestDumpDebug();
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
bool RequestEditQueue(int iAction, int iOffset, int iID);
public:
Frontend();
};
#endif

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.

359
Log.cpp
View File

@@ -1,359 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <cstdio>
#include "nzbget.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
Log::Log()
{
m_Messages.clear();
m_iIDGen = 0;
m_szLogFilename = NULL;
#ifdef DEBUG
m_bExtraDebug = Util::FileExists("extradebug");
#endif
}
Log::~Log()
{
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
if (m_szLogFilename)
{
free(m_szLogFilename);
}
}
void Log::Filelog(const char* msg, ...)
{
if (
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
#ifdef DEBUG
|| (m_szLogFilename && m_bExtraDebug)
#endif
)
{
if (!m_szLogFilename)
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
}
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
time_t rawtime;
time(&rawtime);
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&rawtime, szTime, 50);
#else
ctime_r(&rawtime, szTime);
#endif
szTime[50-1] = '\0';
szTime[strlen(szTime) - 1] = '\0'; // trim LF
FILE* file = fopen(m_szLogFilename, "a+");
if (file)
{
#ifdef WIN32
unsigned long iThreadId = GetCurrentThreadId();
#else
unsigned long iThreadId = (unsigned long)pthread_self();
#endif
#ifdef DEBUG
fprintf(file, "%s\t%lu\t%s%s", szTime, iThreadId, tmp2, LINE_ENDING);
#else
fprintf(file, "%s\t%s%s", szTime, tmp2, LINE_ENDING);
#endif
fclose(file);
}
else
{
perror(m_szLogFilename);
}
}
}
#undef debug
#ifdef HAVE_VARIADIC_MACROS
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
#else
void debug(const char* msg, ...)
#endif
{
#ifdef DEBUG
char tmp1[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp1, 1024, msg, ap);
tmp1[1024-1] = '\0';
va_end(ap);
char tmp2[1024];
#ifdef HAVE_VARIADIC_MACROS
if (szFuncname)
{
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, Util::BaseFileName(szFilename), iLineNr, szFuncname);
}
else
{
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, Util::BaseFileName(szFilename), iLineNr);
}
#else
snprintf(tmp2, 1024, "%s", tmp1);
#endif
tmp2[1024-1] = '\0';
g_pLog->m_mutexLog.Lock();
if (!g_pOptions)
{
if (g_pLog->m_bExtraDebug)
{
printf("%s\n", tmp2);
g_pLog->Filelog("DEBUG\t%s", tmp2);
}
g_pLog->m_mutexLog.Unlock();
return;
}
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("DEBUG\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkDebug, tmp2);
}
g_pLog->m_mutexLog.Unlock();
#endif
}
void error(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("ERROR\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkError, tmp2);
}
g_pLog->m_mutexLog.Unlock();
}
void warn(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("WARNING\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkWarning, tmp2);
}
g_pLog->m_mutexLog.Unlock();
}
void info(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("INFO\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkInfo, tmp2);
}
g_pLog->m_mutexLog.Unlock();
}
void detail(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
{
g_pLog->Filelog("DETAIL\t%s", tmp2);
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
g_pLog->AppendMessage(Message::mkDetail, tmp2);
}
g_pLog->m_mutexLog.Unlock();
}
void abort(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
printf("\n%s", tmp2);
g_pLog->Filelog(tmp2);
g_pLog->m_mutexLog.Unlock();
exit(-1);
}
//************************************************************
// Message
Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText)
{
m_iID = iID;
m_eKind = eKind;
m_tTime = tTime;
if (szText)
{
m_szText = strdup(szText);
}
else
{
m_szText = NULL;
}
}
Message::~ Message()
{
if (m_szText)
{
free(m_szText);
}
}
void Log::AppendMessage(Message::EKind eKind, const char * szText)
{
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
m_Messages.push_back(pMessage);
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
{
Message* pMessage = m_Messages.front();
delete pMessage;
m_Messages.pop_front();
}
}
Log::Messages* Log::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void Log::UnlockMessages()
{
m_mutexLog.Unlock();
}
void Log::ResetLog()
{
remove(g_pOptions->GetLogFile());
}

121
Log.h
View File

@@ -1,121 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef LOG_H
#define LOG_H
#include <deque>
#include <time.h>
#include "Thread.h"
void error(const char* msg, ...);
void warn(const char* msg, ...);
void info(const char* msg, ...);
void detail(const char* msg, ...);
void abort(const char* msg, ...);
#ifdef HAVE_VARIADIC_MACROS
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
#else
void debug(const char* msg, ...);
#endif
class Message
{
public:
enum EKind
{
mkInfo,
mkWarning,
mkError,
mkDebug,
mkDetail
};
private:
unsigned int m_iID;
EKind m_eKind;
time_t m_tTime;
char* m_szText;
public:
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
~Message();
unsigned int GetID() { return m_iID; }
EKind GetKind() { return m_eKind; }
time_t GetTime() { return m_tTime; }
const char* GetText() { return m_szText; }
};
class Log
{
public:
typedef std::deque<Message*> Messages;
private:
Mutex m_mutexLog;
Messages m_Messages;
char* m_szLogFilename;
unsigned int m_iIDGen;
#ifdef DEBUG
bool m_bExtraDebug;
#endif
void Filelog(const char* msg, ...);
void AppendMessage(Message::EKind eKind, const char* szText);
friend void error(const char* msg, ...);
friend void warn(const char* msg, ...);
friend void info(const char* msg, ...);
friend void abort(const char* msg, ...);
friend void detail(const char* msg, ...);
#ifdef HAVE_VARIADIC_MACROS
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
#else
friend void debug(const char* msg, ...);
#endif
public:
Log();
~Log();
Messages* LockMessages();
void UnlockMessages();
void ResetLog();
};
#ifdef HAVE_VARIADIC_MACROS
#ifdef DEBUG
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
#else
#define debug(...) do { } while(0)
#endif
#endif
extern Log* g_pLog;
#endif

View File

@@ -1,21 +1,554 @@
bin_PROGRAMS = nzbget
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NetAddress.cpp NetAddress.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h \
PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp \
Util.h XmlRpc.cpp XmlRpc.h nzbget.cpp nzbget.h
#
# This file is part of nzbget. See <http://nzbget.net>.
#
# Copyright (C) 2008-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This 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, see <http://www.gnu.org/licenses/>.
#
EXTRA_DIST = nzbget.conf.example postprocess-example.sh \
win32.h NTService.cpp NTService.h \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
bin_PROGRAMS = nzbget
nzbget_SOURCES = \
daemon/connect/Connection.cpp \
daemon/connect/Connection.h \
daemon/connect/TlsSocket.cpp \
daemon/connect/TlsSocket.h \
daemon/connect/WebDownloader.cpp \
daemon/connect/WebDownloader.h \
daemon/extension/FeedScript.cpp \
daemon/extension/FeedScript.h \
daemon/extension/CommandScript.cpp \
daemon/extension/CommandScript.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 \
daemon/feed/FeedFile.h \
daemon/feed/FeedFilter.cpp \
daemon/feed/FeedFilter.h \
daemon/feed/FeedInfo.cpp \
daemon/feed/FeedInfo.h \
daemon/frontend/ColoredFrontend.cpp \
daemon/frontend/ColoredFrontend.h \
daemon/frontend/Frontend.cpp \
daemon/frontend/Frontend.h \
daemon/frontend/LoggableFrontend.cpp \
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 \
daemon/main/nzbget.h \
daemon/main/Options.cpp \
daemon/main/Options.h \
daemon/main/WorkState.cpp \
daemon/main/WorkState.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 \
daemon/nntp/NewsServer.h \
daemon/nntp/NntpConnection.cpp \
daemon/nntp/NntpConnection.h \
daemon/nntp/ServerPool.cpp \
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/ParParser.cpp \
daemon/postprocess/ParParser.h \
daemon/postprocess/ParRenamer.cpp \
daemon/postprocess/ParRenamer.h \
daemon/postprocess/PrePostProcessor.cpp \
daemon/postprocess/PrePostProcessor.h \
daemon/postprocess/RarRenamer.cpp \
daemon/postprocess/RarRenamer.h \
daemon/postprocess/RarReader.cpp \
daemon/postprocess/RarReader.h \
daemon/postprocess/Rename.cpp \
daemon/postprocess/Rename.h \
daemon/postprocess/Repair.cpp \
daemon/postprocess/Repair.h \
daemon/postprocess/Unpack.cpp \
daemon/postprocess/Unpack.h \
daemon/postprocess/DirectUnpack.cpp \
daemon/postprocess/DirectUnpack.h \
daemon/queue/DirectRenamer.cpp \
daemon/queue/DirectRenamer.h \
daemon/queue/DiskState.cpp \
daemon/queue/DiskState.h \
daemon/queue/DownloadInfo.cpp \
daemon/queue/DownloadInfo.h \
daemon/queue/DupeCoordinator.cpp \
daemon/queue/DupeCoordinator.h \
daemon/queue/HistoryCoordinator.cpp \
daemon/queue/HistoryCoordinator.h \
daemon/queue/NzbFile.cpp \
daemon/queue/NzbFile.h \
daemon/queue/QueueCoordinator.cpp \
daemon/queue/QueueCoordinator.h \
daemon/queue/QueueEditor.cpp \
daemon/queue/QueueEditor.h \
daemon/queue/Scanner.cpp \
daemon/queue/Scanner.h \
daemon/queue/UrlCoordinator.cpp \
daemon/queue/UrlCoordinator.h \
daemon/remote/BinRpc.cpp \
daemon/remote/BinRpc.h \
daemon/remote/MessageBase.h \
daemon/remote/RemoteClient.cpp \
daemon/remote/RemoteClient.h \
daemon/remote/RemoteServer.cpp \
daemon/remote/RemoteServer.h \
daemon/remote/WebServer.cpp \
daemon/remote/WebServer.h \
daemon/remote/XmlRpc.cpp \
daemon/remote/XmlRpc.h \
daemon/util/Log.cpp \
daemon/util/Log.h \
daemon/util/NString.cpp \
daemon/util/NString.h \
daemon/util/Container.h \
daemon/util/Observer.cpp \
daemon/util/Observer.h \
daemon/util/Script.cpp \
daemon/util/Script.h \
daemon/util/Thread.cpp \
daemon/util/Thread.h \
daemon/util/Service.cpp \
daemon/util/Service.h \
daemon/util/FileSystem.cpp \
daemon/util/FileSystem.h \
daemon/util/Util.cpp \
daemon/util/Util.h \
daemon/nserv/NServMain.h \
daemon/nserv/NServMain.cpp \
daemon/nserv/NServFrontend.h \
daemon/nserv/NServFrontend.cpp \
daemon/nserv/NntpServer.h \
daemon/nserv/NntpServer.cpp \
daemon/nserv/NzbGenerator.h \
daemon/nserv/NzbGenerator.cpp \
daemon/nserv/YEncoder.h \
daemon/nserv/YEncoder.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/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
# Simd decoder and Crc32
nzbget_SOURCES += \
lib/yencode/YEncode.h \
lib/yencode/SimdInit.cpp \
lib/yencode/SimdDecoder.cpp \
lib/yencode/ScalarDecoder.cpp \
lib/yencode/Sse2Decoder.cpp \
lib/yencode/Ssse3Decoder.cpp \
lib/yencode/PclmulCrc.cpp \
lib/yencode/NeonDecoder.cpp \
lib/yencode/AcleCrc.cpp \
lib/yencode/SliceCrc.cpp
lib/yencode/Sse2Decoder.$(OBJEXT) : CXXFLAGS+=$(SSE2_CXXFLAGS)
lib/yencode/Ssse3Decoder.$(OBJEXT) : CXXFLAGS+=$(SSSE3_CXXFLAGS)
lib/yencode/PclmulCrc.$(OBJEXT) : CXXFLAGS+=$(PCLMUL_CXXFLAGS)
lib/yencode/NeonDecoder.$(OBJEXT) : CXXFLAGS+=$(NEON_CXXFLAGS)
lib/yencode/AcleCrc.$(OBJEXT) : CXXFLAGS+=$(ACLECRC_CXXFLAGS)
AM_CPPFLAGS = \
-I$(srcdir)/daemon/connect \
-I$(srcdir)/daemon/extension \
-I$(srcdir)/daemon/feed \
-I$(srcdir)/daemon/frontend \
-I$(srcdir)/daemon/main \
-I$(srcdir)/daemon/nntp \
-I$(srcdir)/daemon/postprocess \
-I$(srcdir)/daemon/queue \
-I$(srcdir)/daemon/remote \
-I$(srcdir)/daemon/util \
-I$(srcdir)/daemon/nserv \
-I$(srcdir)/lib/par2 \
-I$(srcdir)/lib/yencode
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/DupeMatcherTest.cpp \
tests/postprocess/RarRenamerTest.cpp \
tests/postprocess/RarReaderTest.cpp \
tests/postprocess/DirectUnpackTest.cpp \
tests/queue/NzbFileTest.cpp \
tests/nntp/ServerPoolTest.cpp \
tests/util/FileSystemTest.cpp \
tests/util/NStringTest.cpp \
tests/util/UtilTest.cpp
if WITH_PAR2
nzbget_SOURCES += \
tests/postprocess/ParCheckerTest.cpp \
tests/postprocess/ParRenamerTest.cpp
endif
AM_CPPFLAGS += \
-I$(srcdir)/lib/catch \
-I$(srcdir)/tests/suite
endif
EXTRA_DIST = \
$(windows_FILES) \
$(osx_FILES) \
$(linux_FILES) \
$(testdata_FILES) \
$(par2doc_FILES)
windows_FILES = \
daemon/windows/StdAfx.cpp \
daemon/windows/WinService.cpp \
daemon/windows/WinService.h \
daemon/windows/WinConsole.cpp \
daemon/windows/WinConsole.h \
nzbget.vcxproj \
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/resources/install.bmp \
windows/resources/uninstall.bmp \
windows/nzbget-setup.nsi
osx_FILES = \
osx/App_Prefix.pch \
osx/NZBGet-Info.plist \
osx/DaemonController.h \
osx/DaemonController.m \
osx/MainApp.h \
osx/MainApp.m \
osx/MainApp.xib \
osx/PFMoveApplication.h \
osx/PFMoveApplication.m \
osx/PreferencesDialog.h \
osx/PreferencesDialog.m \
osx/PreferencesDialog.xib \
osx/RPC.h \
osx/RPC.m \
osx/WebClient.h \
osx/WebClient.m \
osx/WelcomeDialog.h \
osx/WelcomeDialog.m \
osx/WelcomeDialog.xib \
osx/NZBGet.xcodeproj/project.pbxproj \
osx/Resources/Images/mainicon.icns \
osx/Resources/Images/statusicon.png \
osx/Resources/Images/statusicon@2x.png \
osx/Resources/licenses/license-bootstrap.txt \
osx/Resources/licenses/license-jquery-GPL.txt \
osx/Resources/licenses/license-jquery-MIT.txt \
osx/Resources/Credits.rtf \
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 \
linux/build-toolchain-android \
linux/build-toolchain-freebsd
doc_FILES = \
README \
ChangeLog \
COPYING
par2doc_FILES = \
lib/par2/AUTHORS \
lib/par2/README
exampleconf_FILES = \
nzbget.conf
webui_FILES = \
webui/index.html \
webui/index.js \
webui/downloads.js \
webui/edit.js \
webui/fasttable.js \
webui/history.js \
webui/messages.js \
webui/status.js \
webui/style.css \
webui/upload.js \
webui/util.js \
webui/config.js \
webui/feed.js \
webui/lib/bootstrap.js \
webui/lib/bootstrap.min.js \
webui/lib/bootstrap.css \
webui/lib/jquery.js \
webui/lib/jquery.min.js \
webui/lib/raphael.js \
webui/lib/raphael.min.js \
webui/lib/elycharts.js \
webui/lib/elycharts.min.js \
webui/img/icons.png \
webui/img/icons-2x.png \
webui/img/transmit.gif \
webui/img/transmit-file.gif \
webui/img/favicon.ico \
webui/img/download-anim-green-2x.png \
webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif \
webui/img/favicon-256x256-opaque.png \
webui/img/favicon-256x256.png
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 \
tests/testdata/parchecker2/crc.txt \
tests/testdata/parchecker2/testfile.7z.001 \
tests/testdata/parchecker2/testfile.7z.002 \
tests/testdata/parchecker2/testfile.7z.003 \
tests/testdata/parchecker2/testfile.7z.par2 \
tests/testdata/parchecker2/testfile.7z.vol0+1.PAR2 \
tests/testdata/parchecker2/testfile.7z.vol1+2.PAR2 \
tests/testdata/parchecker2/testfile.7z.vol3+3.PAR2 \
tests/testdata/rarrenamer/testfile3.part01.rar \
tests/testdata/rarrenamer/testfile3.part02.rar \
tests/testdata/rarrenamer/testfile3.part03.rar \
tests/testdata/rarrenamer/testfile5.part01.rar \
tests/testdata/rarrenamer/testfile5.part02.rar \
tests/testdata/rarrenamer/testfile5.part03.rar \
tests/testdata/rarrenamer/testfile3oldnam.rar \
tests/testdata/rarrenamer/testfile3oldnam.r00 \
tests/testdata/rarrenamer/testfile3oldnam.r01 \
tests/testdata/rarrenamer/testfile3encdata.part01.rar \
tests/testdata/rarrenamer/testfile3encdata.part02.rar \
tests/testdata/rarrenamer/testfile3encdata.part03.rar \
tests/testdata/rarrenamer/testfile3encnam.part01.rar \
tests/testdata/rarrenamer/testfile3encnam.part02.rar \
tests/testdata/rarrenamer/testfile3encnam.part03.rar \
tests/testdata/rarrenamer/testfile5encdata.part01.rar \
tests/testdata/rarrenamer/testfile5encdata.part02.rar \
tests/testdata/rarrenamer/testfile5encdata.part03.rar \
tests/testdata/rarrenamer/testfile5encnam.part01.rar \
tests/testdata/rarrenamer/testfile5encnam.part02.rar \
tests/testdata/rarrenamer/testfile5encnam.part03.rar
# Install
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
scriptsdir = $(datadir)/nzbget
nobase_dist_scripts_SCRIPTS = $(scripts_FILES)
# Note about "sed":
# We need to make some changes in installed files.
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
# 1) copy the original file to original.temp (delete existing original.temp, if any);
# 2) sed < original.temp > original
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/scripts:installed to $(scriptsdir)/scripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
# (only if they do not exist there to prevent override by update)
install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# 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 ".git" doesn't exists we keep and reuse file "code_revision.cpp",
# which was possibly created early.
# 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=`git branch | sed -n -e 's/^\* \(.*\)/\1/p'`; \
M=`git status --porcelain` ; \
if test "$$M" != "" ; then \
M="M" ; \
fi ; \
if test "$$B" = "master" ; then \
V="$$M" ; \
elif test "$$B" = "develop" ; then \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M" ; \
else \
V=`git rev-list HEAD | wc -l | xargs` ; \
V="$${V}$$M ($$B)" ; \
fi ; \
H=`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 "#include \"nzbget.h\"" ;\
echo "const char* code_revision(void)" ;\
echo "{" ;\
echo " const char* revision = \"$$V\";" ;\
echo " return revision;" ;\
echo "}" ;\
) > code_revision.cpp ; \
fi \
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 "#include \"nzbget.h\"" ;\
echo "const char* code_revision(void)" ;\
echo "{" ;\
echo " const char* revision = \"\";" ;\
echo " return revision;" ;\
echo "}" ;\
) > code_revision.cpp ; \
fi
FORCE:
# Ignore "code_revision.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
clean-bak: rm *~
# Fix premissions
dist-hook:
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

2048
Makefile.in vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,402 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6204; // = "nzb4" (nzb version 4)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
/**
* NZBGet communication protocol uses only two basic data types: integer and char.
* Integer values are passed using network byte order (Big-Endian).
* Use function "htonl" and "ntohl" to convert integers to/from machine
* (host) byte order.
* All char-strings ends with NULL-char.
*
* NOTE:
* NZBGet communication protocol is intended for usage only by NZBGet itself.
* The communication works only if server and client has the same version.
* The compatibility with previous program versions is not provided.
* Third-party programs should use JSON-RPC or XML-RPC to communicate with NZBGet.
*/
// Possible values for field "m_iType" of struct "SNZBRequestBase":
enum eRemoteRequest
{
eRemoteRequestDownload = 1,
eRemoteRequestPauseUnpause,
eRemoteRequestList,
eRemoteRequestSetDownloadRate,
eRemoteRequestDumpDebug,
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog,
eRemoteRequestScan
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
// File-Actions affect one file, Group-Actions affect all files in group.
// Group is a list of files, added to queue from one NZB-File.
enum eRemoteEditAction
{
eRemoteEditActionFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eRemoteEditActionFileMoveTop, // move to top of queue
eRemoteEditActionFileMoveBottom, // move to bottom of queue
eRemoteEditActionFilePause, // pause
eRemoteEditActionFileResume, // resume (unpause)
eRemoteEditActionFileDelete, // delete
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eRemoteEditActionGroupMoveTop, // move to top of queue
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
eRemoteEditActionGroupPause, // pause
eRemoteEditActionGroupResume, // resume (unpause)
eRemoteEditActionGroupDelete, // delete
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionGroupSetCategory, // set or change category for a group
eRemoteEditActionGroupMerge, // merge group
eRemoteEditActionGroupSetParameter // set post-process parameter
};
// The basic SNZBRequestBase struct, used in all requests
struct SNZBRequestBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
};
// The basic SNZBResposneBase struct, used in all responses
struct SNZBResponseBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
};
// A download request
struct SNZBDownloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, be empty
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
// A download response
struct SNZBDownloadResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// A list and status request
struct SNZBListRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList; // 1 - return file list
int32_t m_bServerState; // 1 - return server state
};
// A list response
struct SNZBListResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
int32_t m_bServerPaused; // 1 - server is currently in paused-state
int32_t m_iThreadCount; // Number of threads running
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
int32_t m_iUpTimeSec; // Server up time in seconds
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
int32_t m_iNrTrailingNZBEntries; // Number of List-NZB-entries, following to this structure
int32_t m_iNrTrailingPPPEntries; // Number of List-PPP-entries, following to this structure
int32_t m_iNrTrailingFileEntries; // Number of List-File-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
// SNZBListResponseEntry m_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
// SNZBListResponseEntry m_PPPEntries[m_iNrTrailingPPPEntries] // variable sized
// SNZBListResponseEntry m_FileEntries[m_iNrTrailingFileEntries] // variable sized
};
// A list response nzb entry
struct SNZBListResponseNZBEntry
{
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szCategory[m_iCategoryLen]; // variable sized
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
};
// A list response pp-parameter entry
struct SNZBListResponsePPPEntry
{
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
int32_t m_iValueLen; // Length of Value-string (m_szValue), following to this record
//char m_szName[m_iNameLen]; // variable sized
//char m_szValue[m_iValueLen]; // variable sized
};
// A list response file entry
struct SNZBListResponseFileEntry
{
int32_t m_iID; // Entry-ID
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_bPaused; // 1 - file is paused
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
//char m_szSubject[m_iSubjectLen]; // variable sized
//char m_szFilename[m_iFilenameLen]; // variable sized
};
// A log request
struct SNZBLogRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // Only one of these two parameters
int32_t m_iLines; // can be set. The another one must be set to "0".
};
// A log response
struct SNZBLogResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A log response entry
struct SNZBLogResponseEntry
{
int32_t m_iID; // ID of Log-entry
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTextLen]; // variable sized
};
// A Pause/Unpause request
struct SNZBPauseUnpauseRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
};
// A Pause/Unpause response
struct SNZBPauseUnpauseResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Request setting the download rate
struct SNZBSetDownloadRateRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
};
// A setting download rate response
struct SNZBSetDownloadRateResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// An edit queue request
struct SNZBEditQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
//char m_szText[m_iTextLen]; // variable sized
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
};
// An edit queue response
struct SNZBEditQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Request dumping of debug info
struct SNZBDumpDebugRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Dumping of debug response
struct SNZBDumpDebugResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Shutdown server request
struct SNZBShutdownRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Shutdown server response
struct SNZBShutdownResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Server version request
struct SNZBVersionRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Server version response
struct SNZBVersionResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// PostQueue request
struct SNZBPostQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// A PostQueue response
struct SNZBPostQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBPostQueueResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of PostQueue-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all PostQueue-entries, following to this structure
// SNZBPostQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A PostQueue response entry
struct SNZBPostQueueResponseEntry
{
int32_t m_iID; // ID of Post-entry
int32_t m_iStage; // See PrePostProcessor::EPostJobStage
int32_t m_iStageProgress; // Progress of current stage, value in range 0..1000
int32_t m_iFileProgress; // Progress of current file, value in range 0..1000
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szParFilename[m_iParFilename]; // variable sized
//char m_szInfoName[m_iInfoNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
};
// Write log request
struct SNZBWriteLogRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Write log response
struct SNZBWriteLogResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Scan nzb directory request
struct SNZBScanRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Scan nzb directory response
struct SNZBScanResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,125 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef NCURSESFRONTEND_H
#define NCURSESFRONTEND_H
#ifndef DISABLE_CURSES
#include <vector>
#include "Frontend.h"
#include "Log.h"
#include "DownloadInfo.h"
class NCursesFrontend : public Frontend
{
private:
enum EInputMode
{
eNormal,
eEditQueue,
eDownloadRate
};
bool m_bUseColor;
int m_iDataUpdatePos;
bool m_bUpdateNextTime;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
int m_iQueueWinHeight;
int m_iQueueWinClientHeight;
int m_iMessagesWinTop;
int m_iMessagesWinHeight;
int m_iMessagesWinClientHeight;
int m_iSelectedQueueEntry;
int m_iLastEditEntry;
bool m_bLastPausePars;
int m_iQueueScrollOffset;
GroupQueue m_groupQueue;
char* m_szHint;
time_t m_tStartHint;
// Inputting numbers
int m_iInputNumberIndex;
int m_iInputValue;
#ifdef WIN32
CHAR_INFO* m_pScreenBuffer;
CHAR_INFO* m_pOldScreenBuffer;
int m_iScreenBufferSize;
std::vector<WORD> m_ColorAttr;
#else
void* m_pWindow; // WINDOW*
#endif
EInputMode m_eInputMode;
bool m_bShowNZBname;
bool m_bShowTimestamp;
bool m_bGroupFiles;
float m_QueueWindowPercentage;
#ifdef WIN32
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
#endif
void PlotLine(const char * szString, int iRow, int iPos, int iColorPair);
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
void PrintMessages();
void PrintQueue();
void PrintFileQueue();
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
void PrintGroupQueue();
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
void PrepareGroupQueue();
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
void ClearGroupQueue();
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
void PrintKeyInputBar();
void PrintStatus();
void UpdateInput(int initialKey);
void Update(int iKey);
void SetCurrentQueueEntry(int iEntry);
void CalcWindowSizes();
void RefreshScreen();
int ReadConsoleKey();
int CalcQueueSize();
void NeedUpdateData();
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
void SetHint(const char* szHint);
protected:
virtual void Run();
public:
NCursesFrontend();
virtual ~NCursesFrontend();
};
#endif
#endif

0
NEWS
View File

View File

@@ -1,291 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
#include "NNTPConnection.h"
#include "Connection.h"
#include "NewsServer.h"
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
{
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
}
NNTPConnection::~NNTPConnection()
{
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
free(m_szLineBuf);
}
const char* NNTPConnection::Request(const char* req)
{
if (!req)
{
return NULL;
}
m_bAuthError = false;
WriteLine(req);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
return NULL;
}
if (!strncmp(answer, "480", 3))
{
debug("%s requested authorization", m_pNetAddress->GetHost());
//authentication required!
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
}
bool NNTPConnection::Authenticate()
{
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
!((NewsServer*)m_pNetAddress)->GetPassword())
{
return true;
}
return AuthInfoUser();
}
bool NNTPConnection::AuthInfoUser(int iRecur)
{
if (iRecur > 10)
{
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (!strncmp(answer, "281", 3))
{
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
{
return AuthInfoPass(++iRecur);
}
else if (!strncmp(answer, "480", 3))
{
return AuthInfoUser(++iRecur);
}
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
bool NNTPConnection::AuthInfoPass(int iRecur)
{
if (iRecur > 10)
{
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
else if (!strncmp(answer, "2", 1))
{
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
{
return AuthInfoPass(++iRecur);
}
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
const char* NNTPConnection::JoinGroup(const char* grp)
{
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
{
// already in group
strcpy(m_szLineBuf, "211 ");
return m_szLineBuf;
}
char tmp[1024];
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
tmp[1024-1] = '\0';
const char* answer = Request(tmp);
if (m_bAuthError)
{
return answer;
}
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
if (m_szActiveGroup)
{
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
}
else
{
debug("Error changing group on %s to %s: %s.",
GetServer()->GetHost(), grp, answer);
}
return answer;
}
bool NNTPConnection::DoConnect()
{
debug("Opening connection to %s", GetServer()->GetHost());
bool res = Connection::DoConnect();
if (!res)
{
return res;
}
#ifndef DISABLE_TLS
if (GetNewsServer()->GetTLS())
{
if (!StartTLS())
{
return false;
}
}
#endif
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
return false;
}
debug("Connection to %s established", GetServer()->GetHost());
return true;
}
bool NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
Request("quit\r\n");
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
}
return Connection::DoDisconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, m_pNetAddress->GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);
}

View File

@@ -1,59 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef NNTPCONNECTION_H
#define NNTPCONNECTION_H
#include "NewsServer.h"
#include "Connection.h"
class NNTPConnection : public Connection
{
private:
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
virtual bool DoConnect();
virtual bool DoDisconnect();
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
public:
NNTPConnection(NewsServer* server);
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};
#endif

View File

@@ -1,573 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <string.h>
#include <list>
#ifdef WIN32
#include <comutil.h>
#import "MSXML.dll" named_guids
using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#endif
#include "nzbget.h"
#include "NZBFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "DiskState.h"
#include "Util.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
NZBFile::NZBFile(const char* szFileName, const char* szCategory)
{
debug("Creating NZBFile");
m_szFileName = strdup(szFileName);
m_pNZBInfo = new NZBInfo();
m_pNZBInfo->AddReference();
m_pNZBInfo->SetFilename(szFileName);
m_pNZBInfo->SetCategory(szCategory);
m_pNZBInfo->BuildDestDirName();
m_FileInfos.clear();
}
NZBFile::~NZBFile()
{
debug("Destroying NZBFile");
// Cleanup
if (m_szFileName)
{
free(m_szFileName);
}
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
delete *it;
}
m_FileInfos.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void NZBFile::LogDebugInfo()
{
debug(" NZBFile %s", m_szFileName);
}
void NZBFile::DetachFileInfos()
{
m_FileInfos.clear();
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
// make Article-List big enough
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
pFileInfo->GetArticles()->push_back(NULL);
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
}
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
// deleting empty articles
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
int i = 0;
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
{
if (*it == NULL)
{
pArticles->erase(it);
it = pArticles->begin() + i;
}
else
{
it++;
i++;
}
}
if (!pArticles->empty())
{
ParseSubject(pFileInfo);
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
else
{
delete pFileInfo;
}
}
void NZBFile::ParseSubject(FileInfo* pFileInfo)
{
// tokenize subject, considering spaces as separators and quotation
// marks as non separatable token delimiters.
// then take the last token containing dot (".") as a filename
typedef std::list<char*> TokenList;
TokenList tokens;
tokens.clear();
// tokenizing
char* p = (char*)pFileInfo->GetSubject();
char* start = p;
bool quot = false;
while (true)
{
char ch = *p;
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
if (sep)
{
// end of token
int len = (int)(p - start);
if (len > 0)
{
char* token = (char*)malloc(len + 1);
strncpy(token, start, len);
token[len] = '\0';
tokens.push_back(token);
}
start = p;
if (ch != '\"' || quot)
{
start++;
}
quot = *start == '\"';
if (quot)
{
start++;
char* q = strchr(start, '\"');
if (q)
{
p = q - 1;
}
else
{
quot = false;
}
}
}
if (ch == '\0')
{
break;
}
p++;
}
if (!tokens.empty())
{
// finding the best candidate for being a filename
char* besttoken = tokens.back();
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
{
char* s = *it;
char* p = strchr(s, '.');
if (p && (p[1] != '\0'))
{
besttoken = s;
break;
}
}
pFileInfo->SetFilename(besttoken);
// free mem
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
{
free(*it);
}
}
else
{
// subject is empty or contains only separators?
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
pFileInfo->MakeValidFilename();
}
/**
* Check if the parsing of subject was correct
*/
void NZBFile::CheckFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo1 = *it;
int iDupe = 0;
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
{
iDupe++;
}
}
// If more than two files have the same parsed filename but different subjects,
// this means, that the parsing was not correct.
// in this case we take subjects as filenames to prevent
// false "duplicate files"-alarm.
// It's Ok for just two files to have the same filename, this is
// an often case by posting-errors to repost bad files
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
{
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
pFileInfo2->MakeValidFilename();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->LoadArticles(pFileInfo2);
g_pDiskState->SaveFile(pFileInfo2);
pFileInfo2->ClearArticles();
}
}
}
}
}
#ifdef WIN32
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
CoInitialize(NULL);
HRESULT hr;
MSXML::IXMLDOMDocumentPtr doc;
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
if (FAILED(hr))
{
return NULL;
}
// Load the XML document file...
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
VARIANT_BOOL success;
if (bFromBuffer)
{
success = doc->loadXML(szBuffer);
}
else
{
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
success = doc->load(v);
}
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
const char* szErrMsg = r;
error("Error parsing nzb-file: %s", szErrMsg);
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
}
return pFile;
}
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
{
while (char ch = *szFilename++)
{
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') )
{
*szURL++ = ch;
}
else
{
*szURL++ = '%';
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
}
}
*szURL = NULL;
}
bool NZBFile::ParseNZB(IUnknown* nzb)
{
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
for (int i = 0; i < fileList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
if (!attribute) return false;
_bstr_t subject(attribute->Gettext());
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetSubject(subject);
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
for (int g = 0; g < groupList->Getlength(); g++)
{
MSXML::IXMLDOMNodePtr node = groupList->Getitem(g);
_bstr_t group = node->Gettext();
pFileInfo->GetGroups()->push_back(strdup((const char*)group));
}
MSXML::IXMLDOMNodeListPtr segmentList = node->selectNodes("segments/segment");
for (int g = 0; g < segmentList->Getlength(); g++)
{
MSXML::IXMLDOMNodePtr node = segmentList->Getitem(g);
_bstr_t id = node->Gettext();
char szId[2048];
snprintf(szId, 2048, "<%s>", (const char*)id);
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("number");
if (!attribute) return false;
_bstr_t number(attribute->Gettext());
attribute = node->Getattributes()->getNamedItem("bytes");
if (!attribute) return false;
_bstr_t bytes(attribute->Gettext());
int partNumber = atoi(number);
int lsize = atoi(bytes);
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
}
AddFileInfo(pFileInfo);
}
return true;
}
#else
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
xmlTextReaderPtr doc;
if (bFromBuffer)
{
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
}
else
{
doc = xmlReaderForFile(szFileName, NULL, 0);
}
if (!doc)
{
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
}
xmlFreeTextReader(doc);
return pFile;
}
bool NZBFile::ParseNZB(void* nzb)
{
FileInfo* pFileInfo = NULL;
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
// walk through whole doc and search for segments-tags
int ret = xmlTextReaderRead(node);
while (ret == 1)
{
if (node)
{
xmlChar *name, *value;
name = xmlTextReaderName(node);
if (name == NULL)
{
name = xmlStrdup(BAD_CAST "--");
}
value = xmlTextReaderValue(node);
if (xmlTextReaderNodeType(node) == 1)
{
if (!strcmp("file", (char*)name))
{
pFileInfo = new FileInfo();
pFileInfo->SetFilename(m_szFileName);
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
if (!strcmp("subject",(char*)name))
{
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetSubject((char*)value);
}
}
}
else if (!strcmp("segment",(char*)name))
{
long long lsize = -1;
int partNumber = -1;
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
xmlFree(value);
value = xmlTextReaderValue(node);
if (!strcmp("bytes",(char*)name))
{
lsize = atol((char*)value);
}
if (!strcmp("number",(char*)name))
{
partNumber = atol((char*)value);
}
}
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
/* Get the #text part */
ret = xmlTextReaderRead(node);
if (partNumber > 0)
{
// new segment, add it!
xmlFree(value);
value = xmlTextReaderValue(node);
char tmp[2048];
snprintf(tmp, 2048, "<%s>", (char*)value);
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(tmp);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
}
else if (!strcmp("group",(char*)name))
{
ret = xmlTextReaderRead(node);
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->GetGroups()->push_back(strdup((char*)value));
}
}
if (xmlTextReaderNodeType(node) == 15)
{
/* Close the file element, add the new file to file-list */
if (!strcmp("file",(char*)name))
{
AddFileInfo(pFileInfo);
}
}
xmlFree(name);
xmlFree(value);
}
ret = xmlTextReaderRead(node);
}
if (ret != 0)
{
error("Failed to parse nzb-file\n");
return false;
}
return true;
}
#endif

View File

@@ -1,69 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef NZBFILE_H
#define NZBFILE_H
#include <vector>
#include "DownloadInfo.h"
class NZBFile
{
public:
typedef std::vector<FileInfo*> FileInfos;
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
NZBFile(const char* szFileName, const char* szCategory);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo);
void CheckFilenames();
#ifdef WIN32
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
bool ParseNZB(void* nzb);
#endif
static NZBFile* Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer);
public:
virtual ~NZBFile();
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void DetachFileInfos();
void LogDebugInfo();
};
#endif

View File

@@ -1,54 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NetAddress.h"
NetAddress::NetAddress(const char* szHost, int iPort)
{
m_szHost = NULL;
m_iPort = iPort;
if (szHost)
m_szHost = strdup(szHost);
}
NetAddress::~NetAddress()
{
if (m_szHost)
free(m_szHost);
m_szHost = NULL;
}

View File

@@ -1,71 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NewsServer.h"
#include "Log.h"
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel) : NetAddress(szHost, iPort)
{
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = iLevel;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
if (szUser)
{
m_szUser = strdup(szUser);
}
if (szPass)
{
m_szPassword = strdup(szPass);
}
}
NewsServer::~NewsServer()
{
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
}

View File

@@ -1,53 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef NEWSSERVER_H
#define NEWSSERVER_H
#include "NetAddress.h"
class NewsServer : public NetAddress
{
private:
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
bool m_bJoinGroup;
bool m_bTLS;
public:
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
virtual ~NewsServer();
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
};
#endif

View File

@@ -1,62 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include "Observer.h"
#include "Log.h"
Subject::Subject()
{
m_Observers.clear();
}
void Subject::Attach(Observer* Observer)
{
m_Observers.push_back(Observer);
}
void Subject::Detach(Observer* Observer)
{
m_Observers.remove(Observer);
}
void Subject::Notify(void* Aspect)
{
debug("Notifying observers");
for (std::list<Observer*>::iterator it = m_Observers.begin(); it != m_Observers.end(); it++)
{
Observer* Observer = *it;
Observer->Update(this, Aspect);
}
}

View File

File diff suppressed because it is too large Load Diff

319
Options.h
View File

@@ -1,319 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef OPTIONS_H
#define OPTIONS_H
#include <vector>
#include "Thread.h"
class Options
{
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestListFiles,
opClientRequestListGroups,
opClientRequestListStatus,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScan
};
enum EMessageTarget
{
mtNone,
mtScreen,
mtLog,
mtBoth
};
enum EOutputMode
{
omLoggable,
omColored,
omNCurses
};
enum ELoadPars
{
lpNone,
lpOne,
lpAll
};
enum EScriptLogKind
{
slNone,
slDetail,
slInfo,
slWarning,
slError,
slDebug
};
class OptEntry
{
private:
char* m_szName;
char* m_szValue;
void SetName(const char* szName);
void SetValue(const char* szValue);
friend class Options;
public:
OptEntry();
~OptEntry();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
typedef std::vector<OptEntry*> OptEntries;
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
// Options
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szTempDir;
char* m_szQueueDir;
char* m_szNzbDir;
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;
int m_iTerminateTimeout;
bool m_bAppendNZBDir;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
bool m_bRenameBroken;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bDupeCheck;
char* m_szServerIP;
char* m_szServerPassword;
int m_szServerPort;
char* m_szLockFile;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadPostQueue;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
ELoadPars m_eLoadPars;
bool m_bParCheck;
bool m_bParRepair;
char* m_szPostProcess;
char* m_szNZBProcess;
bool m_bStrictParName;
bool m_bNoConfig;
int m_iUMask;
int m_iUpdateInterval;
bool m_bCursesNZBName;
bool m_bCursesTime;
bool m_bCursesGroup;
bool m_bCrcCheck;
bool m_bRetryOnCrcError;
int m_iThreadLimit;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EScriptLogKind m_eProcessLogKind;
bool m_bAllowReProcess;
bool m_bTLS;
bool m_bDumpCore;
bool m_bParPauseQueue;
bool m_bPostPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
int m_iParTimeLimit;
// 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;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szCategory;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
// Current state
bool m_bPause;
float m_fDownloadRate;
EClientOperation m_eClientOperation;
void InitDefault();
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitScheduler();
void CheckOptions();
void PrintUsage(char* com);
void Dump();
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
OptEntry* FindOption(const char* optname);
const char* GetOption(const char* optname);
void SetOption(const char* optname, const char* value);
bool SetOptionString(const char* option);
bool ValidateOptionName(const char* optname);
void LoadConfig(const char* configfile);
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty);
void ParseFileIDList(int argc, char* argv[], int optind);
bool ParseTime(const char* szTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
public:
Options(int argc, char* argv[]);
~Options();
// Options
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetDestDir() { return m_szDestDir; }
const char* GetTempDir() { return m_szTempDir; }
const char* GetQueueDir() { return m_szQueueDir; }
const char* GetNzbDir() { return m_szNzbDir; }
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
bool GetResetLog() const { return m_bResetLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
bool GetDecode() { return m_bDecode; };
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
bool GetContinuePartial() { return m_bContinuePartial; }
bool GetRenameBroken() { return m_bRenameBroken; }
int GetRetries() { return m_iRetries; }
int GetRetryInterval() { return m_iRetryInterval; }
bool GetSaveQueue() { return m_bSaveQueue; }
bool GetDupeCheck() { return m_bDupeCheck; }
char* GetServerIP() { return m_szServerIP; }
char* GetServerPassword() { return m_szServerPassword; }
int GetServerPort() { return m_szServerPort; }
char* GetLockFile() { return m_szLockFile; }
char* GetDaemonUserName() { return m_szDaemonUserName; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
char* GetLogFile() { return m_szLogFile; }
ELoadPars GetLoadPars() { return m_eLoadPars; }
bool GetParCheck() { return m_bParCheck; }
bool GetParRepair() { return m_bParRepair; }
const char* GetPostProcess() { return m_szPostProcess; }
const char* GetNZBProcess() { return m_szNZBProcess; }
bool GetStrictParName() { return m_bStrictParName; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
bool GetCursesNZBName() { return m_bCursesNZBName; }
bool GetCursesTime() { return m_bCursesTime; }
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
int GetThreadLimit() { return m_iThreadLimit; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
int GetDiskSpace() { return m_iDiskSpace; }
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
bool GetTLS() { return m_bTLS; }
bool GetDumpCore() { return m_bDumpCore; }
bool GetParPauseQueue() { return m_bParPauseQueue; }
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
int GetParTimeLimit() { return m_iParTimeLimit; }
// 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; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetCategory() { return m_szCategory; }
const char* GetLastArg() { return m_szLastArg; }
bool GetAddTop() { return m_bAddTop; }
float GetSetRate() { return m_fSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
// Current state
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
bool GetPause() const { return m_bPause; }
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
float GetDownloadRate() const { return m_fDownloadRate; }
};
#endif

View File

@@ -1,562 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#ifndef DISABLE_PARCHECK
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#else
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#endif
#include "nzbget.h"
#include "ParChecker.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
const char* Par2CmdLineErrStr[] = { "OK",
"data files are damaged and there is enough recovery data available to repair them",
"data files are damaged and there is insufficient recovery data available to be able to repair them",
"there was something wrong with the command line arguments",
"the PAR2 files did not contain sufficient information about the data files to be able to verify them",
"repair completed but the data files still appear to be damaged",
"an error occured when accessing files",
"internal error occurred",
"out of memory" };
class Repairer : public Par2Repairer
{
friend class ParChecker;
};
ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psUndefined;
m_szParFilename = NULL;
m_szNZBFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
m_eStage = ptLoadingPars;
m_QueuedParFiles.clear();
}
ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szErrMsg)
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
}
void ParChecker::SetParFilename(const char * szParFilename)
{
if (m_szParFilename)
{
free(m_szParFilename);
}
m_szParFilename = strdup(szParFilename);
}
void ParChecker::SetInfoName(const char * szInfoName)
{
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
void ParChecker::SetNZBFilename(const char * szNZBFilename)
{
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
m_szNZBFilename = strdup(szNZBFilename);
}
void ParChecker::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParChecker::Run()
{
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
CommandLine commandLine;
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
SetStatus(psFailed);
return;
}
Result res;
Repairer* pRepairer = new Repairer();
m_pRepairer = pRepairer;
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
UpdateProgress();
res = pRepairer->PreProcess(commandLine);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
SetStatus(psFailed);
delete pRepairer;
return;
}
char BufReason[1024];
BufReason[0] = '\0';
if (m_szErrMsg)
{
free(m_szErrMsg);
}
m_szErrMsg = NULL;
m_eStage = ptVerifyingSources;
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
m_mutexQueuedParFiles.Unlock();
if (!hasMorePars)
{
int iBlockFound = 0;
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_mutexQueuedParFiles.Unlock();
if (!requested && !hasMorePars)
{
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
BufReason[1024-1] = '\0';
m_szErrMsg = strdup(BufReason);
break;
}
if (!hasMorePars)
{
m_semNeedMoreFiles.Wait();
}
}
if (IsStopped())
{
break;
}
bMoreFilesLoaded = LoadMorePars();
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
SetStatus(psFailed);
delete pRepairer;
return;
}
if (res == eSuccess)
{
info("Repair not needed for %s", m_szInfoName);
m_bRepairNotNeeded = true;
}
else if (res == eRepairPossible)
{
if (g_pOptions->GetParRepair())
{
info("Repairing %s", m_szInfoName);
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iProcessedFiles = 0;
m_eStage = ptRepairing;
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
UpdateProgress();
res = pRepairer->Process(commandLine, true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
info("Successfully repaired %s", m_szInfoName);
}
}
else
{
info("Repair possible for %s", m_szInfoName);
res = eSuccess;
}
}
if (m_bCancelled)
{
error("Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
SetStatus(psFailed);
}
else if (res == eSuccess)
{
SetStatus(psFinished);
}
else
{
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
{
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
}
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
SetStatus(psFailed);
}
delete pRepairer;
}
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
QueuedParFiles moreFiles;
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
{
char* szParFilename = *it;
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
return !moreFiles.empty();
}
void ParChecker::AddParFile(const char * szParFilename)
{
m_mutexQueuedParFiles.Lock();
m_QueuedParFiles.push_back(strdup(szParFilename));
m_semNeedMoreFiles.Post();
m_mutexQueuedParFiles.Unlock();
}
void ParChecker::QueueChanged()
{
m_mutexQueuedParFiles.Lock();
m_semNeedMoreFiles.Post();
m_mutexQueuedParFiles.Unlock();
}
bool ParChecker::CheckSplittedFragments()
{
bool bFragmentsAdded = false;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
}
return bFragmentsAdded;
}
bool ParChecker::AddSplittedFragments(const char* szFilename)
{
char szDirectory[1024];
strncpy(szDirectory, szFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
int iBaseLen = strlen(szBasename);
list<CommandLine::ExtraFile> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (!strncasecmp(filename, szBasename, iBaseLen))
{
const char* p = filename + iBaseLen;
if (*p == '.')
{
for (p++; *p && strchr("0123456789", *p); p++) ;
if (!*p)
{
debug("Found splitted fragment %s", filename);
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
extrafiles.push_back(extrafile);
}
}
}
}
bool bFragmentsAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles = extrafiles.size();
m_bVerifyingExtraFiles = true;
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
m_bVerifyingExtraFiles = false;
}
return bFragmentsAdded;
}
void ParChecker::signal_filename(std::string str)
{
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
if (m_eStage == ptRepairing)
{
m_eStage = ptVerifyingRepaired;
}
info("%s %s", szStageMessage[m_eStage], str.c_str());
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
void ParChecker::signal_progress(double progress)
{
m_iFileProgress = (int)progress;
if (m_eStage == ptRepairing)
{
// calculating repair-data for all files
m_iStageProgress = m_iFileProgress;
}
else
{
// processing individual files
int iTotalFiles = 0;
if (m_eStage == ptVerifyingRepaired)
{
// repairing individual files
iTotalFiles = m_iFilesToRepair;
}
else
{
// verifying individual files
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
}
if (iTotalFiles > 0)
{
if (m_iFileProgress < 1000)
{
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
}
else
{
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
}
}
else
{
m_iStageProgress = 0;
}
}
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
UpdateProgress();
}
void ParChecker::signal_done(std::string str, int available, int total)
{
m_iProcessedFiles++;
if (m_eStage == ptVerifyingSources)
{
if (available < total && !m_bVerifyingExtraFiles)
{
bool bFileExists = true;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
!sourcefile->GetTargetExists())
{
bFileExists = false;
break;
}
}
if (bFileExists)
{
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
}
else
{
warn("File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
}
void ParChecker::Cancel()
{
#ifdef HAVE_PAR2_CANCEL
((Repairer*)m_pRepairer)->cancelled = true;
m_bCancelled = true;
#else
error("Could not cancel par-repair. The used version of libpar2 does not support the cancelling of par-repair. Libpar2 needs to be patched for that feature to work.");
#endif
}
#endif

View File

@@ -1,121 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARCHECKER_H
#define PARCHECKER_H
#ifndef DISABLE_PARCHECK
#include <deque>
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
class ParChecker : public Thread, public Subject
{
public:
enum EStatus
{
psUndefined,
psWorking,
psFailed,
psFinished
};
enum EStage
{
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
};
typedef std::deque<char*> QueuedParFiles;
private:
char* m_szInfoName;
char* m_szNZBFilename;
char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
char* m_szErrMsg;
bool m_bRepairNotNeeded;
QueuedParFiles m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
Semaphore m_semNeedMoreFiles;
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
bool m_bVerifyingExtraFiles;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
protected:
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParChecker();
virtual ~ParChecker();
virtual void Run();
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetNZBFilename() { return m_szNZBFilename; }
void SetNZBFilename(const char* szNZBFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
const char* GetErrMsg() { return m_szErrMsg; }
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
void Cancel();
bool GetCancelled() { return m_bCancelled; }
};
#endif
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,149 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PREPOSTPROCESSOR_H
#define PREPOSTPROCESSOR_H
#include <deque>
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#endif
class PrePostProcessor : public Thread
{
private:
typedef std::deque<char*> FileList;
class QueueCoordinatorObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
};
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
{
private:
PrePostProcessor* m_Owner;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
friend class PrePostProcessor;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
#endif
private:
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bNZBScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
bool m_bRequestedNZBDirScan;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckTimestamp);
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const char* szNZBFilename,
bool bIgnoreFirstInPostQueue, bool bIgnorePaused, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
bool JobExists(PostQueue* pPostQueue, const char* szNZBFilename, const char* szParFilename, bool bParCheck);
bool ClearCompletedJobs(const char* szNZBFilename);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SavePostQueue();
void SanitisePostQueue();
void CheckDiskSpace();
void AddFileToQueue(const char* szFilename, const char* szCategory);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
void ApplySchedulerState();
bool PauseDownload();
bool UnpauseDownload();
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, FileInfo* pFileInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, FileInfo* pFileInfo);
bool FindMainPars(const char* szPath, FileList* pFileList);
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, const char* szDestDir, const char* szNZBFilename,
const char* szCategory, NZBParameterList* pParameters, const char* szQueuedFilename,
bool bParCheck, bool bLockQueue, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
Mutex m_mutexQueue;
PostQueue m_PostQueue;
PostQueue m_CompletedJobs;
#ifndef DISABLE_PARCHECK
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
void ParCheckerUpdate(Subject* Caller, void* Aspect);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
FileInfo* GetParCleanupQueueGroup(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
bool HasFailedParJobs(const char* szNZBFilename, bool bIgnoreRepairPossible);
bool RequestMorePars(const char* szNZBFilename, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, const char* szNZBFilename, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void UpdateParProgress();
void StartParJob(PostInfo* pPostInfo);
#endif
public:
PrePostProcessor();
virtual ~PrePostProcessor();
virtual void Run();
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
PostQueue* LockPostQueue();
void UnlockPostQueue();
void ScanNZBDir();
};
#endif

View File

@@ -1,919 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "QueueCoordinator.h"
#include "Options.h"
#include "ServerPool.h"
#include "ArticleDownloader.h"
#include "DiskState.h"
#include "Log.h"
#include "Util.h"
#include "Decoder.h"
extern Options* g_pOptions;
extern ServerPool* g_pServerPool;
extern DiskState* g_pDiskState;
QueueCoordinator::QueueCoordinator()
{
debug("Creating QueueCoordinator");
m_bHasMoreJobs = true;
m_DownloadQueue.clear();
m_ActiveDownloads.clear();
ResetSpeedStat();
m_iAllBytes = 0;
m_tStartServer = 0;
m_tStartDownload = 0;
m_tPausedFrom = 0;
m_bStandBy = true;
YDecoder::Init();
}
QueueCoordinator::~QueueCoordinator()
{
debug("Destroying QueueCoordinator");
// Cleanup
debug("Deleting DownloadQueue");
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
delete *it;
}
m_DownloadQueue.clear();
debug("Deleting ArticleDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
delete *it;
}
m_ActiveDownloads.clear();
YDecoder::Final();
debug("QueueCoordinator destroyed");
}
void QueueCoordinator::Run()
{
debug("Entering QueueCoordinator-loop");
m_mutexDownloadQueue.Lock();
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
{
if (g_pOptions->GetReloadQueue())
{
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
}
else
{
g_pDiskState->DiscardDownloadQueue();
}
}
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
m_mutexDownloadQueue.Unlock();
m_tStartServer = time(NULL);
m_tLastCheck = m_tStartServer;
bool bWasStandBy = true;
bool bArticeDownloadsRunning = false;
int iResetCounter = 0;
while (!IsStopped())
{
if (!g_pOptions->GetPause())
{
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
if (pConnection)
{
// start download for next article
FileInfo* pFileInfo;
ArticleInfo* pArticleInfo;
m_mutexDownloadQueue.Lock();
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
{
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
bArticeDownloadsRunning = true;
}
else
{
g_pServerPool->FreeConnection(pConnection, false);
}
m_mutexDownloadQueue.Unlock();
}
}
else
{
m_mutexDownloadQueue.Lock();
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
m_mutexDownloadQueue.Unlock();
}
bool bStandBy = !bArticeDownloadsRunning;
if (bStandBy ^ bWasStandBy)
{
EnterLeaveStandBy(bStandBy);
bWasStandBy = bStandBy;
}
// sleep longer in StandBy
int iSleepInterval = bStandBy ? 100 : 5;
usleep(iSleepInterval * 1000);
AddSpeedReading(0);
iResetCounter+= iSleepInterval;
if (iResetCounter >= 1000)
{
// this code should not be called too often, once per second is OK
g_pServerPool->CloseUnusedConnections();
ResetHangingDownloads();
iResetCounter = 0;
AdjustStartTime();
}
}
// waiting for downloads
debug("QueueCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
m_mutexDownloadQueue.Lock();
completed = m_ActiveDownloads.size() == 0;
m_mutexDownloadQueue.Unlock();
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("QueueCoordinator: Downloads are completed");
debug("Exiting QueueCoordinator-loop");
}
void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
{
debug("Adding NZBFile to queue");
m_mutexDownloadQueue.Lock();
DownloadQueue tmpDownloadQueue;
tmpDownloadQueue.clear();
DownloadQueue DupeList;
DupeList.clear();
int index1 = 0;
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
index1++;
FileInfo* pFileInfo = *it;
if (g_pOptions->GetDupeCheck())
{
bool dupe = false;
if (IsDupe(pFileInfo))
{
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
dupe = true;
}
int index2 = 0;
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
{
index2++;
FileInfo* pFileInfo2 = *it2;
if (pFileInfo != pFileInfo2 &&
!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
(pFileInfo->GetSize() < pFileInfo2->GetSize() ||
(pFileInfo->GetSize() == pFileInfo2->GetSize() && index2 < index1)))
{
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
dupe = true;
break;
}
}
if (dupe)
{
DupeList.push_back(pFileInfo);
continue;
}
}
if (bAddFirst)
{
tmpDownloadQueue.push_front(pFileInfo);
}
else
{
tmpDownloadQueue.push_back(pFileInfo);
}
}
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
{
if (bAddFirst)
{
m_DownloadQueue.push_front(*it);
}
else
{
m_DownloadQueue.push_back(*it);
}
}
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
{
FileInfo* pFileInfo = *it;
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(pFileInfo);
}
delete pFileInfo;
}
pNZBFile->DetachFileInfos();
Aspect aspect = { eaNZBFileAdded, &m_DownloadQueue, pNZBFile->GetNZBInfo(), NULL };
Notify(&aspect);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
}
bool QueueCoordinator::AddFileToQueue(const char* szFileName, const char* szCategory)
{
// Parse the buffer and make it into a NZBFile
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFileName, szCategory);
// Did file parse correctly?
if (!pNZBFile)
{
return false;
}
// Add NZBFile to Queue
AddNZBFileToQueue(pNZBFile, false);
delete pNZBFile;
return true;
}
/*
* NOTE: see note to "AddSpeedReading"
*/
float QueueCoordinator::CalcCurrentDownloadSpeed()
{
if (m_bStandBy)
{
return 0;
}
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
if (iTimeDiff == 0)
{
return 0;
}
float fSpeed = m_iSpeedTotalBytes / 1024.0f / iTimeDiff;
return fSpeed;
}
/*
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
* but this would results in a big performance loss (the function
* "AddSpeedReading" is called extremly often), so we better agree with calculation
* errors possible because of simultaneuos access from several threads.
* The used algorithm is able to recover after few seconds.
* In any case the calculation errors can not result in fatal system
* errors (segmentation faults).
*/
void QueueCoordinator::AddSpeedReading(int iBytes)
{
int iNowSlot = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
if (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
{
//record bytes in next slot
m_iSpeedBytesIndex++;
if (m_iSpeedBytesIndex >= SPEEDMETER_SLOTS)
{
m_iSpeedBytesIndex = 0;
}
//Adjust counters with outging information.
m_iSpeedTotalBytes -= m_iSpeedBytes[m_iSpeedBytesIndex];
//Note we should really use the start time of the next slot
//but its easier to just use the outgoing slot time. This
//will result in a small error.
m_iSpeedStartTime = m_iSpeedTime[m_iSpeedBytesIndex];
//Now reset.
m_iSpeedBytes[m_iSpeedBytesIndex] = 0;
m_iSpeedTime[m_iSpeedBytesIndex] = iNowSlot;
}
if (m_iSpeedTotalBytes == 0)
{
m_iSpeedStartTime = iNowSlot;
}
m_iSpeedBytes[m_iSpeedBytesIndex] += iBytes;
m_iSpeedTotalBytes += iBytes;
m_iAllBytes += iBytes;
}
void QueueCoordinator::ResetSpeedStat()
{
m_iSpeedStartTime = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
{
m_iSpeedBytes[i] = 0;
m_iSpeedTime[i] = m_iSpeedStartTime;
}
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
}
long long QueueCoordinator::CalcRemainingSize()
{
long long lRemainingSize = 0;
m_mutexDownloadQueue.Lock();
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pFileInfo = *it;
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
{
lRemainingSize += pFileInfo->GetRemainingSize();
}
}
m_mutexDownloadQueue.Unlock();
return lRemainingSize;
}
/*
* NOTE: DownloadQueue must be locked prior to call of this function
* Returns True if Entry was deleted from Queue or False if it was scheduled for Deletion.
* NOTE: "False" does not mean unsuccess; the entry is (or will be) deleted in any case.
*/
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
{
pFileInfo->SetDeleted(true);
bool hasDownloads = false;
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
ArticleDownloader* pArticleDownloader = *it;
if (pArticleDownloader->GetFileInfo() == pFileInfo)
{
hasDownloads = true;
pArticleDownloader->Stop();
}
}
if (!hasDownloads)
{
Aspect aspect = { eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
DeleteFileInfo(pFileInfo, false);
}
return hasDownloads;
}
void QueueCoordinator::Stop()
{
Thread::Stop();
debug("Stopping ArticleDownloads");
m_mutexDownloadQueue.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
(*it)->Stop();
}
m_mutexDownloadQueue.Unlock();
debug("ArticleDownloads are notified");
}
bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo)
{
//debug("QueueCoordinator::GetNextArticle()");
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
pFileInfo = *it;
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
{
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->LoadArticles(pFileInfo);
}
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
{
pArticleInfo = *at;
if (pArticleInfo->GetStatus() == 0)
{
return true;
}
}
}
}
return false;
}
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
{
debug("Starting new ArticleDownloader");
ArticleDownloader* pArticleDownloader = new ArticleDownloader();
pArticleDownloader->SetAutoDestroy(true);
pArticleDownloader->Attach(this);
pArticleDownloader->SetFileInfo(pFileInfo);
pArticleDownloader->SetArticleInfo(pArticleInfo);
pArticleDownloader->SetConnection(pConnection);
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
m_ActiveDownloads.push_back(pArticleDownloader);
pArticleDownloader->Start();
}
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
char name[1024];
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
name[1024-1] = '\0';
pArticleInfo->SetResultFilename(name);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", name);
tmpname[1024-1] = '\0';
pArticleDownloader->SetTempFilename(tmpname);
char szNZBNiceName[1024];
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
name[1024-1] = '\0';
pArticleDownloader->SetInfoName(name);
if (g_pOptions->GetDirectWrite())
{
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
pArticleDownloader->SetOutputFilename(name);
}
}
DownloadQueue* QueueCoordinator::LockQueue()
{
m_mutexDownloadQueue.Lock();
return &m_DownloadQueue;
}
void QueueCoordinator::UnlockQueue()
{
m_mutexDownloadQueue.Unlock();
}
void QueueCoordinator::Update(Subject* Caller, void* Aspect)
{
debug("Notification from ArticleDownloader received");
ArticleDownloader* pArticleDownloader = (ArticleDownloader*) Caller;
if ((pArticleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed) ||
(pArticleDownloader->GetStatus() == ArticleDownloader::adRetry))
{
ArticleCompleted(pArticleDownloader);
}
}
void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
debug("Article downloaded");
FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
bool bPaused = false;
bool fileCompleted = false;
m_mutexDownloadQueue.Lock();
if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
{
pArticleInfo->SetStatus(ArticleInfo::aiFinished);
}
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
{
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
}
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adRetry)
{
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
bPaused = true;
}
if (!bPaused)
{
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
}
if (!pFileInfo->GetFilenameConfirmed() &&
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
pArticleDownloader->GetArticleFilename())
{
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
pFileInfo->SetFilenameConfirmed(true);
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
{
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
fileCompleted = false;
DeleteQueueEntry(pFileInfo);
}
}
bool deleteFileObj = false;
if (pFileInfo->GetDeleted())
{
int cnt = 0;
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
if ((*it)->GetFileInfo() == pFileInfo)
{
cnt++;
}
}
if (cnt == 1)
{
// this was the last Download for a file deleted from queue
deleteFileObj = true;
}
}
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
{
// all jobs done
m_mutexDownloadQueue.Unlock();
pArticleDownloader->CompleteFileParts();
m_mutexDownloadQueue.Lock();
deleteFileObj = true;
}
// delete Download from Queue
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
ArticleDownloader* pa = *it;
if (pa == pArticleDownloader)
{
m_ActiveDownloads.erase(it);
break;
}
}
if (deleteFileObj)
{
bool fileDeleted = pFileInfo->GetDeleted();
// delete File from Queue
pFileInfo->SetDeleted(true);
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
DeleteFileInfo(pFileInfo, fileCompleted);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
}
m_mutexDownloadQueue.Unlock();
}
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
{
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pa = *it;
if (pa == pFileInfo)
{
m_DownloadQueue.erase(it);
break;
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(pFileInfo);
}
if (!bCompleted)
{
// deleting temporary files
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
{
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pa = *it;
if (pa->GetResultFilename())
{
remove(pa->GetResultFilename());
}
}
}
if (g_pOptions->GetDirectWrite())
{
char name[1024];
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
name[1024-1] = '\0';
remove(name);
}
}
delete pFileInfo;
}
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
{
debug("Checking if the file is already queued");
// checking on disk
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
{
return true;
}
// checking in queue
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pQueueEntry = *it;
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
pFileInfo != pQueueEntry)
{
return true;
}
}
return false;
}
void QueueCoordinator::LogDebugInfo()
{
debug("--------------------------------------------");
debug("Dumping debug info to log");
debug("--------------------------------------------");
debug(" QueueCoordinator");
debug(" ----------------");
m_mutexDownloadQueue.Lock();
debug(" Active Downloads: %i", m_ActiveDownloads.size());
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
ArticleDownloader* pArticleDownloader = *it;
pArticleDownloader->LogDebugInfo();
}
m_mutexDownloadQueue.Unlock();
debug("");
g_pServerPool->LogDebugInfo();
}
void QueueCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
{
return;
}
m_mutexDownloadQueue.Lock();
time_t tm = ::time(NULL);
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
ArticleDownloader* pArticleDownloader = *it;
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
{
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
debug("Terminating hanging download %s", pArticleDownloader->GetInfoName());
if (pArticleDownloader->Terminate())
{
error("Terminated hanging download %s", pArticleDownloader->GetInfoName());
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
}
else
{
error("Could not terminate hanging download %s", Util::BaseFileName(pArticleInfo->GetResultFilename()));
}
m_ActiveDownloads.erase(it);
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
delete pArticleDownloader;
it = m_ActiveDownloads.begin();
continue;
}
it++;
}
m_mutexDownloadQueue.Unlock();
}
void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
{
m_mutexStat.Lock();
m_bStandBy = bEnter;
if (bEnter)
{
m_tPausedFrom = time(NULL);
}
else
{
if (m_tStartDownload == 0)
{
m_tStartDownload = time(NULL);
}
else
{
m_tStartDownload += time(NULL) - m_tPausedFrom;
}
m_tPausedFrom = 0;
ResetSpeedStat();
}
m_mutexStat.Unlock();
}
void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy)
{
m_mutexStat.Lock();
if (m_tStartServer > 0)
{
*iUpTimeSec = (int)(time(NULL) - m_tStartServer);
}
else
{
*iUpTimeSec = 0;
}
*bStandBy = m_bStandBy;
if (m_bStandBy)
{
*iDnTimeSec = (int)(m_tPausedFrom - m_tStartDownload);
}
else
{
*iDnTimeSec = (int)(time(NULL) - m_tStartDownload);
}
*iAllBytes = m_iAllBytes;
m_mutexStat.Unlock();
}
/*
* Detects large step changes of system time and adjust statistics.
*/
void QueueCoordinator::AdjustStartTime()
{
time_t m_tCurTime = time(NULL);
time_t tDiff = m_tCurTime - m_tLastCheck;
if (tDiff > 60 || tDiff < 0)
{
m_tStartServer += tDiff + 1; // "1" because the method is called once per second
if (m_tStartDownload != 0)
{
m_tStartDownload += tDiff + 1;
}
}
m_tLastCheck = m_tCurTime;
}
bool QueueCoordinator::SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
{
if (pNZBInfo->GetPostProcess())
{
char szNZBNiceName[1024];
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
error("Could not change category for %s. File in post-process-stage", szNZBNiceName);
return false;
}
char szOldDestDir[1024];
strncpy(szOldDestDir, pNZBInfo->GetDestDir(), 1024);
szOldDestDir[1024-1] = '\0';
pNZBInfo->SetCategory(szCategory);
pNZBInfo->BuildDestDirName();
bool bDirUnchanged = !strcmp(pNZBInfo->GetDestDir(), szOldDestDir);
bool bOK = bDirUnchanged || ArticleDownloader::MoveCompletedFiles(pNZBInfo, szOldDestDir);
return bOK;
}
/*
* NOTE: DownloadQueue must be locked prior to call of this function
*/
bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo)
{
if (pDestNZBInfo->GetPostProcess() || pSrcNZBInfo->GetPostProcess())
{
char szDestNZBNiceName[1024];
pDestNZBInfo->GetNiceNZBName(szDestNZBNiceName, 1024);
char szSrcNZBNiceName[1024];
pSrcNZBInfo->GetNiceNZBName(szSrcNZBNiceName, 1024);
error("Could not merge %s and %s. File in post-process-stage", szDestNZBNiceName, szSrcNZBNiceName);
return false;
}
// set new dest directory, new category and move downloaded files to new dest directory
pSrcNZBInfo->SetFilename(pSrcNZBInfo->GetFilename());
SetQueueEntryNZBCategory(pSrcNZBInfo, pDestNZBInfo->GetCategory());
// reattach file items to new NZBInfo-object
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pSrcNZBInfo)
{
pFileInfo->SetNZBInfo(pDestNZBInfo);
}
}
pDestNZBInfo->SetFileCount(pDestNZBInfo->GetFileCount() + pSrcNZBInfo->GetFileCount());
pDestNZBInfo->SetSize(pDestNZBInfo->GetSize() + pSrcNZBInfo->GetSize());
// reattach completed file items to new NZBInfo-object
for (NZBInfo::Files::iterator it = pSrcNZBInfo->GetCompletedFiles()->begin(); it != pSrcNZBInfo->GetCompletedFiles()->end(); it++)
{
char* szFileName = *it;
pDestNZBInfo->GetCompletedFiles()->push_back(szFileName);
}
pSrcNZBInfo->GetCompletedFiles()->clear();
// concatenate QueuedFilenames using character '|' as separator
int iLen = strlen(pDestNZBInfo->GetQueuedFilename()) + strlen(pSrcNZBInfo->GetQueuedFilename()) + 1;
char* szQueuedFilename = (char*)malloc(iLen);
snprintf(szQueuedFilename, iLen, "%s|%s", pDestNZBInfo->GetQueuedFilename(), pSrcNZBInfo->GetQueuedFilename());
szQueuedFilename[iLen - 1] = '\0';
pDestNZBInfo->SetQueuedFilename(szQueuedFilename);
free(szQueuedFilename);
return true;
}

View File

@@ -1,129 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef QUEUECOORDINATOR_H
#define QUEUECOORDINATOR_H
#include <deque>
#include <list>
#include <time.h>
#ifdef WIN32
#include <sys/timeb.h>
#endif
#include "Thread.h"
#include "NZBFile.h"
#include "ArticleDownloader.h"
#include "DownloadInfo.h"
#include "Observer.h"
#include "QueueEditor.h"
#include "NNTPConnection.h"
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter, public NZBInfoLocker
{
public:
typedef std::list<ArticleDownloader*> ActiveDownloads;
enum EAspectAction
{
eaNZBFileAdded,
eaFileCompleted,
eaFileDeleted
};
struct Aspect
{
EAspectAction eAction;
DownloadQueue* pDownloadQueue;
NZBInfo* pNZBInfo;
FileInfo* pFileInfo;
};
private:
DownloadQueue m_DownloadQueue;
ActiveDownloads m_ActiveDownloads;
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
// statistics
static const int SPEEDMETER_SLOTS = 30;
static const int SPEEDMETER_SLOTSIZE = 1; //Split elapsed time into this number of secs.
int m_iSpeedBytes[SPEEDMETER_SLOTS];
int m_iSpeedTotalBytes;
int m_iSpeedTime[SPEEDMETER_SLOTS];
int m_iSpeedStartTime;
int m_iSpeedBytesIndex;
long long m_iAllBytes;
time_t m_tStartServer;
time_t m_tLastCheck;
time_t m_tStartDownload;
time_t m_tPausedFrom;
bool m_bStandBy;
Mutex m_mutexStat;
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
void AdjustStartTime();
public:
QueueCoordinator();
virtual ~QueueCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* Caller, void* Aspect);
// statistics
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
virtual void AddSpeedReading(int iBytes);
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
// Editing the queue
DownloadQueue* LockQueue();
void UnlockQueue() ;
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
bool AddFileToQueue(const char* szFileName, const char* szCategory);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
virtual void LockNZBInfo(NZBInfo* pNZBInfo) { LockQueue(); }
virtual void UnlockNZBInfo(NZBInfo* pNZBInfo) { UnlockQueue(); }
void LogDebugInfo();
};
#endif

View File

@@ -1,768 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "DownloadInfo.h"
#include "QueueEditor.h"
#include "QueueCoordinator.h"
#include "DiskState.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
const int MAX_ID = 100000000;
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
{
m_pFileInfo = pFileInfo;
m_iOffset = iOffset;
}
QueueEditor::QueueEditor()
{
debug("Creating QueueEditor");
}
QueueEditor::~QueueEditor()
{
debug("Destroying QueueEditor");
}
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetID() == iID)
{
return pFileInfo;
}
}
return NULL;
}
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
{
int iEntry = 0;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (pFileInfo2 == pFileInfo)
{
return iEntry;
}
iEntry ++;
}
return -1;
}
/*
* Set the pause flag of the specific entry in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
{
pFileInfo->SetPaused(bPause);
}
/*
* Removes entry with index iEntry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
}
/*
* Moves entry identified with iID in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
{
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
if (iEntry > -1)
{
int iNewEntry = iEntry + iOffset;
if (iNewEntry < 0)
{
iNewEntry = 0;
}
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
{
iNewEntry = (int)pDownloadQueue->size() - 1;
}
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
{
FileInfo* fi = (*pDownloadQueue)[iEntry];
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
}
}
}
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return EditList(&cIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
return bOK;
}
bool QueueEditor::LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
return InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
if (eAction == eaGroupMoveOffset)
{
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
}
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
{
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
}
else if (eAction == eaGroupMerge)
{
MergeGroups(pDownloadQueue, &cItemList);
}
else
{
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
switch (eAction)
{
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
}
}
return cItemList.size() > 0;
}
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
EEditAction EEditAction, int iOffset)
{
if (EEditAction == eaFileMoveTop)
{
iOffset = -MAX_ID;
}
else if (EEditAction == eaFileMoveBottom)
{
iOffset = MAX_ID;
}
pItemList->reserve(pIDList->size());
if (bSmartOrder && iOffset != 0 &&
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
{
//add IDs to list in order they currently have in download queue
int iLastDestPos = -1;
int iStart, iEnd, iStep;
if (iOffset < 0)
{
iStart = 0;
iEnd = pDownloadQueue->size();
iStep = 1;
}
else
{
iStart = pDownloadQueue->size() - 1;
iEnd = -1;
iStep = -1;
}
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
{
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
int iID = pFileInfo->GetID();
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
{
if (iID == *it)
{
int iWorkOffset = iOffset;
int iDestPos = iIndex + iWorkOffset;
if (iLastDestPos == -1)
{
if (iDestPos < 0)
{
iWorkOffset = -iIndex;
}
else if (iDestPos > int(pDownloadQueue->size()) - 1)
{
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
}
}
else
{
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
{
iWorkOffset = iLastDestPos - iIndex + 1;
}
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
{
iWorkOffset = iLastDestPos - iIndex - 1;
}
}
iLastDestPos = iIndex + iWorkOffset;
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
break;
}
}
}
}
else
{
// check ID range
int iMaxID = 0;
int iMinID = MAX_ID;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
int ID = pFileInfo->GetID();
if (ID > iMaxID)
{
iMaxID = ID;
}
if (ID < iMinID)
{
iMinID = ID;
}
}
//add IDs to list in order they were transmitted in command
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
{
int iID = *it;
if (iMinID <= iID && iID <= iMaxID)
{
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
if (pFileInfo)
{
pItemList->push_back(new EditItem(pFileInfo, iOffset));
}
}
}
}
}
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
{
IDList cIDList;
cIDList.clear();
// collecting files belonging to group
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
}
}
if (eAction == eaGroupMoveOffset)
{
// calculating offset in terms of files
FileList cGroupList;
BuildGroupList(pDownloadQueue, &cGroupList);
unsigned int iNum = 0;
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pGroupInfo = *it;
if (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
break;
}
}
int iFileOffset = 0;
if (iOffset > 0)
{
if (iNum + iOffset >= cGroupList.size() - 1)
{
eAction = eaGroupMoveBottom;
}
else
{
for (unsigned int i = iNum + 2; i < cGroupList.size() && iOffset > 0; i++, iOffset--)
{
iFileOffset += FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
}
}
}
else
{
if (iNum + iOffset <= 0)
{
eAction = eaGroupMoveTop;
}
else
{
for (unsigned int i = iNum; i > 0 && iOffset < 0; i--, iOffset++)
{
iFileOffset -= FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
}
}
}
iOffset = iFileOffset;
}
else if (eAction == eaGroupDelete)
{
pFileInfo->GetNZBInfo()->SetDeleted(true);
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, (EEditAction)0, (EEditAction)0, (EEditAction)0 };
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, NULL);
}
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
{
pGroupList->clear();
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
FileInfo* pGroupInfo = NULL;
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
{
FileInfo* pGroupInfo1 = *itg;
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupList->push_back(pFileInfo);
}
}
}
bool QueueEditor::ItemExists(FileList* pFileList, FileInfo* pFileInfo)
{
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
if (*it == pFileInfo)
{
return true;
}
}
return false;
}
void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset)
{
// Build list of all groups; List contains first file of each group
FileList cGroupList;
BuildGroupList(pDownloadQueue, &cGroupList);
// Find affected groups. It includes groups being moved and groups directly
// above or under of these groups (those order is also changed)
FileList cAffectedGroupList;
cAffectedGroupList.clear();
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eaFileMoveOffset, iOffset);
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
unsigned int iNum = 0;
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pFileInfo = *it;
if (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (!ItemExists(&cAffectedGroupList, pFileInfo))
{
cAffectedGroupList.push_back(pFileInfo);
}
if (iOffset < 0)
{
for (int i = iNum - 1; i >= -iOffset-1; i--)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
cAffectedGroupList.push_back(cGroupList[i]);
}
}
}
if (iOffset > 0)
{
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
cAffectedGroupList.push_back(cGroupList[i]);
}
}
if (iNum + 1 < cGroupList.size())
{
cAffectedGroupList.push_back(cGroupList[iNum + 1]);
}
}
break;
}
}
delete pItem;
}
cGroupList.clear();
// Aligning groups
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
{
FileInfo* pFileInfo = *it;
AlignGroup(pDownloadQueue, pFileInfo);
}
}
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
{
FileInfo* pLastFileInfo = NULL;
unsigned int iLastNum = 0;
unsigned int iNum = 0;
while (iNum < pDownloadQueue->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (pLastFileInfo && iNum - iLastNum > 1)
{
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
iLastNum++;
}
else
{
iLastNum = iNum;
}
pLastFileInfo = pFileInfo;
}
iNum++;
}
}
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
{
while (true)
{
FileList GroupFileList;
GroupFileList.clear();
FileInfo* pFirstFileInfo = NULL;
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
{
EditItem* pItem = *it;
if (!pFirstFileInfo ||
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
{
GroupFileList.push_back(pItem->m_pFileInfo);
if (!pFirstFileInfo)
{
pFirstFileInfo = pItem->m_pFileInfo;
}
delete pItem;
pItemList->erase(it);
it = pItemList->begin();
continue;
}
it++;
}
if (!GroupFileList.empty())
{
PausePars(&GroupFileList, bExtraParsOnly);
}
else
{
break;
}
}
}
/**
* If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files.
* If the parameter "bExtraParsOnly" is set to "true", we use the following strategy:
* At first we find all par-files, which do not have "vol" in their names, then we pause
* all vols and do not affect all just-pars.
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
* and do not affect it, but pause all other pars.
*/
void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
{
debug("QueueEditor: Pausing pars");
FileList Pars, Vols;
Pars.clear();
Vols.clear();
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
FileInfo* pFileInfo = *it;
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (strstr(szLoFileName, ".par2"))
{
if (!bExtraParsOnly)
{
pFileInfo->SetPaused(true);
}
else
{
if (strstr(szLoFileName, ".vol"))
{
Vols.push_back(pFileInfo);
}
else
{
Pars.push_back(pFileInfo);
}
}
}
}
if (bExtraParsOnly)
{
if (!Pars.empty())
{
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPaused(true);
}
}
else
{
// pausing all Vol-files except the smallest one
FileInfo* pSmallest = NULL;
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
{
FileInfo* pFileInfo = *it;
if (!pSmallest)
{
pSmallest = pFileInfo;
}
else if (pSmallest->GetSize() > pFileInfo->GetSize())
{
pSmallest->SetPaused(true);
pSmallest = pFileInfo;
}
else
{
pFileInfo->SetPaused(true);
}
}
}
}
}
void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
{
debug("QueueEditor: setting category '%s' for '%s'", szCategory, Util::BaseFileName(pNZBInfo->GetFilename()));
g_pQueueCoordinator->SetQueueEntryNZBCategory(pNZBInfo, szCategory);
}
/**
* Check if deletion of already downloaded files is possible (when nzb id deleted from queue).
* The deletion is most always possible, except the case if all remaining files in queue
* (belonging to this nzb-file) are PARS.
*/
bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (!strstr(szLoFileName, ".par2"))
{
// non-par file found
return true;
}
}
return false;
}
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return;
}
EditItem* pDestItem = pItemList->front();
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
{
EditItem* pItem = *it;
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
{
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo());
}
delete pItem;
}
// align group ("AlignGroup" needs the first file item as parameter)
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pDestItem->m_pFileInfo->GetNZBInfo())
{
AlignGroup(pDownloadQueue, pFileInfo);
break;
}
}
delete pDestItem;
}
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
{
debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename()));
char* szStr = strdup(szParamString);
char* szValue = strchr(szStr, '=');
if (szValue)
{
*szValue = '\0';
szValue++;
pNZBInfo->SetParameter(szStr, szValue);
}
else
{
error("Could not set nzb parameter for %s: invalid argument: %s", Util::BaseFileName(pNZBInfo->GetFilename()), szParamString);
}
free(szStr);
}

View File

@@ -1,106 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef QUEUEEDITOR_H
#define QUEUEEDITOR_H
#include <vector>
#include "DownloadInfo.h"
class QueueEditor
{
public:
typedef std::vector<int> IDList;
enum EEditAction
{
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eaFileMoveTop,
eaFileMoveBottom,
eaFilePause,
eaFileResume,
eaFileDelete,
eaFilePauseAllPars,
eaFilePauseExtraPars,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
eaGroupPause,
eaGroupResume,
eaGroupDelete,
eaGroupPauseAllPars,
eaGroupPauseExtraPars,
eaGroupSetCategory,
eaGroupMerge,
eaGroupSetParameter
};
private:
class EditItem
{
public:
int m_iOffset;
FileInfo* m_pFileInfo;
EditItem(FileInfo* pFileInfo, int iOffset);
};
typedef std::vector<EditItem*> ItemList;
typedef std::vector<FileInfo*> FileList;
private:
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
void PausePars(FileList* pFileList, bool bExtraParsOnly);
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
void DeleteEntry(FileInfo* pFileInfo);
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
public:
QueueEditor();
~QueueEditor();
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
};
#endif

327
README
View File

@@ -2,6 +2,10 @@
NZBGet ReadMe
=====================================
This is a short documentation. For more information please
visit NZBGet home page at
http://nzbget.net
Contents
--------
1. About NZBGet
@@ -29,6 +33,9 @@ In server/client mode NZBGet runs as server in background.
Then you use client to send requests to server. The sample requests
are: download nzb-file, list files in queue, etc.
There is also a built-in web-interface. The server has RPC-support
and can be controlled from third party applications too.
Standalone-tool, server and client are all contained in only one
executable file "nzbget". The mode in which the program works
depends on command-line parameters passed to the program.
@@ -37,26 +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.
The current version (0.6.0) should run at least on:
- Linux Debian 4.0 on x86;
- Linux with uClibc and BusyBox on MIPSEL and ARM;
- FreeBSD 7.0 on x86;
- OpenSolaris 2008.11 on x86;
- Mac OS X;
- Windows XP SP2 on x86.
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".
@@ -65,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:
@@ -79,17 +76,16 @@ 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://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for encrypted connections (TLS/SSL):
- GnuTLS (http://www.gnu.org/software/gnutls)
or
- OpenSSL (http://www.openssl.org)
or
- GnuTLS (http://www.gnu.org/software/gnutls)
All these libraries are included in modern Linux distributions and
- for gzip support in web-server and web-client (enabled by default):
- zlib (http://www.zlib.net)
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
@@ -99,24 +95,41 @@ download the libraries at the given URLs and compile them (see hints below).
4. Installation on POSIX
=====================================
Well, the usual stuff:
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
- untar the nzbget-source via
tar -zxf nzbget-VERSION.tar.gz
- change into nzbget-directory via
cd nzbget-VERSION
- configure it via
./configure
(maybe you have to tell configure, where to find some libraries.
./configure --help is your friend!)
also see "Configure-options" later.)
./configure --help is your friend!
also see "Configure-options" later)
- in a case you don't have root access or want to install the program
in your home directory use the configure parameter --prefix, e. g.:
./configure --prefix ~/usr
- compile it via
make
- become root via
- to install system wide become root via:
su
- install it via
- install it via:
make install
- install configuration files into <prefix>/etc via:
make install-conf
(you can skip this step if you intend to store configuration
files in a non-standard location)
Configure-options
-----------------
You may run configure with additional arguments:
@@ -125,13 +138,16 @@ 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=(GnuTLS, OpenSSL) - to select which TLS/SSL library
--with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
should be used for encrypted server connections.
--disable-tls - to make without TLS/SSL support. Use this option if
you can not neither GnuTLS nor OpenSSL.
you can not neither OpenSSL nor GnuTLS.
--disable-gzip - to make without gzip support. Use this option
if you can not use zlib.
--enable-debug - to build in debug-mode, if you want to see and log
debug-messages.
@@ -139,37 +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.
If you use nzbget on a very slow computer like NAS-device, it may be good to
limit the time allowed for par-repair (option "ParTimeLimit" in nzbget
configuration file). This feature requires a patched version of libpar2.
To compile that version download the original source code of libpar2
(version 0.2) and apply patches "libpar2-0.2-bugfixes.patch" and
"libpar2-0.2-cancel.patch", provided with nzbget:
cd libpar2-0.2
cp ~/nzbget/libpar2-0.2-*.patch .
patch < libpar2-0.2-bugfixes.patch
patch < libpar2-0.2-cancel.patch
./configure
make
make install
If you are not able to use libpar2 or libsigc++ or do not want them you can
make nzbget without support for par-check using option "--disable-parcheck":
In a case errors occur during this process the inclusion of par2-module
can be disabled using configure option "--disable-parcheck":
./configure --disable-parcheck
@@ -177,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":
@@ -190,20 +182,20 @@ make the program without support for curses using option "--disable-curses":
Optional package: TLS
-------------------------
To enable encrypted server connections (TLS/SSL) you need to build the program
with TLS/SSL support. NZBGet can use two libraries: GnuTLS or OpenSSL.
with TLS/SSL support. NZBGet can use two libraries: OpenSSL or GnuTLS.
Configure-script checks which library is installed and use it. If both are
avialable it gives the precedence to GnuTLS. You may override that with
the option --with-tlslib=(GnuTLS, OpenSSL). For example to build whith OpenSSL:
available it gives the precedence to OpenSSL. You may override that with
the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
./configure --with-tlslib=OpenSSL
./configure --with-tlslib= GnuTLS
Following configure-parameters may be usefull:
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":
@@ -214,48 +206,44 @@ TLS/SSL support using option "--disable-tls":
5. Compiling on Windows
=====================================
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://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:
NZBGet is developed using MS Visual Studio 2015 (Community Edition). The project
file is provided.
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)
Download a precompiled version of GnuTLS from http://josefsson.org/gnutls4win
and create lib-file as described there in section "Using the GnuTLS DLL from
your Visual Studio program".
After libsigc++ and libpar2 are compiled in static libraries (.lib), the
library for GnuTLS is created and include- and libraries-paths are configured
in MS Visual C++ 2005 you should be able to compile NZBGet.
Also required are:
- Regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
- Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm)
=====================================
6. Configuration
=====================================
NZBGet needs a configuration-file to work properly.
NZBGet needs a configuration file.
You need to set at least the option "MAINDIR" and one newsserver in
configuration file. Have a look at the example in nzbget.conf.example,
it has comments on how to use each option.
An example configuration file is provided in "nzbget.conf", which
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
system configuration and configure options - typically "/usr/local",
"/usr" or "/opt"). The installer adjusts the file according to your
system paths. If you have performed the installation step
"make install-conf" this file is already copied to "<prefix>/etc" and
NZBGet finds it automatically. If you install the program manually
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
to one of the locations listed below.
Open the file in a text editor and modify it accodring to your needs.
You need to set at least the option "MAINDIR" and one news server in
configuration file. The file has comments on how to use each option.
The program looks for configuration file in following standard
locations (in this order):
On POSIX systems:
<EXE-DIR>/nzbget.conf
~/.nzbget
/etc/nzbget.conf
/usr/etc/nzbget.conf
@@ -279,6 +267,12 @@ options via command-line.
NZBGet can be used in either standalone mode which downloads a single file
or as a server which is able to queue up numerous download requests.
TIP for Windows users: NZBGet is controlled via various command line
parameters. For easier using there is a simple shell script included
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
be running a command shell with PATH adjusted to find NZBGet executable.
Then you can type all commands without full path to nzbget.exe.
Standalone mode:
----------------
@@ -306,10 +300,16 @@ To stop server use:
nzbget -Q
Depending on which frontend has been selected in the nzbget.conf file
(option "outputmode") the server should display a message that
it is ready to receive download requests (this applies only to console
mode, not to daemon mode).
TIP for POSIX users: with included script "nzbgetd" you can use standard
commands to control daemon:
nzbgetd start
nzbgetd stop
etc.
When NZBGet is started in console server mode it displays a message that
it is ready to receive download requests. In daemon mode it doesn't print any
messages to console since it runs in background.
When the server is running it is possible to queue up downloads. This can be
done either in terminal with "nzbget -A <nzb-file>" or by uploading
@@ -336,9 +336,18 @@ It prints something like:
[1] nzbname\filename1.rar (50.00 MB)
[2] nzbname\filename1.r01 (50.00 MB)
[3] another-nzb\filename3.r01 (100.00 MB)
[4] another-nzb\filename3.r02 (100.00 MB)
The numbers in square braces are ID's of files in queue. They can be used
in edit-command. For example to move file with ID 2 to the top of queue:
This is the list of individual files listed within nzb-file. To print
the list of nzb-files (without content) add G-modifier to the list command:
[1] nzbname (4.56 GB)
[2] another-nzb (4.20 GB)
The numbers in square braces are ID's of files or groups in queue.
They can be used in edit-command. For example to move file with
ID 2 to the top of queue:
nzbget -E T 2
@@ -351,8 +360,8 @@ or to delete files from queue:
nzbget -E D 3 10-15 20-21 16
The edit-command has also a group-mode which affects all files from the
same nzb-request. You need to pass one ID of any file in the group. For
example to delete all files from the first nzb-request:
same nzb-file. You need to pass an ID of the group. For example to delete
the whole group 1:
nzbget -E G D 1
@@ -371,7 +380,7 @@ Running client & server on seperate machines:
Since nzbget communicates via TCP/IP it's possible to have a server running on
one computer and adding downloads via a client on another computer.
Do this by setting the "serverip" option in the nzbget.conf file to point to the
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
IP of the server (default is localhost which means client and server runs on
same computer)
@@ -390,26 +399,88 @@ nzbget-client-commands in this terminal.
Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-process-script,
defined in configuration file. See example configuration file for the
description of parameters passed to the script.
After the download of nzb-file is completed nzbget can call post-processing
scripts, defined in configuration file.
An example script for unraring of downloaded files is provided in file
postprocess-example.sh. That example is for POSIX systems (not for Windows).
Example post-processing scripts are provided in directory "scripts".
To use the scripts copy them into your local directory and set options
<ScriptDir>, <PostScript> and <ScriptOrder>.
For information on writing your own post-processing scripts please
visit NZBGet web site.
Web-interface
-------------
NZBGet has a built-in web-server providing the access to the program
functions via web-interface.
To activate web-interface set the option "WebDir" to the path with
web-interface files. If you install using "make install-conf" as
described above the option is set automatically. If you install using
binary files you should check if the option is set correctly.
To access web-interface from your web-browser use the server address
and port defined in NZBGet configuration file in options "ControlIP" and
"ControlPort". For example:
http://localhost:6789/
For login credentials type username and the password defined by
options "ControlUsername" (default "nzbget") and "ControlPassword"
(default "tegbzn6789").
In a case your browser forget credentials, to prevent typing them each
time, there is a workaround - use URL in the form:
http://localhost:6789/username:password/
Please note, that in this case the password is saved in a bookmark or in
browser history in plain text and is easy to find by persons having
access to your computer.
=====================================
8. Authors
=====================================
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
Up to version 0.2.3 it was developed and maintained by Bo Cordes Petersen
(placebodk@users.sourceforge.net).
Beginning at version 0.3.0 the program is being developed by Andrei Prygounkov
NZBGet is developed and maintained by Andrey Prygunkov
(hugbug@users.sourceforge.net).
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).
The original project was initially created by Sven Henkel
(sidddy@users.sourceforge.net) in 2004 and later developed by
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
Since then the program has been completely rewritten.
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
@@ -424,21 +495,13 @@ The complete content of license is provided in file COPYING.
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
Binary distribution for Windows contains code from the following libraries:
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- GnuTLS (http://www.gnu.org/software/gnutls)
libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
=====================================
10. Contact
=====================================
If you encounter any problem, feel free to use forums on
If you encounter any problem, feel free to use the forum
sourceforge.net/projects/nzbget
nzbget.net/forum
or contact me at

19
README.md Normal file
View File

@@ -0,0 +1,19 @@
# NZBGet #
[![License](https://img.shields.io/badge/license-GPL-blue.svg)](http://www.gnu.org/licenses/)
[![Build Status](https://img.shields.io/travis/nzbget/nzbget/develop.svg)](https://travis-ci.org/nzbget/nzbget)
[![Code Quality: Cpp](https://img.shields.io/lgtm/grade/cpp/g/nzbget/nzbget.svg?label=code%20quality:%20c%2b%2b)](https://lgtm.com/projects/g/nzbget/nzbget/context:cpp)
[![Code Quality: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/nzbget/nzbget.svg?label=code%20quality:%20js)](https://lgtm.com/projects/g/nzbget/nzbget/context:javascript)
[![Total Alerts](https://img.shields.io/lgtm/alerts/g/nzbget/nzbget.svg)](https://lgtm.com/projects/g/nzbget/nzbget/alerts)
[![Total downloads](https://img.shields.io/github/downloads/nzbget/nzbget/total.svg)](https://github.com/nzbget/nzbget/releases)
[![Downloads (latest release)](https://img.shields.io/github/downloads/nzbget/nzbget/latest/total.svg?label=latest%20release)](https://github.com/nzbget/nzbget/releases/latest)
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 performance and efficiency.
NZBGet can run on almost any device - classic PC, NAS, media player, SAT-receiver, WLAN-router, etc.
The download area provides precompiled binaries for Windows, macOS, Linux (compatible with
many CPUs and platform variants), FreeBSD and Android. For other platforms
the program can be compiled from sources.

View File

@@ -1,948 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifdef WIN32
#include <windows.h>
#else
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include <stdarg.h>
#include "nzbget.h"
#include "RemoteClient.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
RemoteClient::RemoteClient()
{
m_pConnection = NULL;
m_pNetAddress = NULL;
m_bVerbose = true;
/*
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
printf("sizeof(SNZBListResponseFileEntry)=%i\n", sizeof(SNZBListResponseFileEntry));
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
printf("sizeof(SNZBLogResponse)=%i\n", sizeof(SNZBLogResponse));
printf("sizeof(SNZBLogResponseEntry)=%i\n", sizeof(SNZBLogResponseEntry));
printf("sizeof(SNZBPauseUnpauseRequest)=%i\n", sizeof(SNZBPauseUnpauseRequest));
printf("sizeof(SNZBSetDownloadRateRequest)=%i\n", sizeof(SNZBSetDownloadRateRequest));
printf("sizeof(SNZBEditQueueRequest)=%i\n", sizeof(SNZBEditQueueRequest));
printf("sizeof(SNZBDumpDebugRequest)=%i\n", sizeof(SNZBDumpDebugRequest));
*/
}
RemoteClient::~RemoteClient()
{
if (m_pConnection)
{
delete m_pConnection;
}
if (m_pNetAddress)
{
delete m_pNetAddress;
}
}
void RemoteClient::printf(const char * msg,...)
{
if (m_bVerbose)
{
va_list ap;
va_start(ap, msg);
::vprintf(msg, ap);
va_end(ap);
}
}
void RemoteClient::perror(const char * msg)
{
if (m_bVerbose)
{
::perror(msg);
}
}
bool RemoteClient::InitConnection()
{
// Create a connection to the server
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = new Connection(m_pNetAddress);
bool OK = m_pConnection->Connect();
if (!OK)
{
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
}
return OK;
}
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
{
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
bool RemoteClient::ReceiveBoolResponse()
{
printf("Request sent\n");
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
SNZBDownloadResponse BoolResponse;
memset(&BoolResponse, 0, sizeof(BoolResponse));
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
if (iResponseLen != sizeof(BoolResponse) ||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
return false;
}
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
char* buf = (char*)malloc(iTextLen);
iResponseLen = m_pConnection->Recv(buf, iTextLen);
if (iResponseLen != iTextLen)
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
return false;
}
printf("server returned: %s\n", buf);
free(buf);
return ntohl(BoolResponse.m_bSuccess);
}
/*
* Sends a message to the running nzbget process.
*/
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst)
{
// Read the file into the buffer
char* szBuffer = NULL;
int iLength = 0;
if (!Util::LoadFileIntoBuffer(szFilename, &szBuffer, &iLength))
{
printf("Could not load file %s\n", szFilename);
return false;
}
bool OK = InitConnection();
if (OK)
{
SNZBDownloadRequest DownloadRequest;
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadRequest.m_szCategory[0] = '\0';
if (szCategory)
{
strncpy(DownloadRequest.m_szCategory, szCategory, NZBREQUESTFILENAMESIZE - 1);
}
DownloadRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
{
perror("m_pConnection->Send");
OK = false;
}
else
{
m_pConnection->Send(szBuffer, iLength);
OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
}
}
// Cleanup
if (szBuffer)
{
free(szBuffer);
}
return OK;
}
void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue)
{
if (ntohl(pListResponse->m_iTrailingDataLength) > 0)
{
NZBQueue cNZBQueue;
const char* pBufPtr = pTrailingData;
// read nzb entries
for (unsigned int i = 0; i < ntohl(pListResponse->m_iNrTrailingNZBEntries); i++)
{
SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) pBufPtr;
const char* szFileName = pBufPtr + sizeof(SNZBListResponseNZBEntry);
const char* szDestDir = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen);
const char* szCategory = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
const char* m_szQueuedFilename = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen);
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo)));
pNZBInfo->SetFilename(szFileName);
pNZBInfo->SetDestDir(szDestDir);
pNZBInfo->SetCategory(szCategory);
pNZBInfo->SetQueuedFilename(m_szQueuedFilename);
cNZBQueue.push_back(pNZBInfo);
pBufPtr += sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen) +
ntohl(pListAnswer->m_iQueuedFilenameLen);
}
//read ppp entries
for (unsigned int i = 0; i < ntohl(pListResponse->m_iNrTrailingPPPEntries); i++)
{
SNZBListResponsePPPEntry* pListAnswer = (SNZBListResponsePPPEntry*) pBufPtr;
const char* szName = pBufPtr + sizeof(SNZBListResponsePPPEntry);
const char* szValue = pBufPtr + sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen);
NZBInfo* pNZBInfo = cNZBQueue.at(ntohl(pListAnswer->m_iNZBIndex) - 1);
pNZBInfo->SetParameter(szName, szValue);
pBufPtr += sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen) +
ntohl(pListAnswer->m_iValueLen);
}
//read file entries
for (unsigned int i = 0; i < ntohl(pListResponse->m_iNrTrailingFileEntries); i++)
{
SNZBListResponseFileEntry* pListAnswer = (SNZBListResponseFileEntry*) pBufPtr;
const char* szSubject = pBufPtr + sizeof(SNZBListResponseFileEntry);
const char* szFileName = pBufPtr + sizeof(SNZBListResponseFileEntry) + ntohl(pListAnswer->m_iSubjectLen);
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
pFileInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
pFileInfo->SetRemainingSize(Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
pFileInfo->SetSubject(szSubject);
pFileInfo->SetFilename(szFileName);
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
NZBInfo* pNZBInfo = cNZBQueue.at(ntohl(pListAnswer->m_iNZBIndex) - 1);
pFileInfo->SetNZBInfo(pNZBInfo);
pDownloadQueue->push_back(pFileInfo);
pBufPtr += sizeof(SNZBListResponseFileEntry) + ntohl(pListAnswer->m_iSubjectLen) +
ntohl(pListAnswer->m_iFilenameLen);
}
}
}
bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
{
if (!InitConnection()) return false;
SNZBListRequest ListRequest;
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = htonl(true);
ListRequest.m_bServerState = htonl(true);
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
}
printf("Request sent\n");
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
return false;
}
char* pBuf = NULL;
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
m_pConnection->Disconnect();
if (bFiles)
{
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
{
printf("Server has no files queued for download\n");
}
else
{
printf("Queue List\n");
printf("-----------------------------------\n");
DownloadQueue cRemoteQueue;
BuildFileList(&ListResponse, pBuf, &cRemoteQueue);
long long lRemaining = 0;
long long lPaused = 0;
for (DownloadQueue::iterator it = cRemoteQueue.begin(); it != cRemoteQueue.end(); it++)
{
FileInfo* pFileInfo = *it;
char szCompleted[100];
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
{
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())), "%");
}
char szStatus[100];
if (pFileInfo->GetPaused())
{
sprintf(szStatus, " (paused)");
lPaused += pFileInfo->GetRemainingSize();
}
else
{
szStatus[0] = '\0';
lRemaining += pFileInfo->GetRemainingSize();
}
char szNZBNiceName[1024];
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
printf("[%i] %s%c%s (%.2f MB%s)%s\n", pFileInfo->GetID(), szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0), szCompleted, szStatus);
delete pFileInfo;
}
printf("-----------------------------------\n");
printf("Files: %i\n", cRemoteQueue.size());
if (lPaused > 0)
{
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0),
(float)(Util::Int64ToFloat(lPaused) / 1024.0 / 1024.0));
}
else
{
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
}
}
}
if (bGroups)
{
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
{
printf("Server has no files queued for download\n");
}
else
{
printf("Queue List\n");
printf("-----------------------------------\n");
DownloadQueue cRemoteQueue;
BuildFileList(&ListResponse, pBuf, &cRemoteQueue);
GroupQueue cGroupQueue;
GroupInfo::BuildGroups(&cRemoteQueue, &cGroupQueue);
long long lRemaining = 0;
long long lPaused = 0;
for (GroupQueue::iterator it = cGroupQueue.begin(); it != cGroupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
long long lUnpausedRemainingSize = pGroupInfo->GetRemainingSize() - pGroupInfo->GetPausedSize();
lRemaining += lUnpausedRemainingSize;
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
char szPaused[20];
szPaused[0] = '\0';
if (pGroupInfo->GetPausedSize() > 0)
{
char szPausedSize[20];
Util::FormatFileSize(szPausedSize, sizeof(szPausedSize), pGroupInfo->GetPausedSize());
sprintf(szPaused, " + %s paused", szPausedSize);
lPaused += pGroupInfo->GetPausedSize();
}
char szNZBNiceName[1024];
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
char szCategory[1024];
szCategory[0] = '\0';
if (pGroupInfo->GetNZBInfo()->GetCategory() && strlen(pGroupInfo->GetNZBInfo()->GetCategory()) > 0)
{
sprintf(szCategory, " (%s)", pGroupInfo->GetNZBInfo()->GetCategory());
}
char szParameters[1024];
szParameters[0] = '\0';
for (NZBParameterList::iterator it = pGroupInfo->GetNZBInfo()->GetParameters()->begin(); it != pGroupInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
if (szParameters[0] == '\0')
{
strncat(szParameters, " (", 1024);
}
else
{
strncat(szParameters, ", ", 1024);
}
NZBParameter* pNZBParameter = *it;
strncat(szParameters, pNZBParameter->GetName(), 1024);
strncat(szParameters, "=", 1024);
strncat(szParameters, pNZBParameter->GetValue(), 1024);
}
if (szParameters[0] != '\0')
{
strncat(szParameters, ")", 1024);
}
printf("[%i-%i] %s (%i file%s, %s%s)%s%s\n", pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), szNZBNiceName,
pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining,
szPaused, szCategory, szParameters);
delete pGroupInfo;
}
for (DownloadQueue::iterator it = cRemoteQueue.begin(); it != cRemoteQueue.end(); it++)
{
delete *it;
}
printf("-----------------------------------\n");
printf("Groups: %i\n", cGroupQueue.size());
printf("Files: %i\n", cRemoteQueue.size());
if (lPaused > 0)
{
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0),
(float)(Util::Int64ToFloat(lPaused) / 1024.0 / 1024.0));
}
else
{
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
}
}
}
free(pBuf);
if (!bFiles && !bGroups)
{
long long lRemaining = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
}
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
long long iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
float fAverageSpeed = Util::Int64ToFloat(ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0);
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
{
printf("Speed limit: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadLimit) / 1024.0));
}
int sec = ntohl(ListResponse.m_iUpTimeSec);
int h = sec / 3600;
int m = (sec % 3600) / 60;
int s = sec % 60;
printf("Up time: %.2d:%.2d:%.2d\n", h, m, s);
sec = ntohl(ListResponse.m_iDownloadTimeSec);
h = sec / 3600;
m = (sec % 3600) / 60;
s = sec % 60;
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
printf("Downloaded: %.2f MB\n", (float)(Util::Int64ToFloat(iAllBytes) / 1024.0 / 1024.0));
printf("Threads running: %i\n", ntohl(ListResponse.m_iThreadCount));
if (ntohl(ListResponse.m_iPostJobCount) > 0)
{
printf("Post-jobs: %i\n", (int)ntohl(ListResponse.m_iPostJobCount));
}
if (ntohl(ListResponse.m_bServerStandBy))
{
printf("Server state: Stand-By\n");
}
else
{
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
}
return true;
}
bool RemoteClient::RequestServerLog(int iLines)
{
if (!InitConnection()) return false;
SNZBLogRequest LogRequest;
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = htonl(iLines);
LogRequest.m_iIDFrom = 0;
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
}
printf("Request sent\n");
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
return false;
}
char* pBuf = NULL;
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
m_pConnection->Disconnect();
if (LogResponse.m_iTrailingDataLength == 0)
{
printf("Log is empty\n");
}
else
{
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
printf("-----------------------------------\n");
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
{
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
switch (ntohl(pLogAnswer->m_iKind))
{
case Message::mkDebug:
printf("[DEBUG] %s\n", szText);
break;
case Message::mkError:
printf("[ERROR] %s\n", szText);
break;
case Message::mkWarning:
printf("[WARNING] %s\n", szText);
break;
case Message::mkInfo:
printf("[INFO] %s\n", szText);
break;
case Message::mkDetail:
printf("[DETAIL] %s\n", szText);
break;
}
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
}
printf("-----------------------------------\n");
free(pBuf);
}
return true;
}
bool RemoteClient::RequestServerPauseUnpause(bool bPause)
{
if (!InitConnection()) return false;
SNZBPauseUnpauseRequest PauseUnpauseRequest;
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
PauseUnpauseRequest.m_bPause = htonl(bPause);
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
return false;
}
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
{
if (!InitConnection()) return false;
SNZBSetDownloadRateRequest SetDownloadRateRequest;
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
return false;
}
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerDumpDebug()
{
if (!InitConnection()) return false;
SNZBDumpDebugRequest DumpDebugInfo;
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
return false;
}
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder)
{
if (iIDCount <= 0 || pIDList == NULL)
{
printf("File(s) not specified\n");
return false;
}
if (!InitConnection()) return false;
int iTextLen = szText ? strlen(szText) + 1 : 0;
// align size to 4-bytes, needed by ARM-processor (and may be others)
iTextLen += iTextLen % 4 > 0 ? 4 - iTextLen % 4 : 0;
int iLength = sizeof(int32_t) * iIDCount + iTextLen;
SNZBEditQueueRequest EditQueueRequest;
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
EditQueueRequest.m_iAction = htonl(iAction);
EditQueueRequest.m_iOffset = htonl((int)iOffset);
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
EditQueueRequest.m_iTextLen = htonl(iTextLen);
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
char* pTrailingData = (char*)malloc(iLength);
if (iTextLen > 0)
{
strcpy(pTrailingData, szText);
}
int32_t* pIDs = (int32_t*)(pTrailingData + iTextLen);
for (int i = 0; i < iIDCount; i++)
{
pIDs[i] = htonl(pIDList[i]);
}
bool OK = false;
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
{
perror("m_pConnection->Send");
}
else
{
m_pConnection->Send(pTrailingData, iLength);
OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
}
free(pTrailingData);
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerShutdown()
{
if (!InitConnection()) return false;
SNZBShutdownRequest ShutdownRequest;
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerVersion()
{
if (!InitConnection()) return false;
SNZBVersionRequest VersionRequest;
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestPostQueue()
{
if (!InitConnection()) return false;
SNZBPostQueueRequest PostQueueRequest;
InitMessageBase(&PostQueueRequest.m_MessageBase, eRemoteRequestPostQueue, sizeof(PostQueueRequest));
if (m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
}
printf("Request sent\n");
// Now listen for the returned list
SNZBPostQueueResponse PostQueueResponse;
int iResponseLen = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
if (iResponseLen != sizeof(PostQueueResponse) ||
(int)ntohl(PostQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(PostQueueResponse.m_MessageBase.m_iStructSize) != sizeof(PostQueueResponse))
{
if (iResponseLen < 0)
{
printf("No response received (timeout)\n");
}
else
{
printf("Invalid response received: either not nzbget-server or wrong server version\n");
}
return false;
}
char* pBuf = NULL;
if (ntohl(PostQueueResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(PostQueueResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
m_pConnection->Disconnect();
if (ntohl(PostQueueResponse.m_iTrailingDataLength) == 0)
{
printf("Server has no files queued for post-processing\n");
}
else
{
printf("Post-Processing List\n");
printf("-----------------------------------\n");
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(PostQueueResponse.m_iNrTrailingEntries); i++)
{
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) pBufPtr;
int iStageProgress = ntohl(pPostQueueAnswer->m_iStageProgress);
static const int EXECUTING_SCRIPT = 5;
char szCompleted[100];
szCompleted[0] = '\0';
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
{
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
}
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
}
free(pBuf);
printf("-----------------------------------\n");
}
return true;
}
bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
{
if (!InitConnection()) return false;
SNZBWriteLogRequest WriteLogRequest;
InitMessageBase(&WriteLogRequest.m_MessageBase, eRemoteRequestWriteLog, sizeof(WriteLogRequest));
WriteLogRequest.m_iKind = htonl(iKind);
int iLength = strlen(szText) + 1;
WriteLogRequest.m_iTrailingDataLength = htonl(iLength);
if (m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
}
m_pConnection->Send(szText, iLength);
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestScan()
{
if (!InitConnection()) return false;
SNZBScanRequest ScanRequest;
InitMessageBase(&ScanRequest.m_MessageBase, eRemoteRequestScan, sizeof(ScanRequest));
bool OK = m_pConnection->Send((char*)(&ScanRequest), sizeof(ScanRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}

View File

@@ -1,67 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef REMOTECLIENT_H
#define REMOTECLIENT_H
#include "Options.h"
#include "MessageBase.h"
#include "Connection.h"
#include "DownloadInfo.h"
class RemoteClient
{
private:
Connection* m_pConnection;
NetAddress* m_pNetAddress;
bool m_bVerbose;
bool InitConnection();
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
bool ReceiveBoolResponse();
void printf(const char* msg, ...);
void perror(const char* msg);
public:
RemoteClient();
~RemoteClient();
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst);
bool RequestServerList(bool bFiles, bool bGroups);
bool RequestServerPauseUnpause(bool bPause);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(int iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder);
bool RequestServerLog(int iLines);
bool RequestServerShutdown();
bool RequestServerVersion();
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
bool RequestScan();
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
};
#endif

View File

@@ -1,226 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
extern Options* g_pOptions;
//*****************************************************************
// RemoteServer
RemoteServer::RemoteServer()
{
debug("Creating RemoteServer");
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = NULL;
}
RemoteServer::~RemoteServer()
{
debug("Destroying RemoteServer");
if (m_pConnection)
{
delete m_pConnection;
}
delete m_pNetAddress;
}
void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
while (!IsStopped())
{
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind() == 0;
}
// Accept connections and store the "new" socket value
SOCKET iSocket = INVALID_SOCKET;
if (bBind)
{
iSocket = m_pConnection->Accept();
}
if (!bBind || iSocket == INVALID_SOCKET)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
{
break;
}
usleep(500 * 1000);
delete m_pConnection;
m_pConnection = NULL;
continue;
}
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetSocket(iSocket);
commandThread->Start();
}
if (m_pConnection)
{
m_pConnection->Disconnect();
}
debug("Exiting RemoteServer-loop");
}
void RemoteServer::Stop()
{
Thread::Stop();
if (m_pConnection)
{
m_pConnection->SetSuppressErrors(true);
m_pConnection->Cancel();
#ifdef WIN32
m_pConnection->Disconnect();
#endif
}
}
//*****************************************************************
// RequestProcessor
void RequestProcessor::Run()
{
// Read the first 4 bytes to determine request type
bool bOK = false;
int iSignature = 0;
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
if (iBytesReceived < 0)
{
return;
}
// Info - connection received
#ifdef WIN32
char* ip = NULL;
#else
char ip[20];
#endif
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
ip = inet_ntoa(PeerName.sin_addr);
#else
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
#endif
}
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
{
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
{
// XML-RPC or JSON-RPC request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
eHttpMethod = XmlRpcProcessor::hmPost;
szUrl++;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
{
eProtocol = XmlRpcProcessor::rpXmlRpc;
}
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
{
eProtocol = XmlRpcProcessor::rpJsonRpc;
}
if (eProtocol != XmlRpcProcessor::rpUndefined)
{
XmlRpcProcessor processor;
processor.SetConnection(&con);
processor.SetClientIP(ip);
processor.SetProtocol(eProtocol);
processor.SetHttpMethod(eHttpMethod);
processor.SetUrl(szUrl);
processor.Execute();
bOK = true;
}
}
}
if (!bOK)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
}
closesocket(m_iSocket);
}

View File

@@ -1,57 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef REMOTESERVER_H
#define REMOTESERVER_H
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
class RemoteServer : public Thread
{
private:
NetAddress* m_pNetAddress;
Connection* m_pConnection;
public:
RemoteServer();
~RemoteServer();
virtual void Run();
virtual void Stop();
};
class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
public:
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
#endif

View File

@@ -1,236 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "Scheduler.h"
#include "ScriptController.h"
#include "Options.h"
#include "Log.h"
extern Options* g_pOptions;
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess)
{
m_iHours = iHours;
m_iMinutes = iMinutes;
m_iWeekDaysBits = iWeekDaysBits;
m_eCommand = eCommand;
m_iDownloadRate = iDownloadRate;
m_szProcess = NULL;
if (szProcess)
{
m_szProcess = strdup(szProcess);
}
m_tLastExecuted = 0;
}
Scheduler::Task::~Task()
{
if (m_szProcess)
{
free(m_szProcess);
}
}
Scheduler::Scheduler()
{
debug("Creating Scheduler");
m_tLastCheck = 0;
m_TaskList.clear();
}
Scheduler::~Scheduler()
{
debug("Destroying Scheduler");
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
delete *it;
}
}
void Scheduler::AddTask(Task* pTask)
{
m_mutexTaskList.Lock();
m_TaskList.push_back(pTask);
m_mutexTaskList.Unlock();
}
bool Scheduler::CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2)
{
return (pTask1->m_iHours < pTask2->m_iHours) ||
((pTask1->m_iHours == pTask2->m_iHours) && (pTask1->m_iMinutes < pTask2->m_iMinutes));
}
void Scheduler::FirstCheck()
{
m_mutexTaskList.Lock();
m_TaskList.sort(CompareTasks);
m_mutexTaskList.Unlock();
// check all tasks for the last week
time_t tCurrent = time(NULL);
m_tLastCheck = tCurrent - 60*60*24*7;
m_bDetectClockChanges = false;
m_bExecuteProcess = false;
m_bDownloadRateChanged = false;
m_bPauseChanged = false;
CheckTasks();
}
void Scheduler::IntervalCheck()
{
m_bDetectClockChanges = true;
m_bExecuteProcess = true;
m_bDownloadRateChanged = false;
m_bPauseChanged = false;
CheckTasks();
}
void Scheduler::CheckTasks()
{
m_mutexTaskList.Lock();
time_t tCurrent = time(NULL);
struct tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
struct tm tmLastCheck;
if (m_bDetectClockChanges)
{
// Detect large step changes of system time
time_t tDiff = tCurrent - m_tLastCheck;
if (tDiff > 60*90 || tDiff < -60*90)
{
debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)");
m_bExecuteProcess = false;
m_tLastCheck = tCurrent;
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
pTask->m_tLastExecuted = 0;
}
}
}
localtime_r(&m_tLastCheck, &tmLastCheck);
struct tm tmLoop;
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
tmLoop.tm_hour = tmCurrent.tm_hour;
tmLoop.tm_min = tmCurrent.tm_min;
tmLoop.tm_sec = tmCurrent.tm_sec;
time_t tLoop = mktime(&tmLoop);
while (tLoop <= tCurrent)
{
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
{
Task* pTask = *it;
if (pTask->m_tLastExecuted != tLoop)
{
struct tm tmAppoint;
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
tmAppoint.tm_hour = pTask->m_iHours;
tmAppoint.tm_min = pTask->m_iMinutes;
tmAppoint.tm_sec = 0;
time_t tAppoint = mktime(&tmAppoint);
int iWeekDay = tmAppoint.tm_wday;
if (iWeekDay == 0)
{
iWeekDay = 7;
}
bool bWeekDayOK = pTask->m_iWeekDaysBits == 0 || (pTask->m_iWeekDaysBits & (1 << (iWeekDay - 1)));
bool bDoTask = bWeekDayOK && m_tLastCheck < tAppoint && tAppoint <= tCurrent;
//debug("TEMP: 1) m_tLastCheck=%i, tCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
if (bDoTask)
{
ExecuteTask(pTask);
pTask->m_tLastExecuted = tLoop;
}
}
}
tLoop += 60*60*24; // inc day
localtime_r(&tLoop, &tmLoop);
}
m_tLastCheck = tCurrent;
m_mutexTaskList.Unlock();
}
void Scheduler::ExecuteTask(Task* pTask)
{
if (pTask->m_eCommand == scDownloadRate)
{
debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate);
}
else
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
}
switch (pTask->m_eCommand)
{
case scDownloadRate:
m_iDownloadRate = pTask->m_iDownloadRate;
m_bDownloadRateChanged = true;
break;
case scPause:
case scUnpause:
m_bPause = pTask->m_eCommand == scPause;
m_bPauseChanged = true;
break;
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szProcess);
}
break;
}
}

View File

@@ -1,91 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SCHEDULER_H
#define SCHEDULER_H
#include <list>
#include "Thread.h"
class Scheduler
{
public:
enum ECommand
{
scPause,
scUnpause,
scDownloadRate,
scProcess
};
class Task
{
private:
int m_iHours;
int m_iMinutes;
int m_iWeekDaysBits;
ECommand m_eCommand;
int m_iDownloadRate;
char* m_szProcess;
time_t m_tLastExecuted;
public:
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess);
~Task();
friend class Scheduler;
};
private:
typedef std::list<Task*> TaskList;
TaskList m_TaskList;
Mutex m_mutexTaskList;
time_t m_tLastCheck;
bool m_bDetectClockChanges;
bool m_bDownloadRateChanged;
bool m_bExecuteProcess;
int m_iDownloadRate;
bool m_bPauseChanged;
bool m_bPause;
void ExecuteTask(Task* pTask);
void CheckTasks();
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
public:
Scheduler();
~Scheduler();
void AddTask(Task* pTask);
void FirstCheck();
void IntervalCheck();
bool GetDownloadRateChanged() { return m_bDownloadRateChanged; }
int GetDownloadRate() { return m_iDownloadRate; }
bool GetPauseChanged() { return m_bPauseChanged; }
bool GetPause() { return m_bPause; }
};
#endif

View File

@@ -1,776 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include "nzbget.h"
#include "ScriptController.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
extern char* (*szEnvironmentVariables)[];
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
static const int POSTPROCESS_PARCHECK_ALL = 92;
#ifndef WIN32
#define CHILD_WATCHDOG 1
#endif
#ifdef CHILD_WATCHDOG
/**
* Sometimes the forked child process doesn't start properly and hangs
* just during the starting. I didn't find any explanation about what
* could cause that problem except of a general advice, that
* "a forking in a multithread application is not recommended".
*
* Workaround:
* 1) child process prints a line into stdout directly after the start;
* 2) parent process waits for a line for 60 seconds. If it didn't receive it
* the cild process assumed to hang and will be killed. Another attempt
* will be made.
*/
class ChildWatchDog : public Thread
{
private:
pid_t m_hProcessID;
protected:
virtual void Run();
public:
void SetProcessID(pid_t hProcessID) { m_hProcessID = hProcessID; }
};
void ChildWatchDog::Run()
{
static const int WAIT_SECONDS = 60;
time_t tStart = time(NULL);
while (!IsStopped() && (time(NULL) - tStart) < WAIT_SECONDS)
{
usleep(10 * 1000);
}
if (!IsStopped())
{
info("Restarting hanging child process");
kill(m_hProcessID, SIGKILL);
}
}
#endif
EnvironmentStrings::EnvironmentStrings()
{
}
EnvironmentStrings::~EnvironmentStrings()
{
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
free(*it);
}
m_strings.clear();
}
void EnvironmentStrings::InitFromCurrentProcess()
{
for (int i = 0; (*szEnvironmentVariables)[i]; i++)
{
char* szVar = (*szEnvironmentVariables)[i];
Append(strdup(szVar));
}
}
void EnvironmentStrings::Append(char* szString)
{
m_strings.push_back(szString);
}
#ifdef WIN32
/*
* Returns environment block in format suitable for using with CreateProcess.
* The allocated memory must be freed by caller using "free()".
*/
char* EnvironmentStrings::GetStrings()
{
int iSize = 1;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
iSize += strlen(szVar) + 1;
}
char* szStrings = (char*)malloc(iSize);
char* szPtr = szStrings;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
strcpy(szPtr, szVar);
szPtr += strlen(szVar) + 1;
}
szPtr = '\0';
return szStrings;
}
#else
/*
* Returns environment block in format suitable for using with execve
* The allocated memory must be freed by caller using "free()".
*/
char** EnvironmentStrings::GetStrings()
{
char** pStrings = (char**)malloc((m_strings.size() + 1) * sizeof(char*));
char** pPtr = pStrings;
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
char* szVar = *it;
*pPtr = szVar;
pPtr++;
}
*pPtr = NULL;
return pStrings;
}
#endif
ScriptController::ScriptController()
{
m_szScript = NULL;
m_szWorkingDir = NULL;
m_szArgs = NULL;
m_szInfoName = NULL;
m_szDefaultKindPrefix = NULL;
m_bTerminated = false;
m_environmentStrings.InitFromCurrentProcess();
}
void ScriptController::SetEnvVar(const char* szName, const char* szValue)
{
int iLen = strlen(szName) + strlen(szValue) + 2;
char* szVar = (char*)malloc(iLen);
snprintf(szVar, iLen, "%s=%s", szName, szValue);
m_environmentStrings.Append(szVar);
}
void ScriptController::PrepareEnvironmentStrings()
{
Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries();
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
{
Options::OptEntry* pOptEntry = *it;
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBOP_%s", pOptEntry->GetName());
// convert to upper case; replace "." with "_".
for (char* szPtr = szVarname; *szPtr; szPtr++)
{
if (*szPtr == '.')
{
*szPtr = '_';
}
*szPtr = toupper(*szPtr);
}
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pOptEntry->GetValue());
}
g_pOptions->UnlockOptEntries();
}
int ScriptController::Execute()
{
if (!Util::FileExists(m_szScript))
{
error("Could not start %s: could not find file %s", m_szInfoName, m_szScript);
return -1;
}
PrepareEnvironmentStrings();
int iExitCode = 0;
int pipein;
#ifdef CHILD_WATCHDOG
bool bChildConfirmed = false;
while (!bChildConfirmed && !m_bTerminated)
{
#endif
#ifdef WIN32
// build command line
char szCmdLine[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
snprintf(szCmdLine + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
}
szCmdLine[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
// create pipes to write and read data
HANDLE hReadPipe, hWritePipe;
SECURITY_ATTRIBUTES SecurityAttributes;
memset(&SecurityAttributes, 0, sizeof(SecurityAttributes));
SecurityAttributes.nLength = sizeof(SecurityAttributes);
SecurityAttributes.bInheritHandle = TRUE;
CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0);
STARTUPINFO StartupInfo;
memset(&StartupInfo, 0, sizeof(StartupInfo));
StartupInfo.cb = sizeof(StartupInfo);
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
StartupInfo.hStdInput = 0;
StartupInfo.hStdOutput = hWritePipe;
StartupInfo.hStdError = hWritePipe;
PROCESS_INFORMATION ProcessInfo;
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
char* szEnvironmentStrings = m_environmentStrings.GetStrings();
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, szEnvironmentStrings, m_szWorkingDir, &StartupInfo, &ProcessInfo);
if (!bOK)
{
DWORD dwErrCode = GetLastError();
char szErrMsg[255];
szErrMsg[255-1] = '\0';
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
NULL, dwErrCode, 0, szErrMsg, 255, NULL))
{
error("Could not start %s: %s", m_szInfoName, szErrMsg);
}
else
{
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
}
free(szEnvironmentStrings);
return -1;
}
free(szEnvironmentStrings);
debug("Child Process-ID: %i", (int)ProcessInfo.dwProcessId);
m_hProcess = ProcessInfo.hProcess;
// close unused "write" end
CloseHandle(hWritePipe);
pipein = _open_osfhandle((intptr_t)hReadPipe, _O_RDONLY);
#else
int p[2];
int pipeout;
// create the pipe
if (pipe(p))
{
error("Could not open pipe: errno %i", errno);
return -1;
}
char** pEnvironmentStrings = m_environmentStrings.GetStrings();
pipein = p[0];
pipeout = p[1];
debug("forking");
pid_t pid = fork();
if (pid == -1)
{
error("Could not start %s: errno %i", m_szInfoName, errno);
free(pEnvironmentStrings);
return -1;
}
else if (pid == 0)
{
// here goes the second instance
// close up the "read" end
close(pipein);
// make the pipeout to be the same as stdout and stderr
dup2(pipeout, 1);
dup2(pipeout, 2);
close(pipeout);
#ifdef CHILD_WATCHDOG
fwrite("\n", 1, 1, stdout);
fflush(stdout);
#endif
execve(m_szScript, (char* const*)m_szArgs, (char* const*)pEnvironmentStrings);
fprintf(stdout, "[ERROR] Could not start script: %s", strerror(errno));
fflush(stdout);
_exit(-1);
}
// continue the first instance
debug("forked");
debug("Child Process-ID: %i", (int)pid);
free(pEnvironmentStrings);
m_hProcess = pid;
// close unused "write" end
close(pipeout);
#endif
// open the read end
FILE* readpipe = fdopen(pipein, "r");
if (!readpipe)
{
error("Could not open pipe to %s", m_szInfoName);
return -1;
}
#ifdef CHILD_WATCHDOG
debug("Creating child watchdog");
ChildWatchDog* pWatchDog = new ChildWatchDog();
pWatchDog->SetAutoDestroy(false);
pWatchDog->SetProcessID(pid);
pWatchDog->Start();
#endif
char* buf = (char*)malloc(10240);
debug("Entering pipe-loop");
while (!feof(readpipe) && !m_bTerminated)
{
if (fgets(buf, 10240, readpipe))
{
#ifdef CHILD_WATCHDOG
if (!bChildConfirmed)
{
bChildConfirmed = true;
pWatchDog->Stop();
debug("Child confirmed");
}
#endif
ProcessOutput(buf);
}
}
debug("Exited pipe-loop");
#ifdef CHILD_WATCHDOG
debug("Destroying WatchDog");
if (!bChildConfirmed)
{
pWatchDog->Stop();
}
while (pWatchDog->IsRunning())
{
usleep(1 * 1000);
}
delete pWatchDog;
#endif
free(buf);
fclose(readpipe);
if (m_bTerminated)
{
warn("Interrupted %s", m_szInfoName);
}
iExitCode = 0;
#ifdef WIN32
WaitForSingleObject(m_hProcess, INFINITE);
DWORD dExitCode = 0;
GetExitCodeProcess(m_hProcess, &dExitCode);
iExitCode = dExitCode;
#else
int iStatus = 0;
waitpid(m_hProcess, &iStatus, 0);
if (WIFEXITED(iStatus))
{
iExitCode = WEXITSTATUS(iStatus);
}
#endif
#ifdef CHILD_WATCHDOG
} // while (!bChildConfirmed && !m_bTerminated)
#endif
if (!m_bTerminated)
{
info("Completed %s", m_szInfoName);
debug("Exit code %i", iExitCode);
}
return iExitCode;
}
void ScriptController::Terminate()
{
debug("Stopping %s", m_szInfoName);
m_bTerminated = true;
#ifdef WIN32
BOOL bOK = TerminateProcess(m_hProcess, -1);
#else
bool bOK = kill(m_hProcess, SIGKILL) == 0;
#endif
if (bOK)
{
debug("Terminated %s", m_szInfoName);
}
else
{
error("Could not terminate %s", m_szInfoName);
}
debug("Stopped %s", m_szInfoName);
}
void ScriptController::ProcessOutput(char* szText)
{
debug("Processing output received from script");
for (char* pend = szText + strlen(szText) - 1; pend >= szText && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
if (strlen(szText) == 0)
{
// skip empty lines
return;
}
if (!strncmp(szText, "[INFO] ", 7))
{
AddMessage(Message::mkInfo, false, g_pOptions->GetInfoTarget(), szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
AddMessage(Message::mkWarning, false, g_pOptions->GetWarningTarget(), szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
AddMessage(Message::mkError, false, g_pOptions->GetErrorTarget(), szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
AddMessage(Message::mkDetail, false, g_pOptions->GetDetailTarget(), szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
AddMessage(Message::mkDebug, false, g_pOptions->GetDebugTarget(), szText + 8);
}
else
{
switch (m_eDefaultLogKind)
{
case Options::slNone:
break;
case Options::slDetail:
AddMessage(Message::mkDetail, true, g_pOptions->GetDetailTarget(), szText);
break;
case Options::slInfo:
AddMessage(Message::mkInfo, true, g_pOptions->GetInfoTarget(), szText);
break;
case Options::slWarning:
AddMessage(Message::mkWarning, true, g_pOptions->GetWarningTarget(), szText);
break;
case Options::slError:
AddMessage(Message::mkError, true, g_pOptions->GetErrorTarget(), szText);
break;
case Options::slDebug:
AddMessage(Message::mkDebug, true, g_pOptions->GetDebugTarget(), szText);
break;
}
}
debug("Processing output received from script - completed");
}
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
{
switch (eKind)
{
case Message::mkDetail:
detail("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkInfo:
info("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkWarning:
warn("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkError:
error("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkDebug:
debug("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
}
}
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
{
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
PostScriptController* pScriptController = new PostScriptController();
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->SetScript(szScript);
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
pScriptController->SetAutoDestroy(false);
pPostInfo->SetScriptThread(pScriptController);
pScriptController->Start();
}
void PostScriptController::Run()
{
char szParStatus[10];
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
szParStatus[10-1] = '\0';
char szCollectionCompleted[10];
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
szCollectionCompleted[10-1] = '\0';
char szHasFailedParJobs[10];
snprintf(szHasFailedParJobs, 10, "%i", (int)m_bHasFailedParJobs);
szHasFailedParJobs[10-1] = '\0';
char szDestDir[1024];
strncpy(szDestDir, m_pPostInfo->GetDestDir(), 1024);
szDestDir[1024-1] = '\0';
char szNZBFilename[1024];
strncpy(szNZBFilename, m_pPostInfo->GetNZBFilename(), 1024);
szNZBFilename[1024-1] = '\0';
char szParFilename[1024];
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
szParFilename[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script for %s", m_pPostInfo->GetInfoName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Post-Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[9];
szArgs[0] = GetScript();
szArgs[1] = m_pPostInfo->GetDestDir();
szArgs[2] = szNZBFilename;
szArgs[3] = szParFilename;
szArgs[4] = szParStatus;
szArgs[5] = szCollectionCompleted;
szArgs[6] = szHasFailedParJobs;
szArgs[7] = m_pPostInfo->GetCategory();
szArgs[8] = NULL;
SetArgs(szArgs);
SetEnvVar("NZBPP_DIRECTORY", m_pPostInfo->GetDestDir());
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
SetEnvVar("NZBPP_NZBCOMPLETED", szCollectionCompleted);
SetEnvVar("NZBPP_PARFAILED", szHasFailedParJobs);
SetEnvVar("NZBPP_CATEGORY", m_pPostInfo->GetCategory());
for (NZBParameterList::iterator it = m_pPostInfo->GetParameters()->begin(); it != m_pPostInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pParameter->GetValue());
}
#ifndef DISABLE_PARCHECK
int iResult = Execute();
if (iResult == POSTPROCESS_PARCHECK_ALL)
{
if (m_pPostInfo->GetParCheck())
{
error("%s requested par-check/repair for all collections, but they were already checked", szInfoName);
}
else if (!m_bNZBFileCompleted)
{
error("%s requested par-check/repair for all collections, but it was not the call for the last collection", szInfoName);
}
else
{
info("%s requested par-check/repair for all collections", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
}
}
else if (iResult == POSTPROCESS_PARCHECK_CURRENT)
{
if (m_pPostInfo->GetParCheck())
{
error("%s requested par-check/repair for current collection, but the collection was already checked", szInfoName);
}
else if (strlen(m_pPostInfo->GetParFilename()) == 0)
{
error("%s requested par-check/repair for current collection, but the collection doesn't have any par-files", szInfoName);
}
else
{
info("%s requested par-check/repair for current collection", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpCurrent);
}
}
#else
Execute();
#endif
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
{
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(eKind, szText);
}
}
void PostScriptController::Stop()
{
debug("Stopping post-process-script");
Thread::Stop();
Terminate();
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory)
{
info("Executing nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
NZBScriptController* pScriptController = new NZBScriptController();
pScriptController->SetScript(szScript);
char szInfoName[1024];
snprintf(szInfoName, 1024, "nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
szInfoName[1024-1] = '\0';
pScriptController->SetInfoName(szInfoName);
// remove trailing slash
char szDir[1024];
strncpy(szDir, szDirectory, 1024);
szDir[1024-1] = '\0';
int iLen = strlen(szDir);
if (szDir[iLen-1] == PATH_SEPARATOR)
{
szDir[iLen-1] = '\0';
}
pScriptController->SetDefaultKindPrefix("NZB-Process: ");
pScriptController->SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[4];
szArgs[0] = szScript;
szArgs[1] = szDir;
szArgs[2] = szNZBFilename;
szArgs[3] = NULL;
pScriptController->SetArgs(szArgs);
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->Execute();
delete pScriptController;
}
void SchedulerScriptController::StartScript(const char* szScript)
{
info("Executing scheduled process-script %s", Util::BaseFileName(szScript));
SchedulerScriptController* pScriptController = new SchedulerScriptController();
pScriptController->SetScript(szScript);
pScriptController->SetAutoDestroy(true);
pScriptController->Start();
}
void SchedulerScriptController::Run()
{
char szInfoName[1024];
snprintf(szInfoName, 1024, "scheduled process-script %s", Util::BaseFileName(GetScript()));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
const char* szArgs[2];
szArgs[0] = GetScript();
szArgs[1] = NULL;
SetArgs(szArgs);
SetDefaultKindPrefix("Scheduled Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
Execute();
}

View File

@@ -1,123 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SCRIPTCONTROLLER_H
#define SCRIPTCONTROLLER_H
#include <list>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "Options.h"
class EnvironmentStrings
{
private:
typedef std::vector<char*> Strings;
Strings m_strings;
public:
EnvironmentStrings();
~EnvironmentStrings();
void InitFromCurrentProcess();
void Append(char* szString);
#ifdef WIN32
char* GetStrings();
#else
char** GetStrings();
#endif
};
class ScriptController
{
private:
const char* m_szScript;
const char* m_szWorkingDir;
const char** m_szArgs;
const char* m_szInfoName;
const char* m_szDefaultKindPrefix;
EnvironmentStrings m_environmentStrings;
Options::EScriptLogKind m_eDefaultLogKind;
bool m_bTerminated;
#ifdef WIN32
HANDLE m_hProcess;
#else
pid_t m_hProcess;
#endif
void ProcessOutput(char* szText);
void PrepareEnvironmentStrings();
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
ScriptController();
int Execute();
void Terminate();
void SetScript(const char* szScript) { m_szScript = szScript; }
const char* GetScript() { return m_szScript; }
void SetWorkingDir(const char* szWorkingDir) { m_szWorkingDir = szWorkingDir; }
void SetArgs(const char** szArgs) { m_szArgs = szArgs; }
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
void SetDefaultKindPrefix(const char* szDefaultKindPrefix) { m_szDefaultKindPrefix = szDefaultKindPrefix; }
void SetDefaultLogKind(Options::EScriptLogKind eDefaultLogKind) { m_eDefaultLogKind = eDefaultLogKind; }
void SetEnvVar(const char* szName, const char* szValue);
};
class PostScriptController : public Thread, ScriptController
{
private:
PostInfo* m_pPostInfo;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
{
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory);
};
class SchedulerScriptController : public Thread, ScriptController
{
public:
virtual void Run();
static void StartScript(const char* szScript);
};
#endif

View File

@@ -1,251 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
/*
*
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
* The queue of mutexes that was used did not work for every
* implementation of POSIX threads. Now a m_Semaphore is used.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <unistd.h>
#include <errno.h>
#endif
#include "nzbget.h"
#include "ServerPool.h"
#include "Log.h"
static const int CONNECTION_HOLD_SECODNS = 5;
ServerPool::PooledConnection::PooledConnection(NewsServer* server) : NNTPConnection(server)
{
m_bInUse = false;
m_tFreeTime = 0;
}
ServerPool::ServerPool()
{
debug("Creating ServerPool");
m_iMaxLevel = 0;
m_iTimeout = 60;
m_Servers.clear();
m_Connections.clear();
m_Semaphores.clear();
}
ServerPool::~ ServerPool()
{
debug("Destroying ServerPool");
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
{
delete *it;
}
m_Semaphores.clear();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
delete *it;
}
m_Servers.clear();
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
delete *it;
}
m_Connections.clear();
}
void ServerPool::AddServer(NewsServer* pNewsServer)
{
debug("Adding server to ServerPool");
m_Servers.push_back(pNewsServer);
}
void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");
m_iMaxLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (m_iMaxLevel < pNewsServer->GetLevel())
{
m_iMaxLevel = pNewsServer->GetLevel();
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
}
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
{
int iMaxConnectionsForLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (iLevel == pNewsServer->GetLevel())
{
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
}
}
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
m_Semaphores.push_back(sem);
}
}
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
{
bool bWaitVal = false;
if (bWait)
{
debug("Getting connection (wait)");
bWaitVal = m_Semaphores[iLevel]->Wait();
}
else
{
bWaitVal = m_Semaphores[iLevel]->TryWait();
}
if (!bWaitVal)
{
// signal received or wait timeout
return NULL;
}
m_mutexConnections.Lock();
PooledConnection* pConnection = NULL;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
{
// free connection found, take it!
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
}
}
m_mutexConnections.Unlock();
if (!pConnection)
{
error("ServerPool: serious error, no free connection found, but there should be one.");
}
return pConnection;
}
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
if (bUsed)
{
debug("Freeing used connection");
}
m_mutexConnections.Lock();
((PooledConnection*)pConnection)->SetInUse(false);
if (bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
m_mutexConnections.Unlock();
}
void ServerPool::CloseUnusedConnections()
{
m_mutexConnections.Lock();
time_t curtime = ::time(NULL);
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
pConnection->Disconnect();
}
}
}
m_mutexConnections.Unlock();
}
void ServerPool::LogDebugInfo()
{
debug(" ServerPool");
debug(" ----------------");
debug(" Max-Level: %i", m_iMaxLevel);
m_mutexConnections.Lock();
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
/*
debug(" Semaphores: %i", m_Semaphores.size());
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
{
sem_t* sem = m_Semaphores[iLevel];
int iSemValue;
sem_getvalue(sem, &iSemValue);
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
}
*/
m_mutexConnections.Unlock();
}

View File

@@ -1,79 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2005 Florian Penzkofer <f.penzkofer@sent.com>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef SERVERPOOL_H
#define SERVERPOOL_H
#include <vector>
#include <time.h>
#include "Thread.h"
#include "NewsServer.h"
#include "NNTPConnection.h"
class ServerPool
{
private:
class PooledConnection : public NNTPConnection
{
private:
bool m_bInUse;
time_t m_tFreeTime;
public:
PooledConnection(NewsServer* server);
bool GetInUse() { return m_bInUse; }
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
time_t GetFreeTime() { return m_tFreeTime; }
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
};
typedef std::vector<NewsServer*> Servers;
typedef std::vector<Semaphore*> Semaphores;
typedef std::vector<PooledConnection*> Connections;
Servers m_Servers;
Connections m_Connections;
Semaphores m_Semaphores;
int m_iMaxLevel;
Mutex m_mutexConnections;
int m_iTimeout;
public:
ServerPool();
~ServerPool();
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
void AddServer(NewsServer *s);
void InitConnections();
int GetMaxLevel() { return m_iMaxLevel; }
NNTPConnection* GetConnection(int iLevel, bool bWait);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();
void LogDebugInfo();
};
#endif

1560
TLS.cpp
View File

File diff suppressed because it is too large Load Diff

211
TLS.h
View File

@@ -1,211 +0,0 @@
/*
* This file is part of nzbget
*
* Based on "tls.h" from project "mpop" by Martin Lambers
* Original source code available on http://sourceforge.net/projects/mpop/
*
* Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
* Martin Lambers <marlam@marlam.de>
*
* Copyright (C) 2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef TLS_H
#define TLS_H
#ifndef DISABLE_TLS
#ifdef HAVE_LIBGNUTLS
# include <gnutls/gnutls.h>
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
# include <openssl/ssl.h>
#endif /* HAVE_OPENSSL */
/*
* If a function with an 'errstr' argument returns a value != TLS_EOK,
* '*errstr' either points to an allocates string containing an error
* description or is NULL.
* If such a function returns TLS_EOK, 'errstr' will not be changed.
*/
#define TLS_EOK 0 /* no error */
#define TLS_ELIBFAILED 1 /* The underlying library failed */
#define TLS_ESEED 2 /* Cannot seed pseudo random number generator */
#define TLS_ECERT 3 /* Certificate check or verification failed */
#define TLS_EIO 4 /* Input/output error */
#define TLS_EFILE 5 /* A file does not exist/cannot be read */
#define TLS_EHANDSHAKE 6 /* TLS handshake failed */
/*
* Always use tls_clear() before using a tls_t!
* Never call a tls_*() function with tls_t NULL!
*/
typedef struct
{
int have_trust_file;
int is_active;
#ifdef HAVE_LIBGNUTLS
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
SSL_CTX *ssl_ctx;
SSL *ssl;
#endif /* HAVE_OPENSSL */
} tls_t;
/*
* Information about a X509 certificate.
* The 6 owner_info and issuer_info fields are:
* Common Name
* Organization
* Organizational unit
* Locality
* State/Province
* Country
* Each of these entries may be NULL if it was not provided.
*/
typedef struct
{
unsigned char sha1_fingerprint[20];
unsigned char md5_fingerprint[16];
time_t activation_time;
time_t expiration_time;
char *owner_info[6];
char *issuer_info[6];
} tls_cert_info_t;
/*
* tls_lib_init()
*
* Initialize underlying TLS library. If this function returns TLS_ELIBFAILED,
* *errstr will always point to an error string.
* Used error codes: TLS_ELIBFAILED
*/
int tls_lib_init(char **errstr);
/*
* tls_clear()
*
* Clears a tls_t type (marks it inactive).
*/
void tls_clear(tls_t *tls);
/*
* tls_init()
*
* Initializes a tls_t. If both 'key_file' and 'ca_file' are not NULL, they are
* set to be used when the peer request a certificate. If 'trust_file' is not
* NULL, it will be used to verify the peer certificate.
* All files must be in PEM format.
* If 'force_sslv3' is set, then only the SSLv3 protocol will be accepted. This
* option might be needed to talk to some obsolete broken servers. Only use this
* if you have to.
* Used error codes: TLS_ELIBFAILED, TLS_EFILE
*/
int tls_init(tls_t *tls,
const char *key_file, const char *ca_file, const char *trust_file,
int force_sslv3, char **errstr);
/*
* tls_start()
*
* Starts TLS encryption on a socket.
* 'tls' must be initialized using tls_init().
* If 'no_certcheck' is true, then no checks will be performed on the peer
* certificate. If it is false and no trust file was set with tls_init(),
* only sanity checks are performed on the peer certificate. If it is false
* and a trust file was set, real verification of the peer certificate is
* performed.
* 'hostname' is the host to start TLS with. It is needed for sanity checks/
* verification.
* 'tci' must be allocated with tls_cert_info_new(). Information about the
* peer's certificata will be stored in it. It can later be freed with
* tls_cert_info_free(). 'tci' is allowed to be NULL; no certificate
* information will be passed in this case.
* Used error codes: TLS_ELIBFAILED, TLS_ECERT, TLS_EHANDSHAKE
*/
int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck,
tls_cert_info_t *tci, char **errstr);
/*
* tls_is_active()
*
* Returns whether 'tls' is an active TLS connection.
*/
int tls_is_active(tls_t *tls);
/*
* tls_cert_info_new()
* Returns a new tls_cert_info_t
*/
tls_cert_info_t *tls_cert_info_new(void);
/*
* tls_cert_info_free()
* Frees a tls_cert_info_t
*/
void tls_cert_info_free(tls_cert_info_t *tci);
/*
* tls_cert_info_get()
*
* Extracts certificate information from the TLS connection 'tls' and stores
* it in 'tci'. See the description of tls_cert_info_t above.
* Used error codes: TLS_ECERT
*/
int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr);
/*
* tls_getbuf()
*
* Reads a buffer using TLS and stores it in 's'.
* Used error codes: TLS_EIO
*/
int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr);
/*
* tls_putbuf()
*
* Writes 'len' characters from the string 's' using TLS.
* Used error codes: TLS_EIO
*/
int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr);
/*
* tls_close()
*
* Close a TLS connection and mark it inactive
*/
void tls_close(tls_t *tls);
/*
* tls_lib_deinit()
*
* Deinit underlying TLS library.
*/
void tls_lib_deinit(void);
#endif
#endif

View File

@@ -1,304 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <process.h>
#else
#include <fcntl.h>
#endif
#include "Log.h"
#include "Thread.h"
int Thread::m_iThreadCount = 1; // take the main program thread into account
Mutex Thread::m_mutexThread;
#ifndef WIN32
#ifndef HAVE_UNNAMED_SEMAPHORES
int Semaphore::m_iID = 1;
Mutex Semaphore::m_mutexID;
#endif
#endif
Mutex::Mutex()
{
#ifdef WIN32
InitializeCriticalSection(&m_mutexObj);
#else
pthread_mutex_init(&m_mutexObj, NULL);
#endif
}
Mutex::~ Mutex()
{
#ifdef WIN32
DeleteCriticalSection(&m_mutexObj);
#else
pthread_mutex_destroy(&m_mutexObj);
#endif
}
void Mutex::Lock()
{
#ifdef WIN32
EnterCriticalSection(&m_mutexObj);
#ifdef DEBUG
// CriticalSections on Windows can be locked many times from the same thread,
// but we do not want this and must treat such situations as errors and detect them.
if (m_mutexObj.RecursionCount > 1)
{
error("Internal program error: inconsistent thread-lock detected");
}
#endif
#else
pthread_mutex_lock(&m_mutexObj);
#endif
}
void Mutex::Unlock()
{
#ifdef WIN32
LeaveCriticalSection(&m_mutexObj);
#else
pthread_mutex_unlock(&m_mutexObj);
#endif
}
Semaphore::Semaphore()
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
#else
CreateSemObj(0);
#endif
}
Semaphore::Semaphore(int iValue)
{
#ifdef WIN32
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
#else
CreateSemObj(iValue);
#endif
}
#ifndef WIN32
void Semaphore::CreateSemObj(int iValue)
{
#ifdef HAVE_UNNAMED_SEMAPHORES
m_semObj = (sem_t*)malloc(sizeof(sem_t));
int iRet = sem_init(m_semObj, 0, iValue);
if (iRet == -1)
{
error("sem_init failed: error %i", errno);
}
#else
m_mutexID.Lock();
int iID = m_iID;
m_iID++;
m_mutexID.Unlock();
snprintf(m_szName, sizeof(m_szName), "/NZBGET-%i", iID);
m_semObj = sem_open(m_szName, O_CREAT, S_IRWXU, iValue);
if (m_semObj == SEM_FAILED)
{
error("sem_open failed: error %i", errno);
}
#endif
}
#endif
Semaphore::~ Semaphore()
{
#ifdef WIN32
CloseHandle(m_semObj);
#else
#ifdef HAVE_UNNAMED_SEMAPHORES
sem_destroy(m_semObj);
free(m_semObj);
#else
sem_close(m_semObj);
sem_unlink(m_szName);
#endif
#endif
}
void Semaphore::Post()
{
#ifdef WIN32
ReleaseSemaphore(m_semObj, 1, NULL);
#else
sem_post(m_semObj);
#endif
}
bool Semaphore::Wait()
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, INFINITE) == WAIT_OBJECT_0;
#else
return sem_wait(m_semObj) == 0;
#endif
}
bool Semaphore::TryWait()
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
#else
return sem_trywait(m_semObj) == 0;
#endif
}
Thread::Thread()
{
debug("Creating Thread");
m_Thread = 0;
m_bRunning = false;
m_bStopped = false;
m_bAutoDestroy = false;
}
Thread::~Thread()
{
debug("Destroying Thread");
}
void Thread::Start()
{
debug("Starting Thread");
m_bRunning = true;
// NOTE: we must garantee, that in a time we setting m_bRunning
// to value returned from pthread_create, the thread-object still exists.
// This is not obviously!
// pthread_create could wait long enough before returning result
// back to allow the started thread to complete it job
// and terminate.
// We lock mutex m_mutexThread on calling pthread_create; the started thread
// then also try to lock the mutex (see thread_handler) and therefore
// must wait until we unlock it
m_mutexThread.Lock();
#ifdef WIN32
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
m_bRunning = m_Thread != NULL;
#else
pthread_attr_t m_Attr;
pthread_attr_init(&m_Attr);
pthread_attr_setdetachstate(&m_Attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setinheritsched(&m_Attr , PTHREAD_INHERIT_SCHED);
m_bRunning = !pthread_create(&m_Thread, &m_Attr, Thread::thread_handler, (void *) this);
pthread_attr_destroy(&m_Attr);
#endif
m_mutexThread.Unlock();
}
void Thread::Stop()
{
debug("Stopping Thread");
m_bStopped = true;
}
bool Thread::Kill()
{
debug("Killing Thread");
m_mutexThread.Lock();
#ifdef WIN32
bool terminated = TerminateThread(m_Thread, 0) != 0;
#else
bool terminated = pthread_cancel(m_Thread) == 0;
#endif
if (terminated)
{
m_iThreadCount--;
}
m_mutexThread.Unlock();
return terminated;
}
#ifdef WIN32
void __cdecl Thread::thread_handler(void* pObject)
#else
void* Thread::thread_handler(void* pObject)
#endif
{
m_mutexThread.Lock();
m_iThreadCount++;
m_mutexThread.Unlock();
debug("Entering Thread-func");
Thread* pThread = (Thread*)pObject;
pThread->Run();
debug("Thread-func exited");
pThread->m_bRunning = false;
if (pThread->m_bAutoDestroy)
{
debug("Autodestroying Thread-object");
delete pThread;
}
m_mutexThread.Lock();
m_iThreadCount--;
m_mutexThread.Unlock();
#ifndef WIN32
return NULL;
#endif
}
int Thread::GetThreadCount()
{
m_mutexThread.Lock();
int iThreadCount = m_iThreadCount;
m_mutexThread.Unlock();
return iThreadCount;
}

117
Thread.h
View File

@@ -1,117 +0,0 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef THREAD_H
#define THREAD_H
#ifndef WIN32
#include <pthread.h>
#include <semaphore.h>
#endif
class Mutex
{
private:
#ifdef WIN32
CRITICAL_SECTION m_mutexObj;
#else
pthread_mutex_t m_mutexObj;
#endif
public:
Mutex();
~Mutex();
void Lock();
void Unlock();
};
class Semaphore
{
private:
#ifdef WIN32
HANDLE m_semObj;
#else
sem_t* m_semObj;
#ifndef HAVE_UNNAMED_SEMAPHORES
char m_szName[20];
static int m_iID;
static Mutex m_mutexID;
#endif
void CreateSemObj(int iValue);
#endif
public:
Semaphore();
Semaphore(int iValue);
~Semaphore();
void Post();
bool Wait();
bool TryWait();
};
class Thread
{
private:
static Mutex m_mutexThread;
static int m_iThreadCount;
#ifdef WIN32
HANDLE m_Thread;
#else
pthread_t m_Thread;
#endif
bool m_bRunning;
bool m_bStopped;
bool m_bAutoDestroy;
#ifdef WIN32
static void __cdecl thread_handler(void* pObject);
#else
static void *thread_handler(void* pObject);
#endif
public:
Thread();
virtual ~Thread();
virtual void Start();
virtual void Stop();
bool Kill();
bool IsStopped() { return m_bStopped; };
bool IsRunning() const { return m_bRunning; }
void SetRunning(bool bOnOff) { m_bRunning = bOnOff; }
bool GetAutoDestroy() { return m_bAutoDestroy; }
void SetAutoDestroy(bool bAutoDestroy) { m_bAutoDestroy = bAutoDestroy; }
static int GetThreadCount();
protected:
virtual void Run() {}; // Virtual function - override in derivatives
};
#endif

1133
Util.cpp
View File

File diff suppressed because it is too large Load Diff

148
Util.h
View File

@@ -1,148 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef UTIL_H
#define UTIL_H
#ifdef WIN32
#include <stdio.h>
#include <io.h>
#include <sys/timeb.h>
#else
#include <dirent.h>
#endif
#ifdef WIN32
extern int optind, opterr;
extern char *optarg;
int getopt(int argc, char *argv[], char *optstring);
#endif
class DirBrowser
{
private:
#ifdef WIN32
struct _finddata_t m_FindData;
intptr_t m_hFile;
bool m_bFirst;
#else
DIR* m_pDir;
struct dirent* m_pFindData;
#endif
public:
DirBrowser(const char* szPath);
~DirBrowser();
const char* Next();
};
class Util
{
public:
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SetFileSize(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
static bool FileExists(const char* szFilename);
static bool DirectoryExists(const char* szDirFilename);
static bool CreateDirectory(const char* szDirFilename);
static bool ForceDirectories(const char* szPath);
static long long FileSize(const char* szFilename);
static long long FreeDiskSize(const char* szPath);
static bool DirEmpty(const char* szDirFilename);
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
#ifndef WIN32
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
#endif
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
static long long JoinInt64(unsigned long Hi, unsigned long Lo);
static void SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo);
/**
* Int64ToFloat converts Int64 to float.
* Simple (float)Int64 does not work on all compilers,
* for example on ARM for NSLU2 (unslung).
*/
static float Int64ToFloat(long long Int64);
static float EqualTime(_timeval* t1, _timeval* t2);
static bool EmptyTime(_timeval* t);
static float DiffTime(_timeval* t1, _timeval* t2);
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
/*
* Encodes string to be used as content of xml-tag.
* Returns new string allocated with malloc, it need to be freed by caller.
*/
static char* XmlEncode(const char* raw);
/*
* Decodes string from xml.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void XmlDecode(char* raw);
/*
* Returns pointer to tag-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* XmlFindTag(const char* szXml, const char* szTag, int* pValueLength);
/*
* Parses tag-content into szValueBuf.
*/
static bool XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd);
/*
* Creates JSON-string by replace the certain characters with escape-sequences.
* Returns new string allocated with malloc, it need to be freed by caller.
*/
static char* JsonEncode(const char* raw);
/*
* Decodes JSON-string.
* The string is decoded on the place overwriting the content of raw-data.
*/
static void JsonDecode(char* raw);
/*
* Returns pointer to field-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength);
/*
* Returns pointer to field-content and length of content in iValueLength
* The returned pointer points to the part of source-string, no additional strings are allocated.
*/
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
};
#endif

1546
XmlRpc.cpp
View File

File diff suppressed because it is too large Load Diff

219
XmlRpc.h
View File

@@ -1,219 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef XMLRPC_H
#define XMLRPC_H
#include "Connection.h"
class StringBuilder
{
private:
char* m_szBuffer;
int m_iBufferSize;
int m_iUsedSize;
public:
StringBuilder();
~StringBuilder();
void Append(const char* szStr);
const char* GetBuffer() { return m_szBuffer; }
};
class XmlCommand;
class XmlRpcProcessor
{
public:
enum ERpcProtocol
{
rpUndefined,
rpXmlRpc,
rpJsonRpc
};
enum EHttpMethod
{
hmPost,
hmGet
};
private:
Connection* m_pConnection;
const char* m_szClientIP;
char* m_szRequest;
ERpcProtocol m_eProtocol;
EHttpMethod m_eHttpMethod;
char* m_szUrl;
void Dispatch();
void SendResponse(const char* szResponse, bool bFault);
XmlCommand* CreateCommand(const char* szMethodName);
void MutliCall();
public:
XmlRpcProcessor();
~XmlRpcProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetProtocol(ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
void SetUrl(const char* szUrl);
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
class XmlCommand
{
protected:
char* m_szRequest;
char* m_szRequestPtr;
StringBuilder m_StringBuilder;
bool m_bFault;
XmlRpcProcessor::ERpcProtocol m_eProtocol;
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
void BuildErrorResponse(int iErrCode, const char* szErrText);
void BuildBoolResponse(bool bOK);
void AppendResponse(const char* szPart);
bool IsJson() { return m_eProtocol == XmlRpcProcessor::rpJsonRpc; }
bool NextParamAsInt(int* iValue);
bool NextParamAsBool(bool* bValue);
bool NextParamAsStr(char** szValueBuf);
const char* BoolToStr(bool bValue);
char* EncodeStr(const char* szStr);
public:
XmlCommand();
virtual ~XmlCommand() {}
virtual void Execute() = 0;
void PrepareParams();
void SetRequest(char* szRequest) { m_szRequest = szRequest; m_szRequestPtr = m_szRequest; }
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
bool GetFault() { return m_bFault; }
};
class ErrorXmlCommand: public XmlCommand
{
private:
int m_iErrCode;
const char* m_szErrText;
public:
ErrorXmlCommand(int iErrCode, const char* szErrText);
virtual void Execute();
};
class PauseXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class UnPauseXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ShutdownXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class VersionXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DumpDebugXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SetDownloadRateXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class StatusXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListFilesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListGroupsXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class EditQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class PostQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class WriteLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ScanXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
#endif

1140
aclocal.m4 vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,21 @@
/* Define to 1 to not use curses */
#undef DISABLE_CURSES
/* Define to 1 to disable smart par-verification and restoration */
/* Define to 1 to disable gzip-support */
#undef DISABLE_GZIP
/* Define to 1 to not use libxml2, only for development purposes */
#undef DISABLE_LIBXML2
/* 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
@@ -28,6 +37,42 @@
/* Define to 1 if you have the <curses.h> header file. */
#undef HAVE_CURSES_H
/* define if the compiler supports basic C++14 syntax */
#undef HAVE_CXX14
/* 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
/* Define to 1 if gethostbyname_r is supported */
#undef HAVE_GETHOSTBYNAME_R
/* Define to 1 if gethostbyname_r takes 3 arguments */
#undef HAVE_GETHOSTBYNAME_R_3
/* Define to 1 if gethostbyname_r takes 5 arguments */
#undef HAVE_GETHOSTBYNAME_R_5
/* 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
@@ -37,6 +82,9 @@
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
#undef HAVE_LIBGNUTLS
/* Define to 1 if lockf is supported */
#undef HAVE_LOCKF
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
@@ -46,14 +94,20 @@
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
/* Define to 1 to use Nettle library for decryption. */
#undef HAVE_NETTLE
/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
#undef HAVE_OPENSSL
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
/* Define to 1 if pthread_cancel is supported */
#undef HAVE_PTHREAD_CANCEL
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <regex.h> header file. */
#undef HAVE_REGEX_H
/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
#undef HAVE_SC_NPROCESSORS_ONLN
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -61,6 +115,9 @@
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* 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
@@ -79,12 +136,15 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 to use unnamed semaphores */
#undef HAVE_UNNAMED_SEMAPHORES
/* Define to 1 if variadic macros are supported */
#undef HAVE_VARIADIC_MACROS
/* Define to 1 if OpenSSL supports function "X509_check_host". */
#undef HAVE_X509_CHECK_HOST
/* Define to 1 to exclude debug-code */
#undef NDEBUG
/* Name of package */
#undef PACKAGE
@@ -100,11 +160,37 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 to install an empty signal handler for SIGCHLD */
#undef SIGCHLD_HANDLER
/* Determine what socket length (socklen_t) data type is */
#undef SOCKLEN_T
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Enable large inode numbers on Mac OS X 10.5. */
#ifndef _DARWIN_USE_64_BIT_INODE
# define _DARWIN_USE_64_BIT_INODE 1
#endif
/* 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 `unsigned int' if <sys/types.h> does not define. */
#undef size_t

12434
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,51 +1,72 @@
#
# This file is part of nzbget. See <http://nzbget.net>.
#
# Copyright (C) 2008-2021 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This 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, see <http://www.gnu.org/licenses/>.
#
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 0.6.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.6.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_PREREQ(2.65)
AC_INIT(nzbget, 21.2-testing, hugbug@users.sourceforge.net)
AC_CONFIG_AUX_DIR(posix)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign subdir-objects])
AC_CONFIG_SRCDIR([daemon/main/nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
AM_MAINTAINER_MODE
dnl
dnl Architecture check.
dnl
AC_CANONICAL_HOST
dnl
dnl Set default library path, if not specified in environment variable "LIBPREF".
dnl
if test "$LIBPREF" = ""; then
case "$host" in
*-linux*)
LIBPREF="/usr"
;;
*-freebsd*)
LIBPREF="/usr/local"
;;
*-solaris*)
LIBPREF="/usr"
;;
esac
fi
m4_include([posix/ax_cxx_compile_stdcxx.m4])
dnl
dnl Check for programs.
dnl
AC_PROG_CXX
AC_PROG_CC
AC_PATH_PROG(TAR, tar, $FALSE)
AC_PATH_PROG(MAKE, make, $FALSE)
AC_PROG_INSTALL
dnl
dnl Do all tests with c++ compiler.
dnl
AC_LANG(C++)
dnl
dnl Determine compiler switches to support C++14 standard.
dnl
AC_MSG_CHECKING(whether to test compiler features)
AC_ARG_ENABLE(cpp-check,
[AS_HELP_STRING([--disable-cpp-check], [disable check for C++14 compiler features])],
[ ENABLECPPCHECK=$enableval ],
[ ENABLECPPCHECK=yes] )
AC_MSG_RESULT($ENABLECPPCHECK)
if test "$ENABLECPPCHECK" = "yes"; then
AX_CXX_COMPILE_STDCXX(14,,[optional])
if test "$HAVE_CXX14" != "1"; then
AC_MSG_ERROR("A compiler with support for C++14 language features is required. For details visit http://nzbget.net/cpp14")
fi
fi
dnl
dnl Checks for header files.
dnl
AC_CHECK_HEADERS(sys/prctl.h)
AC_CHECK_HEADERS(sys/prctl.h regex.h endian.h getopt.h)
dnl
dnl Check for libs
@@ -56,27 +77,44 @@ AC_SEARCH_LIBS([inet_addr], [nsl])
AC_SEARCH_LIBS([hstrerror], [resolv])
dnl
dnl Android NDK restrictions
dnl
AC_CHECK_FUNC(lockf,
[AC_CHECK_DECL(lockf,
[AC_DEFINE([HAVE_LOCKF], 1, [Define to 1 if lockf is supported])],,
[#include <unistd.h>])])
AC_CHECK_FUNC(pthread_cancel,
[AC_CHECK_DECL(pthread_cancel,
[AC_DEFINE([HAVE_PTHREAD_CANCEL], 1, [Define to 1 if pthread_cancel is supported])],,
[#include <pthread.h>])])
dnl
dnl Getopt
dnl
AC_CHECK_FUNC(getopt_long,
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
dnl
dnl stat64
dnl fsync
dnl
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],
[AC_LIBOBJ(stat64)])
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
AC_SYS_LARGEFILE
dnl
dnl check ctime_r
dnl
AC_MSG_CHECKING(for ctime_r)
AC_LANG_PUSH(C++)
AC_TRY_COMPILE(
[#include <time.h>],
[ time_t clock; char buf[26]; ctime_r(&clock, buf, 26); ],
@@ -85,15 +123,14 @@ AC_TRY_COMPILE(
AC_DEFINE([HAVE_CTIME_R_3], 1, [Define to 1 if ctime_r takes 3 arguments]),
FOUND="no")
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <time.h>],
[ time_t clock; char buf[26]; ctime_r(&clock, buf); ],
AC_MSG_RESULT([[yes, and it takes 2 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_CTIME_R_2], 1, [Define to 1 if ctime_r takes 2 arguments]),
FOUND="no")
AC_TRY_COMPILE(
[#include <time.h>],
[ time_t clock; char buf[26]; ctime_r(&clock, buf); ],
AC_MSG_RESULT([[yes, and it takes 2 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_CTIME_R_2], 1, [Define to 1 if ctime_r takes 2 arguments]),
FOUND="no")
fi
AC_LANG_POP(C++)
if test "$FOUND" = "no"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR("function ctime_r not found")
@@ -101,70 +138,136 @@ fi
dnl
dnl check for __FUNCTION__ or __func__ macro
dnl check getaddrinfo
dnl
AC_MSG_CHECKING(for __FUNCTION__ macro)
AC_LANG_PUSH(C++)
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
AC_MSG_RESULT([yes])
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
HAVE_FUNCTION_MACRO=yes,
AC_MSG_RESULT([no]))
AC_LANG_POP(C++)
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
AC_MSG_CHECKING(for __func__ macro)
AC_LANG_PUSH(C++)
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
AC_MSG_RESULT([yes])
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
HAVE_FUNCTION_MACRO=yes,
AC_MSG_RESULT([no]))
AC_LANG_POP(C++)
fi
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
AC_CHECK_FUNC(getaddrinfo,
FOUND="yes"
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
FOUND="no")
dnl
dnl check gethostbyname_r, if getaddrinfo is not available
dnl
if test "$FOUND" = "no"; then
AC_MSG_CHECKING(for gethostbyname_r)
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
FOUND="no")
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
FOUND="no")
fi
if test "$FOUND" = "no"; then
AC_TRY_COMPILE(
[#include <netdb.h>],
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
FOUND="yes"
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
AC_MSG_RESULT([[no]])
FOUND="no")
fi
if test "$FOUND" = "yes"; then
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
fi
fi
dnl
dnl Unnamed semaphores
dnl Determine what socket length (socklen_t) data type is
dnl
AC_MSG_CHECKING(whether to use unnamed semaphores)
case "$host" in
*-darwin*)
AC_MSG_RESULT([no])
;;
*)
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_UNNAMED_SEMAPHORES],1,Define to 1 to use unnamed semaphores)
;;
esac
AC_MSG_CHECKING([for type of socket length (socklen_t)])
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL)],[
AC_MSG_RESULT(socklen_t)
SOCKLEN_T=socklen_t],[
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
AC_MSG_RESULT(size_t)
SOCKLEN_T=size_t],[
AC_TRY_COMPILE([
#include <stddef.h>
#include <sys/types.h>
#include <sys/socket.h>],[
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
AC_MSG_RESULT(int)
SOCKLEN_T=int],[
AC_MSG_WARN(could not determine)
SOCKLEN_T=int])])])
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
dnl
dnl 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
AC_ARG_WITH(libxml2_includes,
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[CFLAGS="${CFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libxml2_libraries,
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libxml2, libxml-2.0,
[LDFLAGS="${LDFLAGS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"]
[CFLAGS="${CFLAGS} $libxml2_CFLAGS"])
AC_MSG_CHECKING(whether to use libxml2)
AC_ARG_ENABLE(libxml2,
[AS_HELP_STRING([--disable-libxml2], [do not use libxml2 (removes dependency from libxml2-library, only for development purposes)])],
[USELIBXML2=$enableval],
[USELIBXML2=yes] )
AC_MSG_RESULT($USELIBXML2)
if test "$USELIBXML2" = "yes"; then
AC_ARG_WITH(libxml2_includes,
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libxml2_libraries,
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libxml2, libxml-2.0,
[LIBS="${LIBS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
AC_MSG_ERROR("libxml2 library not found"))
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files not found"))
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
AC_MSG_ERROR("libxml2 library not found"))
else
AC_DEFINE([DISABLE_LIBXML2],1,[Define to 1 to not use libxml2, only for development purposes])
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files were not found"))
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
AC_MSG_ERROR("libxml2 library not found"))
dnl
@@ -177,18 +280,23 @@ AC_ARG_ENABLE(curses,
[USECURSES=yes] )
AC_MSG_RESULT($USECURSES)
if test "$USECURSES" = "yes"; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libcurses_includes,
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libcurses_libraries,
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(ncurses, ncurses,
[LIBS="${LIBS} $ncurses_LIBS"]
[CPPFLAGS="${CPPFLAGS} $ncurses_CFLAGS"],
AC_MSG_ERROR("ncurses library not found"))
fi
AC_CHECK_HEADER(ncurses.h,
FOUND=yes
AC_DEFINE([HAVE_NCURSES_H],1,[Define to 1 if you have the <ncurses.h> header file.]),
@@ -210,93 +318,35 @@ if test "$USECURSES" = "yes"; then
fi
AC_SEARCH_LIBS([refresh], [ncurses curses],,
AC_ERROR([Couldn't find curses library]))
AC_SEARCH_LIBS([nodelay], [ncurses curses tinfo],,
AC_ERROR([Couldn't find curses library]))
else
AC_DEFINE([DISABLE_CURSES],1,[Define to 1 to not use curses])
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,
[LDFLAGS="${LDFLAGS} $libsigc_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
fi
AC_LANG_PUSH(C++)
AC_CHECK_HEADER(sigc++/type_traits.h,,
AC_MSG_ERROR("libsigc++-2.0 header files were not found"))
AC_LANG_POP(C++)
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_LANG_PUSH(C++)
AC_CHECK_HEADER(libpar2/libpar2.h,,
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL"))
AC_LANG_POP(C++)
AC_ARG_WITH(libpar2_libraries,
[AS_HELP_STRING([--with-libpar2-libraries=DIR], [libpar2 library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
dnl Q: How to check for c++-class in library?
LIBS="${LIBS} -lpar2"
dnl AC_CHECK_LIB(par2, GenerateCRC32Table, , FOUND=no)
dnl if test "$FOUND" = "no"; then
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL")
dnl fi
dnl
dnl check if libpar2 has support for cancelling
dnl
AC_MSG_CHECKING(whether libpar2 supports cancelling)
AC_LANG_PUSH(C++)
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]]))
AC_LANG_POP(C++)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_SIZE_T
AC_FUNC_FSEEKO
dnl Checks for library functions.
AC_CHECK_FUNCS([stricmp])
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
@@ -311,25 +361,71 @@ AC_ARG_ENABLE(tls,
AC_MSG_RESULT($USETLS)
if test "$USETLS" = "yes"; then
AC_ARG_WITH(tlslib,
[AS_HELP_STRING([--with-tlslib=(GnuTLS, OpenSSL)], [TLS/SSL library to use])],
[AS_HELP_STRING([--with-tlslib=(OpenSSL, GnuTLS)], [TLS/SSL library to use])],
[TLSLIB="$withval"])
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
fi
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
AC_ARG_WITH(openssl_includes,
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES([openssl], [openssl],
[LIBS="${LIBS} $openssl_LIBS"]
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
fi
AC_CHECK_HEADER(openssl/ssl.h,
FOUND=yes
TLSHEADERS=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([ASN1_OBJECT_free], [crypto],
AC_SEARCH_LIBS([SSL_CTX_new], [ssl],
FOUND=yes,
FOUND=no),
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL library])
fi
if test "$FOUND" = "yes"; then
TLSLIB="OpenSSL"
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support and decryption.])
AC_SEARCH_LIBS([X509_check_host], [crypto],
AC_DEFINE([HAVE_X509_CHECK_HOST],1,[Define to 1 if OpenSSL supports function "X509_check_host".]))
fi
fi
fi
if test "$TLSLIB" = "GnuTLS" -o "$TLSLIB" = ""; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(libgnutls_includes,
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libgnutls_libraries,
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES([gnutls], [gnutls],
[LIBS="${LIBS} $gnutls_LIBS"]
[CPPFLAGS="${CPPFLAGS} $gnutls_CFLAGS"])
fi
AC_CHECK_HEADER(gnutls/gnutls.h,
FOUND=yes
TLSHEADERS=yes,
@@ -341,6 +437,27 @@ if test "$USETLS" = "yes"; then
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
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
@@ -349,47 +466,46 @@ if test "$USETLS" = "yes"; then
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
fi
fi
fi
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(openssl_includes,
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(openssl/ssl.h,
FOUND=yes
TLSHEADERS=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([SSL_library_init], [ssl],
if test "$TLSLIB" = "GnuTLS"; then
AC_ARG_WITH(libnettle_includes,
[AS_HELP_STRING([--with-libnettle-includes=DIR], [Nettle include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libnettle_libraries,
[AS_HELP_STRING([--with-libnettle-libraries=DIR], [Nettle library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES([nettle], [nettle],
[LIBS="${LIBS} $nettle_LIBS"]
[CPPFLAGS="${CPPFLAGS} $nettle_CFLAGS"])
fi
AC_CHECK_HEADER(nettle/sha.h,
FOUND=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL library])
if test "$FOUND" = "no"; then
AC_MSG_ERROR([Couldn't find Nettle headers (sha.h)])
fi
AC_SEARCH_LIBS([nettle_pbkdf2_hmac_sha256], [nettle],
FOUND=yes,
FOUND=no)
if test "$FOUND" = "no"; then
AC_MSG_ERROR([Couldn't find Nettle library, required when using GnuTLS])
fi
if test "$FOUND" = "yes"; then
TLSLIB="OpenSSL"
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support.])
AC_DEFINE([HAVE_NETTLE],1,[Define to 1 to use Nettle library for decryption.])
fi
fi
fi
if test "$TLSLIB" = ""; then
if test "$TLSHEADERS" = ""; then
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL headers (gnutls.h or ssl.h)])
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS headers (ssl.h or gnutls.h)])
else
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL library])
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS library])
fi
fi
else
@@ -397,6 +513,88 @@ else
fi
dnl
dnl checks for zlib includes and libraries.
dnl
AC_MSG_CHECKING(whether to use gzip)
AC_ARG_ENABLE(gzip,
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
[USEZLIB=$enableval],
[USEZLIB=yes] )
AC_MSG_RESULT($USEZLIB)
if test "$USEZLIB" = "yes"; then
AC_ARG_WITH(zlib_includes,
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(zlib_libraries,
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES([zlib], [zlib],
[LIBS="${LIBS} $zlib_LIBS"]
[CPPFLAGS="${CPPFLAGS} $zlib_CFLAGS"])
fi
AC_CHECK_HEADER(zlib.h,,
AC_MSG_ERROR("zlib header files not found"))
AC_SEARCH_LIBS([deflateBound], [z], ,
AC_MSG_ERROR("zlib library not found"))
else
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
fi
dnl
dnl Determine if CPU supports SIMD instructions
dnl
AC_MSG_CHECKING(whether to use SIMD-optimized routines)
USE_SIMD=no
case $host_cpu in
i?86|x86_64)
SSE2_CXXFLAGS="-msse2"
SSSE3_CXXFLAGS="-mssse3"
PCLMUL_CXXFLAGS="-msse4.1 -mpclmul"
USE_SIMD=yes
;;
arm*)
NEON_CXXFLAGS="-mfpu=neon"
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
USE_SIMD=yes
;;
aarch64)
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
USE_SIMD=yes
;;
esac
AC_MSG_RESULT($USE_SIMD)
AC_SUBST([SSE2_CXXFLAGS])
AC_SUBST([SSSE3_CXXFLAGS])
AC_SUBST([PCLMUL_CXXFLAGS])
AC_SUBST([NEON_CXXFLAGS])
AC_SUBST([ACLECRC_CXXFLAGS])
dnl
dnl Some Linux systems require an empty signal handler for SIGCHLD
dnl in order for exit codes to be correctly delivered to parent process.
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to install the handler.
dnl
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
AC_ARG_ENABLE(sigchld-handler,
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
[SIGCHLDHANDLER=$enableval],
[SIGCHLDHANDLER=yes])
AC_MSG_RESULT($SIGCHLDHANDLER)
if test "$SIGCHLDHANDLER" = "yes"; then
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
fi
dnl
dnl Debugging. Default: no
dnl
@@ -405,41 +603,32 @@ AC_ARG_ENABLE(debug,
[AS_HELP_STRING([--enable-debug], [enable debugging])],
[ ENABLEDEBUG=$enableval ],
[ ENABLEDEBUG=no] )
if test "$ENABLEDEBUG" = "yes"; then
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
if test "$CC" = "gcc"; then
CXXFLAGS="-g -Wall"
else
CXXFLAGS=""
fi
fi
AC_MSG_RESULT($ENABLEDEBUG)
dnl
dnl Backtracing on segmentation faults
if test "$ENABLEDEBUG" = "yes"; then
AC_MSG_CHECKING(for backtrace)
AC_TRY_COMPILE(
[#include <execinfo.h>]
[#include <stdio.h>]
[#include <stdlib.h>],
[ void *array[100]; size_t size; char **strings; ]
[ size = backtrace(array, 100); ]
[ strings = backtrace_symbols(array, size); ],
FOUND=yes
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
FOUND=no
AC_MSG_RESULT([[no]]))
AC_MSG_CHECKING(for rdynamic linker flag)
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -rdynamic"
AC_TRY_LINK([], [],
AC_MSG_RESULT([[yes]]),
AC_MSG_RESULT([[no]])
[LDFLAGS="$old_LDFLAGS"])
dnl
dnl Begin of debugging code
dnl
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
dnl
dnl check for __FUNCTION__ or __func__ macro
dnl
AC_MSG_CHECKING(for macro returning current function name)
AC_TRY_COMPILE(
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
AC_MSG_RESULT(__FUNCTION__)
FUNCTION_MACRO_NAME=__FUNCTION__,
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
AC_MSG_RESULT(__func__)
FUNCTION_MACRO_NAME=__func__,
AC_MSG_RESULT(none)))
if test "$FUNCTION_MACRO_NAME" != ""; then
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
fi
@@ -447,25 +636,67 @@ dnl
dnl variadic macros
dnl
AC_MSG_CHECKING(for variadic macros)
AC_COMPILE_IFELSE([
#define macro(...) macrofunc(__VA_ARGS__)
int macrofunc(int a, int b) { return a + b; }
int test() { return macro(1, 2); }
],
AC_TRY_COMPILE(
[ #define macro(...) macrofunc(__VA_ARGS__) ]
[ int macrofunc(int a, int b) { return a + b; } ],
[ int a=macro(1, 2); ],
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_VARIADIC_MACROS], 1, Define to 1 if variadic macros are supported),
AC_MSG_RESULT([no]))
dnl Substitute flags.
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(TAR)
AC_SUBST(AR)
AC_SUBST(ADDSRCS)
dnl
dnl Backtracing on segmentation faults
dnl
AC_MSG_CHECKING(for backtrace)
AC_TRY_COMPILE(
[#include <execinfo.h>]
[#include <stdio.h>]
[#include <stdlib.h>],
[ void *array[100]; size_t size; char **strings; ]
[ size = backtrace(array, 100); ]
[ strings = backtrace_symbols(array, size); ],
FOUND=yes
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
FOUND=no
AC_MSG_RESULT([[no]]))
dnl
dnl "rdynamic" linker flag
dnl
AC_MSG_CHECKING(for rdynamic linker flag)
old_LDFLAGS="$LDFLAGS"
LDFLAGS="$LDFLAGS -rdynamic"
AC_TRY_LINK([], [],
AC_MSG_RESULT([[yes]]),
AC_MSG_RESULT([[no]])
[LDFLAGS="$old_LDFLAGS"])
dnl
dnl End of debugging code
dnl
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

File diff suppressed because it is too large Load Diff

157
daemon/connect/Connection.h Normal file
View File

@@ -0,0 +1,157 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef CONNECTION_H
#define CONNECTION_H
#include "NString.h"
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
#include "Thread.h"
#endif
#endif
#ifndef DISABLE_TLS
#include "TlsSocket.h"
#endif
class Connection
{
public:
enum EStatus
{
csConnected,
csDisconnected,
csListening,
csCancelled,
csBroken
};
enum EIPVersion
{
ipAuto,
ipV4,
ipV6
};
Connection(const char* host, int port, bool tls);
Connection(SOCKET socket, bool tls);
virtual ~Connection();
static void Init();
static void Final();
virtual bool Connect();
virtual bool Disconnect();
bool Bind();
bool Send(const char* buffer, int size);
bool Recv(char* buffer, int size);
int TryRecv(char* buffer, int size);
char* ReadLine(char* buffer, int size, int* bytesRead);
void ReadBuffer(char** buffer, int *bufLen);
int WriteLine(const char* buffer);
std::unique_ptr<Connection> Accept();
void Cancel();
const char* GetHost() { return m_host; }
int GetPort() { return m_port; }
bool GetTls() { return m_tls; }
const char* GetCipher() { return m_cipher; }
void SetCipher(const char* cipher) { m_cipher = cipher; }
void SetTimeout(int timeout) { m_timeout = timeout; }
void SetIPVersion(EIPVersion ipVersion) { m_ipVersion = ipVersion; }
EStatus GetStatus() { return m_status; }
void SetSuppressErrors(bool suppressErrors);
bool GetSuppressErrors() { return m_suppressErrors; }
const char* GetRemoteAddr();
bool GetGracefull() { return m_gracefull; }
void SetGracefull(bool gracefull) { m_gracefull = gracefull; }
void SetForceClose(bool forceClose) { m_forceClose = forceClose; }
#ifndef DISABLE_TLS
bool StartTls(bool isClient, const char* certFile, const char* keyFile);
#endif
int FetchTotalBytesRead();
protected:
CString m_host;
int m_port;
bool m_tls;
EIPVersion m_ipVersion = ipAuto;
SOCKET m_socket = INVALID_SOCKET;
CString m_cipher;
CharBuffer m_readBuf;
int m_bufAvail = 0;
char* m_bufPtr = nullptr;
EStatus m_status = csDisconnected;
int m_timeout = 60;
bool m_suppressErrors = true;
BString<100> m_remoteAddr;
int m_totalBytesRead = 0;
bool m_gracefull = false;
bool m_forceClose = false;
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
class ConTlsSocket: public TlsSocket
{
public:
ConTlsSocket(SOCKET socket, bool isClient, const char* host,
const char* certFile, const char* keyFile, const char* cipher, Connection* owner) :
TlsSocket(socket, isClient, host, certFile, keyFile, cipher), m_owner(owner) {}
protected:
virtual void PrintError(const char* errMsg) { m_owner->PrintError(errMsg); }
private:
Connection* m_owner;
};
std::unique_ptr<ConTlsSocket> m_tlsSocket;
bool m_tlsError = false;
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
static std::unique_ptr<Mutex> m_getHostByNameMutex;
#endif
#endif
void ReportError(const char* msgPrefix, const char* msgArg, bool printErrCode, int errCode = 0,
const char* errMsg = nullptr);
virtual void PrintError(const char* errMsg);
int GetLastNetworkError();
bool DoConnect();
bool DoDisconnect();
bool InitSocketOpts(SOCKET socket);
bool ConnectWithTimeout(void* address, int address_len);
#ifndef HAVE_GETADDRINFO
in_addr_t ResolveHostAddr(const char* host);
#endif
#ifndef DISABLE_TLS
int recv(SOCKET s, char* buf, int len, int flags);
int send(SOCKET s, const char* buf, int len, int flags);
void CloseTls();
#endif
};
#endif

View File

@@ -0,0 +1,696 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2008-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#ifndef DISABLE_TLS
#include "TlsSocket.h"
#include "Thread.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
CString TlsSocket::m_certStore;
#ifdef HAVE_LIBGNUTLS
#ifdef NEED_GCRYPT_LOCKING
/**
* Mutexes for gcryptlib
*/
std::vector<std::unique_ptr<Mutex>> g_GCryptLibMutexes;
static int gcry_mutex_init(void **priv)
{
g_GCryptLibMutexes.emplace_back(std::make_unique<Mutex>());
*priv = g_GCryptLibMutexes.back().get();
return 0;
}
static int gcry_mutex_destroy(void **lock)
{
Mutex* mutex = ((Mutex*)*lock);
g_GCryptLibMutexes.erase(std::find_if(g_GCryptLibMutexes.begin(), g_GCryptLibMutexes.end(),
[mutex](std::unique_ptr<Mutex>& itMutex)
{
return itMutex.get() == mutex;
}));
return 0;
}
static int gcry_mutex_lock(void **lock)
{
((Mutex*)*lock)->Lock();
return 0;
}
static int gcry_mutex_unlock(void **lock)
{
((Mutex*)*lock)->Unlock();
return 0;
}
static struct gcry_thread_cbs gcry_threads_Mutex =
{ GCRY_THREAD_OPTION_USER, nullptr,
gcry_mutex_init, gcry_mutex_destroy,
gcry_mutex_lock, gcry_mutex_unlock,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
#endif /* NEED_GCRYPT_LOCKING */
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
#ifndef CRYPTO_set_locking_callback
#define NEED_CRYPTO_LOCKING
#endif
#ifdef NEED_CRYPTO_LOCKING
/**
* Mutexes for OpenSSL
*/
std::vector<std::unique_ptr<Mutex>> g_OpenSSLMutexes;
static void openssl_locking(int mode, int n, const char* file, int line)
{
Mutex* mutex = g_OpenSSLMutexes[n].get();
if (mode & CRYPTO_LOCK)
{
mutex->Lock();
}
else
{
mutex->Unlock();
}
}
static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line)
{
return (CRYPTO_dynlock_value*)new Mutex();
}
static void openssl_dynlock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line)
{
Mutex* mutex = (Mutex*)l;
delete mutex;
}
static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
{
Mutex* mutex = (Mutex*)l;
if (mode & CRYPTO_LOCK)
{
mutex->Lock();
}
else
{
mutex->Unlock();
}
}
#endif /* NEED_CRYPTO_LOCKING */
#endif /* HAVE_OPENSSL */
void TlsSocket::Init()
{
debug("Initializing TLS library");
#ifdef HAVE_LIBGNUTLS
int error_code;
#ifdef NEED_GCRYPT_LOCKING
error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex);
if (error_code != 0)
{
error("Could not initialize libcrypt");
return;
}
#endif /* NEED_GCRYPT_LOCKING */
error_code = gnutls_global_init();
if (error_code != 0)
{
error("Could not initialize libgnutls");
return;
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
#ifdef NEED_CRYPTO_LOCKING
for (int i = 0, num = CRYPTO_num_locks(); i < num; i++)
{
g_OpenSSLMutexes.emplace_back(std::make_unique<Mutex>());
}
CRYPTO_set_locking_callback(openssl_locking);
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock);
#endif /* NEED_CRYPTO_LOCKING */
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
#endif /* HAVE_OPENSSL */
}
void TlsSocket::Final()
{
#ifdef HAVE_LIBGNUTLS
gnutls_global_deinit();
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
#ifndef LIBRESSL_VERSION_NUMBER
FIPS_mode_set(0);
#endif
#ifdef NEED_CRYPTO_LOCKING
CRYPTO_set_locking_callback(nullptr);
CRYPTO_set_id_callback(nullptr);
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_remove_state(0);
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && ! defined (LIBRESSL_VERSION_NUMBER)
SSL_COMP_free_compression_methods();
#endif
//ENGINE_cleanup();
CONF_modules_free();
CONF_modules_unload(1);
#ifndef OPENSSL_NO_COMP
COMP_zlib_cleanup();
#endif
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
#endif /* HAVE_OPENSSL */
}
TlsSocket::~TlsSocket()
{
Close();
#ifdef HAVE_OPENSSL
#if OPENSSL_VERSION_NUMBER < 0x10100000L
ERR_remove_state(0);
#endif
#endif
}
void TlsSocket::ReportError(const char* errMsg, bool suppressable)
{
#ifdef HAVE_LIBGNUTLS
const char* errstr = gnutls_strerror(m_retCode);
if (suppressable && m_suppressErrors)
{
debug("%s: %s", errMsg, errstr);
}
else
{
PrintError(BString<1024>("%s: %s", errMsg, errstr));
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
int errcode = ERR_get_error();
do
{
char errstr[1024];
ERR_error_string_n(errcode, errstr, sizeof(errstr));
errstr[1024-1] = '\0';
if (suppressable && m_suppressErrors)
{
debug("%s: %s", errMsg, errstr);
}
else if (errcode != 0)
{
PrintError(BString<1024>("%s: %s", errMsg, errstr));
}
else
{
PrintError(errMsg);
}
errcode = ERR_get_error();
} while (errcode);
#endif /* HAVE_OPENSSL */
}
void TlsSocket::PrintError(const char* errMsg)
{
error("%s", errMsg);
}
bool TlsSocket::Start()
{
#ifdef HAVE_LIBGNUTLS
gnutls_certificate_credentials_t cred;
m_retCode = gnutls_certificate_allocate_credentials(&cred);
if (m_retCode != 0)
{
ReportError("Could not create TLS context", false);
return false;
}
m_context = cred;
if (m_certFile && m_keyFile)
{
m_retCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_context,
m_certFile, m_keyFile, GNUTLS_X509_FMT_PEM);
if (m_retCode != 0)
{
ReportError("Could not load certificate or key file", false);
Close();
return false;
}
}
gnutls_session_t sess;
m_retCode = gnutls_init(&sess, m_isClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
if (m_retCode != 0)
{
ReportError("Could not create TLS session", false);
Close();
return false;
}
m_session = sess;
m_initialized = true;
const char* priority = !m_cipher.Empty() ? m_cipher.Str() :
(m_certFile && m_keyFile ? "NORMAL:!VERS-SSL3.0" : "NORMAL");
m_retCode = gnutls_priority_set_direct((gnutls_session_t)m_session, priority, nullptr);
if (m_retCode != 0)
{
ReportError("Could not select cipher for TLS", false);
Close();
return false;
}
if (m_host)
{
m_retCode = gnutls_server_name_set((gnutls_session_t)m_session, GNUTLS_NAME_DNS, m_host, m_host.Length());
if (m_retCode != 0)
{
ReportError("Could not set hostname for TLS");
Close();
return false;
}
}
m_retCode = gnutls_credentials_set((gnutls_session_t)m_session, GNUTLS_CRD_CERTIFICATE,
(gnutls_certificate_credentials_t*)m_context);
if (m_retCode != 0)
{
ReportError("Could not initialize TLS session", false);
Close();
return false;
}
gnutls_transport_set_ptr((gnutls_session_t)m_session, (gnutls_transport_ptr_t)(size_t)m_socket);
m_retCode = gnutls_handshake((gnutls_session_t)m_session);
if (m_retCode != 0)
{
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
Close();
return false;
}
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
{
Close();
return false;
}
m_connected = true;
return true;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
m_context = SSL_CTX_new(SSLv23_method());
if (!m_context)
{
ReportError("Could not create TLS context", false);
return false;
}
if (m_certFile && m_keyFile)
{
if (SSL_CTX_use_certificate_chain_file((SSL_CTX*)m_context, m_certFile) != 1)
{
ReportError("Could not load certificate file", false);
Close();
return false;
}
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_context, m_keyFile, SSL_FILETYPE_PEM) != 1)
{
ReportError("Could not load key file", false);
Close();
return false;
}
if (!SSL_CTX_set_options((SSL_CTX*)m_context, SSL_OP_NO_SSLv3))
{
ReportError("Could not select minimum protocol version for TLS", false);
Close();
return false;
}
// For ECC certificates
EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
if (!ecdh)
{
ReportError("Could not generate ecdh parameters for TLS", false);
Close();
return false;
}
if (!SSL_CTX_set_tmp_ecdh((SSL_CTX*)m_context, ecdh))
{
ReportError("Could not set ecdh parameters for TLS", false);
EC_KEY_free(ecdh);
Close();
return false;
}
EC_KEY_free(ecdh);
}
if (m_isClient && !m_certStore.Empty())
{
// Enable certificate validation
if (SSL_CTX_load_verify_locations((SSL_CTX*)m_context, m_certStore, nullptr) != 1)
{
ReportError("Could not set certificate store location", false);
Close();
return false;
}
SSL_CTX_set_verify((SSL_CTX*)m_context, SSL_VERIFY_PEER, nullptr);
}
m_session = SSL_new((SSL_CTX*)m_context);
if (!m_session)
{
ReportError("Could not create TLS session", false);
Close();
return false;
}
if (!m_cipher.Empty() && !SSL_set_cipher_list((SSL*)m_session, m_cipher))
{
ReportError("Could not select cipher for TLS", false);
Close();
return false;
}
if (m_isClient && m_host && !SSL_set_tlsext_host_name((SSL*)m_session, m_host))
{
ReportError("Could not set host name for TLS");
Close();
return false;
}
if (!SSL_set_fd((SSL*)m_session, (int)m_socket))
{
ReportError("Could not set the file descriptor for TLS");
Close();
return false;
}
int error_code = m_isClient ? SSL_connect((SSL*)m_session) : SSL_accept((SSL*)m_session);
if (error_code < 1)
{
long verifyRes = SSL_get_verify_result((SSL*)m_session);
if (verifyRes != X509_V_OK)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
" For more info visit http://nzbget.net/certificate-verification",
*m_host, X509_verify_cert_error_string(verifyRes)));
}
else
{
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
}
Close();
return false;
}
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
{
Close();
return false;
}
m_connected = true;
return true;
#endif /* HAVE_OPENSSL */
}
bool TlsSocket::ValidateCert()
{
#ifdef HAVE_LIBGNUTLS
#if GNUTLS_VERSION_NUMBER >= 0x030104
#if GNUTLS_VERSION_NUMBER >= 0x030306
if (FileSystem::DirectoryExists(m_certStore))
{
if (gnutls_certificate_set_x509_trust_dir((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
{
ReportError("Could not set certificate store location");
return false;
}
}
else
#endif
{
if (gnutls_certificate_set_x509_trust_file((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
{
ReportError("Could not set certificate store location");
return false;
}
}
unsigned int status = 0;
if (gnutls_certificate_verify_peers3((gnutls_session_t)m_session, m_host, &status) != 0 ||
gnutls_certificate_type_get((gnutls_session_t)m_session) != GNUTLS_CRT_X509)
{
ReportError("Could not verify TLS certificate");
return false;
}
if (status != 0)
{
if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
{
// Extracting hostname from the certificate
unsigned int cert_list_size = 0;
const gnutls_datum_t* cert_list = gnutls_certificate_get_peers((gnutls_session_t)m_session, &cert_list_size);
if (cert_list_size > 0)
{
gnutls_x509_crt_t cert;
gnutls_x509_crt_init(&cert);
gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
char dn[256];
size_t size = sizeof(dn);
if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &size) == 0)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
" For more info visit http://nzbget.net/certificate-verification", *m_host, dn));
gnutls_x509_crt_deinit(cert);
return false;
}
gnutls_x509_crt_deinit(cert);
}
}
gnutls_datum_t msgdata;
if (gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, &msgdata, 0) == 0)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
" For more info visit http://nzbget.net/certificate-verification", *m_host, msgdata.data));
gnutls_free(&msgdata);
}
else
{
ReportError(BString<1024>("TLS certificate verification failed for %s."
" For more info visit http://nzbget.net/certificate-verification", *m_host));
}
return false;
}
#endif
return true;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
// verify a server certificate was presented during the negotiation
X509* cert = SSL_get_peer_certificate((SSL*)m_session);
if (!cert)
{
PrintError(BString<1024>("TLS certificate verification failed for %s: no certificate provided by server."
" For more info visit http://nzbget.net/certificate-verification", *m_host));
return false;
}
#ifdef HAVE_X509_CHECK_HOST
// hostname verification
if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
{
const unsigned char* certHost = nullptr;
// Find the position of the CN field in the Subject field of the certificate
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
if (common_name_loc >= 0)
{
// Extract the CN field
X509_NAME_ENTRY* common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
if (common_name_entry != nullptr)
{
// Convert the CN field to a C string
ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
if (common_name_asn1 != nullptr)
{
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
certHost = ASN1_STRING_get0_data(common_name_asn1);
#else
certHost = ASN1_STRING_data(common_name_asn1);
#endif
}
}
}
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
" For more info visit http://nzbget.net/certificate-verification", *m_host, certHost));
X509_free(cert);
return false;
}
#endif
X509_free(cert);
return true;
#endif /* HAVE_OPENSSL */
}
void TlsSocket::Close()
{
if (m_session)
{
#ifdef HAVE_LIBGNUTLS
if (m_connected)
{
gnutls_bye((gnutls_session_t)m_session, GNUTLS_SHUT_WR);
}
if (m_initialized)
{
gnutls_deinit((gnutls_session_t)m_session);
}
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
if (m_connected)
{
SSL_shutdown((SSL*)m_session);
}
SSL_free((SSL*)m_session);
#endif /* HAVE_OPENSSL */
m_session = nullptr;
}
if (m_context)
{
#ifdef HAVE_LIBGNUTLS
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_context);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
SSL_CTX_free((SSL_CTX*)m_context);
#endif /* HAVE_OPENSSL */
m_context = nullptr;
}
}
int TlsSocket::Send(const char* buffer, int size)
{
#ifdef HAVE_LIBGNUTLS
m_retCode = gnutls_record_send((gnutls_session_t)m_session, buffer, size);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
m_retCode = SSL_write((SSL*)m_session, buffer, size);
#endif /* HAVE_OPENSSL */
if (m_retCode < 0)
{
#ifdef HAVE_OPENSSL
if (ERR_peek_error() == 0)
{
ReportError("Could not write to TLS-Socket: Connection closed by remote host");
}
else
#endif /* HAVE_OPENSSL */
ReportError("Could not write to TLS-Socket");
return -1;
}
return m_retCode;
}
int TlsSocket::Recv(char* buffer, int size)
{
#ifdef HAVE_LIBGNUTLS
m_retCode = gnutls_record_recv((gnutls_session_t)m_session, buffer, size);
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
m_retCode = SSL_read((SSL*)m_session, buffer, size);
#endif /* HAVE_OPENSSL */
if (m_retCode < 0)
{
#ifdef HAVE_OPENSSL
if (ERR_peek_error() == 0)
{
ReportError("Could not read from TLS-Socket: Connection closed by remote host");
}
else
#endif /* HAVE_OPENSSL */
{
ReportError("Could not read from TLS-Socket");
}
return -1;
}
return m_retCode;
}
#endif

View File

@@ -0,0 +1,69 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2008-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef TLSSOCKET_H
#define TLSSOCKET_H
#ifndef DISABLE_TLS
#include "NString.h"
class TlsSocket
{
public:
TlsSocket(SOCKET socket, bool isClient, const char* host,
const char* certFile, const char* keyFile, const char* cipher) :
m_socket(socket), m_isClient(isClient), m_host(host),
m_certFile(certFile), m_keyFile(keyFile), m_cipher(cipher) {}
virtual ~TlsSocket();
static void Init();
static void InitOptions(const char* certStore) { m_certStore = certStore; }
static void Final();
bool Start();
void Close();
int Send(const char* buffer, int size);
int Recv(char* buffer, int size);
void SetSuppressErrors(bool suppressErrors) { m_suppressErrors = suppressErrors; }
protected:
virtual void PrintError(const char* errMsg);
private:
SOCKET m_socket;
bool m_isClient;
CString m_host;
CString m_certFile;
CString m_keyFile;
CString m_cipher;
bool m_suppressErrors = false;
bool m_initialized = false;
bool m_connected = false;
int m_retCode;
static CString m_certStore;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TlsSocket.h
void* m_context = nullptr;
void* m_session = nullptr;
void ReportError(const char* errMsg, bool suppressable = true);
bool ValidateCert();
};
#endif
#endif

View File

@@ -0,0 +1,666 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2012-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "WebDownloader.h"
#include "Log.h"
#include "Options.h"
#include "WorkState.h"
#include "Util.h"
#include "FileSystem.h"
WebDownloader::WebDownloader()
{
debug("Creating WebDownloader");
SetLastUpdateTimeNow();
}
void WebDownloader::SetUrl(const char* url)
{
m_url = WebUtil::UrlEncode(url);
}
void WebDownloader::SetStatus(EStatus status)
{
m_status = status;
Notify(nullptr);
}
void WebDownloader::SetLastUpdateTimeNow()
{
m_lastUpdateTime = Util::CurrentTime();
}
void WebDownloader::Run()
{
debug("Entering WebDownloader-loop");
SetStatus(adRunning);
int remainedDownloadRetries = g_Options->GetUrlRetries() > 0 ? g_Options->GetUrlRetries() : 1;
int remainedConnectRetries = remainedDownloadRetries > 10 ? remainedDownloadRetries : 10;
if (!m_retry)
{
remainedDownloadRetries = 1;
remainedConnectRetries = 1;
}
EStatus Status = adFailed;
while (!IsStopped() && remainedDownloadRetries > 0 && remainedConnectRetries > 0)
{
SetLastUpdateTimeNow();
Status = DownloadWithRedirects(5);
if ((((Status == adFailed) && (remainedDownloadRetries > 1)) ||
((Status == adConnectError) && (remainedConnectRetries > 1)))
&& !IsStopped() && !(!m_force && g_WorkState->GetPauseDownload()))
{
detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
int msec = 0;
while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
!(!m_force && g_WorkState->GetPauseDownload()))
{
Util::Sleep(100);
msec += 100;
}
}
if (IsStopped() || (!m_force && g_WorkState->GetPauseDownload()))
{
Status = adRetry;
break;
}
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
{
break;
}
if (Status != adConnectError)
{
remainedDownloadRetries--;
}
else
{
remainedConnectRetries--;
}
}
if (Status != adFinished && Status != adRetry)
{
Status = adFailed;
}
if (Status == adFailed)
{
if (IsStopped())
{
detail("Download %s cancelled", *m_infoName);
}
else
{
error("Download %s failed", *m_infoName);
}
}
if (Status == adFinished)
{
detail("Download %s completed", *m_infoName);
}
SetStatus(Status);
debug("Exiting WebDownloader-loop");
}
WebDownloader::EStatus WebDownloader::Download()
{
EStatus Status = adRunning;
URL url(m_url);
Status = CreateConnection(&url);
if (Status != adRunning)
{
return Status;
}
m_connection->SetTimeout(g_Options->GetUrlTimeout());
m_connection->SetSuppressErrors(false);
// connection
bool connected = m_connection->Connect();
if (!connected || IsStopped())
{
FreeConnection();
return adConnectError;
}
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s", *m_infoName);
SendHeaders(&url);
Status = DownloadHeaders();
if (Status == adRunning)
{
Status = DownloadBody();
}
if (IsStopped())
{
Status = adFailed;
}
FreeConnection();
if (Status != adFinished)
{
// Download failed, delete broken output file
FileSystem::DeleteFile(m_outputFilename);
}
return Status;
}
WebDownloader::EStatus WebDownloader::DownloadWithRedirects(int maxRedirects)
{
// do sync download, following redirects
EStatus status = adRedirect;
while (status == adRedirect && maxRedirects >= 0)
{
maxRedirects--;
status = Download();
}
if (status == adRedirect && maxRedirects < 0)
{
warn("Too many redirects for %s", *m_infoName);
status = adFailed;
}
return status;
}
WebDownloader::EStatus WebDownloader::CreateConnection(URL *url)
{
if (!url->IsValid())
{
error("URL is not valid: %s", url->GetAddress());
return adFatalError;
}
int port = url->GetPort();
if (port == 0 && !strcasecmp(url->GetProtocol(), "http"))
{
port = 80;
}
if (port == 0 && !strcasecmp(url->GetProtocol(), "https"))
{
port = 443;
}
if (strcasecmp(url->GetProtocol(), "http") && strcasecmp(url->GetProtocol(), "https"))
{
error("Unsupported protocol in URL: %s", url->GetAddress());
return adFatalError;
}
#ifdef DISABLE_TLS
if (!strcasecmp(url->GetProtocol(), "https"))
{
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", url->GetAddress());
return adFatalError;
}
#endif
bool tls = !strcasecmp(url->GetProtocol(), "https");
m_connection = std::make_unique<Connection>(url->GetHost(), port, tls);
return adRunning;
}
void WebDownloader::SendHeaders(URL *url)
{
// retrieve file
m_connection->WriteLine(BString<1024>("GET %s HTTP/1.0\r\n", url->GetResource()));
m_connection->WriteLine(BString<1024>("User-Agent: nzbget/%s\r\n", Util::VersionRevision()));
if ((!strcasecmp(url->GetProtocol(), "http") && (url->GetPort() == 80 || url->GetPort() == 0)) ||
(!strcasecmp(url->GetProtocol(), "https") && (url->GetPort() == 443 || url->GetPort() == 0)))
{
m_connection->WriteLine(BString<1024>("Host: %s\r\n", url->GetHost()));
}
else
{
m_connection->WriteLine(BString<1024>("Host: %s:%i\r\n", url->GetHost(), url->GetPort()));
}
m_connection->WriteLine("Accept: */*\r\n");
#ifndef DISABLE_GZIP
m_connection->WriteLine("Accept-Encoding: gzip\r\n");
#endif
m_connection->WriteLine("Connection: close\r\n");
m_connection->WriteLine("\r\n");
}
WebDownloader::EStatus WebDownloader::DownloadHeaders()
{
EStatus Status = adRunning;
m_confirmedLength = false;
CharBuffer lineBuf(1024*10);
m_contentLen = -1;
bool firstLine = true;
m_gzip = false;
m_redirecting = false;
m_redirected = false;
// Headers
while (!IsStopped())
{
SetLastUpdateTimeNow();
int len = 0;
char* line = m_connection->ReadLine(lineBuf, lineBuf.Size(), &len);
if (firstLine)
{
Status = CheckResponse(lineBuf);
if (Status != adRunning)
{
break;
}
firstLine = false;
}
// Have we encountered a timeout?
if (!line)
{
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", *m_infoName);
}
Status = adFailed;
break;
}
debug("Header: %s", line);
// detect body of response
if (*line == '\r' || *line == '\n')
{
break;
}
Util::TrimRight(line);
ProcessHeader(line);
if (m_redirected)
{
Status = adRedirect;
break;
}
}
return Status;
}
WebDownloader::EStatus WebDownloader::DownloadBody()
{
EStatus Status = adRunning;
m_outFile.Close();
bool end = false;
CharBuffer lineBuf(1024*10);
int writtenLen = 0;
#ifndef DISABLE_GZIP
m_gUnzipStream.reset();
if (m_gzip)
{
m_gUnzipStream = std::make_unique<GUnzipStream>(1024*10);
}
#endif
// Body
while (!IsStopped())
{
SetLastUpdateTimeNow();
char* buffer;
int len;
m_connection->ReadBuffer(&buffer, &len);
if (len == 0)
{
len = m_connection->TryRecv(lineBuf, lineBuf.Size());
buffer = lineBuf;
}
// Connection closed or timeout?
if (len <= 0)
{
if (len == 0 && m_contentLen == -1 && writtenLen > 0)
{
end = true;
break;
}
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", *m_infoName);
}
Status = adFailed;
break;
}
// write to output file
if (!Write(buffer, len))
{
Status = adFatalError;
break;
}
writtenLen += len;
//detect end of file
if (writtenLen == m_contentLen || (m_contentLen == -1 && m_gzip && m_confirmedLength))
{
end = true;
break;
}
}
#ifndef DISABLE_GZIP
m_gUnzipStream.reset();
#endif
m_outFile.Close();
if (!end && Status == adRunning && !IsStopped())
{
warn("URL %s failed: file incomplete", *m_infoName);
Status = adFailed;
}
if (end)
{
Status = adFinished;
}
return Status;
}
WebDownloader::EStatus WebDownloader::CheckResponse(const char* response)
{
if (!response)
{
if (!IsStopped())
{
warn("URL %s: Connection closed by remote host", *m_infoName);
}
return adConnectError;
}
const char* hTTPResponse = strchr(response, ' ');
if (strncmp(response, "HTTP", 4) || !hTTPResponse)
{
warn("URL %s failed: %s", *m_infoName, response);
return adFailed;
}
hTTPResponse++;
if (!strncmp(hTTPResponse, "400", 3) || !strncmp(hTTPResponse, "499", 3))
{
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
return adConnectError;
}
else if (!strncmp(hTTPResponse, "404", 3))
{
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
return adNotFound;
}
else if (!strncmp(hTTPResponse, "301", 3) || !strncmp(hTTPResponse, "302", 3) ||
!strncmp(hTTPResponse, "303", 3) || !strncmp(hTTPResponse, "307", 3) ||
!strncmp(hTTPResponse, "308", 3))
{
m_redirecting = true;
return adRunning;
}
else if (!strncmp(hTTPResponse, "200", 3))
{
// OK
return adRunning;
}
else
{
// unknown error, no special handling
warn("URL %s failed: %s", *m_infoName, response);
return adFailed;
}
}
void WebDownloader::ProcessHeader(const char* line)
{
if (!strncasecmp(line, "Content-Length: ", 16))
{
m_contentLen = atoi(line + 16);
m_confirmedLength = true;
}
else if (!strncasecmp(line, "Content-Encoding: gzip", 22))
{
m_gzip = true;
}
else if (!strncasecmp(line, "Content-Disposition: ", 21))
{
ParseFilename(line);
}
else if (m_redirecting && !strncasecmp(line, "Location: ", 10))
{
ParseRedirect(line + 10);
m_redirected = true;
}
}
void WebDownloader::ParseFilename(const char* contentDisposition)
{
// Examples:
// Content-Disposition: attachment; filename="fname.ext"
// Content-Disposition: attachement;filename=fname.ext
// Content-Disposition: attachement;filename=fname.ext;
const char *p = strstr(contentDisposition, "filename");
if (!p)
{
return;
}
p = strchr(p, '=');
if (!p)
{
return;
}
p++;
while (*p == ' ') p++;
BString<1024> fname = p;
char *pe = fname + strlen(fname) - 1;
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
*pe = '\0';
pe--;
}
WebUtil::HttpUnquote(fname);
m_originalFilename = FileSystem::BaseFileName(fname);
debug("OriginalFilename: %s", *m_originalFilename);
}
void WebDownloader::ParseRedirect(const char* location)
{
const char* newLocation = location;
BString<1024> urlBuf;
URL newUrl(newLocation);
if (!newUrl.IsValid())
{
// redirect within host
BString<1024> resource;
URL oldUrl(m_url);
if (*location == '/')
{
// absolute path within host
resource = location;
}
else
{
// relative path within host
resource = oldUrl.GetResource();
char* p = strchr(resource, '?');
if (p)
{
*p = '\0';
}
p = strrchr(resource, '/');
if (p)
{
p[1] = '\0';
}
resource.Append(location);
}
if (oldUrl.GetPort() > 0)
{
urlBuf.Format("%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), *resource);
}
else
{
urlBuf.Format("%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), *resource);
}
newLocation = urlBuf;
}
detail("URL %s redirected to %s", *m_url, newLocation);
SetUrl(newLocation);
}
bool WebDownloader::Write(void* buffer, int len)
{
if (!m_outFile.Active() && !PrepareFile())
{
return false;
}
#ifndef DISABLE_GZIP
if (m_gzip)
{
m_gUnzipStream->Write(buffer, len);
const void *outBuf;
int outLen = 1;
while (outLen > 0)
{
GUnzipStream::EStatus gZStatus = m_gUnzipStream->Read(&outBuf, &outLen);
if (gZStatus == GUnzipStream::zlError)
{
error("URL %s: GUnzip failed", *m_infoName);
return false;
}
if (outLen > 0 && m_outFile.Write(outBuf, outLen) <= 0)
{
return false;
}
if (gZStatus == GUnzipStream::zlFinished)
{
m_confirmedLength = true;
return true;
}
}
return true;
}
else
#endif
return m_outFile.Write(buffer, len) > 0;
}
bool WebDownloader::PrepareFile()
{
// prepare file for writing
const char* filename = m_outputFilename;
if (!m_outFile.Open(filename, DiskFile::omWrite))
{
error("Could not %s file %s", "create", filename);
return false;
}
if (g_Options->GetWriteBuffer() > 0)
{
m_outFile.SetWriteBuffer(g_Options->GetWriteBuffer() * 1024);
}
return true;
}
void WebDownloader::LogDebugInfo()
{
info(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_status,
*Util::FormatTime(m_lastUpdateTime), FileSystem::BaseFileName(m_outputFilename));
}
void WebDownloader::Stop()
{
debug("Trying to stop WebDownloader");
Thread::Stop();
Guard guard(m_connectionMutex);
if (m_connection)
{
m_connection->SetSuppressErrors(true);
m_connection->Cancel();
}
debug("WebDownloader stopped successfully");
}
void WebDownloader::FreeConnection()
{
if (m_connection)
{
debug("Releasing connection");
Guard guard(m_connectionMutex);
if (m_connection->GetStatus() == Connection::csCancelled)
{
m_connection->Disconnect();
}
m_connection.reset();
}
}

View File

@@ -0,0 +1,104 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2012-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef WEBDOWNLOADER_H
#define WEBDOWNLOADER_H
#include "NString.h"
#include "Observer.h"
#include "Thread.h"
#include "Connection.h"
#include "FileSystem.h"
#include "Util.h"
class WebDownloader : public Thread, public Subject
{
public:
enum EStatus
{
adUndefined,
adRunning,
adFinished,
adFailed,
adRetry,
adNotFound,
adRedirect,
adConnectError,
adFatalError
};
WebDownloader();
EStatus GetStatus() { return m_status; }
virtual void Run();
virtual void Stop();
EStatus Download();
EStatus DownloadWithRedirects(int maxRedirects);
void SetInfoName(const char* infoName) { m_infoName = infoName; }
const char* GetInfoName() { return m_infoName; }
void SetUrl(const char* url);
const char* GetOutputFilename() { return m_outputFilename; }
void SetOutputFilename(const char* outputFilename) { m_outputFilename = outputFilename; }
time_t GetLastUpdateTime() { return m_lastUpdateTime; }
void SetLastUpdateTimeNow();
bool GetConfirmedLength() { return m_confirmedLength; }
const char* GetOriginalFilename() { return m_originalFilename; }
void SetForce(bool force) { m_force = force; }
void SetRetry(bool retry) { m_retry = retry; }
void LogDebugInfo();
protected:
virtual void ProcessHeader(const char* line);
private:
CString m_url;
CString m_outputFilename;
std::unique_ptr<Connection> m_connection;
Mutex m_connectionMutex;
EStatus m_status = adUndefined;
time_t m_lastUpdateTime;
CString m_infoName;
DiskFile m_outFile;
int m_contentLen;
bool m_confirmedLength = false;
CString m_originalFilename;
bool m_force = false;
bool m_redirecting;
bool m_redirected;
bool m_gzip;
bool m_retry = true;
#ifndef DISABLE_GZIP
std::unique_ptr<GUnzipStream> m_gUnzipStream;
#endif
void SetStatus(EStatus status);
bool Write(void* buffer, int len);
bool PrepareFile();
void FreeConnection();
EStatus CheckResponse(const char* response);
EStatus CreateConnection(URL *url);
void ParseFilename(const char* contentDisposition);
void SendHeaders(URL *url);
EStatus DownloadHeaders();
EStatus DownloadBody();
void ParseRedirect(const char* location);
};
#endif

View File

@@ -0,0 +1,131 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "CommandScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
static const int COMMANDPROCESS_SUCCESS = 93;
static const int COMMANDPROCESS_ERROR = 94;
bool CommandScriptController::StartScript(const char* scriptName, const char* command,
std::unique_ptr<Options::OptEntries> modifiedOptions)
{
CommandScriptController* scriptController = new CommandScriptController();
scriptController->m_script = scriptName;
scriptController->m_command = command;
scriptController->m_logId = g_CommandScriptLog->Reset();
scriptController->m_modifiedOptions = std::move(modifiedOptions);
scriptController->SetAutoDestroy(true);
scriptController->Start();
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
return false;
}
void CommandScriptController::Run()
{
ExecuteScriptList(m_script);
}
void CommandScriptController::ExecuteScript(ScriptConfig::Script* script)
{
PrintMessage(Message::mkInfo, "Executing script %s with command %s", script->GetName(), *m_command);
SetArgs({script->GetLocation()});
BString<1024> infoName("script %s with command %s", script->GetName(), *m_command);
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
PrepareParams(script->GetName());
int exitCode = Execute();
infoName[0] = 'S'; // uppercase
SetLogPrefix(nullptr);
switch (exitCode)
{
case COMMANDPROCESS_SUCCESS:
PrintMessage(Message::mkInfo, "%s successful", *infoName);
break;
case COMMANDPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", *infoName);
break;
default:
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", *infoName);
break;
}
}
void CommandScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetEnvVar("NZBCP_COMMAND", m_command);
PrepareEnvScript(nullptr, scriptName);
}
const char* CommandScriptController::GetOptValue(const char* name, const char* value)
{
Options::OptEntry* entry = m_modifiedOptions->FindOption(name);
return entry ? entry->GetValue() : value;
}
void CommandScriptController::AddMessage(Message::EKind kind, const char * text)
{
NzbScriptController::AddMessage(kind, text);
g_CommandScriptLog->AddMessage(m_logId, kind, text);
}
int CommandScriptLog::Reset()
{
Guard guard(m_logMutex);
m_messages.clear();
return ++m_idScriptGen;
}
void CommandScriptLog::AddMessage(int scriptId, Message::EKind kind, const char * text)
{
Guard guard(m_logMutex);
// save only messages from the last started script
if (scriptId == m_idScriptGen)
{
m_messages.emplace_back(++m_idMessageGen, kind, Util::CurrentTime(), text);
}
}

View File

@@ -0,0 +1,63 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef COMMANDSCRIPT_H
#define COMMANDSCRIPT_H
#include "NzbScript.h"
#include "Log.h"
class CommandScriptController : public Thread, public NzbScriptController
{
public:
virtual void Run();
static bool StartScript(const char* scriptName, const char* command, std::unique_ptr<Options::OptEntries> modifiedOptions);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
virtual void AddMessage(Message::EKind kind, const char* text);
virtual const char* GetOptValue(const char* name, const char* value);
private:
CString m_script;
CString m_command;
int m_logId;
std::unique_ptr<Options::OptEntries> m_modifiedOptions;
void PrepareParams(const char* scriptName);
};
class CommandScriptLog
{
public:
GuardedMessageList GuardMessages() { return GuardedMessageList(&m_messages, &m_logMutex); }
int Reset();
void AddMessage(int scriptId, Message::EKind kind, const char* text);
private:
MessageList m_messages;
Mutex m_logMutex;
int m_idMessageGen;
int m_idScriptGen;
};
extern CommandScriptLog* g_CommandScriptLog;
#endif

View File

@@ -0,0 +1,80 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "FeedScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
static const int FEED_SUCCESS = 93;
void FeedScriptController::ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success)
{
FeedScriptController scriptController;
scriptController.m_feedFile = feedFile;
scriptController.m_feedId = feedId;
scriptController.ExecuteScriptList(feedScript);
if (success)
{
*success = scriptController.m_success;
}
}
void FeedScriptController::ExecuteScript(ScriptConfig::Script* script)
{
if (!script->GetFeedScript())
{
return;
}
PrintMessage(Message::mkInfo, "Executing feed-script %s for Feed%i", script->GetName(), m_feedId);
SetArgs({script->GetLocation()});
BString<1024> infoName("feed-script %s for Feed%i", script->GetName(), m_feedId);
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
PrepareParams(script->GetName());
int exitCode = Execute();
if (exitCode != FEED_SUCCESS)
{
infoName[0] = 'F'; // uppercase
PrintMessage(Message::mkError, "%s failed", *infoName);
m_success = false;
}
SetLogPrefix(nullptr);
}
void FeedScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetEnvVar("NZBFP_FILENAME", m_feedFile);
SetIntEnvVar("NZBFP_FEEDID", m_feedId);
PrepareEnvScript(nullptr, scriptName);
}

View File

@@ -0,0 +1,42 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2015-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef FEEDSCRIPT_H
#define FEEDSCRIPT_H
#include "NzbScript.h"
class FeedScriptController : public NzbScriptController
{
public:
static void ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
private:
const char* m_feedFile;
int m_feedId;
bool m_success = true;
void PrepareParams(const char* scriptName);
};
#endif

View File

@@ -0,0 +1,86 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "NzbScript.h"
#include "Options.h"
#include "Log.h"
#include "FileSystem.h"
/**
* If szStripPrefix is not nullptr, only pp-parameters, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is nullptr, all pp-parameters are processed; without stripping.
*/
void NzbScriptController::PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix)
{
int prefixLen = stripPrefix ? strlen(stripPrefix) : 0;
for (NzbParameter& parameter : parameters)
{
const char* value = parameter.GetValue();
if (stripPrefix && !strncmp(parameter.GetName(), stripPrefix, prefixLen) && (int)strlen(parameter.GetName()) > prefixLen)
{
SetEnvVarSpecial("NZBPR", parameter.GetName() + prefixLen, value);
}
else if (!stripPrefix)
{
SetEnvVarSpecial("NZBPR", parameter.GetName(), value);
}
}
}
void NzbScriptController::PrepareEnvScript(NzbParameterList* parameters, const char* scriptName)
{
if (parameters)
{
PrepareEnvParameters(parameters, nullptr);
}
BString<1024> paramPrefix("%s:", scriptName);
if (parameters)
{
PrepareEnvParameters(parameters, paramPrefix);
}
PrepareEnvOptions(paramPrefix);
}
void NzbScriptController::ExecuteScriptList(const char* scriptList)
{
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if (scriptList && *scriptList)
{
// split szScriptList into tokens
Tokenizer tok(scriptList, ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
ExecuteScript(&script);
break;
}
}
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef NZBSCRIPT_H
#define NZBSCRIPT_H
#include "Script.h"
#include "DownloadInfo.h"
#include "ScriptConfig.h"
class NzbScriptController : public ScriptController
{
protected:
void PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix);
void PrepareEnvScript(NzbParameterList* parameters, const char* scriptName);
void ExecuteScriptList(const char* scriptList);
virtual void ExecuteScript(ScriptConfig::Script* script) = 0;
};
#endif

View File

@@ -0,0 +1,309 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "PostScript.h"
#include "Log.h"
#include "Util.h"
#include "Options.h"
#include "WorkState.h"
static const int POSTPROCESS_PARCHECK = 92;
static const int POSTPROCESS_SUCCESS = 93;
static const int POSTPROCESS_ERROR = 94;
static const int POSTPROCESS_NONE = 95;
void PostScriptController::StartJob(PostInfo* postInfo)
{
PostScriptController* scriptController = new PostScriptController();
scriptController->m_postInfo = postInfo;
scriptController->SetAutoDestroy(false);
scriptController->m_prefixLen = 0;
postInfo->SetPostThread(scriptController);
scriptController->Start();
}
void PostScriptController::Run()
{
StringBuilder scriptCommaList;
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
for (NzbParameter& parameter : m_postInfo->GetNzbInfo()->GetParameters())
{
const char* varname = parameter.GetName();
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname) - 1] == ':' &&
(!strcasecmp(parameter.GetValue(), "yes") || !strcasecmp(parameter.GetValue(), "on") || !strcasecmp(parameter.GetValue(), "1")))
{
CString scriptName(varname);
scriptName[strlen(scriptName) - 1] = '\0'; // remove trailing ':'
scriptCommaList.Append(scriptName);
scriptCommaList.Append(",");
}
}
m_postInfo->GetNzbInfo()->GetScriptStatuses()->clear();
}
ExecuteScriptList(scriptCommaList);
m_postInfo->SetStage(PostInfo::ptFinished);
m_postInfo->SetWorking(false);
}
void PostScriptController::ExecuteScript(ScriptConfig::Script* script)
{
// if any script has requested par-check, do not execute other scripts
if (!script->GetPostScript() || m_postInfo->GetRequestParCheck())
{
return;
}
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
BString<1024> progressLabel("Executing post-process-script %s", script->GetName());
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->SetProgressLabel(progressLabel);
}
SetArgs({script->GetLocation()});
BString<1024> infoName("post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
SetInfoName(infoName);
m_script = script;
SetLogPrefix(script->GetDisplayName());
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
PrepareParams(script->GetName());
int exitCode = Execute();
infoName[0] = 'P'; // uppercase
SetLogPrefix(nullptr);
ScriptStatus::EStatus status = AnalyseExitCode(exitCode, infoName);
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->GetNzbInfo()->GetScriptStatuses()->emplace_back(script->GetName(), status);
}
}
void PostScriptController::PrepareParams(const char* scriptName)
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
ResetEnv();
SetIntEnvVar("NZBPP_NZBID", m_postInfo->GetNzbInfo()->GetId());
SetEnvVar("NZBPP_NZBNAME", m_postInfo->GetNzbInfo()->GetName());
SetEnvVar("NZBPP_DIRECTORY", m_postInfo->GetNzbInfo()->GetDestDir());
SetEnvVar("NZBPP_NZBFILENAME", m_postInfo->GetNzbInfo()->GetFilename());
SetEnvVar("NZBPP_QUEUEDFILE", m_postInfo->GetNzbInfo()->GetQueuedFilename());
SetEnvVar("NZBPP_URL", m_postInfo->GetNzbInfo()->GetUrl());
SetEnvVar("NZBPP_FINALDIR", m_postInfo->GetNzbInfo()->GetFinalDir());
SetEnvVar("NZBPP_CATEGORY", m_postInfo->GetNzbInfo()->GetCategory());
SetIntEnvVar("NZBPP_HEALTH", m_postInfo->GetNzbInfo()->CalcHealth());
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_postInfo->GetNzbInfo()->CalcCriticalHealth(false));
SetEnvVar("NZBPP_DUPEKEY", m_postInfo->GetNzbInfo()->GetDupeKey());
SetIntEnvVar("NZBPP_DUPESCORE", m_postInfo->GetNzbInfo()->GetDupeScore());
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBPP_DUPEMODE", dupeModeName[m_postInfo->GetNzbInfo()->GetDupeMode()]);
BString<1024> status = m_postInfo->GetNzbInfo()->MakeTextStatus(true);
SetEnvVar("NZBPP_STATUS", status);
char* detail = strchr(status, '/');
if (detail) *detail = '\0';
SetEnvVar("NZBPP_TOTALSTATUS", status);
const char* scriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
SetEnvVar("NZBPP_SCRIPTSTATUS", scriptStatusName[m_postInfo->GetNzbInfo()->GetScriptStatuses()->CalcTotalStatus()]);
// deprecated
int parStatusCodes[] = { 0, 0, 1, 2, 3, 4 };
NzbInfo::EParStatus parStatus = m_postInfo->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_postInfo->GetNzbInfo()->GetDeleteStatus() != NzbInfo::dsNone ||
m_postInfo->GetNzbInfo()->GetMarkStatus() == NzbInfo::ksBad)
{
parStatus = NzbInfo::psFailure;
}
SetIntEnvVar("NZBPP_PARSTATUS", parStatusCodes[parStatus]);
// deprecated
int unpackStatus[] = { 0, 0, 1, 2, 3, 4 };
SetIntEnvVar("NZBPP_UNPACKSTATUS", unpackStatus[m_postInfo->GetNzbInfo()->GetUnpackStatus()]);
// deprecated
SetIntEnvVar("NZBPP_HEALTHDELETED", (int)m_postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsHealth);
SetIntEnvVar("NZBPP_TOTALARTICLES", (int)m_postInfo->GetNzbInfo()->GetTotalArticles());
SetIntEnvVar("NZBPP_SUCCESSARTICLES", (int)m_postInfo->GetNzbInfo()->GetSuccessArticles());
SetIntEnvVar("NZBPP_FAILEDARTICLES", (int)m_postInfo->GetNzbInfo()->GetFailedArticles());
for (ServerStat& serverStat : m_postInfo->GetNzbInfo()->GetServerStats())
{
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_SUCCESSARTICLES", serverStat.GetServerId()),
serverStat.GetSuccessArticles());
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_FAILEDARTICLES", serverStat.GetServerId()),
serverStat.GetFailedArticles());
}
PrepareEnvScript(m_postInfo->GetNzbInfo()->GetParameters(), scriptName);
}
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode, const char* upInfoName)
{
// The ScriptStatus is accumulated for all scripts:
// If any script has failed the status is "failure", etc.
switch (exitCode)
{
case POSTPROCESS_SUCCESS:
PrintMessage(Message::mkInfo, "%s successful", upInfoName);
return ScriptStatus::srSuccess;
case POSTPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", upInfoName);
return ScriptStatus::srFailure;
case POSTPROCESS_NONE:
PrintMessage(Message::mkInfo, "%s skipped", upInfoName);
return ScriptStatus::srNone;
#ifndef DISABLE_PARCHECK
case POSTPROCESS_PARCHECK:
if (m_postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped)
{
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", upInfoName);
return ScriptStatus::srFailure;
}
else
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", upInfoName);
m_postInfo->SetRequestParCheck(true);
m_postInfo->SetForceRepair(true);
return ScriptStatus::srSuccess;
}
break;
#endif
default:
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", upInfoName);
return ScriptStatus::srFailure;
}
}
void PostScriptController::AddMessage(Message::EKind kind, const char* text)
{
const char* msgText = text + m_prefixLen;
if (!strncmp(msgText, "[NZB] ", 6))
{
debug("Command %s detected", msgText + 6);
if (!strncmp(msgText + 6, "FINALDIR=", 9))
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->GetNzbInfo()->SetFinalDir(msgText + 6 + 9);
}
else if (!strncmp(msgText + 6, "DIRECTORY=", 10))
{
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->GetNzbInfo()->SetDestDir(msgText + 6 + 10);
}
else if (!strncmp(msgText + 6, "NZBPR_", 6))
{
CString param = msgText + 6 + 6;
char* value = strchr(param, '=');
if (value)
{
*value = '\0';
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->GetNzbInfo()->GetParameters()->SetParameter(param, value + 1);
}
else
{
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
{
SetLogPrefix(nullptr);
PrintMessage(Message::mkWarning, "Marking %s as bad", m_postInfo->GetNzbInfo()->GetName());
SetLogPrefix(m_script->GetDisplayName());
m_postInfo->GetNzbInfo()->SetMarkStatus(NzbInfo::ksBad);
}
else
{
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else
{
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
GuardedDownloadQueue guard = DownloadQueue::Guard();
m_postInfo->SetProgressLabel(text);
}
if (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
{
time_t stageTime = m_postInfo->GetStageTime();
time_t startTime = m_postInfo->GetStartTime();
time_t waitTime = Util::CurrentTime();
// wait until Post-processor is unpaused
while (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
{
Util::Sleep(100);
// update time stamps
time_t delta = Util::CurrentTime() - waitTime;
if (stageTime > 0)
{
m_postInfo->SetStageTime(stageTime + delta);
}
if (startTime > 0)
{
m_postInfo->SetStartTime(startTime + delta);
}
}
}
}
void PostScriptController::Stop()
{
debug("Stopping post-process-script");
Thread::Stop();
Terminate();
}

View File

@@ -0,0 +1,47 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef POSTSCRIPT_H
#define POSTSCRIPT_H
#include "Thread.h"
#include "NzbScript.h"
class PostScriptController : public Thread, public NzbScriptController
{
public:
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* postInfo);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
virtual void AddMessage(Message::EKind kind, const char* text);
private:
PostInfo* m_postInfo;
int m_prefixLen;
ScriptConfig::Script* m_script;
void PrepareParams(const char* scriptName);
ScriptStatus::EStatus AnalyseExitCode(int exitCode, const char* upInfoName);
};
#endif

View File

@@ -0,0 +1,495 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "NString.h"
#include "QueueScript.h"
#include "NzbScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
static const char* QUEUE_EVENT_NAMES[] = {
"FILE_DOWNLOADED",
"URL_COMPLETED",
"NZB_MARKED",
"NZB_ADDED",
"NZB_NAMED",
"NZB_DOWNLOADED",
"NZB_DELETED" };
class QueueScriptController : public Thread, public NzbScriptController
{
public:
virtual void Run();
static void StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
virtual void AddMessage(Message::EKind kind, const char* text);
private:
CString m_nzbName;
CString m_nzbFilename;
CString m_url;
CString m_category;
CString m_destDir;
CString m_queuedFilename;
int m_id;
int m_priority;
CString m_dupeKey;
EDupeMode m_dupeMode;
int m_dupeScore;
NzbParameterList m_parameters;
int m_prefixLen;
ScriptConfig::Script* m_script;
QueueScriptCoordinator::EEvent m_event;
bool m_markBad;
NzbInfo::EDeleteStatus m_deleteStatus;
NzbInfo::EUrlStatus m_urlStatus;
NzbInfo::EMarkStatus m_markStatus;
void PrepareParams(const char* scriptName);
};
void QueueScriptController::StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event)
{
QueueScriptController* scriptController = new QueueScriptController();
scriptController->m_nzbName = nzbInfo->GetName();
scriptController->m_nzbFilename = nzbInfo->GetFilename();
scriptController->m_url = nzbInfo->GetUrl();
scriptController->m_category = nzbInfo->GetCategory();
scriptController->m_destDir = nzbInfo->GetDestDir();
scriptController->m_queuedFilename = nzbInfo->GetQueuedFilename();
scriptController->m_id = nzbInfo->GetId();
scriptController->m_priority = nzbInfo->GetPriority();
scriptController->m_dupeKey = nzbInfo->GetDupeKey();
scriptController->m_dupeMode = nzbInfo->GetDupeMode();
scriptController->m_dupeScore = nzbInfo->GetDupeScore();
scriptController->m_parameters.CopyFrom(nzbInfo->GetParameters());
scriptController->m_script = script;
scriptController->m_event = event;
scriptController->m_prefixLen = 0;
scriptController->m_markBad = false;
scriptController->m_deleteStatus = nzbInfo->GetDeleteStatus();
scriptController->m_urlStatus = nzbInfo->GetUrlStatus();
scriptController->m_markStatus = nzbInfo->GetMarkStatus();
scriptController->SetAutoDestroy(true);
scriptController->Start();
}
void QueueScriptController::Run()
{
ExecuteScript(m_script);
SetLogPrefix(nullptr);
if (m_markBad)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_id);
if (nzbInfo)
{
nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
nzbInfo->SetDeleteStatus(NzbInfo::dsBad);
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, nullptr);
}
}
g_QueueScriptCoordinator->CheckQueue();
}
void QueueScriptController::ExecuteScript(ScriptConfig::Script* script)
{
PrintMessage(m_event == QueueScriptCoordinator::qeFileDownloaded ? Message::mkDetail : Message::mkInfo,
"Executing queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
SetArgs({script->GetLocation()});
BString<1024> infoName("queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
PrepareParams(script->GetName());
Execute();
SetLogPrefix(nullptr);
}
void QueueScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetEnvVar("NZBNA_NZBNAME", m_nzbName);
SetIntEnvVar("NZBNA_NZBID", m_id);
SetEnvVar("NZBNA_FILENAME", m_nzbFilename);
SetEnvVar("NZBNA_DIRECTORY", m_destDir);
SetEnvVar("NZBNA_QUEUEDFILE", m_queuedFilename);
SetEnvVar("NZBNA_URL", m_url);
SetEnvVar("NZBNA_CATEGORY", m_category);
SetIntEnvVar("NZBNA_PRIORITY", m_priority);
SetIntEnvVar("NZBNA_LASTID", m_id); // deprecated
SetEnvVar("NZBNA_DUPEKEY", m_dupeKey);
SetIntEnvVar("NZBNA_DUPESCORE", m_dupeScore);
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBNA_DUPEMODE", dupeModeName[m_dupeMode]);
SetEnvVar("NZBNA_EVENT", QUEUE_EVENT_NAMES[m_event]);
const char* deleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD", "GOOD", "COPY", "SCAN" };
SetEnvVar("NZBNA_DELETESTATUS", deleteStatusName[m_deleteStatus]);
const char* urlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
SetEnvVar("NZBNA_URLSTATUS", urlStatusName[m_urlStatus]);
const char* markStatusName[] = { "NONE", "BAD", "GOOD", "SUCCESS" };
SetEnvVar("NZBNA_MARKSTATUS", markStatusName[m_markStatus]);
PrepareEnvScript(&m_parameters, scriptName);
}
void QueueScriptController::AddMessage(Message::EKind kind, const char* text)
{
const char* msgText = text + m_prefixLen;
if (!strncmp(msgText, "[NZB] ", 6))
{
debug("Command %s detected", msgText + 6);
if (!strncmp(msgText + 6, "NZBPR_", 6))
{
CString param = msgText + 6 + 6;
char* value = strchr(param, '=');
if (value)
{
*value = '\0';
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
if (nzbInfo)
{
nzbInfo->GetParameters()->SetParameter(param, value + 1);
}
}
else
{
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else if (!strncmp(msgText + 6, "DIRECTORY=", 10) &&
m_event == QueueScriptCoordinator::qeNzbDownloaded)
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
if (nzbInfo)
{
nzbInfo->SetFinalDir(msgText + 6 + 10);
}
}
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
{
m_markBad = true;
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
if (nzbInfo)
{
nzbInfo->PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName);
nzbInfo->SetMarkStatus(NzbInfo::ksBad);
}
}
else
{
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else
{
NzbInfo* nzbInfo = nullptr;
{
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
if (nzbInfo)
{
nzbInfo->AddMessage(kind, text);
}
}
if (!nzbInfo)
{
ScriptController::AddMessage(kind, text);
}
}
}
void QueueScriptCoordinator::InitOptions()
{
m_hasQueueScripts = false;
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if (script.GetQueueScript())
{
m_hasQueueScripts = true;
break;
}
}
}
void QueueScriptCoordinator::EnqueueScript(NzbInfo* nzbInfo, EEvent event)
{
if (!m_hasQueueScripts)
{
return;
}
Guard guard(m_queueMutex);
if (event == qeNzbDownloaded)
{
// delete all other queued scripts for this nzb
m_queue.erase(std::remove_if(m_queue.begin(), m_queue.end(),
[nzbInfo](std::unique_ptr<QueueItem>& queueItem)
{
return queueItem->GetNzbId() == nzbInfo->GetId();
}),
m_queue.end());
}
// respect option "EventInterval"
time_t curTime = Util::CurrentTime();
if (event == qeFileDownloaded &&
(g_Options->GetEventInterval() == -1 ||
(g_Options->GetEventInterval() > 0 && curTime - nzbInfo->GetQueueScriptTime() > 0 &&
(int)(curTime - nzbInfo->GetQueueScriptTime()) < g_Options->GetEventInterval())))
{
return;
}
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
{
if (UsableScript(script, nzbInfo, event))
{
bool alreadyQueued = false;
if (event == qeFileDownloaded)
{
// check if this script is already queued for this nzb
for (QueueItem* queueItem : &m_queue)
{
if (queueItem->GetNzbId() == nzbInfo->GetId() && queueItem->GetScript() == &script)
{
alreadyQueued = true;
break;
}
}
}
if (!alreadyQueued)
{
std::unique_ptr<QueueItem> queueItem = std::make_unique<QueueItem>(nzbInfo->GetId(), &script, event);
if (m_curItem)
{
m_queue.push_back(std::move(queueItem));
}
else
{
m_curItem = std::move(queueItem);
QueueScriptController::StartScript(nzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
}
}
nzbInfo->SetQueueScriptTime(Util::CurrentTime());
}
}
}
bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event)
{
if (!script.GetQueueScript())
{
return false;
}
if (!Util::EmptyStr(script.GetQueueEvents()) && !strstr(script.GetQueueEvents(), QUEUE_EVENT_NAMES[event]))
{
return false;
}
// check extension scripts assigned for that nzb
for (NzbParameter& parameter : nzbInfo->GetParameters())
{
const char* varname = parameter.GetName();
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname)-1] == ':' &&
(!strcasecmp(parameter.GetValue(), "yes") ||
!strcasecmp(parameter.GetValue(), "on") ||
!strcasecmp(parameter.GetValue(), "1")))
{
BString<1024> scriptName = varname;
scriptName[strlen(scriptName)-1] = '\0'; // remove trailing ':'
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
}
// for URL-events the extension scripts are not assigned yet;
// instead we take the default extension scripts for the category (or global)
if (event == qeUrlCompleted)
{
const char* postScript = g_Options->GetExtensions();
if (!Util::EmptyStr(nzbInfo->GetCategory()))
{
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
{
postScript = categoryObj->GetExtensions();
}
}
if (!Util::EmptyStr(postScript))
{
Tokenizer tok(postScript, ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
return true;
}
}
}
}
return false;
}
NzbInfo* QueueScriptCoordinator::FindNzbInfo(DownloadQueue* downloadQueue, int nzbId)
{
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(nzbId);
if (nzbInfo)
{
return nzbInfo;
}
HistoryInfo* historyInfo = downloadQueue->GetHistory()->Find(nzbId);
if (historyInfo)
{
return historyInfo->GetNzbInfo();
}
return nullptr;
}
void QueueScriptCoordinator::CheckQueue()
{
if (m_stopped)
{
return;
}
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
Guard guard(m_queueMutex);
m_curItem.reset();
NzbInfo* curNzbInfo = nullptr;
Queue::iterator itCurItem;
for (Queue::iterator it = m_queue.begin(); it != m_queue.end(); )
{
std::unique_ptr<QueueItem>& queueItem = *it;
NzbInfo* nzbInfo = FindNzbInfo(downloadQueue, queueItem->GetNzbId());
// in a case this nzb must not be processed further - delete queue script from queue
EEvent event = queueItem->GetEvent();
bool ignoreEvent = !nzbInfo ||
(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone && event != qeNzbDeleted && event != qeNzbMarked) ||
(nzbInfo->GetMarkStatus() == NzbInfo::ksBad && event != qeNzbMarked);
if (ignoreEvent)
{
it = m_queue.erase(it);
if (curNzbInfo)
{
// process from the beginning, while "erase" invalidated "itCurItem"
curNzbInfo = nullptr;
it = m_queue.begin();
}
continue;
}
if (!m_curItem || queueItem->GetEvent() > m_curItem->GetEvent())
{
itCurItem = it;
curNzbInfo = nzbInfo;
}
it++;
}
if (curNzbInfo)
{
m_curItem = std::move(*itCurItem);
m_queue.erase(itCurItem);
QueueScriptController::StartScript(curNzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
}
}
bool QueueScriptCoordinator::HasJob(int nzbId, bool* active)
{
Guard guard(m_queueMutex);
bool working = m_curItem && m_curItem->GetNzbId() == nzbId;
if (active)
{
*active = working;
}
if (!working)
{
for (QueueItem* queueItem : &m_queue)
{
working = queueItem->GetNzbId() == nzbId;
if (working)
{
break;
}
}
}
return working;
}
int QueueScriptCoordinator::GetQueueSize()
{
Guard guard(m_queueMutex);
int queuedCount = m_queue.size();
if (m_curItem)
{
queuedCount++;
}
return queuedCount;
}

View File

@@ -0,0 +1,77 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2017 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef QUEUESCRIPT_H
#define QUEUESCRIPT_H
#include "DownloadInfo.h"
#include "ScriptConfig.h"
class QueueScriptCoordinator
{
public:
enum EEvent
{
qeFileDownloaded, // lowest priority
qeUrlCompleted,
qeNzbMarked,
qeNzbAdded,
qeNzbNamed,
qeNzbDownloaded,
qeNzbDeleted // highest priority
};
void Stop() { m_stopped = true; }
void InitOptions();
void EnqueueScript(NzbInfo* nzbInfo, EEvent event);
void CheckQueue();
bool HasJob(int nzbId, bool* active);
int GetQueueSize();
static NzbInfo* FindNzbInfo(DownloadQueue* downloadQueue, int nzbId);
private:
class QueueItem
{
public:
QueueItem(int nzbId, ScriptConfig::Script* script, EEvent event) :
m_nzbId(nzbId), m_script(script), m_event(event) {}
int GetNzbId() { return m_nzbId; }
ScriptConfig::Script* GetScript() { return m_script; }
EEvent GetEvent() { return m_event; }
private:
int m_nzbId;
ScriptConfig::Script* m_script;
EEvent m_event;
};
typedef std::deque<std::unique_ptr<QueueItem>> Queue;
Queue m_queue;
Mutex m_queueMutex;
std::unique_ptr<QueueItem> m_curItem;
bool m_hasQueueScripts = false;
bool m_stopped = false;
bool UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event);
};
extern QueueScriptCoordinator* g_QueueScriptCoordinator;
#endif

View File

@@ -0,0 +1,200 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "ScanScript.h"
#include "Scanner.h"
#include "Options.h"
#include "Log.h"
#include "FileSystem.h"
class ScanScriptCheck : public NzbScriptController
{
protected:
virtual void ExecuteScript(ScriptConfig::Script* script) { has |= script->GetScanScript(); }
bool has = false;
friend class ScanScriptController;
};
bool ScanScriptController::HasScripts()
{
ScanScriptCheck check;
check.ExecuteScriptList(g_Options->GetExtensions());
return check.has;
}
void ScanScriptController::ExecuteScripts(const char* nzbFilename,
const char* url, const char* directory, CString* nzbName, CString* category,
int* priority, NzbParameterList* parameters, bool* addTop, bool* addPaused,
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode)
{
ScanScriptController scriptController;
scriptController.m_nzbFilename = nzbFilename;
scriptController.m_url = url;
scriptController.m_directory = directory;
scriptController.m_nzbName = nzbName;
scriptController.m_category = category;
scriptController.m_parameters = parameters;
scriptController.m_priority = priority;
scriptController.m_addTop = addTop;
scriptController.m_addPaused = addPaused;
scriptController.m_dupeKey = dupeKey;
scriptController.m_dupeScore = dupeScore;
scriptController.m_dupeMode = dupeMode;
scriptController.m_prefixLen = 0;
const char* extensions = g_Options->GetExtensions();
if (!Util::EmptyStr(*category))
{
Options::Category* categoryObj = g_Options->FindCategory(*category, false);
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
{
extensions = categoryObj->GetExtensions();
}
}
scriptController.ExecuteScriptList(extensions);
}
void ScanScriptController::ExecuteScript(ScriptConfig::Script* script)
{
if (!script->GetScanScript() || !FileSystem::FileExists(m_nzbFilename))
{
return;
}
PrintMessage(Message::mkInfo, "Executing scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
SetArgs({script->GetLocation()});
BString<1024> infoName("scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
PrepareParams(script->GetName());
Execute();
SetLogPrefix(nullptr);
}
void ScanScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetEnvVar("NZBNP_FILENAME", m_nzbFilename);
SetEnvVar("NZBNP_URL", m_url);
SetEnvVar("NZBNP_NZBNAME", strlen(*m_nzbName) > 0 ? **m_nzbName : FileSystem::BaseFileName(m_nzbFilename));
SetEnvVar("NZBNP_CATEGORY", *m_category);
SetIntEnvVar("NZBNP_PRIORITY", *m_priority);
SetIntEnvVar("NZBNP_TOP", *m_addTop ? 1 : 0);
SetIntEnvVar("NZBNP_PAUSED", *m_addPaused ? 1 : 0);
SetEnvVar("NZBNP_DUPEKEY", *m_dupeKey);
SetIntEnvVar("NZBNP_DUPESCORE", *m_dupeScore);
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
SetEnvVar("NZBNP_DUPEMODE", dupeModeName[*m_dupeMode]);
// remove trailing slash
BString<1024> dir = m_directory;
int len = strlen(dir);
if (dir[len-1] == PATH_SEPARATOR)
{
dir[len-1] = '\0';
}
SetEnvVar("NZBNP_DIRECTORY", dir);
PrepareEnvScript(m_parameters, scriptName);
}
void ScanScriptController::AddMessage(Message::EKind kind, const char* text)
{
const char* msgText = text + m_prefixLen;
if (!strncmp(msgText, "[NZB] ", 6))
{
debug("Command %s detected", msgText + 6);
if (!strncmp(msgText + 6, "NZBNAME=", 8))
{
*m_nzbName = msgText + 6 + 8;
}
else if (!strncmp(msgText + 6, "CATEGORY=", 9))
{
*m_category = msgText + 6 + 9;
g_Scanner->InitPPParameters(*m_category, m_parameters, true);
}
else if (!strncmp(msgText + 6, "NZBPR_", 6))
{
CString param = msgText + 6 + 6;
char* value = strchr(param, '=');
if (value)
{
*value = '\0';
m_parameters->SetParameter(param, value + 1);
}
else
{
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else if (!strncmp(msgText + 6, "PRIORITY=", 9))
{
*m_priority = atoi(msgText + 6 + 9);
}
else if (!strncmp(msgText + 6, "TOP=", 4))
{
*m_addTop = atoi(msgText + 6 + 4) != 0;
}
else if (!strncmp(msgText + 6, "PAUSED=", 7))
{
*m_addPaused = atoi(msgText + 6 + 7) != 0;
}
else if (!strncmp(msgText + 6, "DUPEKEY=", 8))
{
*m_dupeKey = msgText + 6 + 8;
}
else if (!strncmp(msgText + 6, "DUPESCORE=", 10))
{
*m_dupeScore = atoi(msgText + 6 + 10);
}
else if (!strncmp(msgText + 6, "DUPEMODE=", 9))
{
const char* dupeMode = msgText + 6 + 9;
if (strcasecmp(dupeMode, "score") && strcasecmp(dupeMode, "all") && strcasecmp(dupeMode, "force"))
{
error("Invalid value \"%s\" for command \"DUPEMODE\" received from %s", dupeMode, GetInfoName());
return;
}
*m_dupeMode = !strcasecmp(dupeMode, "all") ? dmAll :
!strcasecmp(dupeMode, "force") ? dmForce : dmScore;
}
else
{
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
}
}
else
{
ScriptController::AddMessage(kind, text);
}
}

View File

@@ -0,0 +1,57 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCANSCRIPT_H
#define SCANSCRIPT_H
#include "NzbScript.h"
class ScanScriptController : public NzbScriptController
{
public:
static void ExecuteScripts(const char* nzbFilename, const char* url,
const char* directory, CString* nzbName, CString* category, int* priority,
NzbParameterList* parameters, bool* addTop, bool* addPaused,
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode);
static bool HasScripts();
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
virtual void AddMessage(Message::EKind kind, const char* text);
private:
const char* m_nzbFilename;
const char* m_url;
const char* m_directory;
CString* m_nzbName;
CString* m_category;
int* m_priority;
NzbParameterList* m_parameters;
bool* m_addTop;
bool* m_addPaused;
CString* m_dupeKey;
int* m_dupeScore;
EDupeMode* m_dupeMode;
int m_prefixLen;
void PrepareParams(const char* scriptName);
};
#endif

View File

@@ -0,0 +1,114 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "SchedulerScript.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "FileSystem.h"
void SchedulerScriptController::StartScript(const char* param, bool externalProcess, int taskId)
{
std::vector<CString> argv;
if (externalProcess && (argv = Util::SplitCommandLine(param)).empty())
{
error("Could not execute scheduled process-script, failed to parse command line: %s", param);
return;
}
SchedulerScriptController* scriptController = new SchedulerScriptController();
scriptController->m_externalProcess = externalProcess;
scriptController->m_script = param;
scriptController->m_taskId = taskId;
if (externalProcess)
{
scriptController->SetArgs(std::move(argv));
}
scriptController->SetAutoDestroy(true);
scriptController->Start();
}
void SchedulerScriptController::Run()
{
if (m_externalProcess)
{
ExecuteExternalProcess();
}
else
{
ExecuteScriptList(m_script);
}
}
void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* script)
{
if (!script->GetSchedulerScript())
{
return;
}
BString<1024> taskName(" for Task%i", m_taskId);
if (m_taskId == 0)
{
taskName = "";
}
PrintMessage(Message::mkInfo, "Executing scheduler-script %s%s", script->GetName(), *taskName);
SetArgs({script->GetLocation()});
BString<1024> infoName("scheduler-script %s%s", script->GetName(), *taskName);
SetInfoName(infoName);
SetLogPrefix(script->GetDisplayName());
PrepareParams(script->GetName());
Execute();
SetLogPrefix(nullptr);
}
void SchedulerScriptController::PrepareParams(const char* scriptName)
{
ResetEnv();
SetIntEnvVar("NZBSP_TASKID", m_taskId);
PrepareEnvScript(nullptr, scriptName);
}
void SchedulerScriptController::ExecuteExternalProcess()
{
info("Executing scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
BString<1024> infoName("scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
SetInfoName(infoName);
BString<1024> logPrefix = FileSystem::BaseFileName(GetScript());
if (char* ext = strrchr(logPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(logPrefix);
Execute();
}

View File

@@ -0,0 +1,45 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCHEDULERSCRIPT_H
#define SCHEDULERSCRIPT_H
#include "NString.h"
#include "NzbScript.h"
class SchedulerScriptController : public Thread, public NzbScriptController
{
public:
virtual void Run();
static void StartScript(const char* param, bool externalProcess, int taskId);
protected:
virtual void ExecuteScript(ScriptConfig::Script* script);
private:
CString m_script;
bool m_externalProcess;
int m_taskId;
void PrepareParams(const char* scriptName);
void ExecuteExternalProcess();
};
#endif

View File

@@ -0,0 +1,500 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "Util.h"
#include "FileSystem.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:";
static const char* TASK_TIME_SIGNATURE = "### TASK TIME:";
static const char* DEFINITION_SIGNATURE = "###";
void ScriptConfig::InitOptions()
{
InitScripts();
InitConfigTemplates();
CreateTasks();
}
bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
{
// read config file
DiskFile infile;
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omRead))
{
return false;
}
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename());
CString buf;
buf.Reserve(fileLen);
while (infile.ReadLine(buf, fileLen + 1))
{
// 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;
}
CString optname;
CString optvalue;
if (Options::SplitOptionString(buf, optname, optvalue))
{
optEntries->emplace_back(optname, optvalue);
}
}
infile.Close();
Options::ConvertOldOptions(optEntries);
return true;
}
bool ScriptConfig::SaveConfig(Options::OptEntries* optEntries)
{
// save to config file
DiskFile infile;
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omReadWrite))
{
return false;
}
std::vector<CString> config;
std::set<Options::OptEntry*> writtenOptions;
// read config file into memory array
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename()) + 1;
CString content;
content.Reserve(fileLen);
while (infile.ReadLine(content, fileLen + 1))
{
config.push_back(*content);
}
content.Clear();
// write config file back to disk, replace old values of existing options with new values
infile.Seek(0);
for (CString& buf : config)
{
const char* eq = strchr(buf, '=');
if (eq && buf[0] != '#')
{
// remove trailing '\n' and '\r' and spaces
buf.TrimRight();
CString optname;
CString optvalue;
if (g_Options->SplitOptionString(buf, optname, optvalue))
{
Options::OptEntry* optEntry = optEntries->FindOption(optname);
if (optEntry)
{
infile.Print("%s=%s\n", optEntry->GetName(), optEntry->GetValue());
writtenOptions.insert(optEntry);
}
}
}
else
{
infile.Print("%s", *buf);
}
}
// write new options
for (Options::OptEntry& optEntry : *optEntries)
{
std::set<Options::OptEntry*>::iterator fit = writtenOptions.find(&optEntry);
if (fit == writtenOptions.end())
{
infile.Print("%s=%s\n", optEntry.GetName(), optEntry.GetValue());
}
}
// close and truncate the file
int pos = (int)infile.Position();
infile.Close();
FileSystem::TruncateFile(g_Options->GetConfigFilename(), pos);
return true;
}
bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
{
CharBuffer buffer;
if (!FileSystem::LoadFileIntoBuffer(g_Options->GetConfigTemplate(), buffer, true))
{
return false;
}
configTemplates->emplace_back(Script("", ""), buffer);
if (!g_Options->GetScriptDir())
{
return true;
}
Scripts scriptList;
LoadScripts(&scriptList);
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
for (Script& script : scriptList)
{
DiskFile infile;
if (!infile.Open(script.GetLocation(), DiskFile::omRead))
{
configTemplates->emplace_back(std::move(script), "");
continue;
}
StringBuilder templ;
char buf[1024];
bool inConfig = false;
bool inHeader = false;
while (infile.ReadLine(buf, sizeof(buf) - 1))
{
if (!strncmp(buf, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) &&
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 (inConfig)
{
break;
}
inConfig = true;
inHeader = true;
continue;
}
inHeader &= !strncmp(buf, DEFINITION_SIGNATURE, definitionSignatureLen);
if (inConfig && !inHeader)
{
templ.Append(buf);
}
}
infile.Close();
configTemplates->emplace_back(std::move(script), templ);
}
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* scripts)
{
if (Util::EmptyStr(g_Options->GetScriptDir()))
{
return;
}
Scripts tmpScripts;
Tokenizer tokDir(g_Options->GetScriptDir(), ",;");
while (const char* scriptDir = tokDir.Next())
{
LoadScriptDir(&tmpScripts, scriptDir, false);
}
tmpScripts.sort(
[](Script& script1, Script& script2)
{
return strcmp(script1.GetName(), script2.GetName()) < 0;
});
// first add all scripts from ScriptOrder
Tokenizer tokOrder(g_Options->GetScriptOrder(), ",;");
while (const char* scriptName = tokOrder.Next())
{
Scripts::iterator pos = std::find_if(tmpScripts.begin(), tmpScripts.end(),
[scriptName](Script& script)
{
return !strcmp(script.GetName(), scriptName);
});
if (pos != tmpScripts.end())
{
scripts->splice(scripts->end(), tmpScripts, pos);
}
}
// then add all other scripts from scripts directory
scripts->splice(scripts->end(), std::move(tmpScripts));
BuildScriptDisplayNames(scripts);
}
void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir)
{
DirBrowser dir(directory);
while (const char* filename = dir.Next())
{
if (filename[0] != '.' && filename[0] != '_')
{
BString<1024> fullFilename("%s%c%s", directory, PATH_SEPARATOR, filename);
if (!FileSystem::DirectoryExists(fullFilename))
{
BString<1024> scriptName = BuildScriptName(directory, filename, isSubDir);
if (ScriptExists(scripts, scriptName))
{
continue;
}
Script script(scriptName, fullFilename);
if (LoadScriptFile(&script))
{
scripts->push_back(std::move(script));
}
}
else if (!isSubDir)
{
LoadScriptDir(scripts, fullFilename, true);
}
}
}
}
bool ScriptConfig::LoadScriptFile(Script* script)
{
DiskFile infile;
if (!infile.Open(script->GetLocation(), DiskFile::omRead))
{
return false;
}
CharBuffer buffer(1024 * 10 + 1);
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
const int taskTimeSignatureLen = strlen(TASK_TIME_SIGNATURE);
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
// check if the file contains pp-script-signature
// read first 10KB of the file and look for signature
int readBytes = (int)infile.Read(buffer, buffer.Size() - 1);
infile.Close();
buffer[readBytes] = '\0';
bool postScript = false;
bool scanScript = false;
bool queueScript = false;
bool schedulerScript = false;
bool feedScript = false;
char* queueEvents = nullptr;
char* taskTime = nullptr;
bool inConfig = false;
bool afterConfig = false;
// Declarations "QUEUE EVENT:" and "TASK TIME:" can be placed:
// - in script definition body (between opening and closing script signatures);
// - immediately before script definition (before opening script signature);
// - immediately after script definition (after closing script signature).
// The last two pissibilities are provided to increase compatibility of scripts with older
// nzbget versions which do not expect the extra declarations in the script defintion body.
Tokenizer tok(buffer, "\n\r", true);
while (char* line = tok.Next())
{
if (!strncmp(line, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen))
{
queueEvents = line + queueEventsSignatureLen;
}
else if (!strncmp(line, TASK_TIME_SIGNATURE, taskTimeSignatureLen))
{
taskTime = line + taskTimeSignatureLen;
}
bool header = !strncmp(line, DEFINITION_SIGNATURE, definitionSignatureLen);
if (!header && !inConfig)
{
queueEvents = nullptr;
taskTime = nullptr;
}
if (!header && afterConfig)
{
break;
}
if (!strncmp(line, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) && strstr(line, END_SCRIPT_SIGNATURE))
{
if (!inConfig)
{
inConfig = true;
postScript = strstr(line, POST_SCRIPT_SIGNATURE);
scanScript = strstr(line, SCAN_SCRIPT_SIGNATURE);
queueScript = strstr(line, QUEUE_SCRIPT_SIGNATURE);
schedulerScript = strstr(line, SCHEDULER_SCRIPT_SIGNATURE);
feedScript = strstr(line, FEED_SCRIPT_SIGNATURE);
}
else
{
afterConfig = true;
}
}
}
if (!(postScript || scanScript || queueScript || schedulerScript || feedScript))
{
return false;
}
// trim decorations
char* p;
while (queueEvents && *queueEvents && *(p = queueEvents + strlen(queueEvents) - 1) == '#') *p = '\0';
if (queueEvents) queueEvents = Util::Trim(queueEvents);
while (taskTime && *taskTime && *(p = taskTime + strlen(taskTime) - 1) == '#') *p = '\0';
if (taskTime) taskTime = Util::Trim(taskTime);
script->SetPostScript(postScript);
script->SetScanScript(scanScript);
script->SetQueueScript(queueScript);
script->SetSchedulerScript(schedulerScript);
script->SetFeedScript(feedScript);
script->SetQueueEvents(queueEvents);
script->SetTaskTime(taskTime);
return true;
}
BString<1024> ScriptConfig::BuildScriptName(const char* directory, const char* filename, bool isSubDir)
{
if (isSubDir)
{
BString<1024> directory2 = directory;
int len = strlen(directory2);
if (directory2[len-1] == PATH_SEPARATOR || directory2[len-1] == ALT_PATH_SEPARATOR)
{
// trim last path-separator
directory2[len-1] = '\0';
}
return BString<1024>("%s%c%s", FileSystem::BaseFileName(directory2), PATH_SEPARATOR, filename);
}
else
{
return filename;
}
}
bool ScriptConfig::ScriptExists(Scripts* scripts, const char* scriptName)
{
return std::find_if(scripts->begin(), scripts->end(),
[scriptName](Script& script)
{
return !strcmp(script.GetName(), scriptName);
}) != scripts->end();
}
void ScriptConfig::BuildScriptDisplayNames(Scripts* scripts)
{
// 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 (Script& script : scripts)
{
BString<1024> shortName = script.GetName();
if (char* ext = strrchr(shortName, '.')) *ext = '\0'; // strip file extension
const char* displayName = FileSystem::BaseFileName(shortName);
for (Script& script2 : scripts)
{
BString<1024> shortName2 = script2.GetName();
if (char* ext = strrchr(shortName2, '.')) *ext = '\0'; // strip file extension
const char* displayName2 = FileSystem::BaseFileName(shortName2);
if (!strcmp(displayName, displayName2) && script.GetName() != script2.GetName())
{
if (!strcmp(shortName, shortName2))
{
displayName = script.GetName();
}
else
{
displayName = shortName;
}
break;
}
}
script.SetDisplayName(displayName);
}
}
void ScriptConfig::CreateTasks()
{
for (Script& script : m_scripts)
{
if (script.GetSchedulerScript() && !Util::EmptyStr(script.GetTaskTime()))
{
Tokenizer tok(g_Options->GetExtensions(), ",;");
while (const char* scriptName = tok.Next())
{
if (FileSystem::SameFilename(scriptName, script.GetName()))
{
g_Options->CreateSchedulerTask(0, script.GetTaskTime(),
nullptr, Options::scScript, script.GetName());
break;
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTCONFIG_H
#define SCRIPTCONFIG_H
#include "NString.h"
#include "Container.h"
#include "Options.h"
class ScriptConfig
{
public:
class Script
{
public:
Script(const char* name, const char* location) :
m_name(name), m_location(location), m_displayName(name) {};
Script(Script&&) = default;
const char* GetName() { return m_name; }
const char* GetLocation() { return m_location; }
void SetDisplayName(const char* displayName) { m_displayName = displayName; }
const char* GetDisplayName() { return m_displayName; }
bool GetPostScript() { return m_postScript; }
void SetPostScript(bool postScript) { m_postScript = postScript; }
bool GetScanScript() { return m_scanScript; }
void SetScanScript(bool scanScript) { m_scanScript = scanScript; }
bool GetQueueScript() { return m_queueScript; }
void SetQueueScript(bool queueScript) { m_queueScript = queueScript; }
bool GetSchedulerScript() { return m_schedulerScript; }
void SetSchedulerScript(bool schedulerScript) { m_schedulerScript = schedulerScript; }
bool GetFeedScript() { return m_feedScript; }
void SetFeedScript(bool feedScript) { m_feedScript = feedScript; }
void SetQueueEvents(const char* queueEvents) { m_queueEvents = queueEvents; }
const char* GetQueueEvents() { return m_queueEvents; }
void SetTaskTime(const char* taskTime) { m_taskTime = taskTime; }
const char* GetTaskTime() { return m_taskTime; }
private:
CString m_name;
CString m_location;
CString m_displayName;
bool m_postScript = false;
bool m_scanScript = false;
bool m_queueScript = false;
bool m_schedulerScript = false;
bool m_feedScript = false;
CString m_queueEvents;
CString m_taskTime;
};
typedef std::list<Script> Scripts;
class ConfigTemplate
{
public:
ConfigTemplate(Script&& script, const char* templ) :
m_script(std::move(script)), m_template(templ) {}
Script* GetScript() { return &m_script; }
const char* GetTemplate() { return m_template; }
private:
Script m_script;
CString m_template;
};
typedef std::deque<ConfigTemplate> ConfigTemplates;
void InitOptions();
Scripts* GetScripts() { return &m_scripts; }
bool LoadConfig(Options::OptEntries* optEntries);
bool SaveConfig(Options::OptEntries* optEntries);
bool LoadConfigTemplates(ConfigTemplates* configTemplates);
ConfigTemplates* GetConfigTemplates() { return &m_configTemplates; }
private:
Scripts m_scripts;
ConfigTemplates m_configTemplates;
void InitScripts();
void InitConfigTemplates();
void CreateTasks();
void LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir);
void BuildScriptDisplayNames(Scripts* scripts);
void LoadScripts(Scripts* scripts);
bool LoadScriptFile(Script* script);
BString<1024>BuildScriptName(const char* directory, const char* filename, bool isSubDir);
bool ScriptExists(Scripts* scripts, const char* scriptName);
};
extern ScriptConfig* g_ScriptConfig;
#endif

View File

@@ -0,0 +1,724 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "FeedCoordinator.h"
#include "Options.h"
#include "WorkState.h"
#include "WebDownloader.h"
#include "Util.h"
#include "FileSystem.h"
#include "FeedFile.h"
#include "FeedFilter.h"
#include "FeedScript.h"
#include "DiskState.h"
#include "DupeCoordinator.h"
#include "UrlCoordinator.h"
std::unique_ptr<RegEx>& FeedCoordinator::FilterHelper::GetRegEx(int id)
{
m_regExes.resize(id);
return m_regExes[id - 1];
}
void FeedCoordinator::FilterHelper::CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen)
{
const char* dupeStatusName[] = { "", "QUEUED", "DOWNLOADING", "3", "SUCCESS", "5", "6", "7", "WARNING",
"9", "10", "11", "12", "13", "14", "15", "FAILURE" };
DupeCoordinator::EDupeStatus dupeStatus = g_DupeCoordinator->GetDupeStatus(DownloadQueue::Guard(), title, dupeKey);
BString<1024> statuses;
for (int i = 1; i <= (int)DupeCoordinator::dsFailure; i = i << 1)
{
if (dupeStatus & i)
{
if (!statuses.Empty())
{
statuses.Append(",");
}
statuses.Append(dupeStatusName[i]);
}
}
strncpy(statusBuf, statuses, bufLen);
}
FeedCoordinator::FeedCoordinator()
{
debug("Creating FeedCoordinator");
m_downloadQueueObserver.m_owner = this;
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
m_workStateObserver.m_owner = this;
g_WorkState->Attach(&m_workStateObserver);
}
FeedCoordinator::~FeedCoordinator()
{
debug("Destroying FeedCoordinator");
for (FeedDownloader* feedDownloader : m_activeDownloads)
{
delete feedDownloader;
}
m_activeDownloads.clear();
}
void FeedCoordinator::Run()
{
debug("Entering FeedCoordinator-loop");
while (!DownloadQueue::IsLoaded())
{
Util::Sleep(20);
}
if (g_Options->GetServerMode())
{
Guard guard(m_downloadsMutex);
g_DiskState->LoadFeeds(&m_feeds, &m_feedHistory);
}
time_t lastCleanup = 0;
while (!IsStopped())
{
// this code should not be called too often, once per second is OK
if (!g_WorkState->GetPauseDownload() || m_force || g_Options->GetUrlForce())
{
Guard guard(m_downloadsMutex);
time_t current = Util::CurrentTime();
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
{
m_force = false;
// check feed list and update feeds
for (FeedInfo* feedInfo : &m_feeds)
{
if (((feedInfo->GetInterval() > 0 &&
(feedInfo->GetNextUpdate() == 0 ||
current >= feedInfo->GetNextUpdate() ||
current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
feedInfo->GetFetch()) &&
feedInfo->GetStatus() != FeedInfo::fsRunning)
{
StartFeedDownload(feedInfo, feedInfo->GetFetch());
}
else if (feedInfo->GetFetch())
{
m_force = true;
}
}
}
}
CheckSaveFeeds();
ResetHangingDownloads();
if (std::abs(Util::CurrentTime() - lastCleanup) >= 60)
{
// clean up feed history once a minute
CleanupHistory();
CleanupCache();
CheckSaveFeeds();
lastCleanup = Util::CurrentTime();
}
Guard guard(m_downloadsMutex);
if (m_force)
{
// don't sleep too long if there active feeds scheduled for redownload
m_waitCond.WaitFor(m_downloadsMutex, 1000, [&]{ return IsStopped(); });
}
else
{
// no active jobs, we can sleep longer:
// - if option "UrlForce" is active or if the feed list is empty we need to wake up
// only when a new feed preview is requested. We could wait indefinitely for that
// but we need to do some job every now and then and therefore we sleep only 60 seconds.
// - if option "UrlForce" is disabled we need also to wake up when state "DownloadPaused"
// is changed. We detect this via notification from 'WorkState'. However such
// notifications are not 100% reliable due to possible race conditions. Therefore
// we sleep for max. 5 seconds.
int waitInterval = g_Options->GetUrlForce() || m_feeds.empty() ? 60000 : 5000;
m_waitCond.WaitFor(m_downloadsMutex, waitInterval, [&]{ return m_force || IsStopped(); });
}
}
// waiting for downloads
debug("FeedCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
{
Guard guard(m_downloadsMutex);
completed = m_activeDownloads.size() == 0;
}
CheckSaveFeeds();
Util::Sleep(100);
ResetHangingDownloads();
}
debug("FeedCoordinator: Downloads are completed");
debug("Exiting FeedCoordinator-loop");
}
void FeedCoordinator::Stop()
{
Thread::Stop();
debug("Stopping UrlDownloads");
Guard guard(m_downloadsMutex);
for (FeedDownloader* feedDownloader : m_activeDownloads)
{
feedDownloader->Stop();
}
debug("UrlDownloads are notified");
// Resume Run() to exit it
m_waitCond.NotifyAll();
}
void FeedCoordinator::WorkStateUpdate(Subject* caller, void* aspect)
{
m_force = true;
m_waitCond.NotifyAll();
}
void FeedCoordinator::ResetHangingDownloads()
{
if (g_Options->GetUrlTimeout() == 0)
{
return;
}
Guard guard(m_downloadsMutex);
time_t tm = Util::CurrentTime();
for (FeedDownloader* feedDownloader: m_activeDownloads)
{
if (tm - feedDownloader->GetLastUpdateTime() > g_Options->GetUrlTimeout() + 10 &&
feedDownloader->GetStatus() == FeedDownloader::adRunning)
{
error("Cancelling hanging feed download %s", feedDownloader->GetInfoName());
feedDownloader->Stop();
}
}
}
void FeedCoordinator::LogDebugInfo()
{
info(" ---------- FeedCoordinator");
Guard guard(m_downloadsMutex);
info(" Active Downloads: %i", (int)m_activeDownloads.size());
for (FeedDownloader* feedDownloader : m_activeDownloads)
{
feedDownloader->LogDebugInfo();
}
}
void FeedCoordinator::StartFeedDownload(FeedInfo* feedInfo, bool force)
{
debug("Starting new FeedDownloader for %s", feedInfo->GetName());
FeedDownloader* feedDownloader = new FeedDownloader();
feedDownloader->SetAutoDestroy(true);
feedDownloader->Attach(this);
feedDownloader->SetFeedInfo(feedInfo);
feedDownloader->SetUrl(feedInfo->GetUrl());
feedDownloader->SetInfoName(feedInfo->GetName());
feedDownloader->SetForce(force || g_Options->GetUrlForce());
BString<1024> outFilename;
if (feedInfo->GetId() > 0)
{
outFilename.Format("%s%cfeed-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, feedInfo->GetId());
}
else
{
outFilename.Format("%s%cfeed-%i-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, (int)Util::CurrentTime(), rand());
}
feedDownloader->SetOutputFilename(outFilename);
feedInfo->SetStatus(FeedInfo::fsRunning);
feedInfo->SetForce(force);
feedInfo->SetFetch(false);
m_activeDownloads.push_back(feedDownloader);
feedDownloader->Start();
}
void FeedCoordinator::Update(Subject* caller, void* aspect)
{
debug("Notification from FeedDownloader received");
FeedDownloader* feedDownloader = (FeedDownloader*) caller;
if ((feedDownloader->GetStatus() == WebDownloader::adFinished) ||
(feedDownloader->GetStatus() == WebDownloader::adFailed) ||
(feedDownloader->GetStatus() == WebDownloader::adRetry))
{
FeedCompleted(feedDownloader);
}
}
void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
{
debug("Feed downloaded");
FeedInfo* feedInfo = feedDownloader->GetFeedInfo();
bool statusOK = feedDownloader->GetStatus() == WebDownloader::adFinished;
if (statusOK)
{
feedInfo->SetOutputFilename(feedDownloader->GetOutputFilename());
}
// remove downloader from downloader list
{
Guard guard(m_downloadsMutex);
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), feedDownloader));
}
SchedulerNextUpdate(feedInfo, statusOK);
if (statusOK)
{
if (!feedInfo->GetPreview())
{
bool scriptSuccess = true;
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
feedInfo->GetOutputFilename(), feedInfo->GetId(), &scriptSuccess);
if (!scriptSuccess)
{
feedInfo->SetStatus(FeedInfo::fsFailed);
return;
}
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo);
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
{
Guard guard(m_downloadsMutex);
if (feedFile)
{
std::unique_ptr<FeedItemList> feedItems = feedFile->DetachFeedItems();
addedNzbs = ProcessFeed(feedInfo, feedItems.get());
feedFile.reset();
}
feedInfo->SetLastUpdate(Util::CurrentTime());
feedInfo->SetForce(false);
m_save = true;
}
for (std::unique_ptr<NzbInfo>& nzbInfo : addedNzbs)
{
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), false);
}
}
feedInfo->SetStatus(FeedInfo::fsFinished);
}
else
{
feedInfo->SetStatus(FeedInfo::fsFailed);
}
}
void FeedCoordinator::SchedulerNextUpdate(FeedInfo* feedInfo, bool success)
{
time_t current = Util::CurrentTime();
int interval;
if (success)
{
interval = feedInfo->GetInterval() * 60;
feedInfo->SetLastInterval(0);
}
else
{
// On failure schedule next update sooner:
// starting with 1 minute and increasing, but not greater than FeedX.Interval
interval = feedInfo->GetLastInterval() * 2;
interval = std::max(interval, 60);
interval = std::min(interval, feedInfo->GetInterval() * 60);
feedInfo->SetLastInterval(interval);
}
detail("Scheduling update for feed %s in %i minute(s)", feedInfo->GetName(), interval / 60);
feedInfo->SetNextUpdate(current + interval);
}
void FeedCoordinator::FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
{
debug("Filtering feed %s", feedInfo->GetName());
FilterHelper filterHelper;
std::unique_ptr<FeedFilter> feedFilter;
if (!Util::EmptyStr(feedInfo->GetFilter()))
{
feedFilter = std::make_unique<FeedFilter>(feedInfo->GetFilter());
}
for (FeedItemInfo& feedItemInfo : feedItems)
{
feedItemInfo.SetMatchStatus(FeedItemInfo::msAccepted);
feedItemInfo.SetMatchRule(0);
feedItemInfo.SetPauseNzb(feedInfo->GetPauseNzb());
feedItemInfo.SetPriority(feedInfo->GetPriority());
feedItemInfo.SetAddCategory(feedInfo->GetCategory());
feedItemInfo.SetDupeScore(0);
feedItemInfo.SetDupeMode(dmScore);
feedItemInfo.SetFeedFilterHelper(&filterHelper);
feedItemInfo.BuildDupeKey(nullptr, nullptr, nullptr, nullptr);
if (feedFilter)
{
feedFilter->Match(feedItemInfo);
}
}
}
std::vector<std::unique_ptr<NzbInfo>> FeedCoordinator::ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
{
debug("Process feed %s", feedInfo->GetName());
FilterFeed(feedInfo, feedItems);
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
bool firstFetch = feedInfo->GetLastUpdate() == 0;
int added = 0;
for (FeedItemInfo& feedItemInfo : feedItems)
{
if (feedItemInfo.GetMatchStatus() == FeedItemInfo::msAccepted)
{
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
FeedHistoryInfo::EStatus status = FeedHistoryInfo::hsUnknown;
if (firstFetch && feedInfo->GetBacklog())
{
status = FeedHistoryInfo::hsBacklog;
}
else if (!feedHistoryInfo)
{
addedNzbs.push_back(CreateNzbInfo(feedInfo, feedItemInfo));
status = FeedHistoryInfo::hsFetched;
added++;
}
if (feedHistoryInfo)
{
feedHistoryInfo->SetLastSeen(Util::CurrentTime());
}
else
{
m_feedHistory.emplace_back(feedItemInfo.GetUrl(), status, Util::CurrentTime());
}
}
}
if (added)
{
info("%s has %i new item(s)", feedInfo->GetName(), added);
}
else
{
detail("%s has no new items", feedInfo->GetName());
}
return addedNzbs;
}
std::unique_ptr<NzbInfo> FeedCoordinator::CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo)
{
debug("Download %s from %s", feedItemInfo.GetUrl(), feedInfo->GetName());
std::unique_ptr<NzbInfo> nzbInfo = std::make_unique<NzbInfo>();
nzbInfo->SetKind(NzbInfo::nkUrl);
nzbInfo->SetFeedId(feedInfo->GetId());
nzbInfo->SetUrl(feedItemInfo.GetUrl());
// add .nzb-extension if not present
BString<1024> nzbName = feedItemInfo.GetFilename();
char* ext = strrchr(nzbName, '.');
if (ext && !strcasecmp(ext, ".nzb"))
{
*ext = '\0';
}
if (!nzbName.Empty())
{
BString<1024> nzbName2("%s.nzb", *nzbName);
nzbInfo->SetFilename(FileSystem::MakeValidFilename(nzbName2));
}
nzbInfo->SetCategory(feedItemInfo.GetAddCategory());
nzbInfo->SetPriority(feedItemInfo.GetPriority());
nzbInfo->SetAddUrlPaused(feedItemInfo.GetPauseNzb());
nzbInfo->SetDupeKey(feedItemInfo.GetDupeKey());
nzbInfo->SetDupeScore(feedItemInfo.GetDupeScore());
nzbInfo->SetDupeMode(feedItemInfo.GetDupeMode());
nzbInfo->SetSize(feedItemInfo.GetSize());
nzbInfo->SetMinTime(feedItemInfo.GetTime());
nzbInfo->SetMaxTime(feedItemInfo.GetTime());
return nzbInfo;
}
std::shared_ptr<FeedItemList> FeedCoordinator::ViewFeed(int id)
{
if (id < 1 || id > (int)m_feeds.size())
{
return nullptr;
}
std::unique_ptr<FeedInfo>& feedInfo = m_feeds[id - 1];
return PreviewFeed(feedInfo->GetId(), feedInfo->GetName(), feedInfo->GetUrl(), feedInfo->GetFilter(),
feedInfo->GetBacklog(), feedInfo->GetPauseNzb(), feedInfo->GetCategory(),
feedInfo->GetPriority(), feedInfo->GetInterval(), feedInfo->GetExtensions(), 0, nullptr);
}
std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
const char* name, const char* url, const char* filter, bool backlog, bool pauseNzb,
const char* category, int priority, int interval, const char* feedScript,
int cacheTimeSec, const char* cacheId)
{
debug("Preview feed %s", name);
std::unique_ptr<FeedInfo> feedInfo = std::make_unique<FeedInfo>(id, name, url, backlog, interval,
filter, pauseNzb, category, priority, feedScript);
feedInfo->SetPreview(true);
std::shared_ptr<FeedItemList> feedItems;
bool hasCache = false;
if (cacheTimeSec > 0 && *cacheId != '\0')
{
Guard guard(m_downloadsMutex);
for (FeedCacheItem& feedCacheItem : m_feedCache)
{
if (!strcmp(feedCacheItem.GetCacheId(), cacheId))
{
feedCacheItem.SetLastUsage(Util::CurrentTime());
feedItems = feedCacheItem.GetFeedItems();
hasCache = true;
break;
}
}
}
if (!hasCache)
{
bool firstFetch = true;
{
Guard guard(m_downloadsMutex);
for (FeedInfo* feedInfo2 : &m_feeds)
{
if (!strcmp(feedInfo2->GetUrl(), feedInfo->GetUrl()) &&
!strcmp(feedInfo2->GetFilter(), feedInfo->GetFilter()) &&
feedInfo2->GetLastUpdate() > 0)
{
firstFetch = false;
break;
}
}
StartFeedDownload(feedInfo.get(), true);
m_force = true;
m_waitCond.NotifyAll();
}
// wait until the download in a separate thread completes
while (feedInfo->GetStatus() == FeedInfo::fsRunning)
{
Util::Sleep(100);
}
// now can process the feed
if (feedInfo->GetStatus() != FeedInfo::fsFinished)
{
return nullptr;
}
FeedScriptController::ExecuteScripts(
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
feedInfo->GetOutputFilename(), feedInfo->GetId(), nullptr);
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo.get());
if (!feedFile)
{
return nullptr;
}
feedItems = feedFile->DetachFeedItems();
feedFile.reset();
for (FeedItemInfo& feedItemInfo : feedItems.get())
{
feedItemInfo.SetStatus(firstFetch && feedInfo->GetBacklog() ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
if (feedHistoryInfo)
{
feedItemInfo.SetStatus((FeedItemInfo::EStatus)feedHistoryInfo->GetStatus());
}
}
}
FilterFeed(feedInfo.get(), feedItems.get());
feedInfo.reset();
if (cacheTimeSec > 0 && *cacheId != '\0' && !hasCache)
{
Guard guard(m_downloadsMutex);
m_feedCache.emplace_back(url, cacheTimeSec, cacheId, Util::CurrentTime(), feedItems);
}
return feedItems;
}
void FeedCoordinator::FetchFeed(int id)
{
debug("FetchFeeds");
Guard guard(m_downloadsMutex);
for (FeedInfo* feedInfo : &m_feeds)
{
if (feedInfo->GetId() == id || id == 0)
{
feedInfo->SetFetch(true);
m_force = true;
}
}
m_waitCond.NotifyAll();
}
std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
{
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename(), feedInfo->GetName());
if (feedFile->Parse())
{
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
}
else
{
error("Feed file %s kept for troubleshooting (will be deleted on next successful feed fetch)", feedInfo->GetOutputFilename());
feedFile.reset();
}
return feedFile;
}
void FeedCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
{
debug("Notification from URL-Coordinator received");
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
if (queueAspect->action == DownloadQueue::eaUrlCompleted)
{
Guard guard(m_downloadsMutex);
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(queueAspect->nzbInfo->GetUrl());
if (feedHistoryInfo)
{
feedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
}
else
{
m_feedHistory.emplace_back(queueAspect->nzbInfo->GetUrl(), FeedHistoryInfo::hsFetched, Util::CurrentTime());
}
m_save = true;
}
}
bool FeedCoordinator::HasActiveDownloads()
{
Guard guard(m_downloadsMutex);
return !m_activeDownloads.empty();
}
void FeedCoordinator::CheckSaveFeeds()
{
Guard guard(m_downloadsMutex);
if (m_save)
{
debug("CheckSaveFeeds: save");
if (g_Options->GetServerMode())
{
g_DiskState->SaveFeeds(&m_feeds, &m_feedHistory);
}
m_save = false;
}
}
void FeedCoordinator::CleanupHistory()
{
debug("CleanupHistory");
Guard guard(m_downloadsMutex);
time_t oldestUpdate = Util::CurrentTime();
for (FeedInfo* feedInfo : &m_feeds)
{
if (feedInfo->GetLastUpdate() < oldestUpdate)
{
oldestUpdate = feedInfo->GetLastUpdate();
}
}
time_t borderDate = oldestUpdate - g_Options->GetFeedHistory() * 60*60*24;
m_feedHistory.erase(std::remove_if(m_feedHistory.begin(), m_feedHistory.end(),
[borderDate, this](FeedHistoryInfo& feedHistoryInfo)
{
if (feedHistoryInfo.GetLastSeen() < borderDate)
{
detail("Deleting %s from feed history", feedHistoryInfo.GetUrl());
m_save = true;
return true;
}
return false;
}),
m_feedHistory.end());
}
void FeedCoordinator::CleanupCache()
{
debug("CleanupCache");
Guard guard(m_downloadsMutex);
time_t curTime = Util::CurrentTime();
m_feedCache.remove_if(
[curTime](FeedCacheItem& feedCacheItem)
{
if (feedCacheItem.GetLastUsage() + feedCacheItem.GetCacheTimeSec() < curTime ||
feedCacheItem.GetLastUsage() > curTime)
{
debug("Deleting %s from feed cache", feedCacheItem.GetUrl());
return true;
}
return false;
});
}

View File

@@ -0,0 +1,150 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2019 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef FEEDCOORDINATOR_H
#define FEEDCOORDINATOR_H
#include "NString.h"
#include "Log.h"
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "FeedFile.h"
#include "FeedInfo.h"
#include "Observer.h"
#include "Util.h"
class FeedDownloader;
class FeedCoordinator : public Thread, public Observer, public Subject, public Debuggable
{
public:
FeedCoordinator();
virtual ~FeedCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* caller, void* aspect);
void AddFeed(std::unique_ptr<FeedInfo> feedInfo) { m_feeds.push_back(std::move(feedInfo)); }
/* may return empty pointer on error */
std::shared_ptr<FeedItemList> PreviewFeed(int id, const char* name, const char* url,
const char* filter, bool backlog, bool pauseNzb, const char* category, int priority,
int interval, const char* feedScript, int cacheTimeSec, const char* cacheId);
/* may return empty pointer on error */
std::shared_ptr<FeedItemList> ViewFeed(int id);
void FetchFeed(int id);
bool HasActiveDownloads();
Feeds* GetFeeds() { return &m_feeds; }
protected:
virtual void LogDebugInfo();
private:
class DownloadQueueObserver: public Observer
{
public:
FeedCoordinator* m_owner;
virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
};
class WorkStateObserver: public Observer
{
public:
FeedCoordinator* m_owner;
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
};
class FeedCacheItem
{
public:
FeedCacheItem(const char* url, int cacheTimeSec,const char* cacheId,
time_t lastUsage, std::shared_ptr<FeedItemList> feedItems) :
m_url(url), m_cacheTimeSec(cacheTimeSec), m_cacheId(cacheId),
m_lastUsage(lastUsage), m_feedItems(feedItems) {}
const char* GetUrl() { return m_url; }
int GetCacheTimeSec() { return m_cacheTimeSec; }
const char* GetCacheId() { return m_cacheId; }
time_t GetLastUsage() { return m_lastUsage; }
void SetLastUsage(time_t lastUsage) { m_lastUsage = lastUsage; }
std::shared_ptr<FeedItemList> GetFeedItems() { return m_feedItems; }
private:
CString m_url;
int m_cacheTimeSec;
CString m_cacheId;
time_t m_lastUsage;
std::shared_ptr<FeedItemList> m_feedItems;
};
class FilterHelper : public FeedFilterHelper
{
public:
virtual std::unique_ptr<RegEx>& GetRegEx(int id);
virtual void CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen);
private:
std::vector<std::unique_ptr<RegEx>> m_regExes;
};
typedef std::list<FeedCacheItem> FeedCache;
typedef std::deque<FeedDownloader*> ActiveDownloads;
Feeds m_feeds;
ActiveDownloads m_activeDownloads;
FeedHistory m_feedHistory;
Mutex m_downloadsMutex;
DownloadQueueObserver m_downloadQueueObserver;
WorkStateObserver m_workStateObserver;
bool m_force = false;
bool m_save = false;
FeedCache m_feedCache;
ConditionVar m_waitCond;
bool m_wokenUp = false;
void StartFeedDownload(FeedInfo* feedInfo, bool force);
void FeedCompleted(FeedDownloader* feedDownloader);
void FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
std::vector<std::unique_ptr<NzbInfo>> ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
std::unique_ptr<NzbInfo> CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo);
void ResetHangingDownloads();
void DownloadQueueUpdate(Subject* caller, void* aspect);
void CleanupHistory();
void CleanupCache();
void CheckSaveFeeds();
std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
void SchedulerNextUpdate(FeedInfo* feedInfo, bool success);
void WorkStateUpdate(Subject* caller, void* aspect);
};
extern FeedCoordinator* g_FeedCoordinator;
class FeedDownloader : public WebDownloader
{
public:
void SetFeedInfo(FeedInfo* feedInfo) { m_feedInfo = feedInfo; }
FeedInfo* GetFeedInfo() { return m_feedInfo; }
private:
FeedInfo* m_feedInfo;
};
#endif

607
daemon/feed/FeedFile.cpp Normal file
View File

@@ -0,0 +1,607 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#include "nzbget.h"
#include "FeedFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Util.h"
FeedFile::FeedFile(const char* fileName, const char* infoName) :
m_fileName(fileName), m_infoName(infoName)
{
debug("Creating FeedFile");
m_feedItems = std::make_unique<FeedItemList>();
#ifndef WIN32
m_feedItemInfo = nullptr;
m_tagContent.Clear();
#endif
}
void FeedFile::LogDebugInfo()
{
info(" FeedFile %s", *m_fileName);
}
void FeedFile::ParseSubject(FeedItemInfo& feedItemInfo)
{
// if title has quatation marks we use only part within quatation marks
char* p = (char*)feedItemInfo.GetTitle();
char* start = strchr(p, '\"');
if (start)
{
start++;
char* end = strchr(start + 1, '\"');
if (end)
{
int len = (int)(end - start);
char* point = strchr(start + 1, '.');
if (point && point < end)
{
CString filename(start, len);
char* ext = strrchr(filename, '.');
if (ext && !strcasecmp(ext, ".par2"))
{
*ext = '\0';
}
feedItemInfo.SetFilename(filename);
return;
}
}
}
feedItemInfo.SetFilename(feedItemInfo.GetTitle());
}
#ifdef WIN32
bool FeedFile::Parse()
{
CoInitialize(nullptr);
HRESULT hr;
MSXML::IXMLDOMDocumentPtr doc;
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
if (FAILED(hr))
{
return false;
}
// Load the XML document file...
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
_variant_t vFilename(*WString(m_fileName));
// 1. first trying to load via filename without URL-encoding (certain charaters doesn't work when encoded)
VARIANT_BOOL success = doc->load(vFilename);
if (success == VARIANT_FALSE)
{
// 2. now trying filename encoded as URL
char url[2048];
EncodeUrl(m_fileName, url, 2048);
debug("url=\"%s\"", url);
_variant_t vUrl(url);
success = doc->load(vUrl);
}
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
const char* errMsg = r;
error("Error parsing rss feed %s: %s", *m_infoName, errMsg);
return false;
}
bool ok = ParseFeed(doc);
return ok;
}
void FeedFile::EncodeUrl(const char* filename, char* url, int bufLen)
{
WString widefilename(filename);
char* end = url + bufLen;
for (wchar_t* p = widefilename; *p && url < end - 3; p++)
{
wchar_t ch = *p;
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') ||
ch == '-' || ch == '.' || ch == '_' || ch == '~')
{
*url++ = (char)ch;
}
else
{
*url++ = '%';
uint32 a = (uint32)ch >> 4;
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
a = ch & 0xF;
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
}
}
*url = '\0';
}
bool FeedFile::ParseFeed(IUnknown* nzb)
{
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
MSXML::IXMLDOMNodeListPtr itemList = root->selectNodes("/rss/channel/item");
for (int i = 0; i < itemList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
m_feedItems->emplace_back();
FeedItemInfo& feedItemInfo = m_feedItems->back();
MSXML::IXMLDOMNodePtr tag;
MSXML::IXMLDOMNodePtr attr;
// <title>Debian 6</title>
tag = node->selectSingleNode("title");
if (!tag)
{
// bad rss feed
return false;
}
_bstr_t title(tag->Gettext());
feedItemInfo.SetTitle(title);
ParseSubject(feedItemInfo);
// <pubDate>Wed, 26 Jun 2013 00:02:54 -0600</pubDate>
tag = node->selectSingleNode("pubDate");
if (tag)
{
_bstr_t time(tag->Gettext());
time_t unixtime = WebUtil::ParseRfc822DateTime(time);
if (unixtime > 0)
{
feedItemInfo.SetTime(unixtime);
}
}
// <category>Movies &gt; HD</category>
tag = node->selectSingleNode("category");
if (tag)
{
_bstr_t category(tag->Gettext());
feedItemInfo.SetCategory(category);
}
// <description>long text</description>
tag = node->selectSingleNode("description");
if (tag)
{
_bstr_t bdescription(tag->Gettext());
// cleanup CDATA
CString description = (const char*)bdescription;
WebUtil::XmlStripTags(description);
WebUtil::XmlDecode(description);
WebUtil::XmlRemoveEntities(description);
feedItemInfo.SetDescription(description);
}
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
tag = node->selectSingleNode("enclosure");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("url");
if (attr)
{
_bstr_t url(attr->Gettext());
feedItemInfo.SetUrl(url);
}
attr = tag->Getattributes()->getNamedItem("length");
if (attr)
{
_bstr_t bsize(attr->Gettext());
int64 size = atoll(bsize);
feedItemInfo.SetSize(size);
}
}
if (!feedItemInfo.GetUrl())
{
// <link>https://nzb.org/fetch/334534ce/4364564564</link>
tag = node->selectSingleNode("link");
if (!tag)
{
// bad rss feed
return false;
}
_bstr_t link(tag->Gettext());
feedItemInfo.SetUrl(link);
}
// newznab special
//<newznab:attr name="size" value="5423523453534" />
if (feedItemInfo.GetSize() == 0)
{
tag = node->selectSingleNode("newznab:attr[@name='size'] | nZEDb:attr[@name='size']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t bsize(attr->Gettext());
int64 size = atoll(bsize);
feedItemInfo.SetSize(size);
}
}
}
//<newznab:attr name="imdb" value="1588173"/>
tag = node->selectSingleNode("newznab:attr[@name='imdb'] | nZEDb:attr[@name='imdb']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t bval(attr->Gettext());
int val = atoi(bval);
feedItemInfo.SetImdbId(val);
}
}
//<newznab:attr name="rageid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='rageid'] | nZEDb:attr[@name='rageid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t bval(attr->Gettext());
int val = atoi(bval);
feedItemInfo.SetRageId(val);
}
}
//<newznab:attr name="tdvdbid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='tvdbid'] | nZEDb:attr[@name='tvdbid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t bval(attr->Gettext());
int val = atoi(bval);
feedItemInfo.SetTvdbId(val);
}
}
//<newznab:attr name="tvmazeid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='tvmazeid'] | nZEDb:attr[@name='tvmazeid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t bval(attr->Gettext());
int val = atoi(bval);
feedItemInfo.SetTvmazeId(val);
}
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
tag = node->selectSingleNode("newznab:attr[@name='episode'] | nZEDb:attr[@name='episode']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
feedItemInfo.SetEpisode(val);
}
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
tag = node->selectSingleNode("newznab:attr[@name='season'] | nZEDb:attr[@name='season']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
feedItemInfo.SetSeason(val);
}
}
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr | nZEDb:attr");
for (int i = 0; i < itemList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
MSXML::IXMLDOMNodePtr name = node->Getattributes()->getNamedItem("name");
MSXML::IXMLDOMNodePtr value = node->Getattributes()->getNamedItem("value");
if (name && value)
{
_bstr_t bname(name->Gettext());
_bstr_t bval(value->Gettext());
feedItemInfo.GetAttributes()->emplace_back(bname, bval);
}
}
}
return true;
}
#else
bool FeedFile::Parse()
{
#ifdef DISABLE_LIBXML2
error("Could not parse rss feed, program was compiled without libxml2 support");
return false;
#else
xmlSAXHandler SAX_handler = {0};
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
m_ignoreNextError = false;
int ret = xmlSAXUserParseFile(&SAX_handler, this, m_fileName);
if (ret != 0)
{
error("Failed to parse rss feed %s", *m_infoName);
return false;
}
return true;
#endif
}
void FeedFile::Parse_StartElement(const char *name, const char **atts)
{
ResetTagContent();
if (!strcmp("item", name))
{
m_feedItems->emplace_back();
m_feedItemInfo = &m_feedItems->back();
}
else if (!strcmp("enclosure", name) && m_feedItemInfo)
{
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
for (; *atts; atts+=2)
{
if (!strcmp("url", atts[0]))
{
CString url = atts[1];
WebUtil::XmlDecode(url);
m_feedItemInfo->SetUrl(url);
}
else if (!strcmp("length", atts[0]))
{
int64 size = atoll(atts[1]);
m_feedItemInfo->SetSize(size);
}
}
}
else if (m_feedItemInfo &&
(!strcmp("newznab:attr", name) || !strcmp("nZEDb:attr", name)) &&
atts[0] && atts[1] && atts[2] && atts[3] &&
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
{
m_feedItemInfo->GetAttributes()->emplace_back(atts[1], atts[3]);
//<newznab:attr name="size" value="5423523453534" />
if (m_feedItemInfo->GetSize() == 0 &&
!strcmp("size", atts[1]))
{
int64 size = atoll(atts[3]);
m_feedItemInfo->SetSize(size);
}
//<newznab:attr name="imdb" value="1588173"/>
else if (!strcmp("imdb", atts[1]))
{
m_feedItemInfo->SetImdbId(atoi(atts[3]));
}
//<newznab:attr name="rageid" value="33877"/>
else if (!strcmp("rageid", atts[1]))
{
m_feedItemInfo->SetRageId(atoi(atts[3]));
}
//<newznab:attr name="tvdbid" value="33877"/>
else if (!strcmp("tvdbid", atts[1]))
{
m_feedItemInfo->SetTvdbId(atoi(atts[3]));
}
//<newznab:attr name="tvmazeid" value="33877"/>
else if (!strcmp("tvmazeid", atts[1]))
{
m_feedItemInfo->SetTvmazeId(atoi(atts[3]));
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
else if (!strcmp("episode", atts[1]))
{
m_feedItemInfo->SetEpisode(atts[3]);
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
else if (!strcmp("season", atts[1]))
{
m_feedItemInfo->SetSeason(atts[3]);
}
}
}
void FeedFile::Parse_EndElement(const char *name)
{
if (!strcmp("title", name) && m_feedItemInfo)
{
m_feedItemInfo->SetTitle(m_tagContent);
ResetTagContent();
ParseSubject(*m_feedItemInfo);
}
else if (!strcmp("link", name) && m_feedItemInfo &&
(!m_feedItemInfo->GetUrl() || strlen(m_feedItemInfo->GetUrl()) == 0))
{
m_feedItemInfo->SetUrl(m_tagContent);
ResetTagContent();
}
else if (!strcmp("category", name) && m_feedItemInfo)
{
m_feedItemInfo->SetCategory(m_tagContent);
ResetTagContent();
}
else if (!strcmp("description", name) && m_feedItemInfo)
{
// cleanup CDATA
CString description = *m_tagContent;
WebUtil::XmlStripTags(description);
WebUtil::XmlDecode(description);
WebUtil::XmlRemoveEntities(description);
m_feedItemInfo->SetDescription(description);
ResetTagContent();
}
else if (!strcmp("pubDate", name) && m_feedItemInfo)
{
time_t unixtime = WebUtil::ParseRfc822DateTime(m_tagContent);
if (unixtime > 0)
{
m_feedItemInfo->SetTime(unixtime);
}
ResetTagContent();
}
}
void FeedFile::Parse_Content(const char *buf, int len)
{
m_tagContent.Append(buf, len);
}
void FeedFile::ResetTagContent()
{
m_tagContent.Clear();
}
void FeedFile::SAX_StartElement(FeedFile* file, const char *name, const char **atts)
{
file->Parse_StartElement(name, atts);
}
void FeedFile::SAX_EndElement(FeedFile* file, const char *name)
{
file->Parse_EndElement(name);
}
void FeedFile::SAX_characters(FeedFile* file, const char * xmlstr, int len)
{
char* str = (char*)xmlstr;
// trim starting blanks
int off = 0;
for (int i = 0; i < len; i++)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
off++;
}
else
{
break;
}
}
int newlen = len - off;
// trim ending blanks
for (int i = len - 1; i >= off; i--)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
newlen--;
}
else
{
break;
}
}
if (newlen > 0)
{
// interpret tag content
file->Parse_Content(str + off, newlen);
}
}
void* FeedFile::SAX_getEntity(FeedFile* file, const char * name)
{
#ifdef DISABLE_LIBXML2
void* e = nullptr;
#else
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
#endif
if (!e)
{
warn("entity not found");
file->m_ignoreNextError = true;
}
return e;
}
void FeedFile::SAX_error(FeedFile* file, const char *msg, ...)
{
if (file->m_ignoreNextError)
{
file->m_ignoreNextError = false;
return;
}
va_list argp;
va_start(argp, msg);
char errMsg[1024];
vsnprintf(errMsg, sizeof(errMsg), msg, argp);
errMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = errMsg + strlen(errMsg) - 1; pend >= errMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing rss feed %s: %s", *file->m_infoName, errMsg);
}
#endif

62
daemon/feed/FeedFile.h Normal file
View File

@@ -0,0 +1,62 @@
/*
* This file is part of nzbget. See <http://nzbget.net>.
*
* Copyright (C) 2013-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This 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, see <http://www.gnu.org/licenses/>.
*/
#ifndef FEEDFILE_H
#define FEEDFILE_H
#include "NString.h"
#include "FeedInfo.h"
class FeedFile
{
public:
FeedFile(const char* fileName, const char* infoName);
bool Parse();
std::unique_ptr<FeedItemList> DetachFeedItems() { return std::move(m_feedItems); }
void LogDebugInfo();
private:
std::unique_ptr<FeedItemList> m_feedItems;
CString m_fileName;
CString m_infoName;
void ParseSubject(FeedItemInfo& feedItemInfo);
#ifdef WIN32
bool ParseFeed(IUnknown* nzb);
static void EncodeUrl(const char* filename, char* url, int bufLen);
#else
FeedItemInfo* m_feedItemInfo;
StringBuilder m_tagContent;
bool m_ignoreNextError;
static void SAX_StartElement(FeedFile* file, const char *name, const char **atts);
static void SAX_EndElement(FeedFile* file, const char *name);
static void SAX_characters(FeedFile* file, const char * xmlstr, int len);
static void* SAX_getEntity(FeedFile* file, const char * name);
static void SAX_error(FeedFile* file, const char *msg, ...);
void Parse_StartElement(const char *name, const char **atts);
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
void ResetTagContent();
#endif
};
#endif

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