Compare commits

...

184 Commits

Author SHA1 Message Date
Andrey Prygunkov
d6a9c08dc7 version 0.4.1 2008-07-10 17:18:53 +00:00
Andrey Prygunkov
baece5113b updated ChangeLog 2008-07-10 16:17:55 +00:00
Andrey Prygunkov
230db979ed updated version-string (preparing to release 0.4.1) 2008-07-10 16:03:07 +00:00
Andrey Prygunkov
0e31bbdbd4 fixed: seg.fault if a file listed in nzb-file does not have any segments (articles) 2008-07-09 16:12:17 +00:00
Andrey Prygunkov
8399322238 added the compatibility with gcc 4.3 2008-06-27 20:00:04 +00:00
Andrey Prygunkov
03b057be64 added the option <AllowReProcess> to help to post-process-scripts, which make par-check/-repair on it's own 2008-06-22 22:27:52 +00:00
Andrey Prygunkov
5bd621c540 fixed: edit-commands with negative offset did not work via XML-RPC (but worked via JSON-RPC) 2008-06-22 21:36:04 +00:00
Andrey Prygunkov
228a451284 updated the descriptions of several options in the example configuration file 2008-06-21 19:47:51 +00:00
Andrey Prygunkov
3c67755c6b fixed: on first screen-update the remote-client showed only one item of queue in curses-outputmode 2008-06-21 15:01:58 +00:00
Andrey Prygunkov
4d6ffa78f6 reduced CPU-usage in curses-outputmode 2008-06-16 17:47:59 +00:00
Andrey Prygunkov
a9be4a9860 improved speedmeters's responding after program's start and pause/unpause 2008-06-15 19:45:10 +00:00
Andrey Prygunkov
6275ddbcba fixed: umask-option did not allow to enable write-permissions for group and others 2008-06-14 21:38:55 +00:00
Andrey Prygunkov
365784bbed fixed: XML-RPC and JSON-RPC did not work on Big-Endian-CPUs 2008-06-13 20:51:17 +00:00
Andrey Prygunkov
c1623e412d improved download-speed-meter: uses less cpu and calculates the speed for the last 30 seconds (instead of 5 seconds), providing better accuracy 2008-06-06 20:09:22 +00:00
Andrey Prygunkov
13f6cca318 fixed: trailing spaces in nzb-filenames (before the file's extension) caused errors on windows (because windows does not allow trailing spaces in directory names). now they will be trimmed 2008-06-01 20:48:55 +00:00
Andrey Prygunkov
1f46214363 added the server's name to the detail-log-message, displayed on start of article's download 2008-05-29 22:16:07 +00:00
Andrey Prygunkov
c39a807766 options <username> and <password> in news-server's configuration are now optional 2008-05-29 22:00:16 +00:00
Andrey Prygunkov
3082c02fbe fixed: config-file with line-endings in windows-style (CR-LF) was not read properly 2008-05-29 21:49:54 +00:00
Andrey Prygunkov
96a134e69e Delete-key <D> in curses-frontend now must be pressed in uppercase to avoid an accidental deletion; added a hint-text under the status-line on a press of the lowercase-key <d> 2008-05-29 21:39:29 +00:00
Andrey Prygunkov
14fcebdde3 changed version-string to <0.4.1-testing> 2008-05-29 21:16:38 +00:00
Andrey Prygunkov
b4211a57e6 fixed compilation error on PC-BSD 2008-04-08 17:39:26 +00:00
Andrey Prygunkov
4a421c705a updated version-string (preparing to release 0.4.0) 2008-04-08 17:00:33 +00:00
Andrey Prygunkov
695e3a912c improved error-handling on start of post-process-script 2008-04-05 14:14:31 +00:00
Andrey Prygunkov
af190697c2 extended the description of parameter <PostProcess> 2008-03-27 21:46:23 +00:00
Andrey Prygunkov
814ffbd468 removed the accidentally commited debug-code 2008-03-26 18:54:05 +00:00
Andrey Prygunkov
cb10f39db3 fixed a deadlock in postprocessor (could hang the program) 2008-03-26 18:35:00 +00:00
Andrey Prygunkov
c8a496faaa fixed one warning under gcc 2008-03-26 18:23:23 +00:00
Andrey Prygunkov
7c858007b3 removed the logging from signal-handlers (they might cause the hangs, especially during the start/termination of post-process-scripts in debug-mode) 2008-03-25 23:35:13 +00:00
Andrey Prygunkov
3a9e8f1a79 set the proper progress-label for post-item during the loading of additional par-files 2008-03-23 15:39:27 +00:00
Andrey Prygunkov
dfa839c4e0 added the automatic termination (killing) of post-process-script on server's shutdown 2008-03-23 15:26:07 +00:00
Andrey Prygunkov
876fee3bc5 added field <ID> to post-queue-items (for feature usage) 2008-03-23 14:48:56 +00:00
Andrey Prygunkov
22ffd2e0bd added the check for existense of dest-directory after reloading of post-queue and the deletion of items which can not be continued (if directory was deleted) 2008-03-23 13:59:12 +00:00
Andrey Prygunkov
8cd59b9ab5 improved error-reporting by starting of post-process-script and on par-failures 2008-03-22 14:10:37 +00:00
Andrey Prygunkov
988714f92a fixed: negative integer parameters were not correctly parsed by JSON-RPC-server 2008-03-20 15:51:45 +00:00
Andrey Prygunkov
789d39bfb2 updated ChangeLog 2008-03-19 17:52:24 +00:00
Andrey Prygunkov
1c333e68c2 added the saving of script-output in post-job-object and the returning of the output by command <postqueue> via XML-RPC and JSON-RPC 2008-03-18 17:53:45 +00:00
Andrey Prygunkov
e9ff2e4fca fixed many warnings (regarding type conversion) on windows, instead of suppressing them with pragma-directive 2008-03-17 17:54:52 +00:00
Andrey Prygunkov
173622fea1 implemented the output-redirection for post-process-scripts on windows 2008-03-17 16:44:14 +00:00
Andrey Prygunkov
9881f2c39e corrected few output-messages in remote-client 2008-03-16 15:54:28 +00:00
Andrey Prygunkov
0970c0bc52 added the redirecting of post-process-script's output to log; new option <PostLogKind> to specify the default message-kind for unformatted log-messages 2008-03-14 22:42:55 +00:00
Andrey Prygunkov
cb2686dc3c eliminated one warning on GCC 2008-03-13 19:10:11 +00:00
Andrey Prygunkov
e0d79a4079 added option <DiskSpace> to automatically pause the download on low disk space 2008-03-13 19:07:13 +00:00
Andrey Prygunkov
2e54a182c9 fixed: stage-time was not set in post-processor (bug introduced in r143) 2008-03-09 08:48:21 +00:00
Andrey Prygunkov
83a405db8b fixed: by discarding of download-queue not all state-files were deleted from disk 2008-03-07 17:15:00 +00:00
Andrey Prygunkov
cdddecb834 added missed files and fixed the discarding of post-proccessor-queue 2008-03-07 16:45:28 +00:00
Andrey Prygunkov
faf528a94e added the saving and restoring of the post-processor-queue (if server was stopped before all items were processed); new option <ReloadPostQueue> 2008-03-07 16:20:27 +00:00
Andrey Prygunkov
aff3c978cb fixed: RPC-method <listfiles> does not work without parameters (they should be optional) 2008-03-06 17:59:48 +00:00
Andrey Prygunkov
c09c787507 fixed: command <write to log> was adding the current path at the beginning of the message text (bug appeared only on posix) 2008-03-06 17:42:06 +00:00
Andrey Prygunkov
9b82667fb5 added support for HTTP-method <GET> in remote server (XML-RPC and JSON-RPC) 2008-03-06 17:25:37 +00:00
Andrey Prygunkov
6a8d341ecc added remote command (switch -W/--write) to write messages to server's log 2008-03-06 17:21:47 +00:00
Andrey Prygunkov
743ce9f07c refactored a little 2008-03-06 16:59:50 +00:00
Andrey Prygunkov
fff550ca37 fixed few incompatibility-issues with unslung-platform on nslu2 (ARM) 2008-03-03 17:20:46 +00:00
Andrey Prygunkov
3c5c76eec4 added new command <-O/--post> to request the post-processor-queue from server 2008-02-28 17:04:14 +00:00
Andrey Prygunkov
ea05b09491 fixed: compile errors after configure with option <disable-parcheck> 2008-02-27 16:39:49 +00:00
Andrey Prygunkov
d6b8993eb3 fixed: not all characters were properly encoded in JSON-responses 2008-02-27 16:37:59 +00:00
Andrey Prygunkov
0d61926a7e added decoding of escaped strings in JSON-RPC, needed for the method <append> 2008-02-26 17:25:08 +00:00
Andrey Prygunkov
f8acf9278c changed version string to <0.4.0-testing> 2008-02-26 17:21:01 +00:00
Andrey Prygunkov
23f6a778a5 added support for JSON-RPC in remote-server 2008-02-25 20:21:00 +00:00
Andrey Prygunkov
e6e950e58c fixed: field <NZBNicename> was not properly encoded in XMLRPC-responses, producing invalid xml on certain filenames 2008-02-22 15:59:58 +00:00
Andrey Prygunkov
7162a19982 fixed: parameter <state of nzb-job> for post-process-script was not correct (bug introduced in r121) 2008-02-22 15:56:28 +00:00
Andrey Prygunkov
5a77dd8a96 fixed: some characters (in filenames) were not encoded properly in XML-RPC 2008-02-20 23:47:46 +00:00
Andrey Prygunkov
4e89546923 added new parameter to postprocess-script to indicate if any of par-jobs for the same nzb-file failed 2008-02-20 22:50:11 +00:00
Andrey Prygunkov
0429a689a4 fixed: StageTime was not set for script-jobs when par-check was skipped (affected only XML-RPC-method <postqueue>) 2008-02-20 22:19:39 +00:00
Andrey Prygunkov
b7239b611a extended the result of XML-RPC-method <listfiles> with one new field 2008-02-20 21:50:48 +00:00
Andrey Prygunkov
057df4d35c fixed: too many info-messages <Need more N blocks> could be printed during par-check (appeared on posix only) 2008-02-19 18:23:58 +00:00
Andrey Prygunkov
886a07896b fixed: the pausing of a group could cause the start of post-processing for that group 2008-02-19 17:48:30 +00:00
Andrey Prygunkov
60857b55f1 fixed: post-processor hung after par-check when no post-process-script was defined in options (bug introduced in 121) 2008-02-19 16:46:11 +00:00
Andrey Prygunkov
160590149f added the queueing of post-process-scripts and waiting for script's completion before starting of a next job in postprocessor (par-job or script); the purpose of changes was to provide more balanced cpu utilization; the state <executing script> is now viewable via XML-RPC method <postqueue> 2008-02-18 19:33:15 +00:00
Andrey Prygunkov
98c6ab5aac updated messages and progress-info in parchecker 2008-02-17 13:38:09 +00:00
Andrey Prygunkov
7c18cd1009 improved the parchecker: added the detection and processing of files splitted after parring 2008-02-17 11:56:36 +00:00
Andrey Prygunkov
27e53bc363 fixed: by registering the service on windows the fullpath to nzbget.exe was not always added to service's exename, making the registered service unusable 2008-02-14 18:31:22 +00:00
Andrey Prygunkov
f0e5c6efe7 fixed: nzb-files added from the monitoring-directory have double slashes at the end of pathname 2008-02-14 18:14:35 +00:00
Andrey Prygunkov
cbd86d3810 updated the description of option <decode> in nzbget.conf.example 2008-02-14 17:54:21 +00:00
Andrey Prygunkov
6f9a2dd57f added support for UU-format to internal decoder; removed support for uulib-decoder (it did not work well anyway); replaced the option <decoder (yenc, uulib, none)> with the option <decode (yes, no)> 2008-02-14 17:42:45 +00:00
Andrey Prygunkov
7f45bb22f5 fixed seg.fault in uulib-decoder on files in UU-format 2008-02-13 23:27:12 +00:00
Andrey Prygunkov
9137ed6278 improved the internal decoder: 1) added the support for yEnc-files without ypart-statement (sometimes used for small files); 2) added the detection of UU-format and the printing of a specific error-message instead of a general error-message; the download of article with detected unsupported format stops on early stage (after the first line of article's body), saving traffic and time 2008-02-13 21:42:01 +00:00
Andrey Prygunkov
3cda6109e9 improved the bugfix for a seg.fault in libpar2-patch (windows only) 2008-02-13 21:00:37 +00:00
Andrey Prygunkov
0ce6f07064 fixed: executing of post-process-script caused seg.fault if compiled in debug-mode on Linux 2008-02-13 20:34:07 +00:00
Andrey Prygunkov
6bbad0a399 fixed inconsistent line-endings in one of source files 2008-02-12 20:08:57 +00:00
Andrey Prygunkov
8c371cf8af added the gathering of progress-information during par-check; extended the XML-RPC-method <postqueue> with that info 2008-02-12 20:02:09 +00:00
Andrey Prygunkov
d74e484752 improved handling of nzb-files with multiple collections in par-checker 2008-02-11 18:55:11 +00:00
Andrey Prygunkov
8f315d9311 fixed: by dupe-checking of files contained in nzb-file the files with the same size were ignored (not deleted) 2008-02-11 18:50:47 +00:00
Andrey Prygunkov
4a63f14c75 added support for multicalls in XML-RPC-server 2008-02-10 16:14:16 +00:00
Andrey Prygunkov
e42f7426ee added option <DetailTarget> to allow to filter the (not so important) log-messages from articles' downloads (they have now the type <detail> instead of <info>) 2008-02-08 18:57:30 +00:00
Andrey Prygunkov
e96a1d6bde fixed: dupecheck could cause seg.faults when all articles for a file failed 2008-02-08 18:34:04 +00:00
Andrey Prygunkov
d08805dca6 fixed: articles with trailing text after binary data caused the decode failures and the reporting of CRC-errors 2008-02-08 18:28:57 +00:00
Andrey Prygunkov
1ca268e36a refactored: moved global functions in unit <Util> to new class <Util> to prevent possible conflicts with system functions; added error-reporting by moving completed files from tmp- to dst-directory and added code to move files across drives if renaming fails 2008-02-07 18:23:59 +00:00
Andrey Prygunkov
865afb2f9a added detection of errors <server busy> and <remote server not available> (special case for NNTPCache-server) to consider them as connect-errors (and therefore not count as retries); added check for incomplete articles (also mostly for NNTPCache-server) to such errors from CrcErrors (better error reporting) 2008-02-06 17:54:09 +00:00
Andrey Prygunkov
44f448493c fixed a small memory leak on windows 2008-02-06 17:03:44 +00:00
Andrey Prygunkov
cee5f769b6 updated libpar2-patch for msvc to fix a segfault in libpar2 (windows only) 2008-02-06 16:59:42 +00:00
Andrey Prygunkov
ea05d5635a added the automatic cleaning up of the download queue (deletion of unneeded paused par-files) after successful par-check/repair - new option <ParCleanupQueue> 2008-02-05 18:10:02 +00:00
Andrey Prygunkov
984f8b2dd9 fixed another warning on GCC (Linux) 2008-02-05 17:57:20 +00:00
Andrey Prygunkov
737f9b3d1e fixed few warnings under GCC on Linux 2008-02-05 17:45:33 +00:00
Andrey Prygunkov
8b3158de99 refactored: created new class NZBInfo and moved related fields from FileInfo into new class to eliminate the storing of duplicate data (nzb-summary) in each FileInfo-object; changed queue-fileformat to save/load NZBInfo and to prevent unneeded reload/-save of queue-files during parsing of nzb-file 2008-02-05 17:34:49 +00:00
Andrey Prygunkov
cf054c401e added XMLRPC-command <postqueue> 2008-02-02 15:34:22 +00:00
Andrey Prygunkov
3a34fa7a8f set svn-keywords on new files 2008-02-02 15:30:40 +00:00
Andrey Prygunkov
9438f91547 added XMLRPC-command <listgroups> 2008-02-02 15:26:07 +00:00
Andrey Prygunkov
157b694727 added the XMLRPC-support to easier control the server from other applications 2008-02-02 13:23:43 +00:00
Andrey Prygunkov
3caf2fc62e small corrections in text-files 2008-02-02 12:53:06 +00:00
Andrey Prygunkov
e3b144a374 added the memory leak detection on windows 2008-02-02 12:45:41 +00:00
Andrey Prygunkov
f7987b5c3e changed version-string to <0.3.2-testing> 2008-02-02 12:29:15 +00:00
Andrey Prygunkov
0204d0849d updated libpar2-patch for msvc to fix a memory leak in libpar2 (windows only) 2008-01-31 19:07:30 +00:00
Andrey Prygunkov
179f9e9ed6 fixed a memory leak in nzb-parser on windows 2008-01-31 19:02:44 +00:00
Andrey Prygunkov
d5b0a8dd8d fixed a memory leak in thread-management-code on windows 2008-01-31 18:58:41 +00:00
Andrey Prygunkov
4744559b46 removed <-testing> from version-string (prepare for release 0.3.1) 2008-01-30 17:06:20 +00:00
Andrey Prygunkov
51b6549671 fixed: compilation error after configuring with <--disable-parcheck> 2008-01-30 17:04:40 +00:00
Andrey Prygunkov
ec61b13269 fixed: trying to move the last group in queue down results in run-time error 2008-01-28 22:04:09 +00:00
Andrey Prygunkov
d898fff913 fixed: long lines (by bad articles) result in seg.fault 2008-01-28 18:29:13 +00:00
Andrey Prygunkov
fc19d48538 added the removing of trailing dot-characters from generated directory names to provide better compatibility with windows 2008-01-24 18:56:03 +00:00
Andrey Prygunkov
fe512e830c added options <NzbDirInterval> and <NzbDirFileAge> to adjust interval and delay by monitoring of incoming-directory for new nzb-files 2008-01-23 21:26:54 +00:00
Andrey Prygunkov
21be45e89a added remote-command <-V> (--serverversion) to print the server's version 2008-01-23 18:17:00 +00:00
Andrey Prygunkov
1eddc76630 fixed: by deleting files from queue the already downloaded articles were not deleted from temp-directory 2008-01-22 18:32:08 +00:00
Andrey Prygunkov
c4cc0cb745 added option <WriteBufferSize> to slightly reduce disk-io (by using of few megabytes of memory) 2008-01-22 18:29:28 +00:00
Andrey Prygunkov
1944ea7273 added gathering of statistical data: uptime, download-time, amount of downloaded data and average session download speed; extended communication protocol to transfer these data; extended ncurses-outputmode to print uptime, download-time and average download speed 2008-01-21 16:58:49 +00:00
Andrey Prygunkov
7da406ed4f removed superfluous trailing CR-character from one warning-message and reformatted few other messages 2008-01-16 18:21:11 +00:00
Andrey Prygunkov
6a0c1031fa added check for message-id of article returned from server, this improves error-detection for bad articles 2008-01-14 19:44:56 +00:00
Andrey Prygunkov
15b016ef7b extended response-messages in communication protocol for better error-reporting on client-side 2008-01-14 19:36:43 +00:00
Andrey Prygunkov
63b8c11ab5 imporved error reporting in remote server on binding errors (previously they were reported only with debug-binaries); fixed: remote server was listening to all interfaces regardless of option <serverip>, now it listen only to the specified address, but the old behaviour is also possible with address <0.0.0.0>, which means <all interfaces> 2008-01-13 17:09:53 +00:00
Andrey Prygunkov
5ca2279af8 improved the download speed meter, new implementation is much more accurate, especially on fast connections; this also means better speed throttling 2008-01-12 18:29:47 +00:00
Andrey Prygunkov
d8cf6263de eliminated few compiler warnings under gcc 2008-01-11 17:36:57 +00:00
Andrey Prygunkov
ebdd0fd1b8 fixed: it was generated way too many debug-messages in ServerPool, even in StandBy-mode 2008-01-11 16:42:28 +00:00
Andrey Prygunkov
9fa4cace8e improved duplicate check: it now works even for files, which subjects could not be parsed - though these files are not detected as duplicates by adding to queue, they will be detected after correct filename is read from the first article's body 2008-01-10 22:05:17 +00:00
Andrey Prygunkov
27d5f058eb better handling of cancelled downloads (important for keeped-alive connections) 2008-01-10 20:55:08 +00:00
Andrey Prygunkov
849db2a9dc authorization errors are now handled like connect errors and not counted as retries (this might cause the lost of articles under heavy load of newsserver) 2008-01-10 18:41:23 +00:00
Andrey Prygunkov
71531b0077 fixed: SmartOrder did not work by moving the files to the top of queue; fixed: DupeCheck did not check files on disk 2008-01-10 18:19:42 +00:00
Andrey Prygunkov
b254a6b6c9 added edit-commands for <pause all pars> and <pause extra pars> (works best on groups but also with file ranges); the key <P> in curses-outputmode in group-view now switches between three states: pause extra pars -> pause all files -> unpause all files; updated ChangeLog and README 2008-01-09 17:45:11 +00:00
Andrey Prygunkov
1cbf435468 improved communication with news-servers: connections are now keeped open until all files are downloaded (or server is paused), this eliminates the need for establishing of connections and authorization for each article; increased maximum possible download speed on fast connection via eliminating of pauses by starting of new articles' downloads (improved the synchronisation mechanism); another speed optimisation in internal decoder (up to 8% faster) 2008-01-08 21:21:16 +00:00
Andrey Prygunkov
abb33b763b fixed: if nzb-parser decides to use subject as a filename (fallback option), this new filename is not saved on disk (and will not be restored on next server's start) 2008-01-05 21:54:01 +00:00
Andrey Prygunkov
f79bdab2cd fixed: par-checking should not be started on deleting of collections from queue 2008-01-05 16:20:22 +00:00
Andrey Prygunkov
1d294f5037 fixed: decoder does not work with enabled option <DirectWrite> 2008-01-04 23:14:29 +00:00
Andrey Prygunkov
40b7b335b1 improved decoding speed (up to 20% faster); added new option <crccheck> to bypass crc-calculation on slow CPUs 2008-01-04 17:08:47 +00:00
Andrey Prygunkov
c18a3b1d7c fixed a compatibility issue (compilation error), detected on one of test linux systems 2008-01-03 17:53:56 +00:00
Andrey Prygunkov
3d782c985f improved parsing of artcile's subject for better extracting of filename part from it and implemented a fallback-option if the parsing was incorrect; improved dupe check for files from the same nzb-request to detect reposted files and download only the best from them; fixed error on parsing of nzb-files containing percent and other special characters in their names (bug appeared on windows only) 2008-01-02 18:41:05 +00:00
Andrey Prygunkov
d35a2d2f04 reformated sample configuration file and changed default optionnames from lowercase to MixedCase for better readability 2007-12-31 17:10:17 +00:00
Andrey Prygunkov
b85a36944d implemented decode-on-the-fly-technique to reduce disk-io; intermediate files with articles' source text are not created anymore, but only intermediate files with decoded data; futher, decoder can write decoded data directly to the destination file (without any intermediate files at all), this eliminates the necessity of joining of articles later (option <directwrite>) 2007-12-31 16:30:47 +00:00
Andrey Prygunkov
e15063208a fixed: destination directory for files was not initialized, caused by changes in revision 50 (greatly reduced...) 2007-12-27 19:24:48 +00:00
Andrey Prygunkov
255ebb8ccd fixed a compilation error on linux 2007-12-26 23:43:42 +00:00
Andrey Prygunkov
07d04d0e65 greatly reduced the memory consumption by keeping articles' info on disk until the file download starts 2007-12-26 23:31:49 +00:00
Andrey Prygunkov
b3cebce074 small changes in curses-outputmode 2007-12-26 13:21:52 +00:00
Andrey Prygunkov
c03f79155d added option <threadlimit> to prevent program from crash if it wants to create too many threads (sometimes may occur in special cases) 2007-12-26 12:06:25 +00:00
Andrey Prygunkov
d449a26ae0 removed test-code from NZBFile 2007-12-26 00:30:02 +00:00
Andrey Prygunkov
904227ebfc added translation of printed messages to oem-codepage to correctly print filenames with non-english characters (windows only) 2007-12-25 22:48:40 +00:00
Andrey Prygunkov
bff48cd97e fixed: deletion of currently downloading files was not always successful and second attempt was neccessary 2007-12-25 16:10:38 +00:00
Andrey Prygunkov
c72c8df5e9 added check to detect special synchonisation issues on windows in debug-mode 2007-12-25 14:37:19 +00:00
Andrey Prygunkov
8a33307b6c improved error-reporting (_brokenlog.txt) on crc-errors 2007-12-25 14:20:27 +00:00
Andrey Prygunkov
5f19177902 fixed: edit-group-commands not worked on posix due synchonisation issue 2007-12-25 01:34:55 +00:00
Andrey Prygunkov
2cb33bafc4 added option <retryoncrcerror> 2007-12-24 23:53:27 +00:00
Andrey Prygunkov
78baf30339 added replacing of CR and LF charachters with spaces in log-window in ncurses-outputmode for better formatting; normalized line-endings in few source files 2007-12-24 22:28:53 +00:00
Andrey Prygunkov
ea4a48dd20 fixed exception occuring if invalid offset was passed to edit-group-command 2007-12-24 21:01:12 +00:00
Andrey Prygunkov
1b8d9e9bfa changed initalization of options to fix conflict between suboption [G] and negative offsets in edit-command 2007-12-24 20:55:49 +00:00
Andrey Prygunkov
ad9a66e971 implemented edit-command <move-offset> for groups 2007-12-24 20:27:22 +00:00
Andrey Prygunkov
bb5c660e1f keywords set for QueueEditor.cpp/h (second attempt) 2007-12-24 17:51:32 +00:00
Andrey Prygunkov
4f29ed2e4c optimzed QueueEditor to edit lists of files and to provide transactional changes to all affected files 2007-12-24 17:37:23 +00:00
Andrey Prygunkov
6d5929a611 implemented edits for lists of groups; extended server/client communication protocol for group-commands; added suboption <G> for command-line switch <-E> to edit groups; Note: group-commands are still limited to pause, resume, delete, move-top, move-bottom 2007-12-23 22:54:10 +00:00
Andrey Prygunkov
c34ad991c4 refactored QueueEditor 2007-12-21 17:23:35 +00:00
Andrey Prygunkov
8eceb70626 added editing of queue for group-view in curses-outputmode: pause, resume, move-to-top, move-to-bottom; currently work only in server-console or standalone-app, not in remote-client 2007-12-20 23:45:05 +00:00
Andrey Prygunkov
f3f609f747 refactored: new class QueueEditor to handle all edit-requests; related code moved from QueueCoordinator and RemoteServer 2007-12-20 19:22:45 +00:00
Andrey Prygunkov
c02074e7ec creation of necessary directories on program's start extended with automatic creation of all parent directories and error reporting if it was not possible 2007-12-19 22:55:03 +00:00
Andrey Prygunkov
6697737405 added options <cursesnzbname>, <cursesgroup> and <cursestime> to define initial state of curses-outputmode 2007-12-19 20:41:06 +00:00
Andrey Prygunkov
dd3ca56564 toggle files/nzbs-list in curses-outputmode with G-key 2007-12-19 19:58:39 +00:00
Andrey Prygunkov
71805e11f7 removed accidentally commited debug-code in ParChecker.cpp 2007-12-19 18:27:26 +00:00
Andrey Prygunkov
e480781905 fixed a segfault appeared on Windows and caused by changes in revision 23 (added par-checker's status...) 2007-12-19 17:53:18 +00:00
Andrey Prygunkov
4df72eafd2 updated ChangeLog 2007-12-19 17:23:38 +00:00
Andrey Prygunkov
cdbfd3b96d added par-checker's status to status line and output of list-command 2007-12-18 23:23:56 +00:00
Andrey Prygunkov
cee6aa53f4 fully implemented SmartOrder-parameter for Edit-commands 2007-12-17 20:13:20 +00:00
Andrey Prygunkov
2d9bca1d1b added option <updateinterval> 2007-12-16 12:55:21 +00:00
Andrey Prygunkov
80685270e0 added option <umask> to specify permissions for newly created files and dirs (POSIX only) 2007-12-15 13:42:06 +00:00
Andrey Prygunkov
5695eaf3f4 1) fixed: file-ids in Edit-command were not converted to/from network byte order; 2) fixed few warnings under gcc 2007-12-14 22:36:10 +00:00
Andrey Prygunkov
8d46ee3236 eliminated configure-option --disable-parprogress (not needed anymore) 2007-12-14 22:19:51 +00:00
Andrey Prygunkov
702aad905f refactored RemoteServer and implemented initial support for SmartOrder in Edit-Command (currently only for MoveTop- and MoveBottom-commands) 2007-12-14 22:04:44 +00:00
Andrey Prygunkov
1da4976fea Edit-command now accepts inverse ranges of file-ids (this allows to keep relative order of files by moving items to the beginning of queue, since edit-command processes files in a giving order), e.g.: nzbget -E T 10-1 2007-12-13 23:56:59 +00:00
Andrey Prygunkov
94b81f9ce4 Edit-command now accepts more than one file-id or range of file-ids and does not require switch <I>, for example: nzbget -E P 2,6-10,33-39 2007-12-13 23:40:33 +00:00
Andrey Prygunkov
eec1a797b3 changed various structures in communication protocol 2007-12-13 19:08:55 +00:00
Andrey Prygunkov
ca7a90adab show/hide timestamps in curses-outputmode with T-key 2007-12-13 18:24:54 +00:00
Andrey Prygunkov
adedd66dac added few comments and renamed one field in MessageBase.h 2007-12-13 00:34:00 +00:00
Andrey Prygunkov
0a43829772 fixed and extended parameters for post-process-scripts (<result of par-check> and <state of nzb-job>) 2007-12-13 00:02:24 +00:00
Andrey Prygunkov
a35ff448b5 revised communication protocol to achieve compatibility between hosts with different endianness 2007-12-08 13:51:54 +00:00
Andrey Prygunkov
74db2f7785 updated version number in configure-script to 0.3.1-testing to differentiate between executables built from svn and published releases 2007-12-08 13:28:27 +00:00
Andrey Prygunkov
4760198c23 added missing Makefile.cvs (needed by KDevelop to rebuild configure-scripts) 2007-12-08 13:23:43 +00:00
Andrey Prygunkov
078c6c037e eliminated few compiler warnings 2007-12-07 22:20:45 +00:00
Andrey Prygunkov
56d4fcf045 fixed segfault when gethostbyname_r returns hinfo=NULL 2007-12-07 22:08:21 +00:00
Andrey Prygunkov
24c4cd87d7 added option daemonusername 2007-12-01 14:36:53 +00:00
Andrey Prygunkov
1b80cdd9b4 fixed seg fault on nzb-names starting with msgid 2007-11-29 18:53:17 +00:00
Andrey Prygunkov
b66266cf82 fixed an endless loop on receiving of SIGSEGV-signal in debug-mode 2007-11-29 18:06:49 +00:00
70 changed files with 15739 additions and 7675 deletions

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -47,9 +47,13 @@ public:
adRunning,
adFinished,
adFailed,
adDecodeError,
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
@@ -63,20 +67,20 @@ private:
char* m_szTempFilename;
char* m_szArticleFilename;
char* m_szInfoName;
char* m_szOutputFilename;
time_t m_tLastUpdateTime;
Decoder* m_pDecoder;
Semaphore m_semInitialized;
Semaphore m_semWaited;
static const char* m_szJobStatus[];
#ifdef WIN32
struct _timeb m_tStartTime;
#else
struct timeval m_tStartTime;
#endif
int m_iBytes;
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
EStatus Download();
void FreeConnection();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
public:
ArticleDownloader();
@@ -87,7 +91,6 @@ public:
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
const char* GetStatusText() { return m_szJobStatus[m_eStatus]; }
virtual void Run();
virtual void Stop();
bool Terminate();
@@ -95,17 +98,12 @@ public:
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void CompleteFileParts();
void WaitInit();
#ifdef WIN32
struct _timeb* GetStartTime() { return &m_tStartTime; }
#else
struct timeval* GetStartTime() { return &m_tStartTime; }
#endif
int GetBytes() { return m_iBytes; }
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
void LogDebugInfo();
};
@@ -115,6 +113,7 @@ class DownloadSpeedMeter
public:
virtual ~DownloadSpeedMeter() {};
virtual float CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};
#endif

774
BinRpc.cpp Normal file
View File

@@ -0,0 +1,774 @@
/*
* 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>
#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" };
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),
};
//*****************************************************************
// 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)eRemoteRequestPostQueue &&
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;
}
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, 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(SNZBListResponseEntry));
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();
int NrEntries = pDownloadQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bufsize += strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1;
bufsize += strlen(pFileInfo->GetSubject()) + 1;
bufsize += strlen(pFileInfo->GetFilename()) + 1;
bufsize += strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 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;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
unsigned long iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
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_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1);
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 1);
bufptr += sizeof(SNZBListResponseEntry);
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetFilename());
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += ntohl(pListAnswer->m_iSubjectLen);
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iDestDirLen = htonl(ntohl(pListAnswer->m_iDestDirLen) + 4 - (size_t)bufptr % 4);
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
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);
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);
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
if (iNrEntries * sizeof(int32_t) != iBufLength)
{
error("Invalid struct size");
return;
}
if (iNrEntries <= 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
return;
}
int32_t* pIDs = (int32_t*)malloc(iBufLength);
// Read from the socket until nothing remains
char* pBufPtr = (char*)pIDs;
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;
}
QueueEditor::IDList cIDList;
cIDList.reserve(iNrEntries);
for (int i = 0; i < iNrEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
free(pIDs);
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
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);
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);
}

131
BinRpc.h Normal file
View File

@@ -0,0 +1,131 @@
/*
* 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$
*
*/
#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();
};
#endif

171
ChangeLog
View File

@@ -1,3 +1,170 @@
nzbget-0.4.1:
- to avoid accidental deletion of file in curses-frontend the key <D>
not now must be pressed in uppercase;
- options <username> and <password> in news-server's configuration are now
optional;
- added the server's name to the detail-log-message, displayed on the start
of article's download;
- added the option <AllowReProcess> to help to post-process-scripts, which
make par-check/-repair on it's own;
- improved download-speed-meter: it useses now a ölittle bit less cpu and
calculates the speed for the last 30 seconds (instead of 5 seconds),
providing better accuracy; Thanks to ydrol <ydrol@users.sourceforge.net>
for the patch;
- reduced CPU-usage in curses-outputmode; Thanks to ydrol for the patch
<ydrol@users.sourceforge.net>;
- fixed: line-endings in windows-style (CR-LF) in config-file were not
read properly;
- fixed: trailing spaces in nzb-filenames (before the file's extension)
caused errors on windows. Now they will be trimmed;
- fixed: XML-RPC and JSON-RPC did not work on Big-Endian-CPUs (ARM, PPC, etc),
preventing the using of web-interface;
- fixed: umask-option did not allow to enable write-permissions for <group>
and <others>;
- fixed: in curses-outputmode the remote-client showed on first screen-update
only one item of queue;
- fixed: edit-commands with negative offset did not work via XML-RPC
(but worked via JSON-RPC);
- fixed incompatibility issues with gcc 4.3; Thanks to Paul Bredbury
<brebs@users.sourceforge.net> for the patch;
- fixed: segmentation fault if a file listed in nzb-file does not have any
segments (articles);
nzbget-0.4.0:
- added the support for XML-RPC and JSON-RPC to easier control the server
from other applications;
- added web-interface - it is available for download from NZBGet-project's
home page as a separate package "web-interface";
- added the automatic cleaning up of the download queue (deletion of unneeded
paused par-files) after successful par-check/repair - new
option <ParCleanupQueue>;
- added option <DetailTarget> to allow to filter the (not so important)
log-messages from articles' downloads (they have now the type <detail>
instead of <info>);
- added the gathering of progress-information during par-check; it is
available via XML-RPC or JSON-RPC; it is also showed in web-interface;
- improvements in internal decoder: added support for yEnc-files without
ypart-statement (sometimes used for small files); added support for
UU-format;
- removed support for uulib-decoder (it did not work well anyway);
- replaced the option <decoder (yenc, uulib, none)> with the option
<decode (yes, no)>;
- added detection of errors <server busy> and <remote server not available>
(special case for NNTPCache-server) to consider them as connect-errors
(and therefore not count as retries);
- added check for incomplete articles (also mostly for NNTPCache-server) to
differ such errors from CrcErrors (better error reporting);
- improved error-reporting on moving of completed files from tmp- to
dst-directory and added code to move files across drives if renaming fails;
- improved handling of nzb-files with multiple collections in par-checker;
- improved the parchecker: added the detection and processing of files
splitted after parring;
- added the queueing of post-process-scripts and waiting for script's
completion before starting of a next job in postprocessor (par-job or
script) to provide more balanced cpu utilization;
- added the redirecting of post-process-script's output to log; new option
<PostLogKind> to specify the default message-kind for unformatted
log-messages;
- added the returning of script-output by command <postqueue> via XML-RPC
and JSON-RPC; the script-output is also showed in web-interface;
- added the saving and restoring of the post-processor-queue (if server was
stopped before all items were processed); new option <ReloadPostQueue>;
- added new parameter to postprocess-script to indicate if any of par-jobs
for the same nzb-file failed;
- added remote command (switch O/--post) to request the post-processor-queue
from server;
- added remote command (switch -W/--write) to write messages to server's log;
- added option <DiskSpace> to automatically pause the download on low disk
space;
- fixed few incompatibility-issues with unslung-platform on nslu2 (ARM);
- fixed: articles with trailing text after binary data caused the decode
failures and the reporting of CRC-errors;
- fixed: dupecheck could cause seg.faults when all articles for a file failed;
- fixed: by dupe-checking of files contained in nzb-file the files with the
same size were ignored (not deleted from queue);
- updated libpar2-patch for msvc to fix a segfault in libpar2 (windows only);
- fixed: by registering the service on windows the fullpath to nzbget.exe
was not always added to service's exename, making the registered service
unusable;
- fixed: the pausing of a group could cause the start of post-processing for
that group;
- fixed: too many info-messages <Need more N blocks> could be printed during
par-check (appeared on posix only);
nzbget-0.3.1:
- Greatly reduced the memory consumption by keeping articles' info on disk
until the file download starts;
- Implemented decode-on-the-fly-technique to reduce disk-io; downloaded
and decoded data can also be saved directly to the destination file
(without any intermediate files at all); this eliminates the necessity
of joining of articles later (option "DirectWrite");
- Improved communication with news-servers: connections are now keeped open
until all files are downloaded (or server paused); this eliminates the
need for establishing of connections and authorizations for each
article and improves overal download speed;
- Significantly better download speed is now possible on fast connection;
it was limited by delays on starting of new articles' downloads;
the synchronisation mechanism was reworked to fix this issue;
- Download speed meter is much more accurate, especially on fast connections;
this also means better speed throttling;
- Speed optimisations in internal decoder (up to 25% faster);
- CRC-calculation can be bypassed to increase performance on slow CPUs
(option "CrcCheck");
- Improved parsing of artcile's subject for better extracting of filename
part from it and implemented a fallback-option if the parsing was incorrect;
- Improved dupe check for files from the same nzb-request to detect reposted
files and download only the best from them (if option "DupeCheck" is on);
- Articles with incorrect CRC can be treated as "possibly recoverable errors"
and relaunched for download (option "RetryOnCrcError"), it is useful if
multiple servers are available;
- Improved error-check for downloaded articles (crc-check and check for
received message-id) decreases the number of broken files;
- Extensions in curses-outputmode: added group-view-mode (key "G") to show
items in download queue as groups, where one group represents all files
from the same nzb-file; the editing of queue works also in group-mode
(for all files in this group): pause/unpause/delete/move of groups;
- Other extensions in curses-outputmode: key "T" toggles timestamps in log;
added output of statistical data: uptime, download-time, average session
download speed;
- Edit-command accepts more than one ID or range of IDs.
E.g: "nzbget -E P 2,6-10,33-39"; The switch "-I" is not used anymore;
- Move-actions in Edit-command affect files in a smart order to guarantee
that the relative order of files in queue is not changed after the moving;
- Extended syntax of edit-command to edit groups (pause/unpause/delete/move
of groups). E.g: "nzbget -E G P 2";
- Added option "DaemonUserName" to set the user that the daemon (POSIX only)
normally runs at. This allows nzbget daemon to be launched in rc.local
(at boot), and download items as a specific user id; Thanks to Thierry
MERLE <merlum@users.sourceforge.net> for the patch;
- Added option "UMask" to specify permissions for newly created files and dirs
(POSIX only);
- Communication protocol used between server and client was revised to define
the byte order for transferred data. This allows hosts with different
endianness to communicate with each other;
- Added options "CursesNzbName", "CursesGroup" and "CursesTime" to define
initial state of curses-outputmode;
- Added option "UpdateInterval" to adjust update interval for Frontend-output
(useful in remote-mode to reduce network usage);
- Added option "WriteBufferSize" to reduce disk-io (but it could slightly
increase memory usage and therefore disabled by default);
- List-command prints additional statistical info: uptime, download-time,
total amount of downloaded data and average session download speed;
- The creation of necessary directories on program's start was extended
with automatic creation of all parent directories or error reporting
if it was not possible;
- Printed messages are now translated to oem-codepage to correctly print
filenames with non-english characters (windows only);
- Added remote-command "-V (--serverversion)" to print the server's version;
- Added option "ThreadLimit" to prevent program from crash if it wants to
create too many threads (sometimes could occur in special cases);
- Added options "NzbDirInterval" and "NzbDirFileAge" to adjust interval and
delay by monitoring of incoming-directory for new nzb-files;
- Fixed error on parsing of nzb-files containing percent and other special
characters in their names (bug appeared on windows only);
- Reformated sample configuration file and changed default optionnames
from lowercase to MixedCase for better readability;
- Few bugs (seg faults) were fixed.
nzbget-0.3.0:
- The download queue now contains newsgroup-files to be downloaded instead of
nzb-jobs. By adding a new job, the nzb-file is immediately parsed and each
@@ -109,7 +276,7 @@ nzbget-0.3.0:
of completing and state (paused or not) for every file is printed.
The header of queue shows number of total files, number of unpaused
files and size for all and unpaused files. Better using of screen estate
space <20> no more empty lines and separate header for status (total seven
space <20> no more empty lines and separate header for status (total seven
lines gain). The messages are printed on several lines (if they not fill
in one line) without trimming now;
- configure.ac-file updated to work with recent versions of autoconf/automake.
@@ -136,7 +303,7 @@ nzbget-0.3.0:
- Solaris 10 on x86;
- Many memory-leaks and thread issues were fixed;
- The program was thoroughly worked over. Almost every line of code was
revised;
revised.
nzbget-0.2.3
- Fixed problem with losing connection to newsserver after too long idle time

View File

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

View File

@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifndef WIN32
#include <netdb.h>
#include <unistd.h>
@@ -47,6 +48,8 @@
#include "Connection.h"
#include "Log.h"
static const int CONNECTION_READBUFFER_SIZE = 1024;
void Connection::Init()
{
debug("Intiializing global connection data");
@@ -86,18 +89,35 @@ Connection::Connection(NetAddress* pNetAddress)
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_bCanceling = false;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = true;
}
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;
}
Connection::~Connection()
{
debug("Destroying Connection");
if (m_eStatus == csConnected)
if (m_eStatus == csConnected && m_bAutoClose)
{
Disconnect();
}
free(m_szReadBuf);
}
int Connection::Connect()
@@ -127,6 +147,8 @@ int Connection::Disconnect()
int iRes = DoDisconnect();
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
return iRes;
}
@@ -142,12 +164,15 @@ int Connection::Bind()
int iRes = DoBind();
m_eStatus = csListening;
if (iRes == 0)
{
m_eStatus = csListening;
}
return iRes;
}
int Connection::WriteLine(char* line)
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
@@ -156,15 +181,12 @@ int Connection::WriteLine(char* line)
return -1;
}
int iRes = DoWriteLine(line);
if (iRes == EOF)
Connection::DoDisconnect();
int iRes = DoWriteLine(pBuffer);
return iRes;
}
int Connection::Send(char* pBuffer, int iSize)
int Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
@@ -178,17 +200,14 @@ int Connection::Send(char* pBuffer, int iSize)
return iRes;
}
char* Connection::ReadLine(char* pBuffer, int iSize)
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
if (m_eStatus != csConnected)
{
return NULL;
}
char* res = DoReadLine(pBuffer, iSize);
if (res == NULL)
Connection::DoDisconnect();
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
}
@@ -231,6 +250,17 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
char* pBufPtr = (char*)pBuffer;
int NeedBytes = iSize;
if (m_iBufAvail > 0)
{
int len = iSize > m_iBufAvail ? m_iBufAvail : iSize;
memcpy(pBufPtr, m_szBufPtr, len);
pBufPtr += len;
m_szBufPtr += len;
m_iBufAvail -= len;
NeedBytes -= len;
}
// Read from the socket until nothing remains
while (NeedBytes > 0)
{
@@ -301,7 +331,7 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
{
struct hostent* hinfo;
bool err = false;
int h_errnop;
int h_errnop = 0;
#ifdef WIN32
hinfo = gethostbyname(szHost);
err = hinfo == NULL;
@@ -312,6 +342,7 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
char* strbuf = (char*)malloc(strbuflen);
#ifdef HAVE_GETHOSTBYNAME_R_6
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
#else
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
err = hinfo == NULL;
@@ -348,24 +379,25 @@ int Connection::DoDisconnect()
return 0;
}
int Connection::DoWriteLine(char* szText)
int Connection::DoWriteLine(const char* pBuffer)
{
//debug("Connection::doWrite()");
return send(m_iSocket, szText, strlen(szText), 0);
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
char* Connection::DoReadLine(char* pBuffer, int iSize)
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
//debug( "Connection::DoReadLine()" );
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, ReadBufLen, 0);
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, 0);
@@ -383,7 +415,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = p - szBufPtr + 1;
len = (int)(p - szBufPtr + 1);
}
else
{
@@ -399,18 +431,24 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
iSize--;
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
@@ -432,7 +470,18 @@ int Connection::DoBind()
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
@@ -461,7 +510,7 @@ SOCKET Connection::DoAccept()
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
if (iSocket == INVALID_SOCKET && !m_bCanceling)
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, 0);
}
@@ -472,15 +521,14 @@ SOCKET Connection::DoAccept()
void Connection::Cancel()
{
debug("Cancelling connection");
m_bCanceling = true;
if (m_iSocket != INVALID_SOCKET)
{
m_eStatus = csCancelled;
int r = shutdown(m_iSocket, SHUT_RDWR);
if (r == -1)
{
ReportError("Could not shutdown connection", NULL, 0);
}
m_eStatus = csCancelled;
}
}
@@ -499,9 +547,23 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
szErrPrefix[1024-1] = '\0';
#ifdef WIN32
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
#else
const char* szErrMsg = hstrerror(ErrCode);
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
else
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
#endif
}

View File

@@ -43,42 +43,45 @@ public:
protected:
NetAddress* m_pNetAddress;
SOCKET m_iSocket;
static const int ReadBufLen = 1024;
char m_szReadBuf[ReadBufLen + 1];
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
bool m_bCanceling;
int m_iTimeout;
bool m_bSuppressErrors;
bool m_bAutoClose;
unsigned int ResolveHostAddr(const char* szHost);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
virtual int DoConnect();
virtual int DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
public:
Connection(NetAddress* pNetAddress);
Connection(SOCKET iSocket, bool bAutoClose);
virtual ~Connection();
static void Init();
static void Final();
int Connect();
int Disconnect();
int Bind();
int Send(char* pBuffer, int iSize);
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize);
int WriteLine(char* text);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
int WriteLine(const char* pBuffer);
SOCKET Accept();
void Cancel();
NetAddress* GetServer() { return m_pNetAddress; }
SOCKET GetSocket() { return m_iSocket; }
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
protected:
virtual int DoConnect();
virtual int DoDisconnect();
int DoBind();
int DoWriteLine(char* text);
char* DoReadLine(char* pBuffer, int iSize);
SOCKET DoAccept();
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
bool GetSuppressErrors() { return m_bSuppressErrors; }
};
#endif

View File

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

107
Decoder.h
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -29,44 +29,93 @@
#include "Thread.h"
//#define DECODER_INTERNAL_FGETS
class Decoder
{
public:
enum EKind
enum EStatus
{
dcUulib,
dcYenc
eUnknownError,
eFinished,
eArticleIncomplete,
eCrcError,
eInvalidSize,
eNoBinaryData
};
private:
static Mutex m_mutexDecoder;
static unsigned int crc_tab[256];
EKind m_eKind;
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
int m_iDebugStatus;
int m_iDebugLines;
enum EFormat
{
efUnknown,
efYenc,
efUx,
};
bool DecodeUulib();
bool DecodeYenc();
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
public:
Decoder();
~Decoder();
bool Execute();
void SetKind(EKind eKind) { m_eKind = eKind; }
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
const char* GetArticleFilename() { return m_szArticleFilename; }
void LogDebugInfo();
Decoder();
virtual ~Decoder();
virtual EStatus Check() = 0;
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
const char* GetArticleFilename() { return m_szArticleFilename; }
static EFormat DetectFormat(const char* buffer, int len);
};
static void Init();
static void Final();
class YDecoder: public Decoder
{
protected:
static unsigned int crc_tab[256];
bool m_bBegin;
bool m_bPart;
bool m_bBody;
bool m_bEnd;
bool m_bCrc;
unsigned long m_lExpectedCRC;
unsigned long m_lCalculatedCRC;
unsigned long m_iBegin;
unsigned long m_iEnd;
unsigned long m_iSize;
unsigned long m_iEndSize;
bool m_bAutoSeek;
bool m_bNeedSetPos;
bool m_bCrcCheck;
unsigned int DecodeBuffer(char* buffer);
static void crc32gentab();
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
public:
YDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
static void Init();
static void Final();
};
class UDecoder: public Decoder
{
private:
bool m_bBody;
bool m_bEnd;
unsigned int DecodeBuffer(char* buffer, int len);
public:
UDecoder();
virtual EStatus Check();
virtual void Clear();
virtual bool Write(char* buffer, int len, FILE* outfile);
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include "nzbget.h"
#include "DiskState.h"
@@ -43,16 +44,42 @@
extern Options* g_pOptions;
static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version 3\n";
/* 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.
* If parameter "OnlyOrder" is set to true, only the file "queue" will
* be written to disk (It useful, if only the order of files in queue was changed).
* This function saves file "queue" and files with NZB-info. It does not
* save file-infos.
*/
bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
{
debug("Saving queue to disk");
// prepare list of nzb-infos
typedef std::deque<NZBInfo*> NZBList;
NZBList cNZBList;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bool inlist = false;
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
{
NZBInfo* pNZBInfo = *it;
if (pNZBInfo == pFileInfo->GetNZBInfo())
{
inlist = true;
break;
}
}
if (!inlist)
{
cNZBList.push_back(pFileInfo->GetNZBInfo());
}
}
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
@@ -66,28 +93,45 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
return false;
}
fprintf(outfile, "nzbget diskstate file version 1\n");
fprintf(outfile, FORMATVERSION_SIGNATURE);
int cnt = 0;
// save nzb-infos
fprintf(outfile, "%i\n", cNZBList.size());
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
{
NZBInfo* pNZBInfo = *it;
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
unsigned long High, Low;
Util::SplitInt64(pNZBInfo->GetSize(), &High, &Low);
fprintf(outfile, "%lu,%lu\n", High, Low);
}
// 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())
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
fileName[1024-1] = '\0';
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
if (!OnlyOrder)
// find index of nzb-info
int iNZBIndex = 0;
for (unsigned int i = 0; i < cNZBList.size(); i++)
{
SaveFileInfo(pFileInfo, fileName);
iNZBIndex++;
if (cNZBList[i] == pFileInfo->GetNZBInfo())
{
break;
}
}
cnt++;
fprintf(outfile, "%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused());
}
}
fclose(outfile);
if (cnt == 0)
if (pDownloadQueue->empty())
{
remove(fileName);
}
@@ -95,10 +139,13 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
return true;
}
bool DiskState::Load(DownloadQueue* pDownloadQueue)
bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
{
debug("Loading queue from disk");
typedef std::deque<NZBInfo*> NZBList;
NZBList cNZBList;
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
@@ -111,42 +158,84 @@ bool DiskState::Load(DownloadQueue* pDownloadQueue)
return false;
}
bool res = false;
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
{
int id, paused;
while (fscanf(infile, "%i,%i\n", &id, &paused) != EOF)
{
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);
if (res)
{
pFileInfo->SetID(id);
pFileInfo->SetPaused(paused);
pDownloadQueue->push_back(pFileInfo);
}
else
{
warn("Could not load diskstate for file %s", fileName);
delete pFileInfo;
}
}
res = true;
}
else
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
{
error("Could not load diskstate due file version mismatch");
res = false;
fclose(infile);
return false;
}
int size;
char buf[1024];
// load nzb-infos
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
{
NZBInfo* pNZBInfo = new NZBInfo();
cNZBList.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);
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));
}
// 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 > cNZBList.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(cNZBList[iNZBIndex - 1]);
pDownloadQueue->push_back(pFileInfo);
}
else
{
warn("Could not load diskstate for file %s", fileName);
delete pFileInfo;
}
}
fclose(infile);
return true;
return res;
error:
fclose(infile);
error("Error reading diskstate for file %s", fileName);
return false;
}
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)
@@ -161,13 +250,12 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
return false;
}
fprintf(outfile, "%s\n", pFileInfo->GetNZBFilename());
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
fprintf(outfile, "%s\n", pFileInfo->GetDestDir());
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
fprintf(outfile, "%lu,%lu\n", (unsigned long)(pFileInfo->GetSize() >> 32), (unsigned long)(pFileInfo->GetSize()));
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++)
@@ -187,7 +275,15 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
return true;
}
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
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");
@@ -203,28 +299,20 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pFileInfo->SetNZBFilename(buf);
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'
pFileInfo->SetSubject(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pFileInfo->SetDestDir(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pFileInfo->SetFilename(buf);
if (bFileSummary) pFileInfo->SetFilename(buf);
int iFilenameConfirmed;
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
unsigned long High, Low;
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
pFileInfo->SetRemainingSize(pFileInfo->GetSize());
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;
@@ -232,23 +320,26 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pFileInfo->GetGroups()->push_back(strdup(buf));
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
}
if (fscanf(infile, "%i\n", &size) != 1) goto error;
for (int i = 0; i < size; i++)
if (bArticles)
{
int PartNumber, PartSize;
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
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'
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);
ArticleInfo* pArticleInfo = new ArticleInfo();
pArticleInfo->SetPartNumber(PartNumber);
pArticleInfo->SetSize(PartSize);
pArticleInfo->SetMessageID(buf);
pFileInfo->GetArticles()->push_back(pArticleInfo);
}
}
fclose(infile);
@@ -260,11 +351,128 @@ error:
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);
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, "%i\n", (int)pPostInfo->GetParCheck());
fprintf(outfile, "%i\n", (int)pPostInfo->GetParStatus());
fprintf(outfile, "%i\n", (int)pPostInfo->GetParFailed());
fprintf(outfile, "%i\n", (int)pPostInfo->GetStage());
}
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);
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
{
error("Could not load diskstate due file version mismatch");
fclose(infile);
return false;
}
int size;
char buf[1024];
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 (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParCheck(iIntValue);
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParStatus(iIntValue);
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetParFailed(iIntValue);
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pPostInfo->SetStage((PostInfo::EStage)iIntValue);
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::Discard()
bool DiskState::DiscardDownloadQueue()
{
debug("Discarding queue");
@@ -283,15 +491,31 @@ bool DiskState::Discard()
bool res = false;
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
if (!strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
{
int id, paused;
while (fscanf(infile, "%i,%i\n", &id, &paused) == 2)
// skip nzb-infos
int size = 0;
char buf[1024];
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
fileName[1024-1] = '\0';
remove(fileName);
if (!fgets(buf, sizeof(buf), infile)) break;
if (!fgets(buf, sizeof(buf), infile)) break;
if (!fgets(buf, sizeof(buf), infile)) break;
if (!fgets(buf, sizeof(buf), infile)) break;
}
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;
}
@@ -310,27 +534,56 @@ bool DiskState::Discard()
return res;
}
bool DiskState::Exists()
/*
* 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';
struct stat buffer;
bool fileExists = !stat(fileName, &buffer);
return fileExists;
return Util::FileExists(fileName);
}
bool DiskState::DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo)
bool DiskState::PostQueueExists(bool bCompleted)
{
// delete diskstate-file
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(DownloadQueue* pDownloadQueue, 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 Save(pDownloadQueue, true);
return !pDownloadQueue || SaveDownloadQueue(pDownloadQueue);
}
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
@@ -349,11 +602,14 @@ void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
DirBrowser dir(g_pOptions->GetTempDir());
while (const char* filename = dir.Next())
{
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec");
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)
{
int id, part;
if (sscanf(filename, "%i.%i", &id, &part) == 2)
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
(sscanf(filename, "%i.out", &id) == 1))
{
del = true;
ptr = ids;

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -27,19 +27,26 @@
#define DISKSTATE_H
#include "DownloadInfo.h"
#include "PostInfo.h"
class DiskState
{
private:
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
public:
bool Exists();
bool Save(DownloadQueue* pDownloadQueue, bool OnlyOrder);
bool Load(DownloadQueue* pDownloadQueue);
bool Discard();
bool DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
bool SavePostQueue(PostQueue* pPostQueue, bool bCompleted);
bool LoadPostQueue(PostQueue* pPostQueue, bool bCompleted);
bool DiscardDownloadQueue();
bool DiscardPostQueue();
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,21 +34,116 @@
#include <stdlib.h>
#include <string.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
m_szFilename = NULL;
m_szDestDir = NULL;
m_iFileCount = 0;
m_lSize = 0;
m_iRefCount = 0;
}
NZBInfo::~NZBInfo()
{
debug("Destroying NZBInfo");
if (m_szFilename)
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
}
void NZBInfo::AddReference()
{
m_iRefCount++;
}
void NZBInfo::Release()
{
m_iRefCount--;
if (m_iRefCount <= 0)
{
delete this;
}
}
void NZBInfo::SetDestDir(const char* szDestDir)
{
m_szDestDir = strdup(szDestDir);
}
void NZBInfo::SetFilename(const char * szFilename)
{
m_szFilename = strdup(szFilename);
}
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, '_');
// 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, '_');
// 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';
}
ArticleInfo::ArticleInfo()
{
//debug("Creating ArticleInfo");
@@ -92,13 +187,13 @@ FileInfo::FileInfo()
m_szSubject = NULL;
m_szFilename = NULL;
m_bFilenameConfirmed = false;
m_szDestDir = NULL;
m_szNZBFilename = NULL;
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;
}
@@ -115,26 +210,28 @@ FileInfo::~ FileInfo()
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
{
delete *it;
}
m_Articles.clear();
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
free(*it);
}
m_Groups.clear();
ClearArticles();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void FileInfo::ClearArticles()
{
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
{
delete *it;
}
m_Articles.clear();
}
void FileInfo::SetID(int s)
@@ -146,67 +243,17 @@ void FileInfo::SetID(int s)
}
}
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
{
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->AddReference();
}
void FileInfo::SetSubject(const char* szSubject)
{
m_szSubject = strdup(szSubject);
}
void FileInfo::SetDestDir(const char* szDestDir)
{
m_szDestDir = strdup(szDestDir);
}
void FileInfo::SetNZBFilename(const char * szNZBFilename)
{
m_szNZBFilename = strdup(szNZBFilename);
}
void FileInfo::GetNiceNZBName(char* szBuffer, int iSize)
{
MakeNiceNZBName(m_szNZBFilename, szBuffer, iSize);
}
void FileInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
{
strncpy(szBuffer, BaseFileName(szNZBFilename), iSize);
szBuffer[iSize-1] = '\0';
if (char* p = strrchr(szBuffer, '.')) *p = '\0';
}
void FileInfo::ParseSubject()
{
char* fnstart = strstr(m_szSubject, "\"");
char* fnend = NULL;
if (fnstart)
{
fnstart++;
fnend = strstr(fnstart, "\"");
}
if (fnend)
{
char fn[1024];
strncpy(fn, fnstart, fnend - fnstart);
fn[fnend - fnstart] = '\0';
m_szFilename = strdup(fn);
}
else
{
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", m_szSubject);
m_szFilename = strdup(m_szSubject);
}
//replace bad chars in filename
char* p = m_szFilename;
while (*p)
{
if (strchr("\\/:*?\"><'\n\r\t", *p))
{
*p = '_';
}
p++;
}
}
void FileInfo::SetFilename(const char* szFilename)
{
if (m_szFilename)
@@ -216,57 +263,104 @@ void FileInfo::SetFilename(const char* szFilename)
m_szFilename = strdup(szFilename);
}
void FileInfo::BuildDestDirName(const char* szNZBFilename)
void FileInfo::MakeValidFilename()
{
char szBuffer[1024];
if (g_pOptions->GetAppendNZBDir())
{
char postname[1024];
const char* szBaseName = BaseFileName(szNZBFilename);
// if .nzb file has a certain structure, try to strip out certain elements
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
{
// wipe out certain structure
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
}
else
{
strncpy(postname, szBaseName, 1024);
postname[1024-1] = '\0';
}
// wipe out ".nzb"
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), postname);
szBuffer[1024-1] = '\0';
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
m_szDestDir = strdup(szBuffer);
Util::MakeValidFilename(m_szFilename, '_');
}
bool FileInfo::IsDupe()
void FileInfo::LockOutputFile()
{
debug("Checking if the file was already downloaded or queued");
m_mutexOutputFile.Lock();
}
struct stat buffer;
void FileInfo::UnlockOutputFile()
{
m_mutexOutputFile.Unlock();
}
bool FileInfo::IsDupe(const char* szFilename)
{
char fileName[1024];
bool exists = false;
snprintf(fileName, 1024, "%s%c%s", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
if (!exists)
if (Util::FileExists(fileName))
{
snprintf(fileName, 1024, "%s%c%s_broken", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
return exists;
return false;
}
GroupInfo::GroupInfo()
{
m_iFirstID = 0;
m_iLastID = 0;
m_iRemainingFileCount = 0;
m_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++;
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -30,6 +30,34 @@
#include <vector>
#include <deque>
#include "Thread.h"
class NZBInfo
{
private:
int m_iRefCount;
char* m_szFilename;
char* m_szDestDir;
int m_iFileCount;
long long m_lSize;
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; }
void SetDestDir(const char* szDestDir);
long long GetSize() { return m_lSize; }
void SetSize(long long s) { m_lSize = s; }
int GetFileCount() { return m_iFileCount; }
void SetFileCount(int s) { m_iFileCount = s; }
};
class ArticleInfo
{
public:
@@ -71,18 +99,19 @@ public:
private:
int m_iID;
NZBInfo* m_pNZBInfo;
Articles m_Articles;
Groups m_Groups;
char* m_szNZBFilename;
char* m_szSubject;
char* m_szFilename;
char* m_szDestDir;
long long m_lSize;
long long m_lRemainingSize;
bool m_bPaused;
bool m_bDeleted;
bool m_bFilenameConfirmed;
int m_iCompleted;
bool m_bOutputInitialized;
Mutex m_mutexOutputFile;
static int m_iIDGen;
@@ -91,16 +120,15 @@ public:
~FileInfo();
int GetID() { return m_iID; }
void SetID(int s);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
Groups* GetGroups() { return &m_Groups; }
const char* GetNZBFilename() { return m_szNZBFilename; }
void SetNZBFilename(const char* szNZBFilename);
void GetNiceNZBName(char* szBuffer, int iSize);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
const char* GetSubject() { return m_szSubject; }
void SetSubject(const char* szSubject);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
void MakeValidFilename();
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
@@ -111,15 +139,44 @@ public:
void SetPaused(bool Paused) { m_bPaused = Paused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
void BuildDestDirName(const char* szNZBFilename);
const char* GetDestDir() { return m_szDestDir; }
void SetDestDir(const char* szDestDir);
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int s) { m_iCompleted = s; }
void ParseSubject();
bool IsDupe();
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
};
typedef std::deque<FileInfo*> DownloadQueue;
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);
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,8 +34,12 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
@@ -45,9 +49,12 @@
#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()
@@ -63,8 +70,14 @@ Frontend::Frontend()
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()
@@ -77,7 +90,7 @@ bool Frontend::PrepareData()
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("Unable to send request to nzbserver at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Stop();
return false;
}
@@ -91,6 +104,10 @@ bool Frontend::PrepareData()
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;
@@ -195,68 +212,24 @@ void Frontend::ServerDumpDebug()
}
}
bool Frontend::ServerEditQueue(EEditAction eAction, int iEntry)
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
{
DownloadQueue* pDownloadQueue = LockQueue();
int ID = 0;
bool bPause = false;
if (iEntry >= 0 && iEntry < (int)pDownloadQueue->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[iEntry];
ID = pFileInfo->GetID();
bPause = !pFileInfo->GetPaused();
}
UnlockQueue();
if (ID == 0)
{
return false;
}
if (IsRemoteMode())
{
switch (eAction)
{
case eaPauseUnpause:
return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID, ID);
case eaDelete:
return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID, ID);
case eaMoveUp:
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID, ID);
case eaMoveDown:
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID, ID);
case eaMoveTop:
return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID, ID);
case eaMoveBottom:
return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID, ID);
}
return RequestEditQueue(eAction, iOffset, iID);
}
else
{
switch (eAction)
{
case eaPauseUnpause:
return g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, bPause);
case eaDelete:
return g_pQueueCoordinator->EditQueueDeleteEntry(ID);
case eaMoveUp:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1, false);
case eaMoveDown:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1, false);
case eaMoveTop:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
case eaMoveBottom:
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1000000, true);
}
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
}
return false;
}
void Frontend::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
{
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
pMessageBase->m_iType = iRequest;
pMessageBase->m_iSize = iSize;
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -273,11 +246,11 @@ bool Frontend::RequestMessages()
}
SNZBLogRequest LogRequest;
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = m_iNeededLogEntries;
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
if (m_iNeededLogEntries == 0)
{
LogRequest.m_iIDFrom = m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1;
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
}
else
{
@@ -290,17 +263,20 @@ bool Frontend::RequestMessages()
}
// Now listen for the returned log
SNZBLogRequestAnswer LogRequestAnswer;
if (connection.Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
SNZBLogResponse LogResponse;
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
return false;
}
char* pBuf = NULL;
if (LogRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
if (!connection.RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -309,19 +285,19 @@ bool Frontend::RequestMessages()
connection.Disconnect();
if (LogRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
{
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
Message* pMessage = new Message(pLogAnswer->m_iID, (Message::EKind)pLogAnswer->m_iKind, pLogAnswer->m_tTime, szText);
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
m_RemoteMessages.push_back(pMessage);
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
}
free(pBuf);
@@ -342,9 +318,9 @@ bool Frontend::RequestFileList()
}
SNZBListRequest ListRequest;
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = m_bFileList;
ListRequest.m_bServerState = m_bSummary;
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
@@ -352,17 +328,20 @@ bool Frontend::RequestFileList()
}
// Now listen for the returned list
SNZBListRequestAnswer ListRequestAnswer;
if (connection.Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
SNZBListResponse ListResponse;
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
return false;
}
char* pBuf = NULL;
if (ListRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
if (!connection.RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -373,40 +352,67 @@ bool Frontend::RequestFileList()
if (m_bSummary)
{
m_bPause = ListRequestAnswer.m_bServerPaused;
m_lRemainingSize = ListRequestAnswer.m_lRemainingSize;
m_fCurrentDownloadSpeed = ListRequestAnswer.m_fDownloadRate;
m_fDownloadLimit = ListRequestAnswer.m_fDownloadLimit;
m_iThreadCount = ListRequestAnswer.m_iThreadCount;
m_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 && ListRequestAnswer.m_iTrailingDataLength > 0)
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
{
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
typedef std::deque<NZBInfo*> NZBList;
NZBList cNZBList;
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
char* szSubject = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen;
char* szFileName = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
char* szDestDir = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen + pListAnswer->m_iFilenameLen;
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
{
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(pListAnswer->m_iID);
pFileInfo->SetSize(pListAnswer->m_iFileSize);
pFileInfo->SetRemainingSize(pListAnswer->m_iRemainingSize);
pFileInfo->SetPaused(pListAnswer->m_bPaused);
pFileInfo->SetNZBFilename(szNZBFilename);
pFileInfo->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(pListAnswer->m_bFilenameConfirmed);
pFileInfo->SetDestDir(szDestDir);
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
// find nzb-info or create new
NZBInfo* pNZBInfo = NULL;
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
{
NZBInfo* pNZBInfo2 = *it;
if (!strcmp(pNZBInfo2->GetFilename(), szNZBFilename))
{
pNZBInfo = pNZBInfo2;
break;
}
}
if (!pNZBInfo)
{
pNZBInfo = new NZBInfo();
pNZBInfo->SetFilename(szNZBFilename);
pNZBInfo->SetDestDir(szDestDir);
cNZBList.push_back(pNZBInfo);
}
pFileInfo->SetNZBInfo(pNZBInfo);
m_RemoteQueue.push_back(pFileInfo);
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
}
}
if (pBuf)
@@ -438,9 +444,9 @@ bool Frontend::RequestDumpDebug()
return client.RequestServerDumpDebug();
}
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo);
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -31,20 +31,10 @@
#include "Log.h"
#include "DownloadInfo.h"
#include "MessageBase.h"
#include "QueueEditor.h"
class Frontend : public Thread
{
public:
enum EEditAction
{
eaPauseUnpause,
eaDelete,
eaMoveUp,
eaMoveDown,
eaMoveTop,
eaMoveBottom
};
private:
Log::Messages m_RemoteMessages;
DownloadQueue m_RemoteQueue;
@@ -55,8 +45,9 @@ private:
protected:
bool m_bSummary;
bool m_bFileList;
unsigned int m_iNeededLogEntries;
unsigned int m_iNeededLogFirstID;
unsigned int m_iNeededLogEntries;
unsigned int m_iNeededLogFirstID;
int m_iUpdateInterval;
// summary
float m_fCurrentDownloadSpeed;
@@ -64,6 +55,11 @@ protected:
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();
@@ -72,15 +68,15 @@ protected:
DownloadQueue* LockQueue();
void UnlockQueue();
bool IsRemoteMode();
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
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(EEditAction eAction, int iEntry);
bool RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
bool RequestEditQueue(int iAction, int iOffset, int iID);
public:
Frontend();

37
Log.cpp
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -36,6 +36,7 @@
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <cstdio>
#include "nzbget.h"
#include "Options.h"
@@ -50,8 +51,7 @@ Log::Log()
m_iIDGen = 0;
m_szLogFilename = NULL;
#ifdef DEBUG
struct stat buffer;
m_bExtraDebug = !stat("extradebug", &buffer);
m_bExtraDebug = Util::FileExists("extradebug");
#endif
}
@@ -144,11 +144,11 @@ void debug(const char* msg, ...)
#ifdef HAVE_VARIADIC_MACROS
if (szFuncname)
{
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, BaseFileName(szFilename), iLineNr, szFuncname);
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, Util::BaseFileName(szFilename), iLineNr, szFuncname);
}
else
{
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, BaseFileName(szFilename), iLineNr);
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, Util::BaseFileName(szFilename), iLineNr);
}
#else
snprintf(tmp2, 1024, "%s", tmp1);
@@ -257,6 +257,31 @@ void info(const char* msg, ...)
g_pLog->m_mutexLog.Unlock();
}
void detail(const char* msg, ...)
{
char tmp2[1024];
va_list ap;
va_start(ap, msg);
vsnprintf(tmp2, 1024, msg, ap);
tmp2[1024-1] = '\0';
va_end(ap);
g_pLog->m_mutexLog.Lock();
Options::EMessageTarget eMessageTarget = g_pOptions->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];
@@ -269,7 +294,7 @@ void abort(const char* msg, ...)
g_pLog->m_mutexLog.Lock();
printf("%s", tmp2);
printf("\n%s", tmp2);
g_pLog->Filelog(tmp2);

7
Log.h
View File

@@ -35,6 +35,7 @@
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
@@ -49,10 +50,11 @@ class Message
public:
enum EKind
{
mkInfo,
mkInfo,
mkWarning,
mkError,
mkDebug
mkDebug,
mkDetail
};
private:
@@ -91,6 +93,7 @@ private:
friend void warn(const char* msg, ...);
friend void info(const char* msg, ...);
friend void abort(const char* msg, ...);
friend void detail(const char* msg, ...);
#ifdef HAVE_VARIADIC_MACROS
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
#else

View File

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

View File

@@ -2,17 +2,18 @@ bin_PROGRAMS = nzbget
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h RemoteServer.cpp RemoteServer.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
RemoteClient.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h NewsServer.cpp \
NewsServer.h Observer.cpp Observer.h Options.cpp Options.h ParChecker.cpp \
ParChecker.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
nzbget.cpp nzbget.h
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 ServerPool.cpp ServerPool.h Thread.cpp \
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
EXTRA_DIST = nzbget.conf.example \
win32.h NTService.cpp NTService.h \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
nzbget.kdevelop nzbget.sln nzbget.vcproj
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
clean-bak: rm *~

8
Makefile.cvs Normal file
View File

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

View File

@@ -1,8 +1,8 @@
# Makefile.in generated by automake 1.9.5 from Makefile.am.
# Makefile.in generated by automake 1.10 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005 Free Software Foundation, Inc.
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -14,17 +14,11 @@
@SET_MAKE@
SOURCES = $(nzbget_SOURCES)
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
top_builddir = .
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
INSTALL = @INSTALL@
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
@@ -39,17 +33,17 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
bin_PROGRAMS = nzbget$(EXEEXT)
subdir = .
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
config.guess config.sub depcomp install-sh missing
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno configure.status.lineno
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
@@ -60,16 +54,17 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) \
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
RemoteServer.$(OBJEXT) NCursesFrontend.$(OBJEXT) \
NNTPConnection.$(OBJEXT) RemoteClient.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
ServerPool.$(OBJEXT) Thread.$(OBJEXT) Util.$(OBJEXT) \
nzbget.$(OBJEXT)
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
RemoteServer.$(OBJEXT) ServerPool.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) nzbget.$(OBJEXT) BinRpc.$(OBJEXT) \
XmlRpc.$(OBJEXT) PostInfo.$(OBJEXT) ScriptController.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
DEFAULT_INCLUDES = -I.@am__isrc@
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
@@ -98,8 +93,6 @@ distuninstallcheck_listfiles = find . -type f -print
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
ADDSRCS = @ADDSRCS@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
@@ -125,6 +118,8 @@ ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
FALSE = @FALSE@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
@@ -137,6 +132,7 @@ LTLIBOBJS = @LTLIBOBJS@
MAKE = @MAKE@
MAKEINFO = @MAKEINFO@
MKDIR = @MKDIR@
MKDIR_P = @MKDIR_P@
MV = @MV@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
@@ -154,14 +150,12 @@ STRIP = @STRIP@
TAR = @TAR@
TRUE = @TRUE@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_RANLIB = @ac_ct_RANLIB@
ac_ct_STRIP = @ac_ct_STRIP@
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
@@ -173,43 +167,55 @@ build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h RemoteServer.cpp RemoteServer.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
RemoteClient.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h NewsServer.cpp \
NewsServer.h Observer.cpp Observer.h Options.cpp Options.h ParChecker.cpp \
ParChecker.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
nzbget.cpp nzbget.h
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 ServerPool.cpp ServerPool.h Thread.cpp \
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
EXTRA_DIST = nzbget.conf.example \
win32.h NTService.cpp NTService.h \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
nzbget.kdevelop nzbget.sln nzbget.vcproj
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -253,7 +259,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
config.h: stamp-h1
@if test ! -f $@; then \
rm -f stamp-h1; \
$(MAKE) stamp-h1; \
$(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
else :; fi
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
@@ -268,7 +274,7 @@ distclean-hdr:
-rm -f config.h stamp-h1
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; for p in $$list; do \
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
if test -f $$p \
@@ -291,7 +297,7 @@ clean-binPROGRAMS:
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
@rm -f nzbget$(EXEEXT)
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
$(CXXLINK) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -300,6 +306,7 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ArticleDownloader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BinRpc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ColoredFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Connection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
@@ -316,29 +323,32 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PostInfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PrePostProcessor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteClient.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScriptController.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nzbget.Po@am__quote@
.cpp.o:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
.cpp.obj:
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
@@ -390,23 +400,22 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
case $$file in \
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
esac; \
test -d $(distdir) || mkdir $(distdir)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
dir="/$$dir"; \
$(mkdir_p) "$(distdir)$$dir"; \
else \
dir=''; \
fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
@@ -420,7 +429,7 @@ distdir: $(DISTFILES)
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r $(distdir)
dist-gzip: distdir
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
@@ -495,7 +504,7 @@ distcheck: dist
$(am__remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
distuninstallcheck:
@cd $(distuninstallcheck_dir) \
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
@@ -519,7 +528,7 @@ check: check-am
all-am: Makefile $(PROGRAMS) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
@@ -568,12 +577,20 @@ info-am:
install-data-am:
install-dvi: install-dvi-am
install-exec-am: install-binPROGRAMS
install-html: install-html-am
install-info: install-info-am
install-man:
install-pdf: install-pdf-am
install-ps: install-ps-am
installcheck-am:
maintainer-clean: maintainer-clean-am
@@ -595,7 +612,9 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-info-am
uninstall-am: uninstall-binPROGRAMS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
@@ -603,13 +622,14 @@ uninstall-am: uninstall-binPROGRAMS uninstall-info-am
distclean-compile distclean-generic distclean-hdr \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am install-exec \
install-exec-am install-info install-info-am install-man \
install-binPROGRAMS install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-info-am
tags uninstall uninstall-am uninstall-binPROGRAMS
clean-bak: rm *~

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -27,161 +27,322 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6201; // = "nzb"-version-1
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
// The pack-directive prevents aligning of structs.
// This makes them more portable and allows to use together servers and clients
// compiled on different cpu architectures
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
/**
* NZBGet communication protocol uses only two basic data types: integer and char.
* Integer values are passed using network byte order (Big-Endian).
* Use function "htonl" and "ntohl" to convert integers to/from machine
' (host) byte order.
* All char-strings ends with NULL-char.
*/
namespace NZBMessageRequest
// Possible values for field "m_iType" of struct "SNZBRequestBase":
enum eRemoteRequest
{
enum
{
eRequestDownload = 1,
eRequestPauseUnpause,
eRequestList,
eRequestSetDownloadRate,
eRequestDumpDebug,
eRequestEditQueue,
eRequestLog,
eRequestShutdown
eRemoteRequestDownload = 1,
eRemoteRequestPauseUnpause,
eRemoteRequestList,
eRemoteRequestSetDownloadRate,
eRemoteRequestDumpDebug,
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
enum
// File-Actions affect one file, Group-Actions affect all files in group.
// Group is a list of files, added to queue from one NZB-File.
enum eRemoteEditAction
{
eActionMoveOffset = 1, // move to m_iOffset relative to the current position in queue
eActionMoveTop, // move to top of queue
eActionMoveBottom, // move to bottom of queue
eActionPause, // pause
eActionResume, // resume (unpause)
eActionDelete // delete
eRemoteEditActionFileMoveOffset = 1, // move 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)
};
}
// The basic NZBMessageBase struct
struct SNZBMessageBase
// The basic SNZBRequestBase struct, used in all requests
struct SNZBRequestBase
{
int32_t m_iId; // Id must be 'nzbg' in integer-value
int32_t m_iType; // message type, must be > 0
int32_t m_iSize; // Size of the entire struct
char m_szPassword[ NZBREQUESTPASSWORDSIZE ]; // Password needs to be in every request
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
};
// The basic SNZBResposneBase struct, used in all responses
struct SNZBResponseBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
};
// A download request
struct SNZBDownloadRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
char m_szFilename[ NZBREQUESTFILENAMESIZE ];
int32_t m_bAddFirst;
int32_t m_iTrailingDataLength;
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
// A list request
// A download response
struct SNZBDownloadResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// A list and status request
struct SNZBListRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList;
int32_t m_bServerState;
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList; // 1 - return file list
int32_t m_bServerState; // 1 - return server state
};
// A list request-answer
struct SNZBListRequestAnswer
// A list response
struct SNZBListResponse
{
int32_t m_iSize; // Size of the entire struct
int32_t m_iEntrySize; // Size of the SNZBListRequestAnswerEntry-struct
long long m_lRemainingSize;
float m_fDownloadRate;
float m_fDownloadLimit;
int32_t m_bServerPaused;
int32_t m_iThreadCount;
int32_t m_iNrTrailingEntries;
int32_t m_iTrailingDataLength;
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
int32_t m_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_iNrTrailingEntries; // Number of List-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A list request-answer entry
struct SNZBListRequestAnswerEntry
// A list response entry
struct SNZBListResponseEntry
{
int32_t m_iNZBFilenameLen;
int32_t m_iSubjectLen;
int32_t m_iFilenameLen;
int32_t m_iDestDirLen;
int32_t m_iFileSize;
int32_t m_bFilenameConfirmed;
int32_t m_iRemainingSize;
int32_t m_iID;
int32_t m_bPaused;
//char m_szNZBFilename[0]; // variable sized
//char m_szSubject[0]; // variable sized
//char m_szFilename[0]; // variable sized
//char m_szDestDir[0]; // variable sized
int32_t m_iID; // Entry-ID
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_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
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
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szSubject[m_iSubjectLen]; // variable sized
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
};
// A log request
struct SNZBLogRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // Only one of these two parameters
int32_t m_iLines; // can be set. The another one must be set to "0".
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // Only one of these two parameters
int32_t m_iLines; // can be set. The another one must be set to "0".
};
// A log request-answer
struct SNZBLogRequestAnswer
// A log response
struct SNZBLogResponse
{
int32_t m_iSize; // Size of the entire struct
int32_t m_iEntrySize; // Size of the SNZBLogRequestAnswerEntry-struct
int32_t m_iNrTrailingEntries;
int32_t m_iTrailingDataLength;
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A log request-answer entry
struct SNZBLogRequestAnswerEntry
// A log response entry
struct SNZBLogResponseEntry
{
int32_t m_iTextLen;
int32_t m_iID;
int32_t m_iKind; // see Message::Kind in "Log.h"
time_t m_tTime;
//char m_szText[0]; // variable sized
int32_t m_iID; // ID of Log-entry
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTextLen]; // variable sized
};
// A Pause/Unpause request
struct SNZBPauseUnpauseRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_bPause; // The value g_bPause should be set to
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
};
// A Pause/Unpause response
struct SNZBPauseUnpauseResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Request setting the download rate
struct SNZBSetDownloadRateRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
float m_fDownloadRate;
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
};
// A download request
// A setting download rate response
struct SNZBSetDownloadRateResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// An edit queue request
struct SNZBEditQueueRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iIDFrom; // ID of the first file in the range
int32_t m_iIDTo; // ID of the last file in the range, not used yet, must be same as m_iIDFrom
int32_t m_iAction; // action to be done, see later
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iAction; // Action to be executed, see enum 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_iTrailingDataLength; // Length of all ID-entries, following to this structure
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
};
// An edit queue response
struct SNZBEditQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Request dumping of debug info
struct SNZBDumpDebugRequest
{
SNZBMessageBase m_MessageBase; // Must be the first in the struct
int32_t m_iLevel; // Future use
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
// Dumping of debug response
struct SNZBDumpDebugResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Shutdown server request
struct SNZBShutdownRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Shutdown server response
struct SNZBShutdownResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Server version request
struct SNZBVersionRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Server version response
struct SNZBVersionResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// PostQueue request
struct SNZBPostQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// A PostQueue response
struct SNZBPostQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBPostQueueResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of PostQueue-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all PostQueue-entries, following to this structure
// SNZBPostQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// A PostQueue response entry
struct SNZBPostQueueResponseEntry
{
int32_t m_iID; // ID of Post-entry
int32_t m_iStage; // See PrePostProcessor::EPostJobStage
int32_t m_iStageProgress; // Progress of current stage, value in range 0..1000
int32_t m_iFileProgress; // Progress of current file, value in range 0..1000
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szParFilename[m_iParFilename]; // variable sized
//char m_szInfoName[m_iInfoNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
};
// Write log request
struct SNZBWriteLogRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_iKind; // see Message::Kind in "Log.h"
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Write log response
struct SNZBWriteLogResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -46,22 +46,28 @@ private:
eDownloadRate
};
bool m_bUseColor;
int m_iSkipUpdateData;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
int m_iQueueWinHeight;
int m_iQueueWinClientHeight;
int m_iMessagesWinTop;
int m_iMessagesWinHeight;
int m_iMessagesWinClientHeight;
int m_iSelectedQueueEntry;
int m_iQueueScrollOffset;
bool m_bUseColor;
int m_iDataUpdatePos;
bool m_bUpdateNextTime;
int m_iScreenHeight;
int m_iScreenWidth;
int m_iQueueWinTop;
int m_iQueueWinHeight;
int m_iQueueWinClientHeight;
int m_iMessagesWinTop;
int m_iMessagesWinHeight;
int m_iMessagesWinClientHeight;
int m_iSelectedQueueEntry;
int m_iLastEditEntry;
bool m_bLastPausePars;
int m_iQueueScrollOffset;
GroupQueue m_groupQueue;
char* m_szHint;
time_t m_tStartHint;
// Inputting numbres
int m_iInputNumberIndex;
int m_iInputValue;
// Inputting numbers
int m_iInputNumberIndex;
int m_iInputValue;
#ifdef WIN32
CHAR_INFO* m_pScreenBuffer;
@@ -69,12 +75,14 @@ private:
int m_iScreenBufferSize;
std::vector<WORD> m_ColorAttr;
#else
void* m_pWindow; // WINDOW*
void* m_pWindow; // WINDOW*
#endif
EInputMode m_eInputMode;
bool m_bShowNZBname;
float m_QueueWindowPercentage;
EInputMode m_eInputMode;
bool m_bShowNZBname;
bool m_bShowTimestamp;
bool m_bGroupFiles;
float m_QueueWindowPercentage;
#ifdef WIN32
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
@@ -83,17 +91,27 @@ private:
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
void PrintMessages();
void PrintQueue();
void PrintFileQueue();
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
void PrintGroupQueue();
void 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();
void Update();
void UpdateInput(int initialKey);
void Update(int iKey);
void SetCurrentQueueEntry(int iEntry);
void CalcWindowSizes();
void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
void RefreshScreen();
int ReadConsoleKey();
int CalcQueueSize();
void NeedUpdateData();
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
void SetHint(const char* szHint);
protected:
virtual void Run();

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
@@ -41,42 +42,37 @@
#include "Connection.h"
#include "NewsServer.h"
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
{
m_UnavailableGroups.clear();
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(LineBufSize);
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
}
NNTPConnection::~NNTPConnection()
{
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
{
free(m_UnavailableGroups[i]);
m_UnavailableGroups[i] = NULL;
}
m_UnavailableGroups.clear();
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
if (m_szLineBuf)
{
free(m_szLineBuf);
}
free(m_szLineBuf);
}
char* NNTPConnection::Request(char* req)
const char* NNTPConnection::Request(const char* req)
{
if (!req)
{
return NULL;
}
m_bAuthError = false;
WriteLine(req);
char* answer = ReadLine(m_szLineBuf, LineBufSize);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
@@ -88,56 +84,56 @@ char* NNTPConnection::Request(char* req)
debug("%s requested authorization", m_pNetAddress->GetHost());
//authentication required!
if (Authenticate() < 0)
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, LineBufSize);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
}
int NNTPConnection::Authenticate()
bool NNTPConnection::Authenticate()
{
if ((!((NewsServer*)m_pNetAddress)->GetUser()) ||
(!((NewsServer*)m_pNetAddress)->GetPassword()))
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
!((NewsServer*)m_pNetAddress)->GetPassword())
{
return -1;
return true;
}
return AuthInfoUser();
}
int NNTPConnection::AuthInfoUser(int iRecur)
bool NNTPConnection::AuthInfoUser(int iRecur)
{
if (iRecur > 10)
{
return -1;
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* answer = ReadLine(m_szLineBuf, LineBufSize);
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
return -1;
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
return false;
}
if (!strncmp(answer, "281", 3))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
return 0;
return true;
}
else if (!strncmp(answer, "381", 3))
{
@@ -145,44 +141,92 @@ int NNTPConnection::AuthInfoUser(int iRecur)
}
else if (!strncmp(answer, "480", 3))
{
return AuthInfoUser();
return AuthInfoUser(++iRecur);
}
return -1;
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
}
return false;
}
int NNTPConnection::AuthInfoPass(int iRecur)
bool NNTPConnection::AuthInfoPass(int iRecur)
{
if (iRecur > 10)
{
return -1;
return false;
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
tmp[1024-1] = '\0';
WriteLine(tmp);
char* szAnswer = ReadLine(m_szLineBuf, LineBufSize);
if (!szAnswer)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
return -1;
return false;
}
else if (!strncmp(szAnswer, "2", 1))
else if (!strncmp(answer, "2", 1))
{
debug("authorization for %s successful", m_pNetAddress->GetHost());
return 0;
return true;
}
else if (!strncmp(szAnswer, "381", 3))
else if (!strncmp(answer, "381", 3))
{
return AuthInfoPass(++iRecur);
}
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), szAnswer);
return -1;
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
if (GetStatus() != csCancelled)
{
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), 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;
}
int NNTPConnection::DoConnect()
@@ -190,8 +234,11 @@ int NNTPConnection::DoConnect()
debug("Opening connection to %s", GetServer()->GetHost());
int res = Connection::DoConnect();
if (res < 0)
{
return res;
char* answer = DoReadLine(m_szLineBuf, LineBufSize);
}
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
@@ -199,7 +246,6 @@ int NNTPConnection::DoConnect()
return -1;
}
if (strncmp(answer, "2", 1))
{
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
@@ -215,60 +261,11 @@ int NNTPConnection::DoDisconnect()
if (m_eStatus == csConnected)
{
Request("quit\r\n");
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
}
return Connection::DoDisconnect();
}
int NNTPConnection::JoinGroup(char* grp)
{
if (!grp)
{
debug("joinGroup called with NULL-pointer!!");
return -1;
}
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
return 0;
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
{
if (!strcmp(grp, m_UnavailableGroups[i]))
{
debug("Group %s unavailable on %s.", grp, this->GetServer()->GetHost());
return -1;
}
}
char tmp[1024];
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
tmp[1024-1] = '\0';
char* answer = Request(tmp);
if ((answer) && (!strncmp(answer, "2", 1)))
{
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
if (m_szActiveGroup)
free(m_szActiveGroup);
m_szActiveGroup = strdup(grp);
return 0;
}
if (!answer)
{
warn("Error changing group on %s: Connection closed by remote host.",
GetServer()->GetHost());
return -1;
}
else
{
warn("Error changing group on %s to %s: Answer was \"%s\".",
GetServer()->GetHost(), grp, answer);
m_UnavailableGroups.push_back(strdup(grp));
}
return -1;
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -27,32 +27,30 @@
#ifndef NNTPCONNECTION_H
#define NNTPCONNECTION_H
#include <stdio.h>
#include <vector>
#include "NewsServer.h"
#include "Connection.h"
class NNTPConnection : public Connection
{
private:
std::vector <char*> m_UnavailableGroups;
char* m_szActiveGroup;
static const int LineBufSize = 1024*10;
char* m_szLineBuf;
bool m_bAuthError;
virtual int DoConnect();
virtual int DoDisconnect();
void Clear();
public:
NNTPConnection(NewsServer* server);
~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
char* Request(char* req);
int Authenticate();
int AuthInfoUser(int iRecur = 0);
int AuthInfoPass(int iRecur = 0);
int JoinGroup(char* grp);
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -32,9 +32,8 @@
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <list>
#ifdef WIN32
#include <comutil.h>
#import "MSXML.dll" named_guids
@@ -48,17 +47,22 @@ using namespace MSXML;
#include "NZBFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "DiskState.h"
#include "Util.h"
bool ArticleGreater(ArticleInfo* elem1, ArticleInfo* elem2)
{
return elem1->GetPartNumber() > elem2->GetPartNumber();
}
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
NZBFile::NZBFile(const char* szFileName)
{
debug("Creating NZBFile");
m_szFileName = strdup(szFileName);
m_pNZBInfo = new NZBInfo();
m_pNZBInfo->AddReference();
m_pNZBInfo->SetFilename(szFileName);
BuildDestDirName();
m_FileInfos.clear();
}
@@ -73,11 +77,16 @@ NZBFile::~NZBFile()
free(m_szFileName);
}
for (std::vector<FileInfo*>::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
delete *it;
}
m_FileInfos.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void NZBFile::LogDebugInfo()
@@ -90,38 +99,6 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
bool NZBFile::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
{
FILE* pFile = fopen(szFileName, "r");
if (!pFile)
{
return false;
}
// obtain file size.
fseek(pFile , 0 , SEEK_END);
int iSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file.
*pBuffer = (char*) malloc(iSize + 1);
if (!*pBuffer)
{
return false;
}
// copy the file into the buffer.
fread(*pBuffer, 1, iSize, pFile);
fclose(pFile);
(*pBuffer)[iSize] = 0;
*pBufferLength = iSize + 1;
return true;
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
{
return Create(szFileName, szBuffer, iSize, true);
@@ -129,19 +106,7 @@ NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer,
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
{
//return Create(szFileName, NULL, 0, false);
// /*
//TEST
int iBufferLength = 0;
char* szBuffer = NULL;
if (!NZBFile::LoadFileIntoBuffer(szFileName, &szBuffer, &iBufferLength))
{
return false;
}
return Create(szFileName, szBuffer, iBufferLength, true);
// */
return Create(szFileName, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
@@ -153,8 +118,9 @@ void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
}
void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
// deleting empty articles
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
int i = 0;
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
@@ -170,6 +136,177 @@ void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
i++;
}
}
if (!pArticles->empty())
{
ParseSubject(pFileInfo);
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
else
{
delete pFileInfo;
}
}
void NZBFile::ParseSubject(FileInfo* pFileInfo)
{
// tokenize subject, considering spaces as separators and quotation
// marks as non separatable token delimiters.
// then take the last token containing dot (".") as a filename
typedef std::list<char*> TokenList;
TokenList tokens;
tokens.clear();
// tokenizing
char* p = (char*)pFileInfo->GetSubject();
char* start = p;
bool quot = false;
while (true)
{
char ch = *p;
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
if (sep)
{
// end of token
int len = (int)(p - start);
if (len > 0)
{
char* token = (char*)malloc(len + 1);
strncpy(token, start, len);
token[len] = '\0';
tokens.push_back(token);
}
start = p;
if (ch != '\"' || quot)
{
start++;
}
quot = *start == '\"';
if (quot)
{
start++;
char* q = strchr(start, '\"');
if (q)
{
p = q - 1;
}
else
{
quot = false;
}
}
}
if (ch == '\0')
{
break;
}
p++;
}
if (!tokens.empty())
{
// finding the best candidate for being a filename
char* besttoken = tokens.back();
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
{
char* s = *it;
char* p = strchr(s, '.');
if (p && (p[1] != '\0'))
{
besttoken = s;
break;
}
}
pFileInfo->SetFilename(besttoken);
// free mem
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
{
free(*it);
}
}
else
{
// subject is empty or contains only separators?
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
pFileInfo->MakeValidFilename();
}
void NZBFile::BuildDestDirName()
{
char szBuffer[1024];
if (g_pOptions->GetAppendNZBDir())
{
char szNiceNZBName[1024];
m_pNZBInfo->GetNiceNZBName(szNiceNZBName, 1024);
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
szBuffer[1024-1] = '\0';
}
else
{
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
m_pNZBInfo->SetDestDir(szBuffer);
}
/**
* 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
@@ -197,7 +334,12 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
}
else
{
_variant_t v(szFileName);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
success = doc->load(v);
}
if (success == VARIANT_FALSE)
@@ -209,7 +351,11 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
}
NZBFile* pFile = new NZBFile(szFileName);
if (!pFile->parseNZB(doc))
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
@@ -218,7 +364,29 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return pFile;
}
bool NZBFile::parseNZB(IUnknown* nzb)
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
{
while (char ch = *szFilename++)
{
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') )
{
*szURL++ = ch;
}
else
{
*szURL++ = '%';
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
}
}
*szURL = NULL;
}
bool NZBFile::ParseNZB(IUnknown* nzb)
{
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
@@ -231,9 +399,7 @@ bool NZBFile::parseNZB(IUnknown* nzb)
if (!attribute) return false;
_bstr_t subject(attribute->Gettext());
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetNZBFilename(m_szFileName);
pFileInfo->SetSubject(subject);
pFileInfo->ParseSubject();
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
for (int g = 0; g < groupList->Getlength(); g++)
@@ -274,8 +440,7 @@ bool NZBFile::parseNZB(IUnknown* nzb)
}
}
DeleteEmptyArticles(pFileInfo);
m_FileInfos.push_back(pFileInfo);
AddFileInfo(pFileInfo);
}
return true;
}
@@ -287,7 +452,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
xmlTextReaderPtr doc;
if (bFromBuffer)
{
doc = xmlReaderForMemory(szBuffer,iSize-1, "", NULL, 0);
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
}
else
{
@@ -299,7 +464,11 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
}
NZBFile* pFile = new NZBFile(szFileName);
if (!pFile->parseNZB(doc))
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
delete pFile;
pFile = NULL;
@@ -310,7 +479,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
return pFile;
}
bool NZBFile::parseNZB(void* nzb)
bool NZBFile::ParseNZB(void* nzb)
{
FileInfo* pFileInfo = NULL;
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
@@ -334,7 +503,7 @@ bool NZBFile::parseNZB(void* nzb)
if (!strcmp("file", (char*)name))
{
pFileInfo = new FileInfo();
pFileInfo->SetNZBFilename(m_szFileName);
pFileInfo->SetFilename(m_szFileName);
while (xmlTextReaderMoveToNextAttribute(node))
{
@@ -345,7 +514,6 @@ bool NZBFile::parseNZB(void* nzb)
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetSubject((char*)value);
pFileInfo->ParseSubject();
}
}
}
@@ -405,8 +573,7 @@ bool NZBFile::parseNZB(void* nzb)
/* Close the file element, add the new file to file-list */
if (!strcmp("file",(char*)name))
{
DeleteEmptyArticles(pFileInfo);
m_FileInfos.push_back(pFileInfo);
AddFileInfo(pFileInfo);
}
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -38,15 +38,20 @@ public:
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
NZBFile(const char* szFileName);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void DeleteEmptyArticles(FileInfo* pFileInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo);
void BuildDestDirName();
void CheckFilenames();
#ifdef WIN32
bool parseNZB(IUnknown* nzb);
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
bool parseNZB(void* nzb);
bool ParseNZB(void* nzb);
#endif
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
@@ -54,7 +59,6 @@ public:
virtual ~NZBFile();
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
void DetachFileInfos();

View File

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

View File

@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NewsServer.h"
#include "Log.h"
@@ -57,8 +58,12 @@ NewsServer::NewsServer(const char* host, int port, const char* user, const char*
NewsServer::~NewsServer()
{
free(m_szUser);
m_szUser = NULL;
free(m_szPassword);
m_szPassword = NULL;
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -35,6 +35,7 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <cstdio>
#ifdef WIN32
#include <direct.h>
#else
@@ -51,7 +52,6 @@
#include "NewsServer.h"
#include "MessageBase.h"
extern float g_fDownloadRate;
extern ServerPool* g_pServerPool;
#ifdef HAVE_GETOPT_LONG
@@ -64,6 +64,7 @@ static struct option long_options[] =
{"server", no_argument, 0, 's' },
{"daemon", no_argument, 0, 'D' },
{"version", no_argument, 0, 'v'},
{"serverversion", no_argument, 0, 'V'},
{"option", required_argument, 0, 'o'},
{"append", no_argument, 0, 'A'},
{"list", no_argument, 0, 'L'},
@@ -74,54 +75,71 @@ static struct option long_options[] =
{"log", required_argument, 0, 'G'},
{"top", no_argument, 0, 'T'},
{"edit", required_argument, 0, 'E'},
{"fileid", required_argument, 0, 'I'},
{"connect", no_argument, 0, 'C'},
{"quit", no_argument, 0, 'Q'},
#ifdef DEBUG
{"test", no_argument, 0, 't'},
#endif
{"post", no_argument, 0, 'O'},
{"write", required_argument, 0, 'W'},
{0, 0, 0, 0}
};
#endif
static char short_options[] = "c:hno:psvABDCG:LPUR:TE:I:Q";
static char short_options[] = "c:hno:psvABDCE:G:LOPR:TUQVW:";
// Program options
static const char* OPTION_DESTDIR = "destdir";
static const char* OPTION_TEMPDIR = "tempdir";
static const char* OPTION_QUEUEDIR = "queuedir";
static const char* OPTION_NZBDIR = "nzbdir";
static const char* OPTION_CREATELOG = "createlog";
static const char* OPTION_LOGFILE = "logfile";
static const char* OPTION_APPENDNZBDIR = "appendnzbdir";
static const char* OPTION_LOCKFILE = "lockfile";
static const char* OPTION_OUTPUTMODE = "outputmode";
static const char* OPTION_DUPECHECK = "dupecheck";
static const char* OPTION_DOWNLOADRATE = "downloadrate";
static const char* OPTION_RENAMEBROKEN = "renamebroken";
static const char* OPTION_SERVERIP = "serverip";
static const char* OPTION_SERVERPORT = "serverport";
static const char* OPTION_SERVERPASSWORD = "serverpassword";
static const char* OPTION_CONNECTIONTIMEOUT = "connectiontimeout";
static const char* OPTION_SAVEQUEUE = "savequeue";
static const char* OPTION_RELOADQUEUE = "reloadqueue";
static const char* OPTION_CREATEBROKENLOG = "createbrokenlog";
static const char* OPTION_RESETLOG = "resetlog";
static const char* OPTION_DECODER = "decoder";
static const char* OPTION_RETRIES = "retries";
static const char* OPTION_RETRYINTERVAL = "retryinterval";
static const char* OPTION_TERMINATETIMEOUT = "terminatetimeout";
static const char* OPTION_CONTINUEPARTIAL = "continuepartial";
static const char* OPTION_LOGBUFFERSIZE = "logbuffersize";
static const char* OPTION_INFOTARGET = "infotarget";
static const char* OPTION_WARNINGTARGET = "warningtarget";
static const char* OPTION_ERRORTARGET = "errortarget";
static const char* OPTION_DEBUGTARGET = "debugtarget";
static const char* OPTION_LOADPARS = "loadpars";
static const char* OPTION_PARCHECK = "parcheck";
static const char* OPTION_PARREPAIR = "parrepair";
static const char* OPTION_POSTPROCESS = "postprocess";
static const char* OPTION_STRICTPARNAME = "strictparname";
static const char* OPTION_DESTDIR = "DestDir";
static const char* OPTION_TEMPDIR = "TempDir";
static const char* OPTION_QUEUEDIR = "QueueDir";
static const char* OPTION_NZBDIR = "NzbDir";
static const char* OPTION_CREATELOG = "CreateLog";
static const char* OPTION_LOGFILE = "LogFile";
static const char* OPTION_APPENDNZBDIR = "AppendNzbDir";
static const char* OPTION_LOCKFILE = "LockFile";
static const char* OPTION_DAEMONUSERNAME = "DaemonUserName";
static const char* OPTION_OUTPUTMODE = "OutputMode";
static const char* OPTION_DUPECHECK = "DupeCheck";
static const char* OPTION_DOWNLOADRATE = "DownloadRate";
static const char* OPTION_RENAMEBROKEN = "RenameBroken";
static const char* OPTION_SERVERIP = "ServerIp";
static const char* OPTION_SERVERPORT = "ServerPort";
static const char* OPTION_SERVERPASSWORD = "ServerPassword";
static const char* OPTION_CONNECTIONTIMEOUT = "ConnectionTimeout";
static const char* OPTION_SAVEQUEUE = "SaveQueue";
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
static const char* OPTION_RELOADPOSTQUEUE = "ReloadPostQueue";
static const char* OPTION_CREATEBROKENLOG = "CreateBrokenLog";
static const char* OPTION_RESETLOG = "ResetLog";
static const char* OPTION_DECODE = "Decode";
static const char* OPTION_RETRIES = "Retries";
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
static const char* OPTION_CONTINUEPARTIAL = "ContinuePartial";
static const char* OPTION_LOGBUFFERSIZE = "LogBufferSize";
static const char* OPTION_INFOTARGET = "InfoTarget";
static const char* OPTION_WARNINGTARGET = "WarningTarget";
static const char* OPTION_ERRORTARGET = "ErrorTarget";
static const char* OPTION_DEBUGTARGET = "DebugTarget";
static const char* OPTION_DETAILTARGET = "DetailTarget";
static const char* OPTION_LOADPARS = "LoadPars";
static const char* OPTION_PARCHECK = "ParCheck";
static const char* OPTION_PARREPAIR = "ParRepair";
static const char* OPTION_POSTPROCESS = "PostProcess";
static const char* OPTION_STRICTPARNAME = "StrictParName";
static const char* OPTION_UMASK = "UMask";
static const char* OPTION_UPDATEINTERVAL = "UpdateInterval";
static const char* OPTION_CURSESNZBNAME = "CursesNzbName";
static const char* OPTION_CURSESTIME = "CursesTime";
static const char* OPTION_CURSESGROUP = "CursesGroup";
static const char* OPTION_CRCCHECK = "CrcCheck";
static const char* OPTION_RETRYONCRCERROR = "RetryOnCrcError";
static const char* OPTION_THREADLIMIT = "ThreadLimit";
static const char* OPTION_DIRECTWRITE = "DirectWrite";
static const char* OPTION_WRITEBUFFERSIZE = "WriteBufferSize";
static const char* OPTION_NZBDIRINTERVAL = "NzbDirInterval";
static const char* OPTION_NZBDIRFILEAGE = "NzbDirFileAge";
static const char* OPTION_PARCLEANUPQUEUE = "ParCleanupQueue";
static const char* OPTION_DISKSPACE = "DiskSpace";
static const char* OPTION_POSTLOGKIND = "PostLogKind";
static const char* OPTION_ALLOWREPROCESS = "AllowReProcess";
#ifndef WIN32
const char* PossibleConfigLocations[] =
@@ -139,6 +157,7 @@ Options::Options(int argc, char* argv[])
{
// initialize options with default values
m_bConfigInitialized = false;
m_szConfigFilename = NULL;
m_szDestDir = NULL;
m_szTempDir = NULL;
@@ -148,16 +167,18 @@ Options::Options(int argc, char* argv[])
m_eWarningTarget = mtScreen;
m_eErrorTarget = mtScreen;
m_eDebugTarget = mtScreen;
m_eDecoder = dcUulib;
m_eDetailTarget = mtScreen;
m_bDecode = true;
m_bPause = false;
m_bCreateBrokenLog = false;
m_bResetLog = false;
m_fDownloadRate = 0;
m_iEditQueueAction = 0;
m_iEditQueueIDFrom = 0;
m_iEditQueueIDTo = 0;
m_pEditQueueIDList = NULL;
m_iEditQueueIDCount = 0;
m_iEditQueueOffset = 0;
m_szArgFilename = NULL;
m_szLastArg = NULL;
m_iConnectionTimeout = 0;
m_iTerminateTimeout = 0;
m_bServerMode = false;
@@ -176,30 +197,50 @@ Options::Options(int argc, char* argv[])
m_szServerIP = NULL;
m_szServerPassword = NULL;
m_szLockFile = NULL;
m_szDaemonUserName = NULL;
m_eOutputMode = omLoggable;
m_bReloadQueue = false;
m_bReloadPostQueue = false;
m_iLogBufferSize = 0;
m_iLogLines = 0;
m_iWriteLogKind = 0;
m_bCreateLog = false;
m_szLogFile = NULL;
m_eLoadPars = plAll;
m_eLoadPars = lpAll;
m_bParCheck = false;
m_bParRepair = false;
m_bTest = false;
m_szPostProcess = NULL;
m_bStrictParName = false;
m_bNoConfig = false;
m_iUMask = 0;
m_iUpdateInterval = 0;
m_bCursesNZBName = false;
m_bCursesTime = false;
m_bCursesGroup = false;
m_bCrcCheck = false;
m_bRetryOnCrcError = false;
m_bDirectWrite = false;
m_iThreadLimit = 0;
m_iWriteBufferSize = 0;
m_iNzbDirInterval = 0;
m_iNzbDirFileAge = 0;
m_bParCleanupQueue = false;
m_iDiskSpace = 0;
m_ePostLogKind = plNone;
char szFilename[MAX_PATH + 1];
#ifdef WIN32
GetModuleFileName(NULL, szFilename, MAX_PATH + 1);
#else
strncpy(szFilename, argv[0], MAX_PATH + 1);
#endif
szFilename[MAX_PATH] = '\0';
NormalizePathSeparators(szFilename);
Util::NormalizePathSeparators(szFilename);
char* end = strrchr(szFilename, PATH_SEPARATOR);
if (end) *end = '\0';
SetOption("APPDIR", szFilename);
InitDefault();
InitOptFile(argc, argv);
InitCommandLine(argc, argv);
if (m_bPrintOptions)
@@ -269,6 +310,10 @@ Options::~Options()
{
free(m_szArgFilename);
}
if (m_szLastArg)
{
free(m_szLastArg);
}
if (m_szServerIP)
{
free(m_szServerIP);
@@ -285,10 +330,18 @@ Options::~Options()
{
free(m_szLockFile);
}
if (m_szDaemonUserName)
{
free(m_szDaemonUserName);
}
if (m_szPostProcess)
{
free(m_szPostProcess);
}
if (m_pEditQueueIDList)
{
free(m_pEditQueueIDList);
}
for (unsigned int i = 0; i < optEntries.size(); i++)
{
@@ -338,10 +391,11 @@ void Options::InitDefault()
SetOption(OPTION_SERVERPORT, "6789");
SetOption(OPTION_CONNECTIONTIMEOUT, "60");
SetOption(OPTION_SAVEQUEUE, "yes");
SetOption(OPTION_RELOADQUEUE, "ask");
SetOption(OPTION_RELOADQUEUE, "yes");
SetOption(OPTION_RELOADPOSTQUEUE, "yes");
SetOption(OPTION_CREATEBROKENLOG, "no");
SetOption(OPTION_RESETLOG, "no");
SetOption(OPTION_DECODER, "yEnc");
SetOption(OPTION_DECODE, "yes");
SetOption(OPTION_RETRIES, "5");
SetOption(OPTION_RETRYINTERVAL, "10");
SetOption(OPTION_TERMINATETIMEOUT, "600");
@@ -351,57 +405,51 @@ void Options::InitDefault()
SetOption(OPTION_WARNINGTARGET, "both");
SetOption(OPTION_ERRORTARGET, "both");
SetOption(OPTION_DEBUGTARGET, "none");
SetOption(OPTION_DETAILTARGET, "both");
SetOption(OPTION_LOADPARS, "all");
SetOption(OPTION_PARCHECK, "no");
SetOption(OPTION_PARREPAIR, "no");
SetOption(OPTION_POSTPROCESS, "");
SetOption(OPTION_STRICTPARNAME, "yes");
SetOption(OPTION_DAEMONUSERNAME, "root");
SetOption(OPTION_UMASK, "1000");
SetOption(OPTION_UPDATEINTERVAL, "200");
SetOption(OPTION_CURSESNZBNAME, "yes");
SetOption(OPTION_CURSESTIME, "no");
SetOption(OPTION_CURSESGROUP, "no");
SetOption(OPTION_CRCCHECK, "yes");
SetOption(OPTION_RETRYONCRCERROR, "no");
SetOption(OPTION_THREADLIMIT, "100");
SetOption(OPTION_DIRECTWRITE, "no");
SetOption(OPTION_WRITEBUFFERSIZE, "0");
SetOption(OPTION_NZBDIRINTERVAL, "5");
SetOption(OPTION_NZBDIRFILEAGE, "60");
SetOption(OPTION_PARCLEANUPQUEUE, "no");
SetOption(OPTION_DISKSPACE, "0");
SetOption(OPTION_POSTLOGKIND, "none");
SetOption(OPTION_ALLOWREPROCESS, "no");
}
void Options::InitOptFile(int argc, char* argv[])
void Options::InitOptFile()
{
while (true)
if (m_bConfigInitialized)
{
int c;
#ifdef HAVE_GETOPT_LONG
int option_index = 0;
c = getopt_long(argc, argv, short_options, long_options, &option_index);
#else
c = getopt(argc, argv, short_options);
#endif
if (c == -1) break;
switch (c)
{
case 'c':
m_szConfigFilename = strdup(optarg);
break;
case 'n':
m_szConfigFilename = NULL;
m_bNoConfig = true;
return;
case '?':
exit(-1);
break;
}
return;
}
if (!m_szConfigFilename)
if (!m_szConfigFilename && !m_bNoConfig)
{
// search for config file in default locations
#ifdef WIN32
char szFilename[MAX_PATH + 1];
strncpy(szFilename, argv[0], MAX_PATH + 1);
GetModuleFileName(NULL, szFilename, MAX_PATH + 1);
szFilename[MAX_PATH] = '\0';
NormalizePathSeparators(szFilename);
Util::NormalizePathSeparators(szFilename);
char* end = strrchr(szFilename, PATH_SEPARATOR);
if (end) end[1] = '\0';
strcat(szFilename, "nzbget.conf");
struct stat buffer;
if (!stat(szFilename, &buffer) && S_ISREG(buffer.st_mode))
if (Util::FileExists(szFilename))
{
m_szConfigFilename = strdup(szFilename);
}
@@ -413,8 +461,7 @@ void Options::InitOptFile(int argc, char* argv[])
SetOption("$CONFIGFILENAME", szFilename);
szFilename = GetOption("$CONFIGFILENAME");
struct stat buffer;
if (!stat(szFilename, &buffer) && S_ISREG(buffer.st_mode))
if (Util::FileExists(szFilename))
{
m_szConfigFilename = strdup(szFilename);
DelOption("$CONFIGFILENAME");
@@ -429,6 +476,8 @@ void Options::InitOptFile(int argc, char* argv[])
{
LoadConfig(m_szConfigFilename);
}
m_bConfigInitialized = true;
}
void Options::CheckDir(char** dir, const char* szOptionName)
@@ -450,14 +499,17 @@ void Options::CheckDir(char** dir, const char* szOptionName)
usedir[len] = PATH_SEPARATOR;
usedir[len + 1] = '\0';
}
NormalizePathSeparators(usedir);
Util::NormalizePathSeparators(usedir);
}
else
{
abort("FATAL ERROR: Wrong value for option \"%s\"\n", szOptionName);
}
// Ensure the dir is created
mkdir(usedir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (!Util::ForceDirectories(usedir))
{
abort("FATAL ERROR: Directory \"%s\" (option \"%s\") does not exist and could not be created\n", usedir, szOptionName);
}
*dir = usedir;
}
@@ -466,7 +518,7 @@ void Options::InitOptions()
CheckDir(&m_szDestDir, OPTION_DESTDIR);
CheckDir(&m_szTempDir, OPTION_TEMPDIR);
CheckDir(&m_szQueueDir, OPTION_QUEUEDIR);
m_szNzbDir = strdup(GetOption(OPTION_NZBDIR));
m_szPostProcess = strdup(GetOption(OPTION_POSTPROCESS));
m_fDownloadRate = (float)atof(GetOption(OPTION_DOWNLOADRATE));
@@ -478,47 +530,71 @@ void Options::InitOptions()
m_szServerIP = strdup(GetOption(OPTION_SERVERIP));
m_szServerPassword = strdup(GetOption(OPTION_SERVERPASSWORD));
m_szLockFile = strdup(GetOption(OPTION_LOCKFILE));
m_szDaemonUserName = strdup(GetOption(OPTION_DAEMONUSERNAME));
m_iLogBufferSize = atoi(GetOption(OPTION_LOGBUFFERSIZE));
m_szLogFile = strdup(GetOption(OPTION_LOGFILE));
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable" };
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
const int BoolCount = 10;
m_bCreateBrokenLog = (bool)ParseOptionValue(OPTION_CREATEBROKENLOG, BoolCount, BoolNames, (const int*)BoolValues);
m_bResetLog = (bool)ParseOptionValue(OPTION_RESETLOG, BoolCount, BoolNames, (const int*)BoolValues);
m_bAppendNZBDir = (bool)ParseOptionValue(OPTION_APPENDNZBDIR, BoolCount, BoolNames, (const int*)BoolValues);
m_bContinuePartial = (bool)ParseOptionValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, (const int*)BoolValues);
m_bRenameBroken = (bool)ParseOptionValue(OPTION_RENAMEBROKEN, BoolCount, BoolNames, (const int*)BoolValues);
m_bSaveQueue = (bool)ParseOptionValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, (const int*)BoolValues);
m_bDupeCheck = (bool)ParseOptionValue(OPTION_DUPECHECK, BoolCount, BoolNames, (const int*)BoolValues);
m_bCreateLog = (bool)ParseOptionValue(OPTION_CREATELOG, BoolCount, BoolNames, (const int*)BoolValues);
m_bParCheck = (bool)ParseOptionValue(OPTION_PARCHECK, BoolCount, BoolNames, (const int*)BoolValues);
m_bParRepair = (bool)ParseOptionValue(OPTION_PARREPAIR, BoolCount, BoolNames, (const int*)BoolValues);
m_bStrictParName = (bool)ParseOptionValue(OPTION_STRICTPARNAME, BoolCount, BoolNames, (const int*)BoolValues);
m_bReloadQueue = (bool)ParseOptionValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, (const int*)BoolValues);
m_iUMask = strtol(GetOption(OPTION_UMASK), NULL, 8);
m_iUpdateInterval = atoi(GetOption(OPTION_UPDATEINTERVAL));
m_iThreadLimit = atoi(GetOption(OPTION_THREADLIMIT));
m_iWriteBufferSize = atoi(GetOption(OPTION_WRITEBUFFERSIZE));
m_iNzbDirInterval = atoi(GetOption(OPTION_NZBDIRINTERVAL));
m_iNzbDirFileAge = atoi(GetOption(OPTION_NZBDIRFILEAGE));
m_iDiskSpace = atoi(GetOption(OPTION_DISKSPACE));
if (m_iNzbDirInterval > 0)
{
CheckDir(&m_szNzbDir, OPTION_NZBDIR);
}
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable", "enabled", "disabled" };
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
const int BoolCount = 12;
m_bCreateBrokenLog = (bool)ParseOptionValue(OPTION_CREATEBROKENLOG, BoolCount, BoolNames, BoolValues);
m_bResetLog = (bool)ParseOptionValue(OPTION_RESETLOG, BoolCount, BoolNames, BoolValues);
m_bAppendNZBDir = (bool)ParseOptionValue(OPTION_APPENDNZBDIR, BoolCount, BoolNames, BoolValues);
m_bContinuePartial = (bool)ParseOptionValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, BoolValues);
m_bRenameBroken = (bool)ParseOptionValue(OPTION_RENAMEBROKEN, BoolCount, BoolNames, BoolValues);
m_bSaveQueue = (bool)ParseOptionValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, BoolValues);
m_bDupeCheck = (bool)ParseOptionValue(OPTION_DUPECHECK, BoolCount, BoolNames, BoolValues);
m_bCreateLog = (bool)ParseOptionValue(OPTION_CREATELOG, BoolCount, BoolNames, BoolValues);
m_bParCheck = (bool)ParseOptionValue(OPTION_PARCHECK, BoolCount, BoolNames, BoolValues);
m_bParRepair = (bool)ParseOptionValue(OPTION_PARREPAIR, BoolCount, BoolNames, BoolValues);
m_bStrictParName = (bool)ParseOptionValue(OPTION_STRICTPARNAME, BoolCount, BoolNames, BoolValues);
m_bReloadQueue = (bool)ParseOptionValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, BoolValues);
m_bReloadPostQueue = (bool)ParseOptionValue(OPTION_RELOADPOSTQUEUE, BoolCount, BoolNames, BoolValues);
m_bCursesNZBName = (bool)ParseOptionValue(OPTION_CURSESNZBNAME, BoolCount, BoolNames, BoolValues);
m_bCursesTime = (bool)ParseOptionValue(OPTION_CURSESTIME, BoolCount, BoolNames, BoolValues);
m_bCursesGroup = (bool)ParseOptionValue(OPTION_CURSESGROUP, BoolCount, BoolNames, BoolValues);
m_bCrcCheck = (bool)ParseOptionValue(OPTION_CRCCHECK, BoolCount, BoolNames, BoolValues);
m_bRetryOnCrcError = (bool)ParseOptionValue(OPTION_RETRYONCRCERROR, BoolCount, BoolNames, BoolValues);
m_bDirectWrite = (bool)ParseOptionValue(OPTION_DIRECTWRITE, BoolCount, BoolNames, BoolValues);
m_bParCleanupQueue = (bool)ParseOptionValue(OPTION_PARCLEANUPQUEUE, BoolCount, BoolNames, BoolValues);
m_bDecode = (bool)ParseOptionValue(OPTION_DECODE, BoolCount, BoolNames, BoolValues);
m_bAllowReProcess = (bool)ParseOptionValue(OPTION_ALLOWREPROCESS, BoolCount, BoolNames, BoolValues);
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
const EOutputMode OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
const int OutputModeCount = 7;
m_eOutputMode = (EOutputMode)ParseOptionValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, (const int*)OutputModeValues);
m_eOutputMode = (EOutputMode)ParseOptionValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, OutputModeValues);
const char* DecoderNames[] = { "uulib", "yenc", "none", "ydec", "ydecoder" };
const EDecoder DecoderValues[] = { dcUulib, dcYenc, dcNone, dcYenc, dcYenc };
const int DecoderCount = 5;
m_eDecoder = (EDecoder)ParseOptionValue(OPTION_DECODER, DecoderCount, DecoderNames, (const int*)DecoderValues);
const char* LoadParsNames[] = { "none", "one", "all", "1", "0" };
const ELoadPars LoadParsValues[] = { plNone, plOne, plAll, plOne, plNone };
const int LoadParsValues[] = { lpNone, lpOne, lpAll, lpOne, lpNone };
const int LoadParsCount = 4;
m_eLoadPars = (ELoadPars)ParseOptionValue(OPTION_LOADPARS, LoadParsCount, LoadParsNames, (const int*)LoadParsValues);
m_eLoadPars = (ELoadPars)ParseOptionValue(OPTION_LOADPARS, LoadParsCount, LoadParsNames, LoadParsValues);
const char* TargetNames[] = { "screen", "log", "both", "none" };
const EMessageTarget TargetValues[] = { mtScreen, mtLog, mtBoth, mtNone };
const int TargetValues[] = { mtScreen, mtLog, mtBoth, mtNone };
const int TargetCount = 4;
m_eInfoTarget = (EMessageTarget)ParseOptionValue(OPTION_INFOTARGET, TargetCount, TargetNames, (const int*)TargetValues);
m_eWarningTarget = (EMessageTarget)ParseOptionValue(OPTION_WARNINGTARGET, TargetCount, TargetNames, (const int*)TargetValues);
m_eErrorTarget = (EMessageTarget)ParseOptionValue(OPTION_ERRORTARGET, TargetCount, TargetNames, (const int*)TargetValues);
m_eDebugTarget = (EMessageTarget)ParseOptionValue(OPTION_DEBUGTARGET, TargetCount, TargetNames, (const int*)TargetValues);
m_eInfoTarget = (EMessageTarget)ParseOptionValue(OPTION_INFOTARGET, TargetCount, TargetNames, TargetValues);
m_eWarningTarget = (EMessageTarget)ParseOptionValue(OPTION_WARNINGTARGET, TargetCount, TargetNames, TargetValues);
m_eErrorTarget = (EMessageTarget)ParseOptionValue(OPTION_ERRORTARGET, TargetCount, TargetNames, TargetValues);
m_eDebugTarget = (EMessageTarget)ParseOptionValue(OPTION_DEBUGTARGET, TargetCount, TargetNames, TargetValues);
m_eDetailTarget = (EMessageTarget)ParseOptionValue(OPTION_DETAILTARGET, TargetCount, TargetNames, TargetValues);
const char* PostLogKindNames[] = { "none", "detail", "info", "warning", "error", "debug" };
const int PostLogKindValues[] = { plNone, plDetail, plInfo, plWarning, plError, plDebug };
const int PostLogKindCount = 6;
m_ePostLogKind = (EPostLogKind)ParseOptionValue(OPTION_POSTLOGKIND, PostLogKindCount, PostLogKindNames, PostLogKindValues);
}
int Options::ParseOptionValue(const char * OptName, int argc, const char * argn[], const int argv[])
@@ -563,6 +639,13 @@ void Options::InitCommandLine(int argc, char* argv[])
switch (c)
{
case 'c':
m_szConfigFilename = strdup(optarg);
break;
case 'n':
m_szConfigFilename = NULL;
m_bNoConfig = true;
break;
case 'h':
PrintUsage(argv[0]);
exit(0);
@@ -575,6 +658,7 @@ void Options::InitCommandLine(int argc, char* argv[])
m_bPrintOptions = true;
break;
case 'o':
InitOptFile();
if (!SetOptionString(optarg))
{
abort("FATAL ERROR: could not set option: %s\n", optarg);
@@ -623,25 +707,44 @@ void Options::InitCommandLine(int argc, char* argv[])
case 'E':
{
m_eClientOperation = opClientRequestEditQueue;
bool bGroup = !strcasecmp(optarg, "G");
if (bGroup)
{
optind++;
if (optind > argc)
{
abort("FATAL ERROR: Could not parse value of option 'E'\n");
}
optarg = argv[optind-1];
}
if (!strcasecmp(optarg, "T"))
{
m_iEditQueueAction = NZBMessageRequest::eActionMoveTop;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveTop : eRemoteEditActionFileMoveTop;
}
else if (!strcasecmp(optarg, "B"))
{
m_iEditQueueAction = NZBMessageRequest::eActionMoveBottom;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveBottom : eRemoteEditActionFileMoveBottom;
}
else if (!strcasecmp(optarg, "P"))
{
m_iEditQueueAction = NZBMessageRequest::eActionPause;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPause : eRemoteEditActionFilePause;
}
else if (!strcasecmp(optarg, "A"))
{
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPauseAllPars : eRemoteEditActionFilePauseAllPars;
}
else if (!strcasecmp(optarg, "R"))
{
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPauseExtraPars : eRemoteEditActionFilePauseExtraPars;
}
else if (!strcasecmp(optarg, "U"))
{
m_iEditQueueAction = NZBMessageRequest::eActionResume;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupResume : eRemoteEditActionFileResume;
}
else if (!strcasecmp(optarg, "D"))
{
m_iEditQueueAction = NZBMessageRequest::eActionDelete;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupDelete : eRemoteEditActionFileDelete;
}
else
{
@@ -650,90 +753,94 @@ void Options::InitCommandLine(int argc, char* argv[])
{
abort("FATAL ERROR: Could not parse value of option 'E'\n");
}
m_iEditQueueAction = NZBMessageRequest::eActionMoveOffset;
}
break;
}
case 'I':
{
const char* p = strchr(optarg, '-');
if (p)
{
char buf[101];
int maxlen = p - optarg < 100 ? p - optarg : 100;
strncpy(buf, optarg, maxlen);
buf[maxlen] = '\0';
m_iEditQueueIDFrom = atoi(buf);
m_iEditQueueIDTo = atoi(p + 1);
if (m_iEditQueueIDFrom <= 0 || m_iEditQueueIDTo <= 0 ||
m_iEditQueueIDFrom > m_iEditQueueIDTo)
{
abort("FATAL ERROR: wrong value for option 'I'\n");
}
}
else
{
m_iEditQueueIDFrom = atoi(optarg);
if (m_iEditQueueIDFrom <= 0)
{
abort("FATAL ERROR: wrong value for option 'I'\n");
}
m_iEditQueueIDTo = m_iEditQueueIDFrom;
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveOffset : eRemoteEditActionFileMoveOffset;
}
break;
}
case 'Q':
m_eClientOperation = opClientRequestShutdown;
break;
#ifdef DEBUG
case 't':
m_bTest = true;
case 'V':
m_eClientOperation = opClientRequestVersion;
break;
case 'O':
m_eClientOperation = opClientRequestPostQueue;
break;
case 'W':
m_eClientOperation = opClientRequestWriteLog;
if (!strcmp(optarg, "I")) {
m_iWriteLogKind = (int)Message::mkInfo;
}
else if (!strcmp(optarg, "W")) {
m_iWriteLogKind = (int)Message::mkWarning;
}
else if (!strcmp(optarg, "E")) {
m_iWriteLogKind = (int)Message::mkError;
}
else if (!strcmp(optarg, "D")) {
m_iWriteLogKind = (int)Message::mkDetail;
}
else if (!strcmp(optarg, "G")) {
m_iWriteLogKind = (int)Message::mkDebug;
}
else
{
abort("FATAL ERROR: Could not parse value of option 'W'\n");
}
break;
#endif
case '?':
exit(-1);
break;
}
}
InitOptFile();
}
void Options::PrintUsage(char* com)
{
printf("Usage: %s [switches] [<nzb-file>]\n"
"Switches:\n"
" -h, --help Print this help-message\n"
" -v, --version Print version and exit\n"
" -c, --configfile <file> Filename of configuration-file\n"
" -n, --noconfigfile Prevent loading of configuration-file\n"
" (required options must be passed with --option)\n"
" -p, --printconfig Print configuration and exit\n"
" -o, --option <name=value> Set or override option in configuration-file\n"
" -s, --server Start nzbget as a server in console-mode\n"
" -D, --daemon Start nzbget as a server in daemon-mode\n"
" -Q, --quit Shutdown the server\n"
" -A, --append Send file to the server's download queue\n"
" -C, --connect Attach client to server\n"
" -L, --list Request list of downloads from the server\n"
" -P, --pause Pause downloading on the server\n"
" -U, --unpause Unpause downloading on the server\n"
" -R, --rate Set the download rate on the server\n"
" -T, --top Add file to the top (begining) of queue\n"
" (should be used with switch --append)\n"
" -G, --log <lines> Request last <lines> lines from server's screen-log\n"
" -E, --edit <action> Edit queue on the server\n"
" (must be used with switch --fileid):\n"
" where <action> is one of:\n"
" <+offset|-offset> Move file(s) in queue relative to current position\n"
" offset is an integer number\n"
" T Move file(s) to the top of queue\n"
" B Move file(s) to the bottom of queue\n"
" P Pause file(s)\n"
" U Resume (unpause) file(s)\n"
" D Delete file(s)\n"
" -I, --fileid <FileID|FileIDFrom-FileIDTo> File-id(s) for switch '-E',\n"
" as printed by switch --list\n"
"",
com);
printf("Usage:\n"
" %s [switches]\n\n"
"Switches:\n"
" -h, --help Print this help-message\n"
" -v, --version Print version and exit\n"
" -c, --configfile <file> Filename of configuration-file\n"
" -n, --noconfigfile Prevent loading of configuration-file\n"
" (required options must be passed with --option)\n"
" -p, --printconfig Print configuration and exit\n"
" -o, --option <name=value> Set or override option in configuration-file\n"
" -s, --server Start nzbget as a server in console-mode\n"
#ifndef WIN32
" -D, --daemon Start nzbget as a server in daemon-mode\n"
#endif
" -V, --serverversion Print server's version and exit\n"
" -Q, --quit Shutdown the server\n"
" -A, --append <nzb-file> Send file to the server's download queue\n"
" -C, --connect Attach client to server\n"
" -L, --list Request list of downloads from the server\n"
" -P, --pause Pause downloading on the server\n"
" -U, --unpause Unpause downloading on the server\n"
" -R, --rate Set the download rate on the server\n"
" -T, --top Add file to the top (begining) of queue\n"
" (should be used with switch --append)\n"
" -G, --log <lines> Request last <lines> lines from server's screen-log\n"
" -W, --write <D|I|W|E|G> \"Text\" Send text to server's log\n"
" -O, --post Request post-processor-queue from server\n"
" -E, --edit [G] <action> <IDs> Edit queue on the server\n"
" <G> Affect all files in the group (same nzb-file)\n"
" <action> is one of:\n"
" <+offset|-offset> Move file(s) in queue relative to current position,\n"
" offset is an integer value\n"
" T Move file(s) to the top of queue\n"
" B Move file(s) to the bottom of queue\n"
" P Pause file(s)\n"
" U Resume (unpause) file(s)\n"
" A Pause all pars (for groups)\n"
" R Pause extra pars (for groups)\n"
" D Delete file(s)\n"
" <IDs> Comma-separated list of file-ids or ranges\n"
" of file-ids, e. g.: 1-5,3,10-22\n",
Util::BaseFileName(com));
}
void Options::InitFileArg(int argc, char* argv[])
@@ -743,14 +850,28 @@ void Options::InitFileArg(int argc, char* argv[])
// no nzb-file passed
if (!m_bServerMode && !m_bRemoteClientMode &&
(m_eClientOperation == opClientNoOperation ||
m_eClientOperation == opClientRequestDownload))
m_eClientOperation == opClientRequestDownload ||
m_eClientOperation == opClientRequestWriteLog))
{
printf("nzb-file not specified\n");
if (m_eClientOperation == opClientRequestWriteLog)
{
printf("Log-text not specified\n");
}
else
{
printf("Nzb-file not specified\n");
}
exit(-1);
}
}
else if (m_eClientOperation == opClientRequestEditQueue)
{
ParseFileIDList(argc, argv, optind);
}
else
{
m_szLastArg = strdup(argv[optind]);
// Check if the file-name is a relative path or an absolute path
// If the path starts with '/' its an absolute, else relative
const char* szFileName = argv[optind];
@@ -774,10 +895,11 @@ void Options::InitFileArg(int argc, char* argv[])
#endif
if (m_bServerMode || m_bRemoteClientMode ||
!((m_eClientOperation == opClientRequestDownload)
|| (m_eClientOperation == opClientNoOperation)))
!(m_eClientOperation == opClientNoOperation ||
m_eClientOperation == opClientRequestDownload ||
m_eClientOperation == opClientRequestWriteLog))
{
printf("nzb-file not needed for this command\n");
printf("Too many arguments\n");
exit(-1);
}
}
@@ -924,7 +1046,7 @@ void Options::InitServers()
const char* nconnections = GetOption(optname);
bool definition = nlevel || nhost || nport || nusername || npassword || nconnections;
bool completed = nlevel && nhost && nport && nusername && npassword && nconnections;
bool completed = nlevel && nhost && nport && nconnections;
if (!definition)
{
@@ -966,6 +1088,10 @@ void Options::LoadConfig(const char * configfile)
{
buf[strlen(buf)-1] = 0; // remove traling '\n'
}
if (buf[0] != 0 && buf[strlen(buf)-1] == '\r')
{
buf[strlen(buf)-1] = 0; // remove traling '\r' (for windows line endings)
}
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
{
@@ -994,7 +1120,7 @@ bool Options::SetOptionString(const char * option)
{
char optname[1001];
char optvalue[1001];
int maxlen = eq - option < 1000 ? eq - option : 1000;
int maxlen = (int)(eq - option < 1000 ? eq - option : 1000);
strncpy(optname, option, maxlen);
optname[maxlen] = '\0';
strncpy(optvalue, eq + 1, 1000);
@@ -1066,4 +1192,90 @@ void Options::CheckOptions()
abort("FATAL ERROR: Program was compiled without curses-support. Can not use \"curses\" frontend (option \"%s\")\n", OPTION_OUTPUTMODE);
}
#endif
if (!m_bDecode)
{
m_bDirectWrite = false;
}
}
void Options::ParseFileIDList(int argc, char* argv[], int optind)
{
std::vector<int> IDs;
IDs.clear();
while (optind < argc)
{
char* szWritableFileIDList = strdup(argv[optind++]);
char* optarg = strtok(szWritableFileIDList, ", ");
while (optarg)
{
int iEditQueueIDFrom = 0;
int iEditQueueIDTo = 0;
const char* p = strchr(optarg, '-');
if (p)
{
char buf[101];
int maxlen = (int)(p - optarg < 100 ? p - optarg : 100);
strncpy(buf, optarg, maxlen);
buf[maxlen] = '\0';
iEditQueueIDFrom = atoi(buf);
iEditQueueIDTo = atoi(p + 1);
if (iEditQueueIDFrom <= 0 || iEditQueueIDTo <= 0)
{
abort("FATAL ERROR: invalid list of file IDs\n");
}
}
else
{
iEditQueueIDFrom = atoi(optarg);
if (iEditQueueIDFrom <= 0)
{
abort("FATAL ERROR: invalid list of file IDs\n");
}
iEditQueueIDTo = iEditQueueIDFrom;
}
int iEditQueueIDCount = 0;
if (iEditQueueIDTo != 0)
{
if (iEditQueueIDFrom < iEditQueueIDTo)
{
iEditQueueIDCount = iEditQueueIDTo - iEditQueueIDFrom + 1;
}
else
{
iEditQueueIDCount = iEditQueueIDFrom - iEditQueueIDTo + 1;
}
}
else
{
iEditQueueIDCount = 1;
}
for (int i = 0; i < iEditQueueIDCount; i++)
{
if (iEditQueueIDFrom < iEditQueueIDTo || iEditQueueIDTo == 0)
{
IDs.push_back(iEditQueueIDFrom + i);
}
else
{
IDs.push_back(iEditQueueIDFrom - i);
}
}
optarg = strtok(NULL, ", ");
}
free(szWritableFileIDList);
}
m_iEditQueueIDCount = IDs.size();
m_pEditQueueIDList = (int*)malloc(sizeof(int) * m_iEditQueueIDCount);
for (int i = 0; i < m_iEditQueueIDCount; i++)
{
m_pEditQueueIDList[i] = IDs[i];
}
}

128
Options.h
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,41 +34,48 @@ class Options
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestList,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown
opClientNoOperation,
opClientRequestDownload,
opClientRequestList,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog
};
enum EMessageTarget
{
mtNone,
mtScreen,
mtLog,
mtBoth
};
enum EDecoder
{
dcNone,
dcUulib,
dcYenc
mtNone,
mtScreen,
mtLog,
mtBoth
};
enum EOutputMode
{
omLoggable,
omColored,
omNCurses
omLoggable,
omColored,
omNCurses
};
enum ELoadPars
{
plNone,
plOne,
plAll
lpNone,
lpOne,
lpAll
};
enum EPostLogKind
{
plNone,
plDetail,
plInfo,
plWarning,
plError,
plDebug
};
private:
@@ -80,6 +87,8 @@ private:
std::vector< struct OptEntry > optEntries;
bool m_bConfigInitialized;
// Options
char* m_szConfigFilename;
char* m_szDestDir;
@@ -90,7 +99,8 @@ private:
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
EMessageTarget m_eDebugTarget;
EDecoder m_eDecoder;
EMessageTarget m_eDetailTarget;
bool m_bDecode;
bool m_bCreateBrokenLog;
bool m_bResetLog;
int m_iConnectionTimeout;
@@ -106,8 +116,10 @@ private:
char* m_szServerPassword;
int m_szServerPort;
char* m_szLockFile;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadPostQueue;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
@@ -117,6 +129,22 @@ private:
char* m_szPostProcess;
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;
EPostLogKind m_ePostLogKind;
bool m_bAllowReProcess;
// Parsed command-line parameters
bool m_bServerMode;
@@ -124,14 +152,15 @@ private:
bool m_bRemoteClientMode;
int m_iEditQueueAction;
int m_iEditQueueOffset;
int m_iEditQueueIDFrom;
int m_iEditQueueIDTo;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
char* m_szArgFilename;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iLogLines;
bool m_bTest;
int m_iWriteLogKind;
// Current state
bool m_bPause;
@@ -139,7 +168,7 @@ private:
EClientOperation m_eClientOperation;
void InitDefault();
void InitOptFile(int argc, char* argv[]);
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitFileArg(int argc, char* argv[]);
@@ -155,10 +184,11 @@ private:
bool ValidateOptionName(const char* optname);
void LoadConfig(const char* configfile);
void CheckDir(char** dir, const char* szOptionName);
void ParseFileIDList(int argc, char* argv[], int optind);
public:
Options(int argc, char* argv[]);
~Options();
Options(int argc, char* argv[]);
~Options();
// Options
const char* GetDestDir() { return m_szDestDir; }
@@ -171,9 +201,10 @@ public:
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
EDecoder GetDecoder() { return m_eDecoder; };
bool GetDecode() { return m_bDecode; };
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
bool GetContinuePartial() { return m_bContinuePartial; }
bool GetRenameBroken() { return m_bRenameBroken; }
@@ -185,8 +216,10 @@ public:
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; }
@@ -195,21 +228,38 @@ public:
bool GetParRepair() { return m_bParRepair; }
const char* GetPostProcess() { return m_szPostProcess; }
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; }
EPostLogKind GetPostLogKind() { return m_ePostLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
// Parsed command-line parameters
bool GetServerMode() { return m_bServerMode; }
bool GetDaemonMode() { return m_bDaemonMode; }
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
EClientOperation GetClientOperation() { return m_eClientOperation; }
int GetEditQueueAction() { return m_iEditQueueAction; }
int GetEditQueueOffset() { return m_iEditQueueOffset; }
int GetEditQueueIDFrom() { return m_iEditQueueIDFrom; }
int GetEditQueueIDTo() { return m_iEditQueueIDTo; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetLastArg() { return m_szLastArg; }
bool GetAddTop() { return m_bAddTop; }
float GetSetRate() { return m_fSetRate; }
int GetLogLines() { return m_iLogLines; }
bool GetTest() { return m_bTest; }
int GetWriteLogKind() { return m_iWriteLogKind; }
// Current state
void SetPause(bool bOnOff) { m_bPause = bOnOff; }

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
@@ -47,11 +48,9 @@
#include "nzbget.h"
#include "ParChecker.h"
#include "Log.h"
#include "QueueCoordinator.h"
#include "Options.h"
#include "Util.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
const char* Par2CmdLineErrStr[] = { "OK",
@@ -64,11 +63,13 @@ const char* Par2CmdLineErrStr[] = { "OK",
"internal error occurred",
"out of memory" };
class Repairer : public Par2Repairer
{
friend class ParChecker;
};
ParChecker::ParChecker()
{
debug("Creating ParChecker");
@@ -78,6 +79,12 @@ ParChecker::ParChecker()
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_eStage = ptLoadingPars;
m_QueuedParFiles.clear();
}
@@ -101,6 +108,7 @@ ParChecker::~ParChecker()
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
@@ -144,12 +152,24 @@ void ParChecker::SetStatus(EStatus eStatus)
void ParChecker::Run()
{
info("Verifying %s", m_szInfoName);
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
UpdateProgress();
debug("par: %s", m_szParFilename);
CommandLine commandLine;
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
@@ -158,19 +178,23 @@ void ParChecker::Run()
}
Result res;
Repairer* repairer = new Repairer();
#ifdef ENABLE_PARPROGRESS
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
#endif
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));
res = repairer->PreProcess(commandLine);
res = pRepairer->PreProcess(commandLine);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
error("Could not verify %s: ", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
SetStatus(psFailed);
delete repairer;
delete pRepairer;
return;
}
@@ -182,15 +206,25 @@ void ParChecker::Run()
}
m_szErrMsg = NULL;
m_bRepairNotNeeded = false;
m_bRepairing = false;
res = repairer->Process(commandLine, false);
m_eStage = ptVerifyingSources;
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
@@ -200,6 +234,13 @@ void ParChecker::Run()
{
int iBlockFound = 0;
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
@@ -224,18 +265,19 @@ void ParChecker::Run()
break;
}
LoadMorePars(repairer);
repairer->UpdateVerificationResults();
m_bRepairing = false;
res = repairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
bMoreFilesLoaded = LoadMorePars();
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
SetStatus(psFailed);
delete repairer;
delete pRepairer;
return;
}
@@ -249,8 +291,17 @@ void ParChecker::Run()
if (g_pOptions->GetParRepair())
{
info("Repairing %s", m_szInfoName);
m_bRepairing = true;
res = repairer->Process(commandLine, true);
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iProcessedFiles = 0;
m_eStage = ptRepairing;
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
UpdateProgress();
res = pRepairer->Process(commandLine, true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
@@ -278,214 +329,10 @@ void ParChecker::Run()
SetStatus(psFailed);
}
delete repairer;
delete pRepairer;
}
bool ParChecker::ParseParFilename(const char * szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
bool ParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
Blocks blocks;
blocks.clear();
int iBlockFound = 0;
FindPars(pDownloadQueue, &blocks, true, &iBlockFound);
if (iBlockFound == 0 && !g_pOptions->GetStrictParName())
{
FindPars(pDownloadQueue, &blocks, false, &iBlockFound);
}
if (iBlockFound >= iBlockNeeded)
{
char szNZBNiceName[1024];
FileInfo::MakeNiceNZBName(m_szNZBFilename, szNZBNiceName, 1024);
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
// starting from the file with max block count.
// if par-collection was built exponentially and all par-files present,
// this step selects par-files with exact number of blocks we need.
while (iBlockNeeded > 0)
{
BlockInfo* pBestBlockInfo = NULL;
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
{
pBestBlockInfo = pBlockInfo;
}
}
if (pBestBlockInfo)
{
if (pBestBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
}
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
}
else
{
break;
}
}
// 2. then unpause other files
// this step only needed if the par-collection was built not exponentially
// or not all par-files present (or some of them were corrupted)
// this step is not optimal, but we hope, that the first step will work good
// in most cases and we will not need the second step often
while (iBlockNeeded > 0)
{
BlockInfo* pBlockInfo = blocks.front();
if (pBlockInfo->m_pFileInfo->GetPaused())
{
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
}
iBlockNeeded -= pBlockInfo->m_iBlockCount;
}
}
g_pQueueCoordinator->UnlockQueue();
if (pBlockFound)
{
*pBlockFound = iBlockFound;
}
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
{
delete *it;
}
blocks.clear();
return iBlockNeeded <= 0;
}
void ParChecker::FindPars(DownloadQueue * pDownloadQueue, Blocks * pBlocks, bool bStrictParName, int* pBlockFound)
{
*pBlockFound = 0;
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* szBaseParFilename = BaseFileName(m_szParFilename);
char szMainBaseFilename[1024];
int iMainBaseLen = 0;
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
{
// should not happen
error("Internal error: could not parse filename %s", szBaseParFilename);
return;
}
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
szMainBaseFilename[maxlen] = '\0';
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (!strcmp(pFileInfo->GetNZBFilename(), m_szNZBFilename) &&
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
if (bStrictParName)
{
// the pFileInfo->GetFilename() may be not confirmed and may contain
// additional texts if Subject could not be parsed correctly
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
char szCandidateFileName[1024];
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
szCandidateFileName[1024-1] = '\0';
if (!strstr(szLoFileName, szCandidateFileName))
{
continue;
}
}
}
// if it is a par2-file with blocks and it was from the same NZB-request
// and it belongs to the same file collection (same base name),
// then OK, we can use it
BlockInfo* pBlockInfo = new BlockInfo();
pBlockInfo->m_pFileInfo = pFileInfo;
pBlockInfo->m_iBlockCount = iBlocks;
pBlocks->push_back(pBlockInfo);
*pBlockFound += iBlocks;
}
}
}
void ParChecker::LoadMorePars(void* repairer)
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
QueuedParFiles moreFiles;
@@ -496,17 +343,19 @@ void ParChecker::LoadMorePars(void* repairer)
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
{
char* szParFilename = *it;
bool loadedOK = ((Repairer*)repairer)->LoadPacketsFromFile(szParFilename);
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
return !moreFiles.empty();
}
void ParChecker::AddParFile(const char * szParFilename)
@@ -524,9 +373,172 @@ void ParChecker::QueueChanged()
m_mutexQueuedParFiles.Unlock();
}
bool ParChecker::CheckSplittedFragments()
{
bool bFragmentsAdded = false;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
}
return bFragmentsAdded;
}
bool ParChecker::AddSplittedFragments(const char* szFilename)
{
char szDirectory[1024];
strncpy(szDirectory, szFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
int iBaseLen = strlen(szBasename);
list<CommandLine::ExtraFile> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (!strncasecmp(filename, szBasename, iBaseLen))
{
const char* p = filename + iBaseLen;
if (*p == '.')
{
for (p++; *p && strchr("0123456789", *p); p++) ;
if (!*p)
{
debug("Found splitted fragment %s", filename);
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
extrafiles.push_back(extrafile);
}
}
}
}
bool bFragmentsAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles = extrafiles.size();
m_bVerifyingExtraFiles = true;
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
m_bVerifyingExtraFiles = false;
}
return bFragmentsAdded;
}
void ParChecker::signal_filename(std::string str)
{
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
if (m_eStage == ptRepairing)
{
m_eStage = ptVerifyingRepaired;
}
info("%s %s", szStageMessage[m_eStage], str.c_str());
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
void ParChecker::signal_progress(double progress)
{
m_iFileProgress = (int)progress;
if (m_eStage == ptRepairing)
{
// calculating repair-data for all files
m_iStageProgress = m_iFileProgress;
}
else
{
// processing individual files
int iTotalFiles = 0;
if (m_eStage == ptVerifyingRepaired)
{
// repairing individual files
iTotalFiles = m_iFilesToRepair;
}
else
{
// verifying individual files
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
}
if (iTotalFiles > 0)
{
if (m_iFileProgress < 1000)
{
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
}
else
{
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
}
}
else
{
m_iStageProgress = 0;
}
}
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
UpdateProgress();
}
void ParChecker::signal_done(std::string str, int available, int total)
{
m_iProcessedFiles++;
if (m_eStage == ptVerifyingSources)
{
if (available < total && !m_bVerifyingExtraFiles)
{
bool bFileExists = true;
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
!sourcefile->GetTargetExists())
{
bFileExists = false;
break;
}
}
if (bFileExists)
{
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
}
else
{
warn("File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -44,32 +44,57 @@ public:
psFailed,
psFinished
};
struct BlockInfo
enum EStage
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
};
typedef std::deque<char*> QueuedParFiles;
typedef std::deque<BlockInfo*> Blocks;
private:
char* m_szInfoName;
char* m_szNZBFilename;
char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
char* m_szErrMsg;
bool m_bRepairNotNeeded;
QueuedParFiles m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
Semaphore m_semNeedMoreFiles;
bool m_bRepairing;
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
bool m_bVerifyingExtraFiles;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
void LoadMorePars(void* repairer);
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
protected:
/**
* Unpause par2-files
* returns true, if the files with required number of blocks were unpaused,
* or false if there are no more files in queue for this collection or not enough blocks
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParChecker();
virtual ~ParChecker();
@@ -86,7 +111,6 @@ public:
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
};
#endif

154
PostInfo.cpp Normal file
View File

@@ -0,0 +1,154 @@
/*
* 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 "nzbget.h"
#include "PostInfo.h"
#include "Options.h"
extern Options* g_pOptions;
int PostInfo::m_iIDGen = 0;
PostInfo::PostInfo()
{
debug("Creating PostInfo");
m_szNZBFilename = NULL;
m_szDestDir = NULL;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_bWorking = false;
m_bParCheck = false;
m_iParStatus = 0;
m_bParFailed = false;
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_szProgressLabel)
{
free(m_szProgressLabel);
}
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.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::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();
}

113
PostInfo.h Normal file
View File

@@ -0,0 +1,113 @@
/*
* 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 POSTINFO_H
#define POSTINFO_H
#include <deque>
#include "Log.h"
#include "Thread.h"
class PostInfo
{
public:
enum EStage
{
ptQueued,
ptLoadingPars,
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptExecutingScript,
ptFinished
};
typedef std::deque<Message*> Messages;
private:
int m_iID;
char* m_szNZBFilename;
char* m_szDestDir;
char* m_szParFilename;
char* m_szInfoName;
bool m_bWorking;
bool m_bParCheck;
int m_iParStatus;
bool m_bParFailed;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pScriptThread;
Mutex m_mutexLog;
Messages m_Messages;
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);
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; }
bool GetParFailed() { return m_bParFailed; }
void SetParFailed(bool bParFailed) { m_bParFailed = bParFailed; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetScriptThread() { return m_pScriptThread; }
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
Messages* LockMessages();
void UnlockMessages();
};
typedef std::deque<PostInfo*> PostQueue;
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -31,6 +31,7 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "PostInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
@@ -55,47 +56,68 @@ private:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class QueuedFile
class PostParChecker: public ParChecker
{
private:
char* m_szNZBFilename;
char* m_szParFilename;
char* m_szInfoName;
public:
QueuedFile(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
~QueuedFile();
const char* GetNZBFilename() { return m_szNZBFilename; }
const char* GetParFilename() { return m_szParFilename; }
const char* GetInfoName() { return m_szInfoName; }
PrePostProcessor* m_Owner;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
friend class PrePostProcessor;
};
typedef std::deque<QueuedFile*> ParQueue;
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::deque<BlockInfo*> Blocks;
#endif
private:
bool m_bCheckIncomingNZBs;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
void CheckIncomingNZBs();
bool WasLastUnpausedInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, bool bParOK);
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const char* szNZBFilename,
bool bIgnoreFirstInPostQueue, bool bIgnorePaused, bool bCheckPostQueue);
bool CheckScript(FileInfo* pFileInfo);
bool JobExists(PostQueue* pPostQueue, const char* szNZBFilename);
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();
Mutex m_mutexQueue;
PostQueue m_PostQueue;
PostQueue m_CompletedJobs;
#ifndef DISABLE_PARCHECK
ParChecker m_ParChecker;
Mutex m_mutexParChecker;
ParQueue m_ParQueue;
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
void ParCheckerUpdate(Subject* Caller, void* Aspect);
void CheckParQueue();
void CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
bool FindMainPars(const char* szPath, FileList* pFileList);
int GetParCleanupQueueGroup(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
bool HasFailedParJobs(const char* szNZBFilename);
bool ParJobExists(PostQueue* pPostQueue, const char* szParFilename);
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
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:
@@ -105,6 +127,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
PostQueue* LockPostQueue();
void UnlockPostQueue();
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,6 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
@@ -44,11 +46,14 @@
#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()
{
@@ -57,8 +62,15 @@ QueueCoordinator::QueueCoordinator()
m_bHasMoreJobs = true;
m_DownloadQueue.clear();
m_ActiveDownloads.clear();
ResetSpeedStat();
Decoder::Init();
m_iAllBytes = 0;
m_tStartServer = 0;
m_tStartDownload = 0;
m_tPausedFrom = 0;
m_bStandBy = true;
YDecoder::Init();
}
QueueCoordinator::~QueueCoordinator()
@@ -80,7 +92,7 @@ QueueCoordinator::~QueueCoordinator()
}
m_ActiveDownloads.clear();
Decoder::Final();
YDecoder::Final();
debug("QueueCoordinator destroyed");
}
@@ -91,60 +103,82 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Lock();
m_DiskState.CleanupTempDir(&m_DownloadQueue);
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && m_DiskState.Exists())
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
{
if (g_pOptions->GetReloadQueue())
{
m_DiskState.Load(&m_DownloadQueue);
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
}
else
{
m_DiskState.Discard();
g_pDiskState->DiscardDownloadQueue();
}
}
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
m_mutexDownloadQueue.Unlock();
m_tStartServer = time(NULL);
bool bWasStandBy = true;
bool bArticeDownloadsRunning = false;
int iResetCounter = 0;
while (!IsStopped())
{
while (g_pOptions->GetPause() && !IsStopped())
if (!g_pOptions->GetPause())
{
// Sleep for a while
usleep(500 * 1000);
}
if (g_pServerPool->HasFreeConnection())
{
// start download for next article
FileInfo* pFileInfo;
ArticleInfo* pArticleInfo;
m_mutexDownloadQueue.Lock();
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
m_bHasMoreJobs = bHasMoreArticles || !m_ActiveDownloads.empty();
if (bHasMoreArticles && !IsStopped())
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
if (pConnection)
{
StartArticleDownload(pFileInfo, pArticleInfo);
}
m_mutexDownloadQueue.Unlock();
// start download for next article
FileInfo* pFileInfo;
ArticleInfo* pArticleInfo;
if (!IsStopped())
{
// two possibilities:
// 1) hasMoreArticles==false: there are no jobs, waiting for a while
// 2) hasMoreArticles==true: the pause prevents starting of many threads, before the download-thread locks the connection
usleep(100 * 1000);
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
{
// there are no free connection available, waiting for a while
usleep(100 * 1000);
m_mutexDownloadQueue.Lock();
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
m_mutexDownloadQueue.Unlock();
}
ResetHangingDownloads();
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;
}
}
// waiting for downloads
@@ -170,34 +204,76 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
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;
pFileInfo->BuildDestDirName(pNZBFile->GetFileName());
if (g_pOptions->GetDupeCheck() && IsDupe(pFileInfo))
if (g_pOptions->GetDupeCheck())
{
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
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
{
if (bAddFirst)
{
tmpDownloadQueue.push_front(pFileInfo);
}
else
{
m_DownloadQueue.push_back(pFileInfo);
}
tmpDownloadQueue.push_back(pFileInfo);
}
}
if (bAddFirst)
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
{
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(NULL, pFileInfo);
}
delete pFileInfo;
}
pNZBFile->DetachFileInfos();
@@ -207,7 +283,7 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
m_DiskState.Save(&m_DownloadQueue, false);
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
@@ -224,7 +300,7 @@ bool QueueCoordinator::AddFileToQueue(const char* szFileName)
return false;
}
// Add NZBFile to Qeue
// Add NZBFile to Queue
AddNZBFileToQueue(pNZBFile, false);
delete pNZBFile;
@@ -232,45 +308,73 @@ bool QueueCoordinator::AddFileToQueue(const char* szFileName)
return true;
}
/*
* NOTE: see note to "AddSpeedReading"
*/
float QueueCoordinator::CalcCurrentDownloadSpeed()
{
float fSpeedAllDownloads = 0;
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
if (iTimeDiff == 0)
{
return 0;
}
float fSpeed = m_iSpeedTotalBytes / 1024.0f / iTimeDiff;
return fSpeed;
}
m_mutexDownloadQueue.Lock();
/*
* 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;
struct _timeval curtime;
gettimeofday(&curtime, 0);
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];
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
//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++)
{
ArticleDownloader* pArticleDownloader = *it;
float fSpeed = 0.0f;
struct _timeval* arttime = pArticleDownloader->GetStartTime();
#ifdef WIN32
if (arttime->time != 0)
#else
if (arttime->tv_sec != 0)
#endif
{
#ifdef WIN32
float tdiff = (float)((curtime.time - arttime->time) + (curtime.millitm - arttime->millitm) / 1000.0);
#else
float tdiff = (float)((curtime.tv_sec - arttime->tv_sec) + (curtime.tv_usec - arttime->tv_usec) / 1000000.0);
#endif
if (tdiff > 0)
{
fSpeed = (pArticleDownloader->GetBytes() / tdiff / 1024);
}
}
fSpeedAllDownloads += fSpeed;
m_iSpeedBytes[i] = 0;
m_iSpeedTime[i] = m_iSpeedStartTime;
}
m_mutexDownloadQueue.Unlock();
return fSpeedAllDownloads;
m_iSpeedBytesIndex = 0;
m_iSpeedTotalBytes = 0;
}
long long QueueCoordinator::CalcRemainingSize()
@@ -291,153 +395,32 @@ long long QueueCoordinator::CalcRemainingSize()
return lRemainingSize;
}
int QueueCoordinator::GetFileInfoID(unsigned int iEntry)
{
int ID = 0;
m_mutexDownloadQueue.Lock();
if (iEntry < m_DownloadQueue.size())
{
ID = m_DownloadQueue[iEntry]->GetID();
}
m_mutexDownloadQueue.Unlock();
return ID;
}
FileInfo* QueueCoordinator::FindFileInfo(int iID)
{
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetID() == iID)
{
return pFileInfo;
}
}
return NULL;
}
int QueueCoordinator::GetFileInfoEntry(int iID)
{
int iEntry = 0;
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetID() == iID)
{
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
* 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::EditQueuePauseUnpauseEntry(int iID, bool bPause)
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
{
bool res = false;
m_mutexDownloadQueue.Lock();
FileInfo* pFileInfo = FindFileInfo(iID);
if (pFileInfo)
pFileInfo->SetDeleted(true);
bool hasDownloads = false;
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
pFileInfo->SetPaused(bPause);
res = true;
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
m_DiskState.Save(&m_DownloadQueue, true);
}
m_mutexDownloadQueue.Unlock();
return res;
}
/*
* Removes entry with index iEntry
* returns true if successful, false if operation is not possible
*/
bool QueueCoordinator::EditQueueDeleteEntry(int iID)
{
debug("Deleting queue entry");
bool res = false;
m_mutexDownloadQueue.Lock();
FileInfo* pFileInfo = FindFileInfo(iID);
if (pFileInfo)
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
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)
{
ArticleDownloader* pArticleDownloader = *it;
if (pArticleDownloader->GetFileInfo() == pFileInfo)
{
hasDownloads = true;
pArticleDownloader->Stop();
}
}
if (!hasDownloads)
{
DeleteFileInfo(pFileInfo);
}
res = true;
}
m_mutexDownloadQueue.Unlock();
debug("Queue entry deleted");
return res;
}
/*
* Moves entry identified with iID in the queue
* returns true if successful, false if operation is not possible
*/
bool QueueCoordinator::EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection)
{
bool res = false;
m_mutexDownloadQueue.Lock();
int iEntry = GetFileInfoEntry(iID);
if (iEntry >= 0)
{
int iNewEntry = iEntry + iOffset;
if (bAutoCorrection && iNewEntry < 0)
{
iNewEntry = 0;
}
if (bAutoCorrection && (unsigned int)iNewEntry > m_DownloadQueue.size() - 1)
{
iNewEntry = (int)m_DownloadQueue.size() - 1;
}
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= m_DownloadQueue.size() - 1)
{
FileInfo* fi = m_DownloadQueue[iEntry];
m_DownloadQueue.erase(m_DownloadQueue.begin() + iEntry);
m_DownloadQueue.insert(m_DownloadQueue.begin() + iNewEntry, fi);
res = true;
hasDownloads = true;
pArticleDownloader->Stop();
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
if (!hasDownloads)
{
m_DiskState.Save(&m_DownloadQueue, true);
}
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
Notify(&aspect);
m_mutexDownloadQueue.Unlock();
return res;
DeleteFileInfo(pFileInfo, false);
}
return hasDownloads;
}
void QueueCoordinator::Stop()
@@ -463,6 +446,10 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
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;
@@ -477,7 +464,7 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
return false;
}
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
{
debug("Starting new ArticleDownloader");
@@ -486,13 +473,13 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
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();
pArticleDownloader->WaitInit();
}
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
@@ -502,18 +489,25 @@ void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloade
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->GetNiceNZBName(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()
@@ -562,26 +556,23 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
if (!pFileInfo->GetFilenameConfirmed() &&
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
pArticleDownloader->GetArticleFilename())
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);
}
}
m_mutexDownloadQueue.Unlock();
bool deleteFileObj = false;
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
if (pFileInfo->GetDeleted())
{
// all jobs done
pArticleDownloader->CompleteFileParts();
deleteFileObj = true;
}
else if (pFileInfo->GetDeleted())
{
m_mutexDownloadQueue.Lock();
int cnt = 0;
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
@@ -590,7 +581,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
cnt++;
}
}
m_mutexDownloadQueue.Unlock();
if (cnt == 1)
{
// this was the last Download for a file deleted from queue
@@ -598,8 +588,16 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
}
}
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
{
// all jobs done
m_mutexDownloadQueue.Unlock();
pArticleDownloader->CompleteFileParts();
m_mutexDownloadQueue.Lock();
deleteFileObj = true;
}
// delete Download from Queue
m_mutexDownloadQueue.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
ArticleDownloader* pa = *it;
@@ -611,18 +609,20 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
}
if (deleteFileObj)
{
bool fileDeleted = pFileInfo->GetDeleted();
// delete File from Queue
pFileInfo->SetDeleted(true);
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
Notify(&aspect);
DeleteFileInfo(pFileInfo);
DeleteFileInfo(pFileInfo, fileCompleted);
}
m_mutexDownloadQueue.Unlock();
}
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo)
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
{
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
@@ -636,7 +636,32 @@ void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo)
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
m_DiskState.DiscardFileInfo(&m_DownloadQueue, pFileInfo);
g_pDiskState->DiscardFile(&m_DownloadQueue, 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;
@@ -646,16 +671,19 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
{
debug("Checking if the file is already queued");
if (pFileInfo->IsDupe())
// 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->GetDestDir(), pQueueEntry->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()))
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
pFileInfo != pQueueEntry)
{
return true;
}
@@ -713,7 +741,7 @@ void QueueCoordinator::ResetHangingDownloads()
}
else
{
error("Could not terminate hanging download %s", BaseFileName(pArticleInfo->GetResultFilename()));
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
@@ -726,3 +754,51 @@ void QueueCoordinator::ResetHangingDownloads()
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();
}

View File

@@ -29,25 +29,30 @@
#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 "DiskState.h"
#include "QueueEditor.h"
#include "NNTPConnection.h"
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
{
public:
typedef std::list<ArticleDownloader*> ActiveDownloads;
typedef enum EAspectAction
enum EAspectAction
{
eaNZBFileAdded,
eaFileCompleted,
eaFileDeleted
};
typedef struct Aspect
struct Aspect
{
EAspectAction eAction;
FileInfo* pFileInfo;
@@ -58,39 +63,58 @@ public:
private:
DownloadQueue m_DownloadQueue;
ActiveDownloads m_ActiveDownloads;
DiskState m_DiskState;
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
// statistics
static const int SPEEDMETER_SLOTS = 30;
static const int SPEEDMETER_SLOTSIZE = 1; //Split elapsed time into this number of secs.
int m_iSpeedBytes[SPEEDMETER_SLOTS];
int m_iSpeedTotalBytes;
int m_iSpeedTime[SPEEDMETER_SLOTS];
int m_iSpeedStartTime;
int m_iSpeedBytesIndex;
long long m_iAllBytes;
time_t m_tStartServer;
time_t m_tStartDownload;
time_t m_tPausedFrom;
bool m_bStandBy;
Mutex m_mutexStat;
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
FileInfo* FindFileInfo(int iID);
int GetFileInfoEntry(int iID);
void DeleteFileInfo(FileInfo* pFileInfo);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
public:
QueueCoordinator();
virtual ~QueueCoordinator();
virtual void Run();
virtual void Stop();
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
void Update(Subject* Caller, void* Aspect);
// statistics
long long CalcRemainingSize();
virtual float CalcCurrentDownloadSpeed();
virtual void AddSpeedReading(int iBytes);
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
// Editing the queue
DownloadQueue* LockQueue();
void UnlockQueue() ;
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
bool AddFileToQueue(const char* szFileName);
bool EditQueuePauseUnpauseEntry(int iID, bool bPause);
bool EditQueueDeleteEntry(int iID);
bool EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection);
int GetFileInfoID(unsigned int iEntry);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
void LogDebugInfo();
};

658
QueueEditor.cpp Normal file
View File

@@ -0,0 +1,658 @@
/*
* 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 <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)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
}
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
}
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
return bOK;
}
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
{
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
{
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 eaFilePauseAllPars:
case eaFilePauseExtraPars:
// remove compiler warning "enumeration not handled in switch"
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;
}
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;
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
}
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);
}
}
}
}
}

98
QueueEditor.h Normal file
View File

@@ -0,0 +1,98 @@
/*
* This file if 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 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
};
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);
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 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);
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
};
#endif

140
README
View File

@@ -30,32 +30,36 @@ Then you use client to send requests to server. The sample requests
are: download nzb-file, list files in queue, etc.
Standalone-tool, server and client are all contained in only one
executable file "NZBGet". The mode in which the program works
executable file "nzbget". The mode in which the program works
depends on command-line parameters passed to the program.
=====================================
2. Supported OS
=====================================
NZBGet is written in C++ and was initialy developen on Linux.
NZBGet is written in C++ and was initialy developed on Linux.
It was ported to Windows later and tested for compatibility with
several POSIX-OS'es.
The current version (0.3.0) should run at least on:
- Linux Debian 3.1 on x86;
The current version (0.4.1) should run at least on:
- Linux Debian 4.0 on x86;
- Linux BusyBox with uClibc on MIPSEL;
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
- Solaris 10 on x86;
- Windows XP SP2 on x86.
The previous version (0.3.0) was also tested on:
- Linux Debian 3.1 on x86;
- Solaris 10 on x86;
- Linux Debian 3.1 on SPARC (QEmu).
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 few platforms (including Windows), but for most POSIX-systems
you need to compile the program yourself.
for Windows. The binary packages for many routers and NAS devices are
also available in OPTWARE repository (http://www.nslu2-linux.org),
but for most POSIX-systems you need to compile the program yourself.
If you have downloaded binaries you can just jump to section
"Configuration".
@@ -65,8 +69,7 @@ If you have downloaded binaries you can just jump to section
=====================================
NZBGet is developed on a linux-system, but it should run on other
POSIX platforms (tested on FreeBSD and Solaris for x86; although
the par-support and uulib were not tested).
POSIX platforms (see the list of tested platforms above).
NZBGet absolutely needs the following libraries:
@@ -75,22 +78,21 @@ NZBGet absolutely needs the following libraries:
And the following libraries are optional:
- for curses-output-mode:
- for curses-output-mode (enabled by default):
- libcurses (usually part of commercial systems)
or (better)
- libncurses (http://invisible-island.net/ncurses)
- for par-check and -repair:
- for par-check and -repair (enabled by default):
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for support of encoding-formats other than yEnc:
- libuu (http://www.fpx.de/fp/Software/UUDeview)
All these libraries are included in modern Linux distributions and
should be available as installable packages. On other systems you
may need to download the libraries at the given URLs and compile
them (see hints below).
should be available as installable packages. Please note that you also
need the developer packages for these libraries too, they package names
have often suffix "dev" or "devel". On other systems you may need to
download the libraries at the given URLs and compile them (see hints below).
=====================================
4. Installation on POSIX
@@ -105,11 +107,10 @@ Well, the usual stuff:
- configure it via
./configure
(maybe you have to tell configure, where to find some libraries.
./configure --help is your friend! ;-)
./configure --help is your friend!)
also see "Configure-options" later.)
- compile it via
make
(you may get some warnings concerning 'mktemp', simply ignore them!)
- become root via
su
- install it via
@@ -119,80 +120,26 @@ Configure-options
-----------------
You may run configure with additional arguments:
--enable-uulib - to make with uulib-library, in a case you want it
(see later section "Optional package: uulib"). This option is not
enabled by default.
--disable-curses - to make without curses-support. Use this option
if you can not use curses/ncurses.
--disable-parcheck - to make without parcheck-support. Use this option
if you can not use libpar2 or libsigc++.
--disable-parprogress - to not show progress during par-check. This option
may be needed on some systems, where sigc++ does not work very well
(uClibc on MIPSEL is one of them). If par-check segfaults right after
start, then you probably need this option.
--enable-debug - to build in debug-mode, if you want to see and log
debug-messages.
Optional package: uulib
-----------------------
uulib is not required to compile and run nzbget, because nzbget includes
internal decoder for yEnc-format. However, uulib supports many other formats,
you may possibly want to have support for. In this case you can build the
program with uulib-support enabled.
NOTE: enabling uulib does not disable internal decoder. The program built with
uulib-support can use both decoders (internal and uulib), depending on option
"decoder" in program's configuration file.
To build with uulib use option "--enable-uulib" while running configure:
./configure --enable-uulib
The uulib must be installed on your system. On most linux distributions
the package uulib-dev is available. So you only need to install this package
and run configure with parameter "--enable-uulib".
If you do not have this package you can compile uulib yourself:
- download source code of uudeview from
http://www.fpx.de/fp/Software/UUDeview;
- build uudeview as usually:
/.confugure
make
- start nzbget's configure-script with following parameters:
./configure --enable-uulib \
--with-uulib-includes=<path to uudeview>/uulib \
--with-uulib-libraries=<path to uudeview>/uulib
for example:
./configure --enable-uulib \
--with-uulib-includes=/home/user/uudeview-0.5.20/uulib \
--with-uulib-libraries=/home/user/uudeview-0.5.20/uulib
- now you can compile nzbget.
NOTE: after nzbget is compiled, the code of uulib-library is built into
nzbget's executable. You do not need to have uulib on target system
to run nzbget.
Optional package: par-check
---------------------------
NZBGet can check and repair downloaded files for you. For this purpose
it uses library par2 (libpar2), which needs sigc++ on its part.
To build with par-check use option "--enable-parcheck" while running
configure:
./configure --enable-parcheck
The libpar2 and libsigc++ (version 2 or later) must be installed on your
system. On most linux distributions these libraries are available as packages.
So you only need to install theme and run configure with parameter
"--enable-parcheck".
If you do not have these package you can compile them yourself. Please
refer to section "Optional package: uulib" for an example on how to
compile additional library. Following configure-parameters may be usefull:
If you do not have these packages you can compile them yourself.
Following configure-parameters may be usefull:
--with-libpar2-includes
--with-libpar2-libraries
@@ -201,12 +148,17 @@ compile additional library. Following configure-parameters may be usefull:
The library libsigc++ must be installed first, since libpar2 requires it.
If you are not able to use libpar2 or libsigc++ or do not want them you can
make the program without support for par-check using option
"--disable-parcheck":
./configure --disable-parcheck
Optional package: curses
-------------------------
For curses-outputmode you need ncurses or curses on your system.
If you do not have one of them you can download and compile ncurses yourself.
Please refer to section "Optional package: uulib" for an example on how to
compile additional library. Following configure-parameters may be usefull:
Following configure-parameters may be usefull:
--with-libcurses-includes
--with-libcurses-libraries
@@ -311,34 +263,48 @@ mode, not to daemon mode).
When the server is running it is possible to queue up downloads. This can be
done either in terminal with "nzbget -A <nzb-file>" or by uploading
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default, the
directory must exist on server's start; otherwise it will not be monitored).
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default).
To check the status of server start client and connect it to server:
nzbget -C
The client have three different (display) outputmodes, which you can select
in configuration file (on client computer) or in command line. Try them:
nzbget -o outputmode=log -C
nzbget -o outputmode=color -C
nzbget -o outputmode=curses -C
To list files in server's queue:
nzbget -L
It prints something like:
[1] nzbname\filename1.rar (50.00 MB)
[2] nzbname\filename1.r01 (50.00 MB)
[1] nzbname\filename1.rar (50.00 MB)
[2] nzbname\filename1.r01 (50.00 MB)
The numbers in square braces are ID's of files in queue. They can be used
in edit-command. For example to move file with ID 2 to the top of queue:
nzbget -E T -I 2
nzbget -E T 2
or to pause files with IDs from 10 to 20:
nzbget -E P -I 10-20
nzbget -E P 10-20
or to delete file from queue:
or to delete files from queue:
nzbget -E D -I 3
nzbget -E D 3 10-15 20-21 16
The edit-command has also a group-mode which affects all files from the
same nzb-request. You need to pass one ID of any file in the group. For
example to delete all files from the first nzb-request:
nzbget -E G D 1
The switch "o" is useful to override options in configuration files.
For example:

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -34,17 +34,20 @@
#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 "DownloadInfo.h"
#include "Options.h"
#include "NZBFile.h"
#include "Log.h"
#include "Util.h"
@@ -57,14 +60,14 @@ RemoteClient::RemoteClient()
m_bVerbose = true;
/*
printf("sizeof(SNZBMessageBase)=%i\n", sizeof(SNZBMessageBase));
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
printf("sizeof(SNZBListRequestAnswer)=%i\n", sizeof(SNZBListRequestAnswer));
printf("sizeof(SNZBListRequestAnswerEntry)=%i\n", sizeof(SNZBListRequestAnswerEntry));
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
printf("sizeof(SNZBListResponseEntry)=%i\n", sizeof(SNZBListResponseEntry));
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
printf("sizeof(SNZBLogRequestAnswer)=%i\n", sizeof(SNZBLogRequestAnswer));
printf("sizeof(SNZBLogRequestAnswerEntry)=%i\n", sizeof(SNZBLogRequestAnswerEntry));
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));
@@ -85,7 +88,7 @@ RemoteClient::~RemoteClient()
}
}
void RemoteClient::printf(char * msg,...)
void RemoteClient::printf(const char * msg,...)
{
if (m_bVerbose)
{
@@ -96,7 +99,7 @@ void RemoteClient::printf(char * msg,...)
}
}
void RemoteClient::perror(char * msg)
void RemoteClient::perror(const char * msg)
{
if (m_bVerbose)
{
@@ -118,22 +121,58 @@ bool RemoteClient::InitConnection()
return OK;
}
void RemoteClient::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
{
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
pMessageBase->m_iType = iRequest;
pMessageBase->m_iSize = iSize;
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
void RemoteClient::ReceiveCommandResult()
bool RemoteClient::ReceiveBoolResponse()
{
char szAnswer[1024];
strcpy(szAnswer, "N/A");
printf("request sent\n");
m_pConnection->Recv(szAnswer, 1024);
printf("nzbserver returned: %s\n", szAnswer);
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);
}
/*
@@ -144,7 +183,7 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
// Read the file into the buffer
char* szBuffer = NULL;
int iLength = 0;
if (!NZBFile::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
if (!Util::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
{
printf("Could not load file %s\n", szName);
return false;
@@ -154,9 +193,9 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
if (OK)
{
SNZBDownloadRequest DownloadRequest;
InitMessageBase(&DownloadRequest.m_MessageBase, NZBMessageRequest::eRequestDownload, sizeof(DownloadRequest));
DownloadRequest.m_bAddFirst = bAddFirst;
DownloadRequest.m_iTrailingDataLength = iLength;
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
strncpy(DownloadRequest.m_szFilename, szName, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
@@ -169,7 +208,7 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
else
{
m_pConnection->Send(szBuffer, iLength);
ReceiveCommandResult();
OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
}
}
@@ -188,9 +227,9 @@ bool RemoteClient::RequestServerList()
if (!InitConnection()) return false;
SNZBListRequest ListRequest;
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = true;
ListRequest.m_bServerState = true;
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)
{
@@ -198,18 +237,31 @@ bool RemoteClient::RequestServerList()
return false;
}
printf("Request sent\n");
// Now listen for the returned list
SNZBListRequestAnswer ListRequestAnswer;
if (m_pConnection->Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
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 (ListRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
if (!m_pConnection->RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -218,7 +270,7 @@ bool RemoteClient::RequestServerList()
m_pConnection->Disconnect();
if (ListRequestAnswer.m_iTrailingDataLength == 0)
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
{
printf("Server has no files queued for download\n");
}
@@ -230,66 +282,96 @@ bool RemoteClient::RequestServerList()
long long lRemaining = 0;
long long lPaused = 0;
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
{
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
long long lFileSize = Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
long long lRemainingSize = Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
char szCompleted[100];
szCompleted[0] = '\0';
if (pListAnswer->m_iRemainingSize < pListAnswer->m_iFileSize)
if (lRemainingSize < lFileSize)
{
sprintf(szCompleted, ", %i%s", (int)(100 - pListAnswer->m_iRemainingSize * 100.0 / pListAnswer->m_iFileSize), "\%");
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(lRemainingSize) * 100.0 / Util::Int64ToFloat(lFileSize)), "%");
}
char szStatus[100];
if (pListAnswer->m_bPaused)
if (ntohl(pListAnswer->m_bPaused))
{
sprintf(szStatus, " (paused)");
lPaused += pListAnswer->m_iRemainingSize;
lPaused += lRemainingSize;
}
else
{
szStatus[0] = '\0';
lRemaining += pListAnswer->m_iRemainingSize;
lRemaining += lRemainingSize;
}
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
char* szFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
char szNZBNiceName[1024];
strncpy(szNZBNiceName, BaseFileName(szNZBFilename), 1024);
szNZBNiceName[1024-1] = '\0';
if (char* p = strrchr(szNZBNiceName, '.')) *p = '\0';
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
printf("[%i] %s%c%s (%.2f MB%s)%s\n", pListAnswer->m_iID, szNZBNiceName, (int)PATH_SEPARATOR, szFilename, pListAnswer->m_iFileSize / 1024.0 / 1024.0, szCompleted, szStatus);
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename,
(float)(Util::Int64ToFloat(lFileSize) / 1024.0 / 1024.0), szCompleted, szStatus);
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
}
printf("-----------------------------------\n");
printf("Files: %i\n", ListRequestAnswer.m_iNrTrailingEntries);
printf("Files: %i\n", ntohl(ListResponse.m_iNrTrailingEntries));
if (lPaused > 0)
{
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", lRemaining / 1024.0 / 1024.0, lPaused / 1024.0 / 1024.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", lRemaining / 1024.0 / 1024.0);
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
}
printf("Download rate: %.1f KB/s\n", ListRequestAnswer.m_fDownloadRate);
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
free(pBuf);
}
printf("Threads running: %i\n", ListRequestAnswer.m_iThreadCount);
printf("Server state: %s\n", ListRequestAnswer.m_bServerPaused ? "Paused" : "Running");
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 (ListRequestAnswer.m_fDownloadLimit > 0)
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
{
printf("Speed limit: %.1f KB/s\n", ListRequestAnswer.m_fDownloadLimit);
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("Speed limit: Unlimited\n");
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
}
return true;
@@ -300,8 +382,8 @@ bool RemoteClient::RequestServerLog(int iLines)
if (!InitConnection()) return false;
SNZBLogRequest LogRequest;
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
LogRequest.m_iLines = iLines;
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)
@@ -310,18 +392,31 @@ bool RemoteClient::RequestServerLog(int iLines)
return false;
}
printf("Request sent\n");
// Now listen for the returned log
SNZBLogRequestAnswer LogRequestAnswer;
if (m_pConnection->Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
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 (LogRequestAnswer.m_iTrailingDataLength > 0)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
if (!m_pConnection->RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -330,22 +425,22 @@ bool RemoteClient::RequestServerLog(int iLines)
m_pConnection->Disconnect();
if (LogRequestAnswer.m_iTrailingDataLength == 0)
if (LogResponse.m_iTrailingDataLength == 0)
{
printf("Log is empty\n");
}
else
{
printf("Log (last %i entries)\n", LogRequestAnswer.m_iNrTrailingEntries);
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
printf("-----------------------------------\n");
char* pBufPtr = (char*)pBuf;
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
{
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
switch (pLogAnswer->m_iKind)
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
switch (ntohl(pLogAnswer->m_iKind))
{
case Message::mkDebug:
printf("[DEBUG] %s\n", szText);
@@ -359,9 +454,12 @@ bool RemoteClient::RequestServerLog(int iLines)
case Message::mkInfo:
printf("[INFO] %s\n", szText);
break;
case Message::mkDetail:
printf("[DETAIL] %s\n", szText);
break;
}
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
}
printf("-----------------------------------\n");
@@ -377,8 +475,8 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause)
if (!InitConnection()) return false;
SNZBPauseUnpauseRequest PauseUnpauseRequest;
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, NZBMessageRequest::eRequestPauseUnpause, sizeof(PauseUnpauseRequest));
PauseUnpauseRequest.m_bPause = bPause;
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
PauseUnpauseRequest.m_bPause = htonl(bPause);
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
{
@@ -387,10 +485,10 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause)
return false;
}
ReceiveCommandResult();
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return true;
return OK;
}
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
@@ -398,8 +496,8 @@ bool RemoteClient::RequestServerSetDownloadRate(float fRate)
if (!InitConnection()) return false;
SNZBSetDownloadRateRequest SetDownloadRateRequest;
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, NZBMessageRequest::eRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
SetDownloadRateRequest.m_fDownloadRate = fRate;
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
{
@@ -408,10 +506,10 @@ bool RemoteClient::RequestServerSetDownloadRate(float fRate)
return false;
}
ReceiveCommandResult();
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return true;
return OK;
}
bool RemoteClient::RequestServerDumpDebug()
@@ -419,8 +517,7 @@ bool RemoteClient::RequestServerDumpDebug()
if (!InitConnection()) return false;
SNZBDumpDebugRequest DumpDebugInfo;
InitMessageBase(&DumpDebugInfo.m_MessageBase, NZBMessageRequest::eRequestDumpDebug, sizeof(DumpDebugInfo));
DumpDebugInfo.m_iLevel = 0;
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
{
@@ -429,38 +526,52 @@ bool RemoteClient::RequestServerDumpDebug()
return false;
}
ReceiveCommandResult();
bool OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
return true;
return OK;
}
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder)
{
if (iIDTo <= 0)
if (iIDCount <= 0 || pIDList == NULL)
{
printf("File(s) not specified (use option -I)\n");
printf("File(s) not specified\n");
return false;
}
if (!InitConnection()) return false;
SNZBEditQueueRequest EditQueueRequest;
InitMessageBase(&EditQueueRequest.m_MessageBase, NZBMessageRequest::eRequestEditQueue, sizeof(EditQueueRequest));
EditQueueRequest.m_iAction = iAction;
EditQueueRequest.m_iOffset = iOffset;
EditQueueRequest.m_iIDFrom = iIDFrom;
EditQueueRequest.m_iIDTo = iIDTo;
int iLength = sizeof(int32_t) * iIDCount;
bool OK = m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) >= 0;
if (OK)
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_iNrTrailingEntries = htonl(iIDCount);
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
int32_t* pIDs = (int32_t*)malloc(iLength);
for (int i = 0; i < iIDCount; i++)
{
ReceiveCommandResult();
pIDs[i] = htonl(pIDList[i]);
}
else
bool OK = false;
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
{
perror("m_pConnection->Send");
}
else
{
m_pConnection->Send((char*)pIDs, iLength);
OK = ReceiveBoolResponse();
m_pConnection->Disconnect();
}
free(pIDs);
m_pConnection->Disconnect();
return OK;
@@ -470,14 +581,13 @@ bool RemoteClient::RequestServerShutdown()
{
if (!InitConnection()) return false;
SNZBMessageBase QuitRequest;
SNZBShutdownRequest ShutdownRequest;
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
InitMessageBase(&QuitRequest, NZBMessageRequest::eRequestShutdown, sizeof(QuitRequest));
bool OK = m_pConnection->Send((char*)(&QuitRequest), sizeof(QuitRequest)) >= 0;
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
if (OK)
{
ReceiveCommandResult();
OK = ReceiveBoolResponse();
}
else
{
@@ -487,3 +597,134 @@ bool RemoteClient::RequestServerShutdown()
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;
}

View File

@@ -39,10 +39,10 @@ private:
bool m_bVerbose;
bool InitConnection();
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
void ReceiveCommandResult();
void printf(char* msg, ...);
void perror(char* msg);
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
bool ReceiveBoolResponse();
void printf(const char* msg, ...);
void perror(const char* msg);
public:
RemoteClient();
@@ -53,9 +53,12 @@ public:
bool RequestServerPauseUnpause(bool bPause);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
bool RequestServerLog(int iLines);
bool RequestServerShutdown();
bool RequestServerVersion();
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -43,19 +43,12 @@
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
#include "QueueCoordinator.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern void ExitProc();
const char* g_szMessageRequests[] =
{ "N/A", "Download", "Pause/Unpause", "List",
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
};
//*****************************************************************
// RemoteServer
@@ -65,49 +58,64 @@ RemoteServer::RemoteServer()
debug("Creating RemoteServer");
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection = NULL;
}
RemoteServer::~RemoteServer()
{
debug("Destroying RemoteServer");
if (m_pConnection)
{
delete m_pConnection;
}
delete m_pNetAddress;
delete m_pConnection;
}
void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
m_pConnection->Bind();
while (!IsStopped())
{
// Accept connections and store the "new" socket value
SOCKET iSocket = m_pConnection->Accept();
if (iSocket == INVALID_SOCKET)
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind() == 0;
}
// Accept connections and store the "new" socket value
SOCKET iSocket = INVALID_SOCKET;
if (bBind)
{
iSocket = m_pConnection->Accept();
}
if (!bBind || iSocket == INVALID_SOCKET)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
{
break;
}
// error binding on port. wait 0.5 sec. and retry
usleep(500 * 1000);
delete m_pConnection;
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->Bind();
m_pConnection = NULL;
continue;
}
MessageCommand* commandThread = new MessageCommand();
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetSocket(iSocket);
commandThread->Start();
}
m_pConnection->Disconnect();
if (m_pConnection)
{
m_pConnection->Disconnect();
}
debug("Exiting RemoteServer-loop");
}
@@ -115,413 +123,104 @@ void RemoteServer::Run()
void RemoteServer::Stop()
{
Thread::Stop();
m_pConnection->Cancel();
if (m_pConnection)
{
m_pConnection->SetSuppressErrors(true);
m_pConnection->Cancel();
#ifdef WIN32
m_pConnection->Disconnect();
m_pConnection->Disconnect();
#endif
}
}
//*****************************************************************
// MessageCommand
// RequestProcessor
void MessageCommand::SendResponse(char* szAnswer)
void RequestProcessor::Run()
{
send(m_iSocket, szAnswer, strlen(szAnswer), 0);
}
void MessageCommand::ProcessRequest()
{
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
switch (pMessageBase->m_iType)
// Read the first 4 bytes to determine request type
bool bOK = false;
int iSignature = 0;
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
if (iBytesReceived < 0)
{
case NZBMessageRequest::eRequestDownload:
{
RequestDownload();
break;
}
case NZBMessageRequest::eRequestList:
{
RequestList();
break;
}
case NZBMessageRequest::eRequestLog:
{
RequestLog();
break;
}
case NZBMessageRequest::eRequestPauseUnpause:
{
SNZBPauseUnpauseRequest* pPauseUnpauseRequest = (SNZBPauseUnpauseRequest*) & m_RequestBuffer;
g_pOptions->SetPause(pPauseUnpauseRequest->m_bPause);
SendResponse("Pause-/Unpause-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestEditQueue:
{
RequestEditQueue();
break;
}
case NZBMessageRequest::eRequestSetDownloadRate:
{
SNZBSetDownloadRateRequest* pSetDownloadRequest = (SNZBSetDownloadRateRequest*) & m_RequestBuffer;
g_pOptions->SetDownloadRate(pSetDownloadRequest->m_fDownloadRate);
SendResponse("Rate-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestDumpDebug:
{
g_pQueueCoordinator->LogDebugInfo();
SendResponse("Debug-Command completed successfully");
break;
}
case NZBMessageRequest::eRequestShutdown:
{
SendResponse("Stopping server");
ExitProc();
break;
}
default:
error("NZB-Request not yet supported");
break;
}
}
void MessageCommand::RequestDownload()
{
SNZBDownloadRequest* pDownloadRequest = (SNZBDownloadRequest*) & m_RequestBuffer;
const char* pExtraData = (m_iExtraDataLength > 0) ? ((char*)pDownloadRequest + pDownloadRequest->m_MessageBase.m_iSize) : NULL;
int NeedBytes = pDownloadRequest->m_iTrailingDataLength - m_iExtraDataLength;
char* pRecvBuffer = (char*)malloc(pDownloadRequest->m_iTrailingDataLength + 1);
memcpy(pRecvBuffer, pExtraData, m_iExtraDataLength);
char* pBufPtr = pRecvBuffer + m_iExtraDataLength;
// Read from the socket until nothing remains
int iResult = 0;
while (NeedBytes > 0)
{
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
if (NeedBytes == 0)
{
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(pDownloadRequest->m_szFilename, pRecvBuffer, pDownloadRequest->m_iTrailingDataLength);
if (pNZBFile)
{
info("Request: Queue collection %s", pDownloadRequest->m_szFilename);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pDownloadRequest->m_bAddFirst);
delete pNZBFile;
char tmp[1024];
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(pDownloadRequest->m_szFilename));
tmp[1024-1] = '\0';
SendResponse(tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(pDownloadRequest->m_szFilename));
tmp[1024-1] = '\0';
SendResponse(tmp);
}
}
free(pRecvBuffer);
}
void MessageCommand::RequestList()
{
SNZBListRequest* pListRequest = (SNZBListRequest*) & m_RequestBuffer;
SNZBListRequestAnswer ListRequestAnswer;
memset(&ListRequestAnswer, 0, sizeof(ListRequestAnswer));
ListRequestAnswer.m_iSize = sizeof(ListRequestAnswer);
ListRequestAnswer.m_iEntrySize = sizeof(SNZBListRequestAnswerEntry);
char* buf = NULL;
int bufsize = 0;
if (pListRequest->m_bFileList)
{
// Make a data structure and copy all the elements of the list into it
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
int NrEntries = pDownloadQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBListRequestAnswerEntry);
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
bufsize += strlen(pFileInfo->GetNZBFilename()) + 1;
bufsize += strlen(pFileInfo->GetSubject()) + 1;
bufsize += strlen(pFileInfo->GetFilename()) + 1;
bufsize += strlen(pFileInfo->GetDestDir()) + 1;
}
buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) bufptr;
pListAnswer->m_iID = pFileInfo->GetID();
pListAnswer->m_iFileSize = (int)pFileInfo->GetSize();
pListAnswer->m_bFilenameConfirmed = pFileInfo->GetFilenameConfirmed();
pListAnswer->m_iRemainingSize = (int)pFileInfo->GetRemainingSize();
pListAnswer->m_bPaused = pFileInfo->GetPaused();
pListAnswer->m_iNZBFilenameLen = strlen(pFileInfo->GetNZBFilename()) + 1;
pListAnswer->m_iSubjectLen = strlen(pFileInfo->GetSubject()) + 1;
pListAnswer->m_iFilenameLen = strlen(pFileInfo->GetFilename()) + 1;
pListAnswer->m_iDestDirLen = strlen(pFileInfo->GetDestDir()) + 1;
bufptr += sizeof(SNZBListRequestAnswerEntry);
strcpy(bufptr, pFileInfo->GetNZBFilename());
bufptr += pListAnswer->m_iNZBFilenameLen;
strcpy(bufptr, pFileInfo->GetSubject());
bufptr += pListAnswer->m_iSubjectLen;
strcpy(bufptr, pFileInfo->GetFilename());
bufptr += pListAnswer->m_iFilenameLen;
strcpy(bufptr, pFileInfo->GetDestDir());
bufptr += pListAnswer->m_iDestDirLen;
}
g_pQueueCoordinator->UnlockQueue();
ListRequestAnswer.m_iNrTrailingEntries = NrEntries;
ListRequestAnswer.m_iTrailingDataLength = bufsize;
}
if (pListRequest->m_bServerState)
{
ListRequestAnswer.m_fDownloadRate = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
ListRequestAnswer.m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
ListRequestAnswer.m_fDownloadLimit = g_pOptions->GetDownloadRate();
ListRequestAnswer.m_bServerPaused = g_pOptions->GetPause();
ListRequestAnswer.m_iThreadCount = Thread::GetThreadCount() - 1; // not counting itself
}
// Send the request answer
send(m_iSocket, (char*) &ListRequestAnswer, sizeof(ListRequestAnswer), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
if (buf)
{
free(buf);
}
}
void MessageCommand::RequestLog()
{
SNZBLogRequest* pLogRequest = (SNZBLogRequest*) & m_RequestBuffer;
Log::Messages* pMessages = g_pLog->LockMessages();
int iNrEntries = pLogRequest->m_iLines;
unsigned int iIDFrom = pLogRequest->m_iIDFrom;
int iStart = pMessages->size();
if (iNrEntries > 0)
{
if (iNrEntries > (int)pMessages->size())
{
iNrEntries = pMessages->size();
}
iStart = pMessages->size() - iNrEntries;
}
if (iIDFrom > 0 && !pMessages->empty())
{
iStart = iIDFrom - pMessages->front()->GetID();
if (iStart < 0)
{
iStart = 0;
}
iNrEntries = pMessages->size() - iStart;
if (iNrEntries < 0)
{
iNrEntries = 0;
}
}
// calculate required buffer size
int bufsize = iNrEntries * sizeof(SNZBLogRequestAnswerEntry);
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
bufsize += strlen(pMessage->GetText()) + 1;
}
char* buf = (char*) malloc(bufsize);
char* bufptr = buf;
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
{
Message* pMessage = (*pMessages)[i];
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) bufptr;
pLogAnswer->m_iID = pMessage->GetID();
pLogAnswer->m_iKind = pMessage->GetKind();
pLogAnswer->m_tTime = pMessage->GetTime();
pLogAnswer->m_iTextLen = strlen(pMessage->GetText()) + 1;
bufptr += sizeof(SNZBLogRequestAnswerEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += pLogAnswer->m_iTextLen;
}
g_pLog->UnlockMessages();
SNZBLogRequestAnswer LogRequestAnswer;
LogRequestAnswer.m_iSize = sizeof(LogRequestAnswer);
LogRequestAnswer.m_iEntrySize = sizeof(SNZBLogRequestAnswerEntry);
LogRequestAnswer.m_iNrTrailingEntries = iNrEntries;
LogRequestAnswer.m_iTrailingDataLength = bufsize;
// Send the request answer
send(m_iSocket, (char*) &LogRequestAnswer, sizeof(LogRequestAnswer), 0);
// Send the data
if (bufsize > 0)
{
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
}
void MessageCommand::RequestEditQueue()
{
SNZBEditQueueRequest* pEditQueueRequest = (SNZBEditQueueRequest*) & m_RequestBuffer;
int From = pEditQueueRequest->m_iIDFrom;
int To = pEditQueueRequest->m_iIDTo;
int Step = 1;
if ((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveTop) ||
((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveOffset) &&
(pEditQueueRequest->m_iOffset < 0)))
{
Step = -1;
int tmp = From; From = To; To = tmp;
}
for (int ID = From; ID != To + Step; ID += Step)
{
switch (pEditQueueRequest->m_iAction)
{
case NZBMessageRequest::eActionPause:
case NZBMessageRequest::eActionResume:
{
g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, pEditQueueRequest->m_iAction == NZBMessageRequest::eActionPause);
break;
}
case NZBMessageRequest::eActionMoveOffset:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, pEditQueueRequest->m_iOffset, true);
break;
}
case NZBMessageRequest::eActionMoveTop:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
break;
}
case NZBMessageRequest::eActionMoveBottom:
{
g_pQueueCoordinator->EditQueueMoveEntry(ID, + 1000000, true);
break;
}
case NZBMessageRequest::eActionDelete:
{
g_pQueueCoordinator->EditQueueDeleteEntry(ID);
break;
}
}
}
SendResponse("Edit-Command completed successfully");
}
void MessageCommand::SetSocket(SOCKET iSocket)
{
m_iSocket = iSocket;
}
void MessageCommand::Run()
{
int iRequestReceived = 0;
// Read the first package which needs to be a request
iRequestReceived = recv(m_iSocket, (char*) & m_RequestBuffer, sizeof(m_RequestBuffer), 0);
if (iRequestReceived < 0)
{
return;
}
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
// Make sure this is a nzbget request from a client
if (pMessageBase->m_iId != NZBMESSAGE_SIGNATURE)
{
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
if (strcmp(pMessageBase->m_szPassword, g_pOptions->GetServerPassword()))
{
warn("nzbget request received on port %i, but password invalid", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
// Info - connection received
#ifdef WIN32
char* ip = NULL;
#else
char ip[20];
#endif
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
char* ip = inet_ntoa(PeerName.sin_addr);
ip = inet_ntoa(PeerName.sin_addr);
#else
char ip[20];
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
#endif
debug("%s request received from %s", g_szMessageRequests[pMessageBase->m_iType], ip);
}
m_iExtraDataLength = iRequestReceived - pMessageBase->m_iSize;
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
{
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
{
// XML-RPC or JSON-RPC request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
eHttpMethod = XmlRpcProcessor::hmPost;
szUrl++;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
ProcessRequest();
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
{
eProtocol = XmlRpcProcessor::rpXmlRpc;
}
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
{
eProtocol = XmlRpcProcessor::rpJsonRpc;
}
if (eProtocol != XmlRpcProcessor::rpUndefined)
{
XmlRpcProcessor processor;
processor.SetConnection(&con);
processor.SetClientIP(ip);
processor.SetProtocol(eProtocol);
processor.SetHttpMethod(eHttpMethod);
processor.SetUrl(szUrl);
processor.Execute();
bOK = true;
}
}
}
if (!bOK)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
}
// Close the socket
closesocket(m_iSocket);
}

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -30,9 +30,6 @@
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
#include "MessageBase.h"
static int const REQUESTBUFFERSIZE = 8192;
class RemoteServer : public Thread
{
@@ -47,23 +44,14 @@ public:
virtual void Stop();
};
class MessageCommand : public Thread
class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
char m_RequestBuffer[REQUESTBUFFERSIZE];
int m_iExtraDataLength;
void ProcessRequest();
void RequestDownload();
void RequestList();
void RequestLog();
void RequestEditQueue();
void SendResponse(char* szAnswer);
public:
void SetSocket(SOCKET iSocket);
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
#endif

390
ScriptController.cpp Normal file
View File

@@ -0,0 +1,390 @@
/*
* 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 <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"
#include "Options.h"
extern Options* g_pOptions;
void ScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
{
if (!Util::FileExists(szScript))
{
error("Could not start post-process-script: could not find file %s", szScript);
pPostInfo->SetStage(PostInfo::ptFinished);
pPostInfo->SetWorking(false);
return;
}
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
ScriptController* pScriptController = new ScriptController();
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->m_szScript = szScript;
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
pScriptController->SetAutoDestroy(false);
pPostInfo->SetScriptThread(pScriptController);
pScriptController->Start();
}
void ScriptController::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';
int pipein;
#ifdef WIN32
char szCmdLine[2048];
snprintf(szCmdLine, 2048, "\"%s\" \"%s\" \"%s\" \"%s\" %s %s %s", m_szScript, m_pPostInfo->GetDestDir(),
m_pPostInfo->GetNZBFilename(), m_pPostInfo->GetParFilename(), szParStatus, szCollectionCompleted, szHasFailedParJobs);
szCmdLine[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));
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, m_pPostInfo->GetDestDir(), &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 post-process-script: %s", szErrMsg);
}
else
{
error("Could not start post-process-script: error %i", dwErrCode);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
return;
}
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
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';
int p[2];
int pipeout;
// create the pipe
if (pipe(p))
{
error("Could not open pipe: errno %i", errno);
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
return;
}
pipein = p[0];
pipeout = p[1];
debug("forking");
pid_t pid = fork();
if (pid == -1)
{
error("Could not start post-process-script: errno %i", errno);
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
return;
}
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);
execlp(m_szScript, m_szScript, szDestDir, szNZBFilename, szParFilename,
szParStatus, szCollectionCompleted, szHasFailedParJobs, NULL);
fprintf(stdout, "[ERROR] Could not start post-process-script: %s", strerror(errno));
fflush(stdout);
_exit(-1);
}
// continue the first instance
debug("forked");
debug("Child Process-ID: %i", (int)pid);
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 post-process-script");
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
return;
}
char* buf = (char*)malloc(10240);
debug("Entering pipe-loop");
while (!feof(readpipe) && !IsStopped())
{
if (fgets(buf, 10240, readpipe))
{
AddMessage(buf);
}
}
debug("Exited pipe-loop");
free(buf);
fclose(readpipe);
if (IsStopped())
{
warn("Interrupted post-process-script for %s", m_pPostInfo->GetInfoName());
}
#ifdef WIN32
WaitForSingleObject(m_hProcess, INFINITE);
#else
waitpid(m_hProcess, NULL, 0);
#endif
if (!IsStopped())
{
info("Completed post-process-script for %s", m_pPostInfo->GetInfoName());
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void ScriptController::AddMessage(char* szText)
{
debug("Adding message received from post-process-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))
{
info(szText + 7);
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(Message::mkInfo, szText + 7);
}
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
warn(szText + 10);
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(Message::mkWarning, szText + 10);
}
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
error(szText + 8);
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(Message::mkError, szText + 8);
}
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
detail(szText + 9);
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(Message::mkDetail, szText + 9);
}
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
debug(szText + 8);
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(Message::mkDebug, szText + 8);
}
}
else
{
Options::EMessageTarget eMessageTarget = Options::mtNone;
Message::EKind eKind = Message::mkDebug;
switch (g_pOptions->GetPostLogKind())
{
case Options::plNone:
break;
case Options::plDetail:
detail("Post-Process: %s", szText);
eMessageTarget = g_pOptions->GetDetailTarget();
eKind = Message::mkDetail;
break;
case Options::plInfo:
info("Post-Process: %s", szText);
eMessageTarget = g_pOptions->GetInfoTarget();
eKind = Message::mkInfo;
break;
case Options::plWarning:
warn("Post-Process: %s", szText);
eMessageTarget = g_pOptions->GetWarningTarget();
eKind = Message::mkWarning;
break;
case Options::plError:
error("Post-Process: %s", szText);
eMessageTarget = g_pOptions->GetErrorTarget();
eKind = Message::mkError;
break;
case Options::plDebug:
debug("Post-Process: %s", szText);
eMessageTarget = g_pOptions->GetDebugTarget();
eKind = Message::mkDebug;
break;
}
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
{
m_pPostInfo->AppendMessage(eKind, szText);
}
}
debug("Adding message received from post-process-script - completed");
}
void ScriptController::Stop()
{
debug("Stopping post-process-script");
Thread::Stop();
#ifdef WIN32
BOOL bOK = TerminateProcess(m_hProcess, -1);
#else
bool bOK = kill(m_hProcess, 9) == 0;
#endif
if (bOK)
{
debug("Terminated post-process-script");
}
else
{
error("Could not terminate post-process-script");
}
debug("Post-process-script stopped");
}

54
ScriptController.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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 SCRIPTCONTROLLER_H
#define SCRIPTCONTROLLER_H
#include "Thread.h"
#include "PostInfo.h"
class ScriptController : public Thread
{
private:
PostInfo* m_pPostInfo;
const char* m_szScript;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
#ifdef WIN32
HANDLE m_hProcess;
#else
pid_t m_hProcess;
#endif
void AddMessage(char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
};
#endif

View File

@@ -51,6 +51,14 @@
#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");
@@ -58,7 +66,7 @@ ServerPool::ServerPool()
m_iMaxLevel = 0;
m_iTimeout = 60;
m_Servers.clear();
m_FreeConnections.clear();
m_Connections.clear();
m_Semaphores.clear();
}
@@ -66,8 +74,6 @@ ServerPool::~ ServerPool()
{
debug("Destroying ServerPool");
m_FreeConnections.clear();
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
{
delete *it;
@@ -79,6 +85,12 @@ ServerPool::~ ServerPool()
delete *it;
}
m_Servers.clear();
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
delete *it;
}
m_Connections.clear();
}
void ServerPool::AddServer(NewsServer* pNewsServer)
@@ -102,7 +114,9 @@ void ServerPool::InitConnections()
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
m_FreeConnections.push_back(pNewsServer);
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
}
@@ -123,38 +137,41 @@ void ServerPool::InitConnections()
}
}
NNTPConnection* ServerPool::GetConnection(int level)
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
{
debug("Getting connection");
debug("sem_wait...");
// decrease m_Semaphore counter or block
bool bWaitVal = m_Semaphores[level]->Wait();
debug("sem_wait...OK");
bool bWaitVal = false;
if (bWait)
{
debug("Getting connection (wait)");
bWaitVal = m_Semaphores[iLevel]->Wait();
}
else
{
bWaitVal = m_Semaphores[iLevel]->TryWait();
}
if (!bWaitVal)
{
debug("semaphore error: %i", errno);
// signal received or wait timeout
return NULL;
}
m_mutexFree.Lock();
m_mutexConnections.Lock();
NNTPConnection* pConnection = NULL;
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
PooledConnection* pConnection = NULL;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
NewsServer* server = *it;
if (server->GetLevel() == level)
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
{
// free connection found, take it!
pConnection = new NNTPConnection(server);
pConnection->SetTimeout(m_iTimeout);
m_FreeConnections.erase(it);
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
}
}
m_mutexFree.Unlock();
m_mutexConnections.Unlock();
if (!pConnection)
{
@@ -164,22 +181,46 @@ NNTPConnection* ServerPool::GetConnection(int level)
return pConnection;
}
void ServerPool::FreeConnection(NNTPConnection* pConnection)
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
debug("Freeing connection");
if (bUsed)
{
debug("Freeing used connection");
}
// give back free connection
m_mutexFree.Lock();
m_FreeConnections.push_back(pConnection->GetNewsServer());
m_mutexConnections.Lock();
((PooledConnection*)pConnection)->SetInUse(false);
if (bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
m_mutexFree.Unlock();
delete pConnection;
m_mutexConnections.Unlock();
}
bool ServerPool::HasFreeConnection()
void ServerPool::CloseUnusedConnections()
{
return !m_Semaphores[0]->IsLocked();
m_mutexConnections.Lock();
time_t curtime = ::time(NULL);
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
pConnection->Disconnect();
}
}
}
m_mutexConnections.Unlock();
}
void ServerPool::LogDebugInfo()
@@ -189,12 +230,12 @@ void ServerPool::LogDebugInfo()
debug(" Max-Level: %i", m_iMaxLevel);
m_mutexFree.Lock();
m_mutexConnections.Lock();
debug(" Free Connections: %i", m_FreeConnections.size());
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
debug(" Free Connection: level=%i", (*it)->GetLevel());
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
/*
debug(" Semaphores: %i", m_Semaphores.size());
@@ -206,5 +247,5 @@ void ServerPool::LogDebugInfo()
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
}
*/
m_mutexFree.Unlock();
m_mutexConnections.Unlock();
}

View File

@@ -28,7 +28,8 @@
#ifndef SERVERPOOL_H
#define SERVERPOOL_H
#include <list>
#include <vector>
#include <time.h>
#include "Thread.h"
#include "NewsServer.h"
@@ -37,14 +38,28 @@
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;
Servers m_FreeConnections;
Connections m_Connections;
Semaphores m_Semaphores;
int m_iMaxLevel;
Mutex m_mutexFree;
Mutex m_mutexConnections;
int m_iTimeout;
public:
@@ -54,9 +69,9 @@ public:
void AddServer(NewsServer *s);
void InitConnections();
int GetMaxLevel() { return m_iMaxLevel; }
NNTPConnection* GetConnection(int level);
bool HasFreeConnection();
void FreeConnection(NNTPConnection* con);
NNTPConnection* GetConnection(int iLevel, bool bWait);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();
void LogDebugInfo();
};

View File

@@ -35,6 +35,10 @@
#include <stdlib.h>
#include <stdio.h>
#ifdef WIN32
#include <process.h>
#endif
#include "Log.h"
#include "Thread.h"
@@ -63,6 +67,14 @@ 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
@@ -123,6 +135,15 @@ bool Semaphore::Wait()
#endif
}
bool Semaphore::TryWait()
{
#ifdef WIN32
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
#else
return sem_trywait(&m_semObj) == 0;
#endif
}
bool Semaphore::TimedWait(int iMSec)
{
#ifdef WIN32
@@ -195,8 +216,7 @@ void Thread::Start()
m_mutexThread.Lock();
#ifdef WIN32
DWORD ThreadId;
m_Thread = CreateThread(NULL, 0, Thread::thread_handler, (void *) this, 0, &ThreadId);
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
m_bRunning = m_Thread != NULL;
#else
pthread_attr_t m_Attr;
@@ -238,7 +258,7 @@ bool Thread::Kill()
}
#ifdef WIN32
DWORD WINAPI Thread::thread_handler(void* pObject)
void __cdecl Thread::thread_handler(void* pObject)
#else
void* Thread::thread_handler(void* pObject)
#endif
@@ -267,7 +287,9 @@ void* Thread::thread_handler(void* pObject)
m_iThreadCount--;
m_mutexThread.Unlock();
#ifndef WIN32
return NULL;
#endif
}
int Thread::GetThreadCount()
@@ -277,4 +299,3 @@ int Thread::GetThreadCount()
m_mutexThread.Unlock();
return iThreadCount;
}

View File

@@ -64,6 +64,7 @@ public:
~Semaphore();
void Post();
bool Wait();
bool TryWait();
bool TimedWait(int iMSec);
bool IsLocked();
};
@@ -84,7 +85,7 @@ private:
bool m_bAutoDestroy;
#ifdef WIN32
static DWORD WINAPI thread_handler(void* pObject);
static void __cdecl thread_handler(void* pObject);
#else
static void *thread_handler(void* pObject);
#endif

838
Util.cpp
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -27,38 +27,25 @@
#include <config.h>
#endif
#include <string.h>
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#include <sys/statvfs.h>
#endif
#include "nzbget.h"
#include "Util.h"
char* BaseFileName(const char* filename)
{
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
if (p1)
{
if ((p && p < p1) || !p)
{
p = p1;
}
}
if (p)
{
return p + 1;
}
else
{
return (char*)filename;
}
}
#ifdef WIN32
// getopt for WIN32:
@@ -109,7 +96,10 @@ int getopt(int argc, char *argv[], char *optstring)
char *cp = strchr(optstring, c);
if (cp == NULL || c == ':')
{
fprintf(stderr, "Invalid option %c", c);
return '?';
}
cp++;
if (*cp == ':')
@@ -126,6 +116,7 @@ int getopt(int argc, char *argv[], char *optstring)
}
else
{
fprintf(stderr, "Option %c needs an argument", c);
return '?';
}
}
@@ -207,9 +198,30 @@ const char* DirBrowser::Next()
#endif
void NormalizePathSeparators(char* Path)
char* Util::BaseFileName(const char* filename)
{
for (char* p = Path; *p; p++)
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
if (p1)
{
if ((p && p < p1) || !p)
{
p = p1;
}
}
if (p)
{
return p + 1;
}
else
{
return (char*)filename;
}
}
void Util::NormalizePathSeparators(char* szPath)
{
for (char* p = szPath; *p; p++)
{
if (*p == ALT_PATH_SEPARATOR)
{
@@ -217,3 +229,777 @@ void NormalizePathSeparators(char* Path)
}
}
}
bool Util::ForceDirectories(const char* szPath)
{
char* szNormPath = strdup(szPath);
NormalizePathSeparators(szNormPath);
int iLen = strlen(szNormPath);
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
#ifdef WIN32
&& iLen > 3
#endif
)
{
szNormPath[iLen-1] = '\0';
}
struct stat buffer;
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
if (!bOK
#ifdef WIN32
&& strlen(szNormPath) > 2
#endif
)
{
char* szParentPath = strdup(szNormPath);
bOK = true;
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
if (p)
{
#ifdef WIN32
if (p - szParentPath == 2 && szParentPath[1] == ':' && strlen(szParentPath) > 2)
{
szParentPath[3] = '\0';
}
else
#endif
{
*p = '\0';
}
if (strlen(szParentPath) != strlen(szPath))
{
bOK = ForceDirectories(szParentPath);
}
}
if (bOK)
{
mkdir(szNormPath, S_DIRMODE);
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
}
free(szParentPath);
}
free(szNormPath);
return bOK;
}
bool Util::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
{
FILE* pFile = fopen(szFileName, "r");
if (!pFile)
{
return false;
}
// obtain file size.
fseek(pFile , 0 , SEEK_END);
int iSize = ftell(pFile);
rewind(pFile);
// allocate memory to contain the whole file.
*pBuffer = (char*) malloc(iSize + 1);
if (!*pBuffer)
{
return false;
}
// copy the file into the buffer.
fread(*pBuffer, 1, iSize, pFile);
fclose(pFile);
(*pBuffer)[iSize] = 0;
*pBufferLength = iSize + 1;
return true;
}
bool Util::SetFileSize(const char* szFilename, int iSize)
{
bool bOK = false;
#ifdef WIN32
FILE* pFile = fopen(szFilename, "a");
if (pFile)
{
bOK = _chsize_s(pFile->_file, iSize) == 0;
fclose(pFile);
}
#else
// create file
FILE* pFile = fopen(szFilename, "a");
if (pFile)
{
fclose(pFile);
}
// there are no reliable function to expand file on POSIX, so we must try different approaches,
// starting with the fastest one and hoping it will work
// 1) set file size using function "truncate" (it is fast, if works)
truncate(szFilename, iSize);
// check if it worked
pFile = fopen(szFilename, "a");
if (pFile)
{
fseek(pFile, 0, SEEK_END);
bOK = ftell(pFile) == iSize;
if (!bOK)
{
// 2) truncate did not work, expanding the file by writing in it (it is slow)
fclose(pFile);
truncate(szFilename, 0);
pFile = fopen(szFilename, "a");
char c = '0';
fwrite(&c, 1, iSize, pFile);
bOK = ftell(pFile) == iSize;
}
fclose(pFile);
}
#endif
return bOK;
}
//replace bad chars in filename
void Util::MakeValidFilename(char* szFilename, char cReplaceChar)
{
char* p = szFilename;
while (*p)
{
if (strchr("\\/:*?\"><'\n\r\t", *p))
{
*p = cReplaceChar;
}
p++;
}
// remove trailing dots and spaces. they are not allowed in directory names on windows,
// but we remove them on posix also, in a case the directory is accessed from windows via samba.
for (int iLen = strlen(szFilename); iLen > 0 && (szFilename[iLen - 1] == '.' || szFilename[iLen - 1] == ' '); iLen--)
{
szFilename[iLen - 1] = '\0';
}
}
long long Util::JoinInt64(unsigned long Hi, unsigned long Lo)
{
return (((long long)Hi) << 32) + Lo;
}
void Util::SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo)
{
*Hi = (unsigned long)(Int64 >> 32);
*Lo = (unsigned long)Int64;
}
float Util::Int64ToFloat(long long Int64)
{
unsigned long Hi = (unsigned long)(Int64 >> 32);
unsigned long Lo = (unsigned long)Int64;
return ((unsigned long)(1 << 30)) * 4.0f * Hi + Lo;
}
float Util::EqualTime(_timeval* t1, _timeval* t2)
{
#ifdef WIN32
return t1->time == t2->time && t1->millitm == t2->millitm;
#else
return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
#endif
}
bool Util::EmptyTime(_timeval* t)
{
#ifdef WIN32
return t->time == 0 && t->millitm == 0;
#else
return t->tv_sec == 0 && t->tv_usec == 0;
#endif
}
float Util::DiffTime(_timeval* t1, _timeval* t2)
{
#ifdef WIN32
return ((t1->time - t2->time) + (t1->millitm - t2->millitm) / 1000.0f);
#else
return (float)((t1->tv_sec - t2->tv_sec) + (t1->tv_usec - t2->tv_usec) / 1000000.0);
#endif
}
/* Base64 decryption is taken from
* Article "BASE 64 Decoding and Encoding Class 2003" by Jan Raddatz
* http://www.codeguru.com/cpp/cpp/algorithms/article.php/c5099/
*/
const static char BASE64_DEALPHABET [128] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 9
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 - 19
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 - 29
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30 - 39
0, 0, 0, 62, 0, 0, 0, 63, 52, 53, // 40 - 49
54, 55, 56, 57, 58, 59, 60, 61, 0, 0, // 50 - 59
0, 61, 0, 0, 0, 0, 1, 2, 3, 4, // 60 - 69
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 70 - 79
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 80 - 89
25, 0, 0, 0, 0, 0, 0, 26, 27, 28, // 90 - 99
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100 - 109
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 110 - 119
49, 50, 51, 0, 0, 0, 0, 0 // 120 - 127
};
unsigned int DecodeByteQuartet(char* szInputBuffer, char* szOutputBuffer)
{
unsigned int buffer = 0;
if (szInputBuffer[3] == '=')
{
if (szInputBuffer[2] == '=')
{
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
buffer = buffer << 14;
szOutputBuffer [0] = (char)(buffer >> 24);
return 1;
}
else
{
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[2]]) << 6;
buffer = buffer << 8;
szOutputBuffer [0] = (char)(buffer >> 24);
szOutputBuffer [1] = (char)(buffer >> 16);
return 2;
}
}
else
{
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[2]]) << 6;
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[3]]) << 6;
buffer = buffer << 2;
szOutputBuffer [0] = (char)(buffer >> 24);
szOutputBuffer [1] = (char)(buffer >> 16);
szOutputBuffer [2] = (char)(buffer >> 8);
return 3;
}
return 0;
}
unsigned int Util::DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer)
{
unsigned int InputBufferIndex = 0;
unsigned int OutputBufferIndex = 0;
unsigned int InputBufferLength = iInputBufferLength > 0 ? iInputBufferLength : strlen(szInputBuffer);
char ByteQuartet [4];
while (InputBufferIndex < InputBufferLength)
{
for (int i = 0; i < 4; i++)
{
ByteQuartet [i] = szInputBuffer[InputBufferIndex];
// Ignore all characters except the ones in BASE64_ALPHABET
if (!((ByteQuartet [i] >= 48 && ByteQuartet [i] <= 57) ||
(ByteQuartet [i] >= 65 && ByteQuartet [i] <= 90) ||
(ByteQuartet [i] >= 97 && ByteQuartet [i] <= 122) ||
ByteQuartet [i] == '+' || ByteQuartet [i] == '/' || ByteQuartet [i] == '='))
{
// Invalid character
i--;
}
InputBufferIndex++;
}
OutputBufferIndex += DecodeByteQuartet(ByteQuartet, szOutputBuffer + OutputBufferIndex);
}
// OutputBufferIndex gives us the next position of the next decoded character
// inside our output buffer and thus represents the number of decoded characters
// in our buffer.
return OutputBufferIndex;
}
/* END - Base64
*/
char* Util::XmlEncode(const char* raw)
{
// calculate the required outputstring-size based on number of xml-entities and their sizes
int iReqSize = strlen(raw);
for (const char* p = raw; *p; p++)
{
unsigned char ch = *p;
switch (ch)
{
case '>':
case '<':
iReqSize += 4;
break;
case '&':
iReqSize += 5;
break;
case '\'':
case '\"':
iReqSize += 6;
break;
default:
if (ch >= 0x80)
{
iReqSize += 6;
break;
}
}
}
char* result = (char*)malloc(iReqSize + 1);
// copy string
char* output = result;
for (const char* p = raw; ; p++)
{
unsigned char ch = *p;
switch (ch)
{
case '\0':
goto BreakLoop;
case '<':
strcpy(output, "&lt;");
output += 4;
break;
case '>':
strcpy(output, "&gt;");
output += 4;
break;
case '&':
strcpy(output, "&amp;");
output += 5;
break;
case '\'':
strcpy(output, "&apos;");
output += 6;
break;
case '\"':
strcpy(output, "&quot;");
output += 6;
break;
default:
if (ch >= 0x80)
{
sprintf(output, "&#%i;", ch);
output += 6;
}
else
{
*output++ = ch;
}
break;
}
}
BreakLoop:
*output = '\0';
return result;
}
void Util::XmlDecode(char* raw)
{
char* output = raw;
for (char* p = raw;;)
{
switch (*p)
{
case '\0':
goto BreakLoop;
case '&':
{
p++;
if (!strncmp(p, "lt;", 3))
{
*output++ = '<';
p += 3;
}
else if (!strncmp(p, "gt;", 3))
{
*output++ = '>';
p += 3;
}
else if (!strncmp(p, "amp;", 4))
{
*output++ = '&';
p += 4;
}
else if (!strncmp(p, "apos;", 5))
{
*output++ = '\'';
p += 5;
}
else if (!strncmp(p, "quot;", 5))
{
*output++ = '\"';
p += 5;
}
else
{
// unknown entity
*output++ = *(p-1);
p++;
}
break;
}
default:
*output++ = *p++;
break;
}
}
BreakLoop:
*output = '\0';
}
const char* Util::XmlFindTag(const char* szXml, const char* szTag, int* pValueLength)
{
char szOpenTag[100];
snprintf(szOpenTag, 100, "<%s>", szTag);
szOpenTag[100-1] = '\0';
char szCloseTag[100];
snprintf(szCloseTag, 100, "</%s>", szTag);
szCloseTag[100-1] = '\0';
const char* pstart = strstr(szXml, szOpenTag);
if (!pstart) return NULL;
const char* pend = strstr(pstart, szCloseTag);
if (!pend) return NULL;
int iTagLen = strlen(szOpenTag);
*pValueLength = (int)(pend - pstart - iTagLen);
return pstart + iTagLen;
}
bool Util::XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd)
{
int iValueLen = 0;
const char* szValue = XmlFindTag(szXml, szTag, &iValueLen);
if (!szValue)
{
return false;
}
int iLen = iValueLen < iValueBufSize ? iValueLen : iValueBufSize - 1;
strncpy(szValueBuf, szValue, iLen);
szValueBuf[iLen] = '\0';
if (pTagEnd)
{
*pTagEnd = szValue + iValueLen;
}
return true;
}
bool Util::MoveFile(const char* szSrcFilename, const char* szDstFilename)
{
bool bOK = rename(szSrcFilename, szDstFilename) == 0;
#ifndef WIN32
if (!bOK && (errno == EXDEV))
{
FILE* infile = fopen(szSrcFilename, "r");
if (!infile)
{
return false;
}
FILE* outfile = fopen(szDstFilename, "w+");
if (!outfile)
{
fclose(infile);
return false;
}
static const int BUFFER_SIZE = 1024 * 50;
char* buffer = (char*)malloc(BUFFER_SIZE);
int cnt = BUFFER_SIZE;
while (cnt == BUFFER_SIZE)
{
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
fwrite(buffer, 1, cnt, outfile);
}
fclose(infile);
fclose(outfile);
free(buffer);
bOK = remove(szSrcFilename) == 0;
}
#endif
return bOK;
}
bool Util::FileExists(const char* szFilename)
{
struct stat buffer;
bool bExists = !stat(szFilename, &buffer) && S_ISREG(buffer.st_mode);
return bExists;
}
bool Util::DirectoryExists(const char* szDirFilename)
{
struct stat buffer;
bool bExists = !stat(szDirFilename, &buffer) && S_ISDIR(buffer.st_mode);
return bExists;
}
bool Util::CreateDirectory(const char* szDirFilename)
{
mkdir(szDirFilename, S_DIRMODE);
return DirectoryExists(szDirFilename);
}
long long Util::FileSize(const char* szFilename)
{
#ifdef WIN32
struct _stat32i64 buffer;
_stat32i64(szFilename, &buffer);
#else
#ifdef HAVE_STAT64
struct stat64 buffer;
stat64(szFilename, &buffer);
#else
struct stat buffer;
stat(szFilename, &buffer);
#endif
#endif
return buffer.st_size;
}
char* Util::JsonEncode(const char* raw)
{
// calculate the required outputstring-size based on number of escape-entities and their sizes
int iReqSize = strlen(raw);
for (const char* p = raw; *p; p++)
{
unsigned char ch = *p;
switch (ch)
{
case '\"':
case '\\':
case '/':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
iReqSize += 1;
default:
if (ch >= 0x80)
{
iReqSize += 6;
break;
}
}
}
char* result = (char*)malloc(iReqSize + 1);
// copy string
char* output = result;
for (const char* p = raw; ; p++)
{
unsigned char ch = *p;
switch (ch)
{
case '\0':
goto BreakLoop;
case '"':
strcpy(output, "\\\"");
output += 2;
break;
case '\\':
strcpy(output, "\\\\");
output += 2;
break;
case '/':
strcpy(output, "\\/");
output += 2;
break;
case '\b':
strcpy(output, "\\b");
output += 2;
break;
case '\f':
strcpy(output, "\\f");
output += 2;
break;
case '\n':
strcpy(output, "\\n");
output += 2;
break;
case '\r':
strcpy(output, "\\r");
output += 2;
break;
case '\t':
strcpy(output, "\\t");
output += 2;
break;
default:
if (ch >= 0x80)
{
sprintf(output, "\\u%04x", ch);
output += 6;
}
else
{
*output++ = ch;
}
break;
}
}
BreakLoop:
*output = '\0';
return result;
}
void Util::JsonDecode(char* raw)
{
char* output = raw;
for (char* p = raw;;)
{
switch (*p)
{
case '\0':
goto BreakLoop;
case '\\':
{
p++;
switch (*p)
{
case '"':
*output++ = '"';
break;
case '\\':
*output++ = '\\';
break;
case '/':
*output++ = '/';
break;
case 'b':
*output++ = '\b';
break;
case 'f':
*output++ = '\f';
break;
case 'n':
*output++ = '\n';
break;
case 'r':
*output++ = '\r';
break;
case 't':
*output++ = '\t';
break;
case 'u':
*output++ = (char)strtol(p + 1, NULL, 16);
p += 4;
break;
default:
// unknown escape-sequence, should never occur
*output++ = *p;
break;
}
p++;
}
default:
*output++ = *p++;
break;
}
}
BreakLoop:
*output = '\0';
}
const char* Util::JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength)
{
char szOpenTag[100];
snprintf(szOpenTag, 100, "\"%s\"", szFieldName);
szOpenTag[100-1] = '\0';
const char* pstart = strstr(szJsonText, szOpenTag);
if (!pstart) return NULL;
pstart += strlen(szOpenTag);
return JsonNextValue(pstart, pValueLength);
}
const char* Util::JsonNextValue(const char* szJsonText, int* pValueLength)
{
const char* pstart = szJsonText;
while (*pstart && strchr(" ,[{:\r\n\t\f", *pstart)) pstart++;
if (!*pstart) return NULL;
const char* pend = pstart;
char ch = *pend;
bool bStr = ch == '"';
if (bStr)
{
ch = *++pend;
}
while (ch)
{
if (ch == '\\')
{
if (!*++pend || !*++pend) return NULL;
ch = *pend;
}
if (bStr && ch == '"')
{
pend++;
break;
}
else if (!bStr && strchr(" ,]}\r\n\t\f", ch))
{
break;
}
ch = *++pend;
}
*pValueLength = (int)(pend - pstart);
return pstart;
}
long long Util::FreeDiskSize(const char* szPath)
{
#ifdef WIN32
ULARGE_INTEGER lFree, lDummy;
if (GetDiskFreeSpaceEx(szPath, &lFree, &lDummy, &lDummy))
{
return lFree.QuadPart;
}
#else
struct statvfs diskdata;
if (!statvfs(szPath, &diskdata))
{
return (long long)diskdata.f_bsize * (long long)diskdata.f_bavail;
}
#endif
return -1;
}

84
Util.h
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -29,6 +29,7 @@
#ifdef WIN32
#include <stdio.h>
#include <io.h>
#include <sys/timeb.h>
#else
#include <dirent.h>
#endif
@@ -57,8 +58,85 @@ public:
const char* Next();
};
char* BaseFileName(const char* filename);
class Util
{
public:
void NormalizePathSeparators(char* Path);
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SetFileSize(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar);
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 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

1444
XmlRpc.cpp Normal file
View File

File diff suppressed because it is too large Load Diff

215
XmlRpc.h Normal file
View File

@@ -0,0 +1,215 @@
/*
* 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 <vector>
#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();
};
#endif

174
aclocal.m4 vendored
View File

@@ -1,7 +1,7 @@
# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
# generated automatically by aclocal 1.10 -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005 Free Software Foundation, Inc.
# 2005, 2006 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -11,7 +11,12 @@
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
m4_if(m4_PACKAGE_VERSION, [2.61],,
[m4_fatal([this file was generated for autoconf 2.61.
You have another version of autoconf. If you want to use that,
you should regenerate the build system entirely.], [63])])
# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -21,14 +26,29 @@
# ----------------------------
# Automake X.Y traces this macro to ensure aclocal.m4 has been
# generated from the m4 files accompanying Automake X.Y.
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
[am__api_version='1.10'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
m4_if([$1], [1.10], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
# _AM_AUTOCONF_VERSION(VERSION)
# -----------------------------
# aclocal traces this macro to find the Autoconf version.
# This is a private macro too. Using m4_define simplifies
# the logic in aclocal, which can simply ignore this definition.
m4_define([_AM_AUTOCONF_VERSION], [])
# AM_SET_CURRENT_AUTOMAKE_VERSION
# -------------------------------
# Call AM_AUTOMAKE_VERSION so it can be traced.
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
[AM_AUTOMAKE_VERSION([1.9.5])])
[AM_AUTOMAKE_VERSION([1.10])dnl
_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
@@ -85,14 +105,14 @@ am_aux_dir=`cd $ac_aux_dir && pwd`
# AM_CONDITIONAL -*- Autoconf -*-
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 7
# serial 8
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
@@ -101,8 +121,10 @@ AC_DEFUN([AM_CONDITIONAL],
[AC_PREREQ(2.52)dnl
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])
AC_SUBST([$1_FALSE])
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
if $2; then
$1_TRUE=
$1_FALSE='#'
@@ -116,15 +138,14 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 8
# serial 9
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
@@ -152,6 +173,7 @@ AC_REQUIRE([AM_DEP_TRACK])dnl
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
[$1], CXX, [depcc="$CXX" am_compiler_list=],
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
[$1], UPC, [depcc="$UPC" am_compiler_list=],
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
[depcc="$$1" am_compiler_list=])
@@ -217,6 +239,7 @@ AC_CACHE_CHECK([dependency style of $depcc],
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
>/dev/null 2>conftest.err &&
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
@@ -269,7 +292,8 @@ if test "x$enable_dependency_tracking" != xno; then
AMDEPBACKSLASH='\'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
@@ -294,8 +318,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
# So let's grep whole file.
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
# Grep'ing the whole file is not good either: AIX grep has a line
# limit of 2048, but all sed's we know have understand at least 4000.
if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then
dirpart=`AS_DIRNAME("$mf")`
else
continue
@@ -342,8 +367,8 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
# Free Software Foundation, Inc.
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
# 2005, 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -366,16 +391,20 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
[AC_PREREQ([2.58])dnl
[AC_PREREQ([2.60])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
AC_REQUIRE([AC_PROG_INSTALL])dnl
# test to see if srcdir already configured
if test "`cd $srcdir && pwd`" != "`pwd`" &&
test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
fi
fi
# test whether we have cygpath
@@ -395,6 +424,9 @@ m4_ifval([$2],
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
@@ -430,6 +462,10 @@ AC_PROVIDE_IFELSE([AC_PROG_CXX],
[_AM_DEPENDENCIES(CXX)],
[define([AC_PROG_CXX],
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
[_AM_DEPENDENCIES(OBJC)],
[define([AC_PROG_OBJC],
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
])
])
@@ -465,7 +501,7 @@ echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
install_sh=${install_sh-"$am_aux_dir/install-sh"}
install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
AC_SUBST(install_sh)])
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
@@ -543,14 +579,14 @@ rm -f confinc confmf
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
# Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# serial 4
# serial 5
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
@@ -566,6 +602,7 @@ AC_SUBST($1)])
# If it does, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
# Use eval to expand $SHELL
if eval "$MISSING --run true"; then
@@ -576,7 +613,7 @@ else
fi
])
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -584,60 +621,23 @@ fi
# AM_PROG_MKDIR_P
# ---------------
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
#
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
# created by `make install' are always world readable, even if the
# installer happens to have an overly restrictive umask (e.g. 077).
# This was a mistake. There are at least two reasons why we must not
# use `-m 0755':
# - it causes special bits like SGID to be ignored,
# - it may be too restrictive (some setups expect 775 directories).
#
# Do not use -m 0755 and let people choose whatever they expect by
# setting umask.
#
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
# Some implementations (such as Solaris 8's) are not thread-safe: if a
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
# concurrently, both version can detect that a/ is missing, but only
# one can create it and the other will error out. Consequently we
# restrict ourselves to GNU make (using the --version option ensures
# this.)
# Check for `mkdir -p'.
AC_DEFUN([AM_PROG_MKDIR_P],
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
# We used to keeping the `.' as first argument, in order to
# allow $(mkdir_p) to be used without argument. As in
# $(mkdir_p) $(somedir)
# where $(somedir) is conditionally defined. However this is wrong
# for two reasons:
# 1. if the package is installed by a user who cannot write `.'
# make install will fail,
# 2. the above comment should most certainly read
# $(mkdir_p) $(DESTDIR)$(somedir)
# so it does not work when $(somedir) is undefined and
# $(DESTDIR) is not.
# To support the latter case, we have to write
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
# so the `.' trick is pointless.
mkdir_p='mkdir -p --'
else
# On NextStep and OpenStep, the `mkdir' command does not
# recognize any option. It will interpret all options as
# directories to create, and then abort because `.' already
# exists.
for d in ./-p ./--version;
do
test -d $d && rmdir $d
done
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
if test -f "$ac_aux_dir/mkinstalldirs"; then
mkdir_p='$(mkinstalldirs)'
else
mkdir_p='$(install_sh) -d'
fi
fi
AC_SUBST([mkdir_p])])
[AC_PREREQ([2.60])dnl
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
dnl while keeping a definition of mkdir_p for backward compatibility.
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
dnl Makefile.ins that do not define MKDIR_P, so we do our own
dnl adjustment using top_builddir (which is defined more often than
dnl MKDIR_P).
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
case $mkdir_p in
[[\\/$]]* | ?:[[\\/]]*) ;;
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
esac
])
# Helper functions for option handling. -*- Autoconf -*-
@@ -749,9 +749,21 @@ dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
# Copyright (C) 2006 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in.
# This macro is traced by Automake.
AC_DEFUN([_AM_SUBST_NOTMAKE])
# Check how to create a tarball. -*- Autoconf -*-
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.

View File

@@ -9,13 +9,6 @@
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* Define to 1 to show progress during par-check (it must be disabled if
sigc++ doesn't work correctly) */
#undef ENABLE_PARPROGRESS
/* Define to 1 to include support for uulib */
#undef ENABLE_UULIB
/* Define to the name of macro which returns the name of function being
compiled */
#undef FUNCTION_MACRO_NAME
@@ -50,8 +43,8 @@
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
#undef HAVE_NCURSES_NCURSES_H
/* Define to 1 to use pragma pack directive in MessageBase.h */
#undef HAVE_PRAGMA_PACK
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H

7846
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,8 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 0.3.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.3.0)
AC_INIT(nzbget, 0.4.1, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.4.1)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -13,12 +13,9 @@ AC_CANONICAL_HOST
case "$host" in
*86-*-linux*)
LIBPREF1="/usr"
CFLAGS1="${CFLAGS} -m486"
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
;;
*-linux*)
LIBPREF1="/usr"
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
;;
*-freebsd*)
LIBPREF1="/usr/local"
@@ -32,12 +29,6 @@ esac
if test "$LIBPREF" = ""; then
LIBPREF="$LIBPREF1"
fi
if test "$CFLAGS" = ""; then
CFLAGS="$CFLAGS1"
fi
if test "$CPPFLAGS" = ""; then
CPPFLAGS="$CPPFLAGS1"
fi
dnl
@@ -87,6 +78,14 @@ AC_CHECK_FUNC(getopt_long,
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
dnl
dnl stat64
dnl
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],
[AC_LIBOBJ(stat64)])
dnl
dnl check ctime_r
dnl
@@ -166,17 +165,6 @@ if test "$HAVE_FUNCTION_MACRO" != "yes"; then
fi
dnl
dnl check for pragma pack
dnl
AC_MSG_CHECKING(for pragma pack)
AC_TRY_COMPILE([#pragma pack(1)
#pragma pack()],,
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_PRAGMA_PACK],1,[Define to 1 to use pragma pack directive in MessageBase.h]),
AC_MSG_RESULT([no]))
dnl
dnl checks for libxml2 includes and libraries.
dnl
@@ -245,42 +233,6 @@ else
fi
dnl
dnl Use uulib. Deafult: no
dnl
AC_MSG_CHECKING(whether to use uulib for decoding and joining)
AC_ARG_ENABLE(uulib,
[ --enable-uulib use uulib for decoding and joining],
[ ENABLEUULIB=$enableval ],
[ ENABLEUULIB=no] )
AC_MSG_RESULT($ENABLEUULIB)
if test "$ENABLEUULIB" = "yes"; then
AC_DEFINE([ENABLE_UULIB],1,[Define to 1 to include support for uulib])
dnl
dnl checks for uulib includes and libraries.
dnl
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(uulib_includes,
[ --with-uulib-includes=DIR uulib include directory],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_CHECK_HEADER($INCVAL/uudeview.h,,
AC_MSG_ERROR("uulib header files were not found in $INCVAL."))
AC_ARG_WITH(uulib_libraries,
[ --with-uulib-libraries=DIR uulib library directory],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_SEARCH_LIBS([UUInitialize], [uu],,
AC_MSG_ERROR("uulib library not found in $LIBVAL."))
fi
dnl
dnl Use lib2par for par-checking. Deafult: no
dnl
@@ -342,20 +294,6 @@ if test "$ENABLEPARCHECK" = "yes"; then
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
dnl fi
dnl
dnl Enable par-check-progress (via sigc++)? Deafult: yes
dnl It doesn't work on WL500gP (uClibc), so we need a way to disable it
dnl
AC_MSG_CHECKING(whether to show progress during par-check )
AC_ARG_ENABLE(parprogress,
[ --disable-parprogress do not show progress during par-check (it must be disabled if sigc++ doesn't work correctly)],
[ USEPARPROGRESS=$enableval ],
[ USEPARPROGRESS=yes] )
AC_MSG_RESULT($USEPARPROGRESS)
if test "$USEPARPROGRESS" = "yes"; then
AC_DEFINE([ENABLE_PARPROGRESS],1,[Define to 1 to show progress during par-check (it must be disabled if sigc++ doesn't work correctly)])
fi
else
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
fi

View File

@@ -423,3 +423,32 @@ diff -urN libpar2-0.2-original/par2cmdline.h libpar2-0.2-modified/par2cmdline.h
#else // WIN32
#ifdef HAVE_CONFIG_H
diff -urN libpar2-0.2-original/par2repairer.cpp libpar2-0.2-modified/par2repairer.cpp
--- libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
+++ libpar2-0.2-modified/par2repairer.cpp 2008-02-13 15:37:59.899314300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
delete creatorpacket;
+ delete headers;
}
@@ -1261,7 +1262,7 @@
DiskFile::SplitFilename(filename, path, name);
cout << "Target: \"" << name << "\" - missing." << endl;
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name, 0, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
}
}
@@ -1804,7 +1805,7 @@
}
}
}
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name,count, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
sig_progress.emit(1000.0);
return true;
}

View File

@@ -1,16 +1,23 @@
# Sample configuration file for nzbget
# Put this file to one of the following locations:
#
# On POSIX put this file to one of the following locations:
# ~/.nzbget
# /etc/nzbget.conf
# /usr/etc/nzbget.conf
# /usr/local/etc/nzbget.conf
# /opt/etc/nzbget.conf
#
# On Windows put this file in program's directory.
#
# You can also put the file into any location, if you specify the path to it
# using switch "-c", e.g:
# nzbget -c /home/user/myconig.txt
# For quick start change the option MAINDIR and configure one news-server
# See section NEWS-SERVERS.
# PROGRAM OPTIONS
##############################################################################
### PATHS ###
# Root directory for all related tasks
# MAINDIR is a variable and therefore starts with "$"
@@ -19,162 +26,26 @@
$MAINDIR=~/download
# Destination-directory to store the downloaded files
destdir=${MAINDIR}/dst
# Directory to store download queue
queuedir=${MAINDIR}/queue
DestDir=${MAINDIR}/dst
# Directory to monitor for incoming nzb-jobs
nzbdir=${MAINDIR}/nzb
NzbDir=${MAINDIR}/nzb
# Directory to store download queue
QueueDir=${MAINDIR}/queue
# Directory to store temporary files
tempdir=${MAINDIR}/tmp
TempDir=${MAINDIR}/tmp
# Lock-file for daemon-mode, contains process-id (PID)
lockfile=/tmp/nzbget.lock
# Lock-file for daemon-mode, contains process-id (PID) (POSIX only)
LockFile=/tmp/nzbget.lock
# Where to store log file, if it needs to be created (see "createlog")
logfile=${destdir}/nzbget.log
# Save download queue to disk. This allows to reload it on next start (yes, no)
savequeue=yes
# Reload download queue on start, if it exists (yes, no)
reloadqueue=yes
# Create log file (yes, no)
createlog=yes
# Delete log file upon server start (only in server-mode) (yes, no)
resetlog=no
# How various messages must be printed (screen, log, both, none)
# Debug-messages can be only printed if the programm was compiled in
# debug-mode: "./configure --enable-debug"
infotarget=both
warningtarget=both
errortarget=both
debugtarget=both
# Create subdirectory with nzb-filename in destination-directory (yes, no)
appendnzbdir=yes
# Set screen-outputmode (loggable, colored, curses)
outputmode=colored
# Check if the destination file already exists (yes, no)
# If the file exists it will not be added to queue.
# If "no" the file will be downloaded and renamed to "filename_duplicate1",
# no existing files are deleted or overwritten.
dupecheck=no
# Set the maximum download rate in KB/s, "0" means no speed control
downloadrate=0
# Visibly rename broken files on download appending "_broken" (yes, no)
renamebroken=no
# Create a log of all broken files (yes ,no)
# It is a text file placed near downloaded files, which contains
# the names of broken files
createbrokenlog=yes
# Set the IP on which the server listen and which client uses to contact the server
# It could be ip-address oder dns-hostname
serverip=127.0.0.1
# Set the port which the server & client use
serverport=6789
# Set the password needed to succesfully queue a request
serverpassword=tegbzn6789
# Determine how the articles should be decoded (uulib, yenc, none)
# uulib - use uulib to decode files. Supports many encoding formats, but is slow.
# yenc - use internal yEnc-Decoder. Supports only yEnc-format and is very fast.
# none - the articles will not be decoded and joined. External programs
# ("uudeview" is one of them) could be used to decode an join downloaded articles.
decoder=yEnc
# How much retries should be attempted if a download error occurs
retries=4
# Set the interval between retries, in seconds
retryinterval=10
# Set connection timeout, in seconds
connectiontimeout=60
# Timeout until a download-thread is killed (helps on hanging downloads), in seconds
terminatetimeout=600
# How many par2-files to load (none, all, one)
# none - all added par2-files must be automatically paused
# all - all added par2-files must be downloaded
# one - only one main par2-file must be dowloaded and other must be paused
# Paused files remain in queue and should be deleted manually,
# when they not needed anymore
loadpars=all
# Automatic par-verification (yes, no)
# To download only needed par2-files (smart par-files loading) set also
# the option "loadpars" to "one". If option "loadpars" is set to "all",
# all par2-files will be downloaded before verification and repair starts.
# The option "renamebroken" must be set to "no", otherwise the par-checker
# may not find renamed files and failed.
parcheck=no
# Automatic par-repair (yes, no)
# If option "parcheck" is enabled and "parrepair" is not, the program
# only verify downloaded files and downloads needed par2-files, but do
# not start repair-process. This is useful if the server does not have
# enough CPU power, since repairing of large files may take too much
# resources and time on a slow computers.
# This option has effect only if the option "parcheck" is enabled.
parrepair=yes
# Use only par2-files with matching names (yes, no)
# If par-check needs extra par-blocks it searches a par2-files
# in download queue, which can be unpauseed and used for restore. These par2-files
# should have the same base name as a main par2-file, currently loaded in par-checker.
# Sometimes extra par files (especially if they were uploaded not from original poster)
# have not matching names. Normally par-checker does not use these files, but
# you can allow it to use these file by setting "strictparname" to "no".
# This has however a side effect: if NZB-file contains more than one collection
# of files (with different par-sets), par-checker may download par-files from
# a wrong collection. This increases you traffic (but not harm par-check).
# NOTE: par-checker always uses only par-files added from the same NZB-file.
# Option "strictparname" does not change this behavior.
strictparname=yes
# Set path to program, that must be executed after the download of
# nzb-file or one collection in nzb-file (if par-check enabled)
# is completed and possibly par-checked/repaired.
# Six arguments are being passed to this program:
# - path to destination dir, where downloaded files are located;
# - name of nzb-file processed;
# - name of par-file processed (if par-checked) or empty string (if not);
# - result of par-check:
# 0 - not checked: par-check disabled or nzb-file does not contain any par-files;
# 1 - checked and sucessfully repaired;
# 2 - checked and failed to repair;
# - state of nzb-job:
# 0 - there are more collection in this nzb-file queued;
# 1 - this was the last collection in nzb-file;
# NOTE: if par-check is enabled and nzb-file contains more than one collection
# of files the postprocess-program is called after each collection is completed
# and par-checked. If you want to clean up the directory (delete par-files, etc.)
# there are two possibilities, when you can do this:
# 1) you parse the "name of par-file processed" to find out the base name
# of collection and clean up only files from this collection;
# 2) or you just check the parameter "state of nzb-job" and do clean up,
# only if it is equal to "1" (which means, that this was the last
# collection in nzb-file and all files are now completed);
# NOTE: do not forget to uncomment next line.
#postprocess=~/myscript.sh
# Where to store log file, if it needs to be created (see "CreateLog")
LogFile=${destdir}/nzbget.log
# NEWS-SERVERS
##############################################################################
### NEWS-SERVERS ###
# This section defines which servers nzbget should connect to.
# The servers will be ordered by their level, i.e. nzbget will at
@@ -182,8 +53,9 @@ strictparname=yes
# If that server fails, nzbget proceeds with the level-1-server, etc.
# A good idea is surely to put your major download-server at level 0
# and your fill-servers at levels 1,2,...
# NOTE 1: Do not leave out a level in your server-list and start with level 0!
# NOTE 2: Several servers with the same level may be used.
# NOTE: Do not leave out a level in your server-list and start with level 0!
# NOTE: Several servers with the same level may be used, they will have
# the same priority.
# First server, on level 0
# Level of newsserver
@@ -214,3 +86,408 @@ server1.connections=4
#server3.username=me2
#server3.password=mypass2
#server3.connections=1
##############################################################################
### PERMISSIONS (POSIX ONLY) ###
# User name for daemon-mode (POSIX in daemon-mode only).
# Set the user that the daemon normally runs at.
# Set $MAINDIR with an absolute path to be sure where it will write.
# This allows nzbget daemon to be launched in rc.local (at boot), and
# download items as a specific user id.
# NOTE: This option has effect only if the program was started from
# root-account, otherwise it is ignored and the daemon runs under
# current user id
DaemonUserName=root
# Specify default umask (affects file permissions) for newly created
# files (POSIX only).
# The value should be written in octal form (the same as for "umask" shell
# command). If umask not specified (or a value greater than 0777 used, useful
# to disable current config-setting via command-line parameter) the umask-mode
# will not be set and current umask-mode (set via shell) will be used
# NOTE: do not forget to uncomment the next line
#UMask=022
##############################################################################
### DOWNLOAD QUEUE ###
# Save download queue to disk. This allows to reload it on next start (yes, no)
SaveQueue=yes
# Reload download queue on start, if it exists (yes, no)
ReloadQueue=yes
# Reuse articles saved in temp-directory from previous program start (yes, no)
# This allows to continue download of file, if program was exited before
# the file was completed.
ContinuePartial=yes
# Create subdirectory with nzb-filename in destination-directory (yes, no)
AppendNzbDir=yes
# How often incoming-directory (option "NzbDir") must be checked for new
# nzb-files, in seconds.
# Value "0" disables the check.
NzbDirInterval=5
# How old nzb-file should at least be for it to be loaded to queue, in seconds.
# Nzbget checks if nzb-file was not modified in last few seconds, defined by
# this option. That safety interval prevents the loading of files, which
# were not yet completely saved to disk, for example if they are still being
# downloaded in web-browser.
NzbDirFileAge=60
# Check for duplicate files (yes, no)
# If this option is enabled the program checks by adding of a new nzb-file:
# 1) if nzb-file contains duplicate entries. This check aims on detecting
# of reposted files (if first file was not fully uploaded);
# If the program find two files with identical names, only the
# biggest of these files will be added to queue;
# 2) if download queue already contains file with the same name;
# 3) if destination file on disk already exists.
# In last two cases: if the file exists it will not be added to queue;
# If this option is disabled, all files are downloaded and duplicate files
# are renamed to "filename_duplicate1".
# Existing files are never deleted or overwritten.
DupeCheck=no
# Visibly rename broken files on download appending "_broken" (yes, no)
# Do not activate this option if par-check is enabled.
RenameBroken=no
# Decode articles (yes, no)
# yes - decode articles using internal decoder (supports yEnc and UU formats).
# no - the articles will not be decoded and joined. External programs
# (like "uudeview") can be used to decode and join downloaded articles.
# Also useful for debugging to look at article's source text.
Decode=yes
# Write decoded articles directly into destination output file (yes, no)
# With this option enabled the program at first creates the output
# destination file with required size (total size of all articles),
# then writes on the fly decoded articles directly to the file
# without creating of any temporary files, even for decoded articles.
# This may results in major performance improvement, but this higly
# depends on OS and filesystem used.
# Can improve performance on a very fast internet connections,
# but you need to test if it works in your case.
# INFO: Tests showed, that on Linux with EXT3-partition activating of
# this option results in up to 20% better performance, but on Windows with NTFS
# or Linux with FAT32-partitions the performance were decreased.
# The possible reason is that on EXT3-partition Linux can create large files
# very fast (if the content of file does not need to be initialized),
# but Windows on NTFS-partition and also Linux on FAT32-partition need to
# initialize created large file with nulls, resulting in a big performace
# degradation.
# NOTE: for testing try to download few big files (with total size 500-1000MB)
# and measure required time. Do not rely on the program's speed indicator.
# NOTE: if both options "DirectWrite" and "ContinuePartial" are enabled,
# the program will create empty articles-files in temp-directrory. They
# are used to continue download of file on a next program start. To minimize
# disk-io it is recommended to disable option "ContinuePartial", if
# "DirectWrite" is enabled. Especially on a fast connections (where you
# would want to activate "DirectWrite") it should not be a problem to
# redownload the interrupted file.
DirectWrite=no
# Check CRC of downloaded and decoded articles (yes, no)
# Normally this option should be enabled for better detecting of download
# errors. However checking of CRC needs about the same CPU time as
# decoding of articles. On a fast connections with slow CPUs disabling of
# CPU-check may slightly improve performance (if CPU is a limiting factor).
CrcCheck=yes
# How much retries should be attempted if a download error occurs
Retries=4
# Set the interval between retries, in seconds
RetryInterval=10
# Redownload article if CRC-check fails (yes, no)
# Helps to minimize number of broken files, but may be effective
# only if you have multiple download servers (even from the same provider
# but from different locations (e.g. europe, usa)).
# In any case the option increases your traffic.
# For slow connections loading of extra par-blocks may be more effective
# The option "CrcCheck" must be enabled for option "RetryOnCrcError" to work.
RetryOnCrcError=no
# Set connection timeout, in seconds
ConnectionTimeout=60
# Timeout until a download-thread is killed, in seconds
# This can help on hanging downloads, but is dangerous.
# Do not use small values!
TerminateTimeout=600
# Set the maximum number of threads program may create.
# Connection errors or fast connection with slow cpu can cause
# the creating of many (thousands) threads, which results in program crash.
# Limiting the number of threads helps in such situations.
# The option affects only download threads, so the number of existing threads
# may be slightly more than set by the option.
ThreadLimit=100
# Set the maximum download rate in KB/s, "0" means no speed control
DownloadRate=0
# Set the size of memory buffer used by writing the articles, in Bytes.
# Bigger values decrease disk-io, but increase memory usage.
# Value "0" causes the OS-dependend default value to be used.
# With value "-1" (which means "max/auto") the program sets the size of
# buffer according to the size of current article (typically less than 500K).
# NOTE: the value must be written in bytes, do not use postfixes "K" or "M".
# NOTE: to calculate the memory usage multiply WriteBufferSize by max number
# of connections, configured in section "NEWS-SERVERS".
# NOTE: typical article's size not exceed 500000 bytes, so using bigger values
# (like several megabytes) will just waste memory.
# NOTE: for desktop computers with large amount of memory value "-1" (max/auto)
# is recommended, but for computers with very low memory (routers, NAS)
# value "0" (default OS-dependend size) could be better alternative.
# NOTE: write-buffer is managed by OS (system libraries) and therefore
# the effect of the option is highly OS-dependend.
WriteBufferSize=0
# Pause if disk space gets below this value, in MegaBytes.
# Value "0" disables the check.
# Only the disk space on the drive with "DestDir" is checked.
# The drive with "TempDir" is not checked.
DiskSpace=250
##############################################################################
### LOGGING ###
# Create log file (yes, no)
CreateLog=yes
# Delete log file upon server start (only in server-mode) (yes, no)
ResetLog=no
# How various messages must be printed (screen, log, both, none)
# Debug-messages can be printed only if the programm was compiled in
# debug-mode: "./configure --enable-debug"
ErrorTarget=both
WarningTarget=both
InfoTarget=both
DetailTarget=both
DebugTarget=both
# Number of messages stored in buffer and available for remote clients
LogBufferSize=1000
# Create a log of all broken files (yes ,no)
# It is a text file placed near downloaded files, which contains
# the names of broken files
CreateBrokenLog=yes
# See also option "logfile" in secion "PATHS"
##############################################################################
### DISPLAY ###
# Set screen-outputmode (loggable, colored, curses)
# loggable - only messages will be printed to standard output;
# colored - prints messages (with simple coloring for messages categories)
# and download progress info; uses escape-sequenses to move cursor;
# curses - advanced interactive iterface with the ability to edit
# download queue and variaous output options;
OutputMode=curses
# Shows NZB-Filename in file list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with Z-key
CursesNzbName=yes
# Show files in groups (NZB-files) in queue list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with G-key
CursesGroup=no
# Show timestamps in message list in curses-outputmode (yes, no)
# This option controls the initial state of curses-frontend,
# it can be switched on/off in run-time with T-key
CursesTime=no
# Update interval for Frontend-output in MSec (min value 25)
# Bigger values reduce CPU usage (especially in curses-outputmode)
# and network traffic in remote-client mode
UpdateInterval=200
##############################################################################
### CLIENT/SERVER COMMUNICATION ###
# Set the IP on which the server listen and which client uses to contact
# the server. It could be dns-hostname or ip-address (more effective since
# does not require dns-lookup).
# If you want the server to listen to all interfaces, use "0.0.0.0"
ServerIp=127.0.0.1
# Set the port which the server & client use
ServerPort=6789
# Set the password needed to succesfully queue a request
ServerPassword=tegbzn6789
# See also option "logbuffersize" in section "LOGGING"
##############################################################################
### PAR CHECK/REPAIR AND POSTPROCESSING ###
# Reload Post-processor-queue on start, if it exists (yes, no)
# For this option to work the options "SaveQueue" and "ReloadQueue" must
# be also enabled.
ReloadPostQueue=yes
# How many par2-files to load (none, all, one)
# none - all added par2-files must be automatically paused
# all - all added par2-files must be downloaded
# one - only one main par2-file must be dowloaded and other must be paused
# Paused files remain in queue and should be deleted manually,
# when they not needed anymore
LoadPars=one
# Automatic par-verification (yes, no)
# To download only needed par2-files (smart par-files loading) set also
# the option "loadpars" to "one". If option "loadpars" is set to "all",
# all par2-files will be downloaded before verification and repair starts.
# The option "renamebroken" must be set to "no", otherwise the par-checker
# may not find renamed files and fail
ParCheck=no
# Automatic par-repair (yes, no)
# If option "parcheck" is enabled and "parrepair" is not, the program
# only verifies downloaded files and downloads needed par2-files, but do
# not start repair-process. This is useful if the server does not have
# enough CPU power, since repairing of large files may take too much
# resources and time on a slow computers.
# This option has effect only if the option "parcheck" is enabled
ParRepair=yes
# Use only par2-files with matching names (yes, no)
# If par-check needs extra par-blocks it searches for par2-files
# in download queue, which can be unpaused and used for restore.
# These par2-files should have the same base name as the main par2-file,
# currently loaded in par-checker. Sometimes extra par files (especially if
# they were uploaded from a different poster) have not matching names.
# Normally par-checker does not use these files, but you can allow it
# to use these files by setting "strictparname" to "no".
# This has however a side effect: if NZB-file contains more than one collection
# of files (with different par-sets), par-checker may download par-files from
# a wrong collection. This increases you traffic (but not harm par-check).
# NOTE: par-checker always uses only par-files added from the same NZB-file
# and the option "strictparname" does not change this behavior
StrictParName=yes
# Cleanup download queue after successful check/repair (yes, no)
# Enable this option for automatic deletion of unneeded (paused) par-files
# from download queue after successful check/repair.
# NOTE: before cleaning up the program checks if all paused files are par-files.
# If there are paused non-par-files (this means that you have paused them
# manually), the cleanup will be skipped for this collection.
ParCleanupQueue=yes
# Set path to program, that must be executed after the download of nzb-file
# or one collection in nzb-file (if par-check enabled and nzb-file contains
# multiple collections; see note below for the definition of "collection")
# is completed and possibly par-checked/repaired.
# Arguments passed to that program:
# 1 - path to destination dir, where downloaded files are located;
# 2 - name of nzb-file processed;
# 3 - name of par-file processed (if par-checked) or empty string (if not);
# 4 - result of par-check:
# 0 - not checked: par-check disabled or nzb-file does not contain any
# par-files;
# 1 - checked and failed to repair;
# 2 - checked and sucessfully repaired;
# 3 - checked and can be repaired but repair is disabled;
# 5 - state of nzb-job:
# 0 - there are more collections in this nzb-file queued;
# 1 - this was the last collection in nzb-file;
# 6 - indication of failed par-jobs for current nzb-file:
# 0 - no failed par-jobs;
# 1 - current par-job or any of the previous par-jobs for the
# same nzb-files failed;
# NOTE: The parameter "state of nzb-job" is very important and MUST be checked
# even in the simplest scripts.
# If par-check is enabled and nzb-file contains more than one collection
# of files the postprocess-program is called after each collection is completed
# and par-checked. If you want to unpack files or clean up the directory
# (delete par-files, etc.) there are two possibilities, when you can do this:
# 1) you parse the "name of par-file processed" to find out the base name
# of collection and clean up only files from this collection (not reliable,
# because par-files sometimes have different names than rar-files);
# 2) or you just check the parameters "state of nzb-job" and "indication of
# failed par-jobs" and do the processing, only if they are set to "1"
# (which means, that this was the last collection in nzb-file and all files
# are now completed) and to "0" (no failed par-jobs) respectively;
# NOTE 2: if the option "ParCheck" is disabled nzbget calls PostProcess
# only once, not after every collection, because the detection of collection
# is disabled in this case;
# NOTE 3: the term "collection" in the above description actually means
# "par-set". To determine what "collections" are present in nzb-file nzbget
# looks for par-sets. If any collection of files within nzb-file does
# not have any par-files, this collection will not be detected.
# For example, for nzb-file containing three collections but only two par-sets,
# the postprocess will be called two times - after processing of each par-set.
# NOTE 4: do not forget to uncomment the next line
#PostProcess=~/myscript.sh
# Allow multiple post-processing for the same nzb-file (yes,no)
# After the post-processing (par-check and call of a postprocess-script) is
# completed, nzbget adds the nzb-file to a list of completed-jobs. The nzb-file
# stays in the list until the last file from that nzb-file is deleted from
# the download queue (it occurs straight away if the par-check was successful
# and the option "ParCleanupQueue" was set).
# So, if there were paused files in queue and they will be unpaused (manually
# or from a post-process-script) nzbget will not post-process nzb-file again.
# This prevents the unwanted multiple post-processing of the same nzb-file.
# But it might be needed if the par-check/-repair are performed not directly
# by nzbget but from a post-process-script.
# NOTE 1: it is recommended to keep the option disabled. You should enable it
# only if it is suggested by a post-process-script's author.
# NOTE 2: by enabling "AllowReProcess" you should disable the option "ParCheck"
# to prevent multiple par-checking.
AllowReProcess=no
# Set the default message-kind for output received from postprocess-script
# (None, Detail, Info, Warning, Error, Debug).
# NZBGet checks if the line written by the script to stdout or stderr starts
# with special character-sequence, determining the message-kind, e.g.:
# [INFO] bla-bla
# [DETAIL] bla-bla
# [WARNING] bla-bla
# [ERROR] bla-bla
# [DEBUG] bla-bla
# If the message-kind was detected the text is added to log with detected type.
# Otherwise the message becomes the default kind, specified in this option.
PostLogKind=Detail
##############################################################################
### PERFORMANCE ###
# On a very fast connection and slow CPU and/or drive the following
# settings may improve performance:
# 1) Disable par-checking and -repairing ("ParCheck=no"). VERY important,
# because par-checking/repairing needs a lot of CPU-power and
# significantly increases disk usage;
# 2) Try to activate option "DirectWrite" ("DirectWrite=yes");
# 3) Disable option "CrcCheck" ("CrcCheck=no");
# 4) Disable option "ContinuePartial" ("ContinuePartial=no");
# 5) Do not limit download rate ("DownloadRate=0"), because the bandwidth
# throttling eats CPU time;
# 6) Disable logging for info- and debug-messages ("InfoTarget=none",
# "DebugTarget=none");
# 7) Run the program in daemon (Posix) or service (Windows) mode and use
# remote client for short periods of time needed for controlling of
# download process on server. Daemon/Service mode eats less CPU
# resources due to not updating of output on screen.
# 8) Increase the value of option "WriteBufferSize" or better set it to
# "-1" (max/auto) if you have spare 5-20 MB of memory.

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
@@ -38,17 +38,16 @@
#include <winsvc.h>
#else
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#ifdef DEBUG
#ifndef DISABLE_PARCHECK
#include <sigc++/sigc++.h>
#endif
#include <iostream>
#endif
#include "nzbget.h"
@@ -63,6 +62,7 @@
#include "RemoteServer.h"
#include "RemoteClient.h"
#include "MessageBase.h"
#include "DiskState.h"
#include "PrePostProcessor.h"
#include "ParChecker.h"
#ifdef WIN32
@@ -77,35 +77,61 @@ void ProcessClientRequest();
void InstallSignalHandlers();
void Daemonize();
#endif
#ifdef DEBUG
void DoTest();
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
ServerPool* g_pServerPool = NULL;
QueueCoordinator* g_pQueueCoordinator = NULL;
RemoteServer* g_pRemoteServer = NULL;
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor;
PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
/*
* Main loop
*/
int main(int argc, char *argv[])
{
#ifdef WIN32
#ifdef _DEBUG
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
#ifdef DEBUG_CRTMEMLEAKS
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
#endif
);
#endif
#endif
#ifdef WIN32
_set_fmode(_O_BINARY);
InstallUninstallServiceCheck(argc, argv);
#endif
#ifndef DISABLE_PARCHECK
DisableCout();
#endif
// Init options & get the name of the .nzb file
g_pLog = new Log();
g_pServerPool = new ServerPool();
debug("Options parsing");
g_pOptions = new Options(argc, argv);
#ifndef WIN32
if (g_pOptions->GetUMask() < 01000)
{
/* set newly created file permissions */
umask(g_pOptions->GetUMask());
}
#endif
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
{
debug("deleting old log-file");
@@ -114,12 +140,13 @@ int main(int argc, char *argv[])
if (g_pOptions->GetDaemonMode())
{
info("nzbget daemon-mode");
#ifdef WIN32
info("nzbget service-mode");
StartService(Run);
return 0;
#else
Daemonize();
info("nzbget daemon-mode");
#endif
}
else if (g_pOptions->GetServerMode())
@@ -132,6 +159,13 @@ int main(int argc, char *argv[])
}
Run();
#ifdef WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}
@@ -153,7 +187,7 @@ void Run()
}
// Create the queue coordinator
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
if (!g_pOptions->GetRemoteClientMode())
{
g_pQueueCoordinator = new QueueCoordinator();
g_pDownloadSpeedMeter = g_pQueueCoordinator;
@@ -166,7 +200,13 @@ void Run()
g_pRemoteServer->Start();
}
// Create the front-end
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
}
// Create the frontend
if (!g_pOptions->GetDaemonMode())
{
switch (g_pOptions->GetOutputMode())
@@ -191,13 +231,9 @@ void Run()
g_pFrontend->Start();
}
// Start QueueCoordinator
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
// Starting QueueCoordinator and PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
g_pPrePostProcessor->Start();
// Standalone-mode
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->AddFileToQueue(g_pOptions->GetArgFilename()))
{
@@ -205,7 +241,13 @@ void Run()
return;
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState = new DiskState();
}
g_pQueueCoordinator->Start();
g_pPrePostProcessor->Start();
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
@@ -249,14 +291,6 @@ void Run()
debug("RemoteServer stopped");
}
#ifdef DEBUG
if (g_pOptions->GetTest())
{
DoTest();
}
#endif
// Stop Frontend
if (g_pFrontend)
{
@@ -302,7 +336,7 @@ void ProcessClientRequest()
else if (g_pOptions->GetClientOperation() == Options::opClientRequestEditQueue)
{
Client->RequestServerEditQueue(g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
g_pOptions->GetEditQueueIDFrom(), g_pOptions->GetEditQueueIDTo());
g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(), true);
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestLog)
{
@@ -316,6 +350,18 @@ void ProcessClientRequest()
{
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddTop());
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestVersion)
{
Client->RequestServerVersion();
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestPostQueue)
{
Client->RequestPostQueue();
}
else if (g_pOptions->GetClientOperation() == Options::opClientRequestWriteLog)
{
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
}
delete Client;
}
@@ -357,23 +403,30 @@ void SignalProc(int iSignal)
{
case SIGINT:
signal(SIGINT, SIG_DFL); // Reset the signal handler
debug("SIGINT received");
ExitProc();
break;
case SIGTERM:
signal(SIGTERM, SIG_DFL); // Reset the signal handler
debug("SIGTERM received");
ExitProc();
break;
#ifdef DEBUG
case SIGPIPE:
debug("SIGPIPE received, ignoring");
// ignoring
break;
case SIGCHLD:
// ignoring
break;
case SIGSEGV:
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
debug("SIGSEGV received");
break;
default:
debug("Signal %i received", iSignal);
// printf("Signal %i received\n", iSignal);
if (SignalProcList[iSignal - 1])
{
SignalProcList[iSignal - 1](iSignal);
@@ -435,6 +488,14 @@ void Cleanup()
}
debug("Frontend deleted");
debug("Deleting DiskState");
if (g_pDiskState)
{
delete g_pDiskState;
g_pDiskState = NULL;
}
debug("DiskState deleted");
debug("Deleting Options");
if (g_pOptions)
{
@@ -480,11 +541,26 @@ void Daemonize()
setsid(); /* obtain a new process group */
for (i = getdtablesize();i >= 0;--i) close(i); /* close all descriptors */
i = open("/dev/null", O_RDWR); dup(i); dup(i); /* handle standart I/O */
umask(027); /* set newly created file permissions */
chdir(g_pOptions->GetDestDir()); /* change running directory */
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
if (lfp < 0) exit(1); /* can not open */
if (lockf(lfp, F_TLOCK, 0) < 0) exit(0); /* can not lock */
/* Drop user if there is one, and we were run as root */
if ( getuid() == 0 || geteuid() == 0 )
{
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
if (pw)
{
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
setgid(pw->pw_gid); /* Set primary group. */
/* Try setting aux groups correctly - not critical if this fails. */
initgroups( g_pOptions->GetDaemonUserName(),pw->pw_gid);
/* Finally, set uid. */
setuid(pw->pw_uid);
}
}
/* first instance continues */
sprintf(str, "%d\n", getpid());
write(lfp, str, strlen(str)); /* record pid to lockfile */
@@ -495,35 +571,18 @@ void Daemonize()
}
#endif
#ifdef DEBUG
#ifndef DISABLE_PARCHECK
/*
class T1 { public:
sigc::signal<int, std::string> sig_filename;
int Test1() { std::string str = "test"; return sig_filename.emit(str); }
};
class T2 { public:
void Test1() {
T1 t1;
t1.sig_filename.connect(sigc::mem_fun(*this, &T2::signal_filename));
if (t1.Test1() == 4) {
printf("ok\n");
//exit(0);
} else {
printf("error\n");
//exit(-1);
}
}
int signal_filename(std::string str) {
printf("%s\n", str.c_str());
return str.length();
}
};
*/
#endif
void DoTest()
class NullStreamBuf : public std::streambuf
{
printf("testing\n");
public:
int sputc ( char c ) { return (int) c; }
} NullStreamBufInstance;
void DisableCout()
{
// libpar2 prints messages to c++ standard output stream (std::cout).
// However we do not want these messages to be printed.
// Since we do not use std::cout in nzbget we just disable it.
std::cout.rdbuf(&NullStreamBufInstance);
}
#endif

View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -31,19 +31,23 @@
// WIN32
#define snprintf _snprintf
#ifndef strdup
#define strdup _strdup
#endif
#define fdopen _fdopen
#define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep)
#define int32_t __int32
#define mkdir(dir, flags) _mkdir(dir)
#define strcasecmp(a, b) _stricmp(a, b)
#define strncasecmp(a, b, c) _strnicmp(a, b, c)
#pragma warning(disable:4800)
#pragma warning(disable:4267)
#pragma warning(disable:4244)
#pragma warning(disable:4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning)
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
#define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
#define S_DIRMODE NULL
#define usleep(usec) Sleep((usec) / 1000)
#define gettimeofday(tm, ignore) _ftime(tm)
@@ -64,6 +68,7 @@
#define PATH_SEPARATOR '/'
#define ALT_PATH_SEPARATOR '\\'
#define MAX_PATH 1024
#define S_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
#endif

View File

@@ -131,7 +131,7 @@
<Tool
Name="VCLinkerTool"
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib"
LinkIncremental="2"
LinkIncremental="0"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
@@ -175,6 +175,14 @@
RelativePath=".\ArticleDownloader.h"
>
</File>
<File
RelativePath=".\BinRpc.cpp"
>
</File>
<File
RelativePath=".\BinRpc.h"
>
</File>
<File
RelativePath=".\ColoredFrontend.cpp"
>
@@ -323,6 +331,14 @@
RelativePath=".\ParChecker.h"
>
</File>
<File
RelativePath=".\PostInfo.cpp"
>
</File>
<File
RelativePath=".\PostInfo.h"
>
</File>
<File
RelativePath=".\PrePostProcessor.cpp"
>
@@ -339,6 +355,14 @@
RelativePath=".\QueueCoordinator.h"
>
</File>
<File
RelativePath=".\QueueEditor.cpp"
>
</File>
<File
RelativePath=".\QueueEditor.h"
>
</File>
<File
RelativePath=".\RemoteClient.cpp"
>
@@ -355,6 +379,14 @@
RelativePath=".\RemoteServer.h"
>
</File>
<File
RelativePath=".\ScriptController.cpp"
>
</File>
<File
RelativePath=".\ScriptController.h"
>
</File>
<File
RelativePath=".\ServerPool.cpp"
>
@@ -383,6 +415,14 @@
RelativePath=".\win32.h"
>
</File>
<File
RelativePath=".\XmlRpc.cpp"
>
</File>
<File
RelativePath=".\XmlRpc.h"
>
</File>
</Files>
<Globals>
</Globals>

21
win32.h
View File

@@ -1,5 +1,5 @@
/*
* This file if part of nzbget
* This file is part of nzbget
*
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
@@ -30,13 +30,6 @@
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* Define to 1 to show progress during par-check (it must be disabled if
sigc++ doesn't work correctly) */
#define ENABLE_PARPROGRESS
/* Define to 1 to include support for uulib */
#undef ENABLE_UULIB
/* Define to the name of macro which returns the name of function being
compiled */
#define FUNCTION_MACRO_NAME __FUNCTION__
@@ -50,13 +43,10 @@
/* Define to 1 if getopt_long is supported */
#undef HAVE_GETOPT_LONG
/* Define to 1 to use pragma pack directive in MessageBase.h */
#define HAVE_PRAGMA_PACK
/* Define to 1 if variadic macros are supported */
#define HAVE_VARIADIC_MACROS
#define VERSION "0.3.0"
#define VERSION "0.4.1"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE
@@ -66,6 +56,13 @@
#define _USE_32BIT_TIME_T
#ifdef _DEBUG
// detection of memory leaks
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#endif
#include <windows.h>
#include <winbase.h>