Compare commits

...

82 Commits

Author SHA1 Message Date
Andrey Prygunkov
08b1b79ac2 version 0.4.0 2008-04-08 17:56:03 +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
63 changed files with 7309 additions and 2494 deletions

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>
@@ -41,6 +41,7 @@
#include <sys/time.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "ArticleDownloader.h"
@@ -54,8 +55,6 @@ extern DownloadSpeedMeter* g_pDownloadSpeedMeter;
extern Options* g_pOptions;
extern ServerPool* g_pServerPool;
const char* ArticleDownloader::m_szJobStatus[] = { "WAITING", "RUNNING", "FINISHED", "FAILED", "DECODING", "JOINING", "NOT_FOUND", "FATAL_ERROR" };
ArticleDownloader::ArticleDownloader()
{
debug("Creating ArticleDownloader");
@@ -68,6 +67,7 @@ ArticleDownloader::ArticleDownloader()
m_pConnection = NULL;
m_eStatus = adUndefined;
m_bDuplicate = false;
m_eFormat = Decoder::efUnknown;
SetLastUpdateTimeNow();
}
@@ -123,19 +123,17 @@ void ArticleDownloader::Run()
if (g_pOptions->GetContinuePartial())
{
struct stat buffer;
bool fileExists = !stat(m_szResultFilename, &buffer);
if (fileExists)
if (Util::FileExists(m_szResultFilename))
{
// file exists from previous program's start
info("Article %s already downloaded, skipping", m_szInfoName);
detail("Article %s already downloaded, skipping", m_szInfoName);
SetStatus(adFinished);
FreeConnection(true);
return;
}
}
info("Downloading %s", m_szInfoName);
detail("Downloading %s", m_szInfoName);
int retry = g_pOptions->GetRetries();
@@ -172,30 +170,37 @@ void ArticleDownloader::Run()
}
// test connection
bool connected = m_pConnection && m_pConnection->Connect() >= 0;
if (connected && !IsStopped())
bool bConnected = m_pConnection && m_pConnection->Connect() >= 0;
if (bConnected && !IsStopped())
{
// Okay, we got a Connection. Now start downloading!!
// Okay, we got a Connection. Now start downloading.
Status = Download();
}
bool bAuthError = m_pConnection && m_pConnection->GetAuthError();
if (connected)
if (bConnected)
{
// freeing connection allows other threads to start.
// we doing this only if the problem was with article or group.
// if the problem occurs by Connect() we do not free the connection,
// to prevent starting of thousands of threads (cause each of them
// will also free it's connection after the same connect-error).
FreeConnection(Status == adFinished);
if (Status == adConnectError)
{
m_pConnection->Disconnect();
bConnected = false;
Status = adFailed;
}
else
{
// freeing connection allows other threads to start.
// we doing this only if the problem was with article or group.
// if the problem occurs by connecting or authorization we do not
// free the connection, to prevent starting of thousands of threads
// (cause each of them will also free it's connection after the
// same connect-error).
FreeConnection(Status == adFinished);
}
}
if ((Status == adFailed || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
((retry > 1) || !connected || bAuthError) && !IsStopped())
if (((Status == adFailed) || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
(retry > 1 || !bConnected) && !IsStopped())
{
info("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
{
@@ -209,7 +214,7 @@ void ArticleDownloader::Run()
Status = adFailed;
break;
}
if ((Status == adFinished) || (Status == adFatalError) ||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
{
@@ -237,7 +242,7 @@ void ArticleDownloader::Run()
}
// do not count connect-errors, only article- and group-errors
if (connected && !bAuthError)
if (bConnected)
{
level++;
if (level > iMaxLevel)
@@ -266,7 +271,7 @@ void ArticleDownloader::Run()
{
if (IsStopped())
{
info("Download %s cancelled", m_szInfoName);
detail("Download %s cancelled", m_szInfoName);
}
else
{
@@ -281,71 +286,62 @@ void ArticleDownloader::Run()
ArticleDownloader::EStatus ArticleDownloader::Download()
{
const char* szResponse = NULL;
EStatus Status = adRunning;
// at first, change group
bool grpchanged = false;
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
{
grpchanged = m_pConnection->JoinGroup(*it);
if (grpchanged)
szResponse = m_pConnection->JoinGroup(*it);
if (szResponse && !strncmp(szResponse, "2", 1))
{
break;
break;
}
}
if (!grpchanged)
Status = CheckResponse(szResponse, "could not join group");
if (Status != adFinished)
{
if (!m_pConnection->GetAuthError() && !IsStopped())
{
warn("Article %s @ %s failed: Could not join group", m_szInfoName, m_pConnection->GetServer()->GetHost());
}
return adFailed;
return Status;
}
// now, let's begin!
// retrieve article
char tmp[1024];
snprintf(tmp, 1024, "ARTICLE %s\r\n", m_pArticleInfo->GetMessageID());
tmp[1024-1] = '\0';
char* answer = NULL;
for (int retry = 3; retry > 0; retry--)
{
answer = m_pConnection->Request(tmp);
if (answer && !strncmp(answer, "2", 1))
szResponse = m_pConnection->Request(tmp);
if (szResponse && !strncmp(szResponse, "2", 1))
{
break;
}
}
if (!answer)
Status = CheckResponse(szResponse, "could not fetch article");
if (Status != adFinished)
{
if (!m_pConnection->GetAuthError() && !IsStopped())
{
warn("Article %s @ %s failed: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost());
}
return adFailed;
}
if (strncmp(answer, "2", 1))
{
warn("Article %s @ %s failed: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), answer);
return (!strncmp(answer, "41", 2) || !strncmp(answer, "42", 2)) ? adNotFound : adFailed;
return Status;
}
// positive answer!
if (g_pOptions->GetDecoder() == Options::dcYenc)
if (g_pOptions->GetDecode())
{
m_YDecoder.Clear();
m_YDecoder.SetAutoSeek(g_pOptions->GetDirectWrite());
m_YDecoder.SetCrcCheck(g_pOptions->GetCrcCheck());
m_UDecoder.Clear();
}
m_pOutFile = NULL;
EStatus Status = adRunning;
bool bBody = false;
bool bEnd = false;
const int LineBufSize = 1024*10;
char* szLineBuf = (char*)malloc(LineBufSize);
Status = adRunning;
while (!IsStopped())
{
@@ -366,14 +362,18 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
// Have we encountered a timeout?
if (!line)
{
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
if (!IsStopped())
{
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
}
Status = adFailed;
break;
}
//detect end of article
if ((!strcmp(line, ".\r\n")) || (!strcmp(line, ".\n")))
if (!strcmp(line, ".\r\n") || !strcmp(line, ".\n"))
{
bEnd = true;
break;
}
@@ -383,13 +383,14 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
line++;
}
// check id of returned article
if (!bBody)
{
if ((!strcmp(line, "\r\n")) || (!strcmp(line, "\n")))
// detect body of article
if (*line == '\r' || *line == '\n')
{
bBody = true;
}
// check id of returned article
else if (!strncmp(line, "Message-ID: ", 12))
{
char* p = line + 12;
@@ -402,8 +403,13 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
}
}
}
else if (m_eFormat == Decoder::efUnknown && g_pOptions->GetDecode())
{
m_eFormat = Decoder::DetectFormat(line, iLen);
}
if (!Write(line, iLen))
// write to output file
if (((bBody && m_eFormat != Decoder::efUnknown) || !g_pOptions->GetDecode()) && !Write(line, iLen))
{
Status = adFatalError;
break;
@@ -417,29 +423,62 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
fclose(m_pOutFile);
}
if (IsStopped())
if (!bEnd && Status == adRunning && !IsStopped())
{
remove(m_szTempFilename);
return adFailed;
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetServer()->GetHost());
Status = adFailed;
}
if (Status == adFailed)
if (IsStopped())
{
remove(m_szTempFilename);
return adFailed;
Status = adFailed;
}
if (Status == adRunning)
{
FreeConnection(true);
return Decode();
return DecodeCheck();
}
else
{
remove(m_szTempFilename);
return Status;
}
}
ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szResponse, const char* szComment)
{
if (!szResponse)
{
if (!IsStopped())
{
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment);
}
return adConnectError;
}
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
{
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
return adConnectError;
}
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2))
{
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
return adNotFound;
}
else if (!strncmp(szResponse, "2", 1))
{
// OK
return adFinished;
}
else
{
// unknown error, no special handling
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
return adFailed;
}
}
bool ArticleDownloader::Write(char* szLine, int iLen)
{
if (!m_pOutFile && !PrepareFile(szLine))
@@ -447,9 +486,20 @@ bool ArticleDownloader::Write(char* szLine, int iLen)
return false;
}
if (g_pOptions->GetDecoder() == Options::dcYenc)
if (g_pOptions->GetDecode())
{
return m_YDecoder.Write(szLine, m_pOutFile);
if (m_eFormat == Decoder::efYenc)
{
return m_YDecoder.Write(szLine, iLen, m_pOutFile);
}
else if (m_eFormat == Decoder::efUx)
{
return m_UDecoder.Write(szLine, iLen, m_pOutFile);
}
else
{
return false;
}
}
else
{
@@ -462,19 +512,19 @@ bool ArticleDownloader::PrepareFile(char* szLine)
bool bOpen = false;
// prepare file for writing
if (g_pOptions->GetDecoder() == Options::dcYenc)
if (m_eFormat == Decoder::efYenc)
{
if (!strncmp(szLine, "=ybegin part=", 13))
if (!strncmp(szLine, "=ybegin ", 8))
{
if (g_pOptions->GetDupeCheck())
{
m_pFileInfo->LockOutputFile();
if (!m_pFileInfo->GetOutputInitialized())
{
char* pb = strstr(szLine, "name=");
char* pb = strstr(szLine, " name=");
if (pb)
{
pb += 5; //=strlen("name=")
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (!m_szArticleFilename)
@@ -499,15 +549,15 @@ bool ArticleDownloader::PrepareFile(char* szLine)
if (g_pOptions->GetDirectWrite())
{
char* pb = strstr(szLine, "size=");
char* pb = strstr(szLine, " size=");
if (pb)
{
m_pFileInfo->LockOutputFile();
if (!m_pFileInfo->GetOutputInitialized())
{
pb += 5; //=strlen("size=")
pb += 6; //=strlen(" size=")
long iArticleFilesize = atol(pb);
if (!SetFileSize(m_szOutputFilename, iArticleFilesize))
if (!Util::SetFileSize(m_szOutputFilename, iArticleFilesize))
{
error("Could not create file %s!", m_szOutputFilename);
return false;
@@ -531,11 +581,12 @@ bool ArticleDownloader::PrepareFile(char* szLine)
if (bOpen)
{
const char* szFilename = g_pOptions->GetDirectWrite() ? m_szOutputFilename : m_szTempFilename;
m_pOutFile = fopen(szFilename, g_pOptions->GetDirectWrite() ? "r+" : "w");
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
const char* szFilename = bDirectWrite ? m_szOutputFilename : m_szTempFilename;
m_pOutFile = fopen(szFilename, bDirectWrite ? "r+" : "w");
if (!m_pOutFile)
{
error("Could not %s file %s", g_pOptions->GetDirectWrite() ? "open" : "create", szFilename);
error("Could not %s file %s", bDirectWrite ? "open" : "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() == -1)
@@ -551,43 +602,38 @@ bool ArticleDownloader::PrepareFile(char* szLine)
return true;
}
ArticleDownloader::EStatus ArticleDownloader::Decode()
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
{
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
(g_pOptions->GetDecoder() == Options::dcYenc))
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
if (g_pOptions->GetDecode())
{
SetStatus(adDecoding);
char tmpdestfile[1024];
char* szDecoderTempFilename = NULL;
Decoder* pDecoder = NULL;
if (g_pOptions->GetDecoder() == Options::dcYenc)
if (m_eFormat == Decoder::efYenc)
{
pDecoder = &m_YDecoder;
szDecoderTempFilename = m_szTempFilename;
}
else if (g_pOptions->GetDecoder() == Options::dcUulib)
else if (m_eFormat == Decoder::efUx)
{
pDecoder = new UULibDecoder();
pDecoder->SetSrcFilename(m_szTempFilename);
snprintf(tmpdestfile, 1024, "%s.dec", m_szResultFilename);
tmpdestfile[1024-1] = '\0';
szDecoderTempFilename = tmpdestfile;
pDecoder->SetDestFilename(szDecoderTempFilename);
pDecoder = &m_UDecoder;
}
else
{
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFatalError;
}
bool bOK = pDecoder->Execute();
Decoder::EStatus eStatus = pDecoder->Check();
bool bOK = eStatus == Decoder::eFinished;
if (!g_pOptions->GetDirectWrite())
if (!bDirectWrite && bOK)
{
if (bOK)
if (!Util::MoveFile(m_szTempFilename, m_szResultFilename))
{
rename(szDecoderTempFilename, m_szResultFilename);
}
else if (g_pOptions->GetDecoder() == Options::dcUulib)
{
remove(szDecoderTempFilename);
error("Could not rename file %s to %s! Errcode: %i", m_szTempFilename, m_szResultFilename, errno);
}
}
@@ -597,17 +643,12 @@ ArticleDownloader::EStatus ArticleDownloader::Decode()
}
remove(m_szTempFilename);
bool bCrcError = pDecoder->GetCrcError();
if (pDecoder != &m_YDecoder)
{
delete pDecoder;
}
if (bOK)
{
info("Successfully downloaded %s", m_szInfoName);
detail("Successfully downloaded %s", m_szInfoName);
if (g_pOptions->GetDirectWrite() && g_pOptions->GetContinuePartial())
if (bDirectWrite && g_pOptions->GetContinuePartial())
{
// create empty flag-file to indicate that the artcile was downloaded
FILE* flagfile = fopen(m_szResultFilename, "w");
@@ -624,11 +665,26 @@ ArticleDownloader::EStatus ArticleDownloader::Decode()
else
{
remove(m_szResultFilename);
if (bCrcError)
if (eStatus == Decoder::eCrcError)
{
warn("Decoding %s failed: CRC-Error", m_szInfoName);
return adCrcError;
}
else if (eStatus == Decoder::eArticleIncomplete)
{
warn("Decoding %s failed: article incomplete", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eInvalidSize)
{
warn("Decoding %s failed: size mismatch", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eNoBinaryData)
{
warn("Decoding %s failed: no binary data found", m_szInfoName);
return adFailed;
}
else
{
warn("Decoding %s failed", m_szInfoName);
@@ -636,19 +692,19 @@ ArticleDownloader::EStatus ArticleDownloader::Decode()
}
}
}
else if (g_pOptions->GetDecoder() == Options::dcNone)
else
{
// rawmode
rename(m_szTempFilename, m_szResultFilename);
info("Article %s successfully downloaded", m_szInfoName);
if (Util::MoveFile(m_szTempFilename, m_szResultFilename))
{
detail("Article %s successfully downloaded", m_szInfoName);
}
else
{
error("Could not move file %s to %s! Errcode: %i", m_szTempFilename, m_szResultFilename, errno);
}
return adFinished;
}
else
{
// should not occur
error("Internal error: Decoding %s failed", m_szInfoName);
return adFatalError;
}
}
void ArticleDownloader::LogDebugInfo()
@@ -660,7 +716,7 @@ void ArticleDownloader::LogDebugInfo()
ctime_r(&m_tLastUpdateTime, szTime);
#endif
debug(" Download: status=%s, LastUpdateTime=%s, filename=%s", GetStatusText(), szTime, BaseFileName(GetTempFilename()));
debug(" Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(GetTempFilename()));
}
void ArticleDownloader::Stop()
@@ -713,40 +769,46 @@ void ArticleDownloader::CompleteFileParts()
SetStatus(adJoining);
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();
char szNZBNiceName[1024];
m_pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
m_pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
char InfoFilename[1024];
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
InfoFilename[1024-1] = '\0';
if (g_pOptions->GetDecoder() == Options::dcNone)
if (!g_pOptions->GetDecode())
{
info("Moving articles for %s", InfoFilename);
detail("Moving articles for %s", InfoFilename);
}
else if (g_pOptions->GetDirectWrite())
else if (bDirectWrite)
{
info("Checking articles for %s", InfoFilename);
detail("Checking articles for %s", InfoFilename);
}
else
{
info("Joining articles for %s", InfoFilename);
detail("Joining articles for %s", InfoFilename);
}
char ofn[1024];
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
ofn[1024-1] = '\0';
// Ensure the DstDir is created
mkdir(m_pFileInfo->GetDestDir(), S_DIRMODE);
if (!Util::CreateDirectory(m_pFileInfo->GetNZBInfo()->GetDestDir()))
{
error("Could not create directory %s! Errcode: %i", m_pFileInfo->GetNZBInfo()->GetDestDir(), errno);
SetStatus(adJoined);
return;
}
// prevent overwriting existing files
struct stat statbuf;
int dupcount = 0;
while (!stat(ofn, &statbuf))
while (Util::FileExists(ofn))
{
dupcount++;
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
ofn[1024-1] = '\0';
}
@@ -755,16 +817,14 @@ void ArticleDownloader::CompleteFileParts()
snprintf(tmpdestfile, 1024, "%s.tmp", ofn);
tmpdestfile[1024-1] = '\0';
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
!g_pOptions->GetDirectWrite())
if (g_pOptions->GetDecode() && !bDirectWrite)
{
remove(tmpdestfile);
outfile = fopen(tmpdestfile, "w+");
if (!outfile)
{
error("Could not create file %s!", tmpdestfile);
SetStatus(adFinished);
SetStatus(adJoined);
return;
}
if (g_pOptions->GetWriteBufferSize() == -1 && (*m_pFileInfo->GetArticles())[0])
@@ -776,10 +836,15 @@ void ArticleDownloader::CompleteFileParts()
setvbuf(outfile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
}
}
else if (g_pOptions->GetDecoder() == Options::dcNone)
else if (!g_pOptions->GetDecode())
{
remove(tmpdestfile);
mkdir(ofn, S_DIRMODE);
if (!Util::CreateDirectory(ofn))
{
error("Could not create directory %s! Errcode: %i", ofn, errno);
SetStatus(adJoined);
return;
}
}
bool complete = true;
@@ -787,9 +852,7 @@ void ArticleDownloader::CompleteFileParts()
static const int BUFFER_SIZE = 1024 * 50;
char* buffer = NULL;
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
!g_pOptions->GetDirectWrite())
if (g_pOptions->GetDecode() && !bDirectWrite)
{
buffer = (char*)malloc(BUFFER_SIZE);
}
@@ -802,9 +865,7 @@ void ArticleDownloader::CompleteFileParts()
iBrokenCount++;
complete = false;
}
else if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
!g_pOptions->GetDirectWrite())
else if (g_pOptions->GetDecode() && !bDirectWrite)
{
FILE* infile;
const char* fn = pa->GetResultFilename();
@@ -827,16 +888,19 @@ void ArticleDownloader::CompleteFileParts()
{
complete = false;
iBrokenCount++;
info("Could not find file %s. Status is broken", fn);
detail("Could not find file %s. Status is broken", fn);
}
}
else if (g_pOptions->GetDecoder() == Options::dcNone)
else if (!g_pOptions->GetDecode())
{
const char* fn = pa->GetResultFilename();
char dstFileName[1024];
snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
dstFileName[1024-1] = '\0';
rename(fn, dstFileName);
if (!Util::MoveFile(fn, dstFileName))
{
error("Could not move file %s to %s! Errcode: %i", fn, dstFileName, errno);
}
}
}
@@ -848,15 +912,21 @@ void ArticleDownloader::CompleteFileParts()
if (outfile)
{
fclose(outfile);
rename(tmpdestfile, ofn);
if (!Util::MoveFile(tmpdestfile, ofn))
{
error("Could not move file %s to %s! Errcode: %i", tmpdestfile, ofn, errno);
}
}
if (g_pOptions->GetDirectWrite())
if (bDirectWrite)
{
rename(m_szOutputFilename, ofn);
if (!Util::MoveFile(m_szOutputFilename, ofn))
{
error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
}
}
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
if (!bDirectWrite || g_pOptions->GetContinuePartial())
{
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
{
@@ -878,10 +948,9 @@ void ArticleDownloader::CompleteFileParts()
char brokenfn[1024];
snprintf(brokenfn, 1024, "%s_broken", ofn);
brokenfn[1024-1] = '\0';
bool OK = rename(ofn, brokenfn) == 0;
if (OK)
if (Util::MoveFile(ofn, brokenfn))
{
info("Renaming broken file from %s to %s", ofn, brokenfn);
detail("Renaming broken file from %s to %s", ofn, brokenfn);
}
else
{
@@ -890,21 +959,19 @@ void ArticleDownloader::CompleteFileParts()
}
else
{
info("Not renaming broken file %s", ofn);
detail("Not renaming broken file %s", ofn);
}
if (g_pOptions->GetCreateBrokenLog())
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR);
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
FILE* file = fopen(szBrokenLogName, "a");
fprintf(file, "%s (%i/%i)\n", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size());
fclose(file);
}
warn("%s is incomplete!", InfoFilename);
}
SetStatus(adFinished);
SetStatus(adJoined);
}

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>
@@ -51,7 +51,9 @@ public:
adCrcError,
adDecoding,
adJoining,
adJoined,
adNotFound,
adConnectError,
adFatalError
};
@@ -67,16 +69,18 @@ private:
char* m_szInfoName;
char* m_szOutputFilename;
time_t m_tLastUpdateTime;
static const char* m_szJobStatus[];
Decoder::EFormat m_eFormat;
YDecoder m_YDecoder;
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
EStatus Decode();
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();

772
BinRpc.cpp Normal file
View File

@@ -0,0 +1,772 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#endif
#include "nzbget.h"
#include "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

View File

@@ -1,3 +1,64 @@
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;
@@ -25,7 +86,7 @@ nzbget-0.3.1:
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) decrease the number of broken files;
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

View File

@@ -41,6 +41,7 @@
#include "nzbget.h"
#include "ColoredFrontend.h"
#include "Util.h"
ColoredFrontend::ColoredFrontend()
{
@@ -77,10 +78,10 @@ void ColoredFrontend::PrintStatus()
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
{
long long remain_sec = m_lRemainingSize / ((long long int)(fCurrentDownloadSpeed * 1024));
int h = remain_sec / 3600;
int m = (remain_sec % 3600) / 60;
int s = remain_sec % 60;
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);
}
@@ -94,14 +95,14 @@ void ColoredFrontend::PrintStatus()
szDownloadLimit[0] = 0;
}
char szParStatus[128];
if (m_iParJobCount > 0)
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szParStatus, ", %i par", m_iParJobCount);
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
szParStatus[0] = 0;
szPostStatus[0] = 0;
}
#ifdef WIN32
@@ -112,8 +113,8 @@ void ColoredFrontend::PrintStatus()
#endif
snprintf(tmp, 1024, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
timeString, szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
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;
@@ -140,6 +141,10 @@ 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());
@@ -162,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

@@ -91,13 +91,28 @@ Connection::Connection(NetAddress* pNetAddress)
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();
}
@@ -156,7 +171,7 @@ int Connection::Bind()
return iRes;
}
int Connection::WriteLine(char* line)
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::write(char* line)");
@@ -165,12 +180,12 @@ int Connection::WriteLine(char* line)
return -1;
}
int iRes = DoWriteLine(line);
int iRes = DoWriteLine(pBuffer);
return iRes;
}
int Connection::Send(char* pBuffer, int iSize)
int Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
@@ -234,6 +249,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)
{
@@ -352,10 +378,10 @@ 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, int* pBytesRead)
@@ -388,7 +414,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = p - szBufPtr + 1;
len = (int)(p - szBufPtr + 1);
}
else
{

View File

@@ -49,29 +49,31 @@ protected:
EStatus m_eStatus;
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(char* text);
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* pBytesRead);
int WriteLine(char* text);
int WriteLine(const char* pBuffer);
SOCKET Accept();
void Cancel();
NetAddress* GetServer() { return m_pNetAddress; }

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,19 +40,11 @@
#endif
#include "nzbget.h"
#ifdef ENABLE_UULIB
#ifndef PROTOTYPES
#define PROTOTYPES
#endif
#include <uudeview.h>
#endif
#include "Decoder.h"
#include "Log.h"
#include "Util.h"
Mutex UULibDecoder::m_mutexDecoder;
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
unsigned int YDecoder::crc_tab[256];
Decoder::Decoder()
@@ -62,7 +54,6 @@ Decoder::Decoder()
m_szSrcFilename = NULL;
m_szDestFilename = NULL;
m_szArticleFilename = NULL;
m_bCrcError = false;
}
Decoder::~ Decoder()
@@ -75,93 +66,51 @@ Decoder::~ Decoder()
}
}
/*
* UULibDecoder
*/
bool UULibDecoder::Execute()
void Decoder::Clear()
{
bool res = false;
#ifndef ENABLE_UULIB
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
#else
m_mutexDecoder.Lock();
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 (m_szArticleFilename)
{
uulist* att_tmp = UUGetFileListItem(i);
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
if (!att_tmp)
{
break;
}
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
{
if (!strncmp(buffer, "=ybegin ", 8))
{
return efYenc;
}
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
{
return efUx;
}
if ((att_tmp) && (att_tmp->haveparts))
if (!strncmp(buffer, "begin ", 6))
{
bool bOK = true;
buffer += 6; //strlen("begin ")
while (*buffer && *buffer != ' ')
{
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();
m_mutexDecoder.Unlock();
#endif // ENABLE_UULIB
return res;
return efUnknown;
}
/**
* YDecoder
* Very primitive (but fast) implementation of yEnc-Decoder
* YDecoder: fast implementation of yEnc-Decoder
*/
void YDecoder::Init()
@@ -182,20 +131,22 @@ YDecoder::YDecoder()
void YDecoder::Clear()
{
Decoder::Clear();
m_bBody = false;
m_bBegin = false;
m_bPart = false;
m_bEnd = false;
m_bCrc = false;
m_lExpectedCRC = 0;
m_lCalculatedCRC = 0xFFFFFFFF;
m_iBegin = 0;
m_iEnd = 0;
m_iSize = 0;
m_iEndSize = 0;
m_bAutoSeek = false;
m_bNeedSetPos = false;
m_bCrcCheck = false;
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
@@ -254,16 +205,23 @@ unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, uns
unsigned int YDecoder::DecodeBuffer(char* buffer)
{
if (m_bBody)
if (m_bBody && !m_bEnd)
{
if (!strncmp(buffer, "=yend size=", 11))
if (!strncmp(buffer, "=yend ", 6))
{
m_bEnd = true;
char* pc = strstr(buffer, "pcrc32=");
if (pc)
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
if (pb)
{
pc += 7; //=strlen("pcrc32=")
m_lExpectedCRC = strtoul(pc, NULL, 16);
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;
}
@@ -295,34 +253,19 @@ BreakLoop:
if (m_bCrcCheck)
{
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, optr - buffer);
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
}
return optr - buffer;
return (unsigned int)(optr - buffer);
}
else
else
{
if (!strncmp(buffer, "=ypart begin=", 13))
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
{
m_bBody = true;
char* pb = strstr(buffer, "begin=");
if (pb)
{
pb += 6; //=strlen("begin=")
m_iBegin = (int)atoi(pb);
}
pb = strstr(buffer, "end=");
if (pb)
{
pb += 4; //=strlen("end=")
m_iEnd = (int)atoi(pb);
}
}
else if (!strncmp(buffer, "=ybegin part=", 13))
{
char* pb = strstr(buffer, "name=");
m_bBegin = true;
char* pb = strstr(buffer, " name=");
if (pb)
{
pb += 5; //=strlen("name=")
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
if (m_szArticleFilename)
@@ -333,13 +276,43 @@ BreakLoop:
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, FILE* outfile)
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
{
unsigned int wcnt = DecodeBuffer(buffer);
if (wcnt > 0)
@@ -361,13 +334,152 @@ bool YDecoder::Write(char* buffer, FILE* outfile)
return true;
}
bool YDecoder::Execute()
Decoder::EStatus YDecoder::Check()
{
m_lCalculatedCRC ^= 0xFFFFFFFF;
debug("Expected pcrc32=%x", m_lExpectedCRC);
debug("Calculated pcrc32=%x", m_lCalculatedCRC);
m_bCrcError = m_bCrcCheck && (m_lExpectedCRC != m_lCalculatedCRC);
debug("Expected crc32=%x", m_lExpectedCRC);
debug("Calculated crc32=%x", m_lCalculatedCRC);
return m_bBody && m_bEnd && !m_bCrcError;
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;
}

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,41 +31,58 @@
class Decoder
{
public:
enum EStatus
{
eUnknownError,
eFinished,
eArticleIncomplete,
eCrcError,
eInvalidSize,
eNoBinaryData
};
enum EFormat
{
efUnknown,
efYenc,
efUx,
};
static const char* FormatNames[];
protected:
const char* m_szSrcFilename;
const char* m_szDestFilename;
char* m_szArticleFilename;
bool m_bCrcError;
public:
Decoder();
virtual ~Decoder();
virtual bool Execute() = 0;
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; }
bool GetCrcError() { return m_bCrcError; }
};
class UULibDecoder: public Decoder
{
private:
static Mutex m_mutexDecoder;
public:
virtual bool Execute();
static EFormat DetectFormat(const char* buffer, int len);
};
class YDecoder: public Decoder
{
protected:
static unsigned int crc_tab[256];
bool m_bBegin;
bool m_bPart;
bool m_bBody;
bool m_bEnd;
bool m_bCrc;
unsigned long m_lExpectedCRC;
unsigned long m_lCalculatedCRC;
unsigned long m_iBegin;
unsigned long m_iEnd;
unsigned long m_iSize;
unsigned long m_iEndSize;
bool m_bAutoSeek;
bool m_bNeedSetPos;
bool m_bCrcCheck;
@@ -76,9 +93,9 @@ protected:
public:
YDecoder();
virtual bool Execute();
void Clear();
bool Write(char* buffer, FILE* outfile);
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; }
@@ -86,4 +103,19 @@ public:
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>
*
@@ -44,15 +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.
* This function saves only file "queue".
* This function saves file "queue" and files with NZB-info. It does not
* save file-infos.
*/
bool DiskState::Save(DownloadQueue* pDownloadQueue)
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,21 +93,45 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue)
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())
{
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
cnt++;
// find index of nzb-info
int iNZBIndex = 0;
for (unsigned int i = 0; i < cNZBList.size(); i++)
{
iNZBIndex++;
if (cNZBList[i] == pFileInfo->GetNZBInfo())
{
break;
}
}
fprintf(outfile, "%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused());
}
}
fclose(outfile);
if (cnt == 0)
if (pDownloadQueue->empty())
{
remove(fileName);
}
@@ -88,18 +139,13 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue)
return true;
}
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::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';
@@ -112,50 +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, true, false);
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::LoadArticles(FileInfo* pFileInfo)
bool DiskState::SaveFile(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);
return SaveFileInfo(pFileInfo, fileName);
}
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
@@ -170,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++)
@@ -196,6 +275,14 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
return true;
}
bool DiskState::LoadArticles(FileInfo* pFileInfo)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
fileName[1024-1] = '\0';
return LoadFileInfo(pFileInfo, fileName, false, true);
}
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
{
debug("Loading FileInfo from disk");
@@ -210,18 +297,10 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool
char buf[1024];
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetNZBFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetSubject(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetDestDir(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (bFileSummary) pFileInfo->SetFilename(buf);
@@ -232,7 +311,7 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool
unsigned long High, Low;
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
if (bFileSummary) pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
if (bFileSummary) pFileInfo->SetSize(Util::JoinInt64(High, Low));
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
int size;
@@ -272,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");
@@ -295,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;
}
@@ -322,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::PostQueueExists(bool bCompleted)
{
debug("Checking if a saved queue exists on disk");
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
fileName[1024-1] = '\0';
return Util::FileExists(fileName);
}
bool DiskState::DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
{
// delete diskstate-file
// 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 !pDownloadQueue || Save(pDownloadQueue);
return !pDownloadQueue || SaveDownloadQueue(pDownloadQueue);
}
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)

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,6 +27,7 @@
#define DISKSTATE_H
#include "DownloadInfo.h"
#include "PostInfo.h"
class DiskState
{
@@ -35,12 +36,16 @@ private:
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
public:
bool Exists();
bool Save(DownloadQueue* pDownloadQueue);
bool Load(DownloadQueue* pDownloadQueue);
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
bool Discard();
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>
@@ -43,6 +43,105 @@
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");
@@ -86,8 +185,6 @@ 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;
@@ -110,14 +207,6 @@ FileInfo::~ FileInfo()
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
@@ -126,6 +215,11 @@ FileInfo::~ FileInfo()
m_Groups.clear();
ClearArticles();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void FileInfo::ClearArticles()
@@ -146,71 +240,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)
{
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)
{
// 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';
::MakeValidFilename(postname, '_');
// if the resulting name is empty, use basename without cleaing 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';
::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';
}
void FileInfo::SetFilename(const char* szFilename)
{
if (m_szFilename)
@@ -222,7 +262,7 @@ void FileInfo::SetFilename(const char* szFilename)
void FileInfo::MakeValidFilename()
{
::MakeValidFilename(m_szFilename, '_');
Util::MakeValidFilename(m_szFilename, '_');
}
void FileInfo::LockOutputFile()
@@ -237,22 +277,87 @@ void FileInfo::UnlockOutputFile()
bool FileInfo::IsDupe(const char* szFilename)
{
struct stat buffer;
char fileName[1024];
snprintf(fileName, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
bool exists = !stat(fileName, &buffer);
if (exists)
if (Util::FileExists(fileName))
{
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
exists = !stat(fileName, &buffer);
if (exists)
if (Util::FileExists(fileName))
{
return true;
}
return false;
}
GroupInfo::GroupInfo()
{
m_iFirstID = 0;
m_iLastID = 0;
m_iRemainingFileCount = 0;
m_lRemainingSize = 0;
m_lPausedSize = 0;
m_iRemainingParCount = 0;
}
GroupInfo::~GroupInfo()
{
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void GroupInfo::BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue)
{
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo* pGroupInfo = NULL;
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
{
GroupInfo* pGroupInfo1 = *itg;
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupInfo = new GroupInfo();
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
pGroupInfo->m_pNZBInfo->AddReference();
pGroupInfo->m_iFirstID = pFileInfo->GetID();
pGroupInfo->m_iLastID = pFileInfo->GetID();
pGroupQueue->push_back(pGroupInfo);
}
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
{
pGroupInfo->m_iFirstID = pFileInfo->GetID();
}
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
{
pGroupInfo->m_iLastID = pFileInfo->GetID();
}
pGroupInfo->m_iRemainingFileCount++;
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
if (pFileInfo->GetPaused())
{
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
}
char szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (strstr(szLoFileName, ".par2"))
{
pGroupInfo->m_iRemainingParCount++;
}
}
}

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,6 +32,32 @@
#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:
@@ -73,12 +99,11 @@ 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;
@@ -95,12 +120,10 @@ 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; }
@@ -116,8 +139,6 @@ public:
void SetPaused(bool Paused) { m_bPaused = Paused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
const char* GetDestDir() { return m_szDestDir; }
void SetDestDir(const char* szDestDir);
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int s) { m_iCompleted = s; }
void ClearArticles();
@@ -130,4 +151,32 @@ public:
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>
@@ -68,7 +68,7 @@ Frontend::Frontend()
m_bPause = false;
m_fDownloadLimit = 0;
m_iThreadCount = 0;
m_iParJobCount = 0;
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
m_iDnTimeSec = 0;
m_iAllBytes = 0;
@@ -102,9 +102,9 @@ bool Frontend::PrepareData()
m_bPause = g_pOptions->GetPause();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
m_iParJobCount = pParQueue->size();
g_pPrePostProcessor->UnlockParQueue();
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
m_iPostJobCount = pPostQueue->size();
g_pPrePostProcessor->UnlockPostQueue();
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
}
}
@@ -351,19 +351,22 @@ bool Frontend::RequestFileList()
if (m_bSummary)
{
m_bPause = ntohl(ListResponse.m_bServerPaused);
m_lRemainingSize = JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0;
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0;
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_iParJobCount = ntohl(ListResponse.m_iParJobCount);
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 = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
}
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
typedef std::deque<NZBInfo*> NZBList;
NZBList cNZBList;
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
{
@@ -376,14 +379,33 @@ bool Frontend::RequestFileList()
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
pFileInfo->SetSize(JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
pFileInfo->SetRemainingSize(JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
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->SetNZBFilename(szNZBFilename);
pFileInfo->SetSubject(szSubject);
pFileInfo->SetFilename(szFileName);
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
pFileInfo->SetDestDir(szDestDir);
// 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);

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>
@@ -55,7 +55,7 @@ protected:
bool m_bPause;
float m_fDownloadLimit;
int m_iThreadCount;
int m_iParJobCount;
int m_iPostJobCount;
int m_iUpTimeSec;
int m_iDnTimeSec;
long long m_iAllBytes;

34
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>
@@ -50,8 +50,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 +143,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 +256,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];

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

@@ -126,6 +126,9 @@ void LoggableFrontend::PrintMessage(Message * pMessage)
case Message::mkInfo:
printf("[INFO] %s\n", msg);
break;
case Message::mkDetail:
printf("[DETAIL] %s\n", msg);
break;
}
#ifdef WIN32
free(msg);

View File

@@ -8,7 +8,8 @@ nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
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
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 \

View File

@@ -60,7 +60,8 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
RemoteServer.$(OBJEXT) ServerPool.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) nzbget.$(OBJEXT)
Util.$(OBJEXT) nzbget.$(OBJEXT) BinRpc.$(OBJEXT) \
XmlRpc.$(OBJEXT) PostInfo.$(OBJEXT) ScriptController.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
DEFAULT_INCLUDES = -I.@am__isrc@
@@ -208,7 +209,8 @@ nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
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
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 \
@@ -304,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@
@@ -320,14 +323,17 @@ 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:

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>
@@ -36,16 +36,9 @@ static const int NZBREQUESTPASSWORDSIZE = 32;
* 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 must end with NULL-char.
* All char-strings ends with NULL-char.
*/
// 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
// Possible values for field "m_iType" of struct "SNZBRequestBase":
enum eRemoteRequest
{
@@ -57,7 +50,9 @@ enum eRemoteRequest
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestVersion
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
@@ -137,7 +132,7 @@ struct SNZBListResponse
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_iParJobCount; // Number of ParJobs in Par-Checker queue (including current file)
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
@@ -295,8 +290,59 @@ struct SNZBVersionResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
#ifdef HAVE_PRAGMA_PACK
#pragma pack()
#endif
// 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

@@ -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>
@@ -50,6 +50,7 @@
#include "nzbget.h"
#include "NCursesFrontend.h"
#include "Options.h"
#include "Util.h"
#ifdef HAVE_CURSES_H
// curses.h header must be included last to avoid problems on Solaris
@@ -67,11 +68,12 @@ static const int NCURSES_COLORPAIR_INFO = 2;
static const int NCURSES_COLORPAIR_WARNING = 3;
static const int NCURSES_COLORPAIR_ERROR = 4;
static const int NCURSES_COLORPAIR_DEBUG = 5;
static const int NCURSES_COLORPAIR_STATUS = 6;
static const int NCURSES_COLORPAIR_KEYBAR = 7;
static const int NCURSES_COLORPAIR_INFOLINE = 8;
static const int NCURSES_COLORPAIR_TEXTHIGHL = 9;
static const int NCURSES_COLORPAIR_CURSOR = 10;
static const int NCURSES_COLORPAIR_DETAIL = 6;
static const int NCURSES_COLORPAIR_STATUS = 7;
static const int NCURSES_COLORPAIR_KEYBAR = 8;
static const int NCURSES_COLORPAIR_INFOLINE = 9;
static const int NCURSES_COLORPAIR_TEXTHIGHL = 10;
static const int NCURSES_COLORPAIR_CURSOR = 11;
static const int MAX_SCREEN_WIDTH = 512;
@@ -101,25 +103,6 @@ static const int READKEY_EMPTY = ERR;
#endif
NCursesFrontend::GroupInfo::GroupInfo(int iID, const char* szNZBFilename)
{
m_iID = iID;
m_szNZBFilename = strdup(szNZBFilename);
m_iFileCount = 0;
m_lSize = 0;
m_lRemainingSize = 0;
m_lPausedSize = 0;
m_iParCount = 0;
}
NCursesFrontend::GroupInfo::~GroupInfo()
{
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
}
NCursesFrontend::NCursesFrontend()
{
m_iScreenHeight = 0;
@@ -193,6 +176,7 @@ NCursesFrontend::NCursesFrontend()
init_pair(NCURSES_COLORPAIR_WARNING, COLOR_MAGENTA, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_ERROR, COLOR_RED, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_DEBUG, COLOR_WHITE, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_DETAIL, COLOR_GREEN, COLOR_BLACK);
init_pair(NCURSES_COLORPAIR_STATUS, COLOR_BLUE, COLOR_WHITE);
init_pair(NCURSES_COLORPAIR_KEYBAR, COLOR_WHITE, COLOR_BLUE);
init_pair(NCURSES_COLORPAIR_INFOLINE, COLOR_WHITE, COLOR_BLUE);
@@ -240,7 +224,7 @@ void NCursesFrontend::Run()
{
// The data (queue and log) is updated each m_iUpdateInterval msec,
// but the window is updated more often for better reaction on user's input
if (iScreenUpdatePos <= 0)
if (iScreenUpdatePos <= 0 || m_iDataUpdatePos <= 0)
{
iScreenUpdatePos = iScreenUpdateInterval;
Update();
@@ -335,9 +319,9 @@ void NCursesFrontend::CalcWindowSizes()
{
#ifdef WIN32
m_iScreenBufferSize = iNrRows * iNrColumns * sizeof(CHAR_INFO);
m_pScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
m_pScreenBuffer = (CHAR_INFO*)realloc(m_pScreenBuffer, m_iScreenBufferSize);
memset(m_pScreenBuffer, 0, m_iScreenBufferSize);
m_pOldScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
m_pOldScreenBuffer = (CHAR_INFO*)realloc(m_pOldScreenBuffer, m_iScreenBufferSize);
memset(m_pOldScreenBuffer, 0, m_iScreenBufferSize);
#else
clear();
@@ -517,9 +501,9 @@ void NCursesFrontend::PrintMessages()
int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
{
char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG "};
char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG ", "DETAIL "};
const int iMessageTypeColor[] = { NCURSES_COLORPAIR_INFO, NCURSES_COLORPAIR_WARNING,
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG };
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG, NCURSES_COLORPAIR_DETAIL };
char* szText = (char*)Msg->GetText();
@@ -546,10 +530,10 @@ int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
szText = strdup(szText);
}
// replace CR and LF characters with spaces
// replace some special characters with spaces
for (char* p = szText; *p; p++)
{
if (*p == '\n' || *p == '\r')
if (*p == '\n' || *p == '\r' || *p == '\b')
{
*p = ' ';
}
@@ -596,9 +580,9 @@ void NCursesFrontend::PrintStatus()
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
{
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
int h = remain_sec / 3600;
int m = (remain_sec % 3600) / 60;
int s = remain_sec % 60;
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);
}
@@ -612,21 +596,21 @@ void NCursesFrontend::PrintStatus()
szDownloadLimit[0] = 0;
}
char szParStatus[128];
if (m_iParJobCount > 0)
char szPostStatus[128];
if (m_iPostJobCount > 0)
{
sprintf(szParStatus, ", %i par%s", m_iParJobCount, m_iParJobCount > 1 ? "s" : "");
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
}
else
{
szParStatus[0] = 0;
szPostStatus[0] = 0;
}
float fAverageSpeed = m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec / 1024 : 0;
float fAverageSpeed = Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec / 1024 : 0);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s, Avg. %.0f KB/s",
m_iThreadCount, fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0), timeString,
szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, fAverageSpeed);
m_iThreadCount, fCurrentDownloadSpeed, (float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString,
szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, fAverageSpeed);
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
}
@@ -769,13 +753,13 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
{
sprintf(szCompleted, ", %i%%", (int)(100 - pFileInfo->GetRemainingSize() * 100.0 / pFileInfo->GetSize()));
sprintf(szCompleted, ", %i%%", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())));
}
char szNZBNiceName[1024];
if (m_bShowNZBname)
{
pFileInfo->GetNiceNZBName(szNZBNiceName, 1023);
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
int len = strlen(szNZBNiceName);
szNZBNiceName[len] = PATH_SEPARATOR;
szNZBNiceName[len + 1] = '\0';
@@ -786,7 +770,9 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
}
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(), Brace2, szNZBNiceName, pFileInfo->GetFilename(), pFileInfo->GetSize() / 1024.0 / 1024.0, szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
Brace2, szNZBNiceName, pFileInfo->GetFilename(), (float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(szBuffer, iRow, 0, color);
@@ -796,15 +782,15 @@ void NCursesFrontend::FormatFileSize(char * szBuffer, int iBufLen, long long lFi
{
if (lFileSize > 1024 * 1024 * 1024)
{
snprintf(szBuffer, iBufLen, "%.2f GB", (float)lFileSize / 1024 / 1024 / 1024);
snprintf(szBuffer, iBufLen, "%.2f GB", (float)(Util::Int64ToFloat(lFileSize) / 1024 / 1024 / 1024));
}
else if (lFileSize > 1024 * 1024)
{
snprintf(szBuffer, iBufLen, "%.2f MB", (float)lFileSize / 1024 / 1024);
snprintf(szBuffer, iBufLen, "%.2f MB", (float)(Util::Int64ToFloat(lFileSize) / 1024 / 1024));
}
else if (lFileSize > 1024)
{
snprintf(szBuffer, iBufLen, "%.2f KB", (float)lFileSize / 1024);
snprintf(szBuffer, iBufLen, "%.2f KB", (float)(Util::Int64ToFloat(lFileSize) / 1024));
}
else
{
@@ -953,11 +939,11 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
}
char szNZBNiceName[1024];
FileInfo::MakeNiceNZBName(pGroupInfo->GetNZBFilename(), szNZBNiceName, 1023);
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s (%i file%s, %s%s)", Brace1, pGroupInfo->GetID(), Brace2, szNZBNiceName,
pGroupInfo->m_iFileCount, pGroupInfo->m_iFileCount > 1 ? "s" : "", szRemaining, szPaused);
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i-%i%s %s (%i file%s, %s%s)", Brace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), Brace2, szNZBNiceName,
pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining, szPaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PlotLine(szBuffer, iRow, 0, color);
@@ -968,41 +954,7 @@ void NCursesFrontend::PrepareGroupQueue()
m_groupQueue.clear();
DownloadQueue* pDownloadQueue = LockQueue();
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo = *it;
GroupInfo* pGroupInfo = NULL;
for (GroupQueue::iterator itg = m_groupQueue.begin(); itg != m_groupQueue.end(); itg++)
{
GroupInfo* pGroupInfo1 = *itg;
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
{
pGroupInfo = pGroupInfo1;
break;
}
}
if (!pGroupInfo)
{
pGroupInfo = new GroupInfo(pFileInfo->GetID(), pFileInfo->GetNZBFilename());
m_groupQueue.push_back(pGroupInfo);
}
pGroupInfo->m_iFileCount++;
pGroupInfo->m_lSize += pFileInfo->GetSize();
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_iParCount++;
}
}
GroupInfo::BuildGroups(pDownloadQueue, &m_groupQueue);
UnlockQueue();
}
@@ -1024,14 +976,14 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)m_groupQueue.size())
{
GroupInfo* pGroupInfo = m_groupQueue[m_iSelectedQueueEntry];
ID = pGroupInfo->GetID();
ID = pGroupInfo->GetLastID();
if (eAction == QueueEditor::eaFilePause)
{
if (pGroupInfo->GetRemainingSize() == pGroupInfo->GetPausedSize())
{
eAction = QueueEditor::eaFileResume;
}
else if (pGroupInfo->GetPausedSize() == 0 && (pGroupInfo->m_iParCount > 0) &&
else if (pGroupInfo->GetPausedSize() == 0 && (pGroupInfo->GetRemainingParCount() > 0) &&
!(m_bLastPausePars && m_iLastEditEntry == m_iSelectedQueueEntry))
{
eAction = QueueEditor::eaFilePauseExtraPars;
@@ -1075,6 +1027,8 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
m_iLastEditEntry = m_iSelectedQueueEntry;
NeedUpdateData();
if (ID != 0)
{
return ServerEditQueue(eAction, iOffset, ID);

View File

@@ -46,31 +46,6 @@ private:
eDownloadRate
};
class GroupInfo
{
private:
int m_iID;
char* m_szNZBFilename;
public:
int m_iFileCount;
long long m_lSize;
long long m_lRemainingSize;
long long m_lPausedSize;
int m_iParCount;
public:
GroupInfo(int iID, const char* szNZBFilename);
~GroupInfo();
int GetID() { return m_iID; }
const char* GetNZBFilename() { return m_szNZBFilename; }
long long GetSize() { return m_lSize; }
long long GetRemainingSize() { return m_lRemainingSize; }
long long GetPausedSize() { return m_lPausedSize; }
};
typedef std::deque<GroupInfo*> GroupQueue;
bool m_bUseColor;
int m_iDataUpdatePos;
int m_iScreenHeight;

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>
@@ -60,7 +60,7 @@ NNTPConnection::~NNTPConnection()
free(m_szLineBuf);
}
char* NNTPConnection::Request(char* req)
const char* NNTPConnection::Request(char* req)
{
if (!req)
{
@@ -190,25 +190,26 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
return false;
}
bool NNTPConnection::JoinGroup(char* grp)
const char* NNTPConnection::JoinGroup(const char* grp)
{
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
{
// already in group
return true;
strcpy(m_szLineBuf, "211 ");
return m_szLineBuf;
}
char tmp[1024];
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
tmp[1024-1] = '\0';
char* answer = Request(tmp);
const char* answer = Request(tmp);
if (m_bAuthError)
{
return false;
return answer;
}
if ((answer) && (!strncmp(answer, "2", 1)))
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
@@ -217,24 +218,14 @@ bool NNTPConnection::JoinGroup(char* grp)
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
return true;
}
if (GetStatus() != csCancelled)
else
{
if (!answer)
{
warn("Error changing group on %s: Connection closed by remote host.",
GetServer()->GetHost());
}
else
{
warn("Error changing group on %s to %s: Answer was \"%s\".",
GetServer()->GetHost(), grp, answer);
}
debug("Error changing group on %s to %s: %s.",
GetServer()->GetHost(), grp, answer);
}
return false;
return answer;
}
int NNTPConnection::DoConnect()
@@ -254,7 +245,6 @@ int NNTPConnection::DoConnect()
return -1;
}
if (strncmp(answer, "2", 1))
{
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);

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>
@@ -45,11 +45,11 @@ public:
NNTPConnection(NewsServer* server);
~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
char* Request(char* req);
const char* Request(char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
bool JoinGroup(char* grp);
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>
*
@@ -133,8 +133,12 @@ void InstallService(int argc, char *argv[])
return;
}
char szExeName[1024];
GetModuleFileName(NULL, szExeName, 1024);
szExeName[1024-1] = '\0';
char szCmdLine[1024];
snprintf(szCmdLine, 1024, "%s -D", argv[0]);
snprintf(szCmdLine, 1024, "%s -D", szExeName);
szCmdLine[1024-1] = '\0';
SC_HANDLE hService = CreateService(scm, strServiceName,

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>
@@ -59,6 +59,10 @@ 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();
}
@@ -78,6 +82,11 @@ NZBFile::~NZBFile()
delete *it;
}
m_FileInfos.clear();
if (m_pNZBInfo)
{
m_pNZBInfo->Release();
}
}
void NZBFile::LogDebugInfo()
@@ -131,8 +140,10 @@ void NZBFile::AddFileInfo(FileInfo* pFileInfo)
if (!pArticles->empty())
{
ParseSubject(pFileInfo);
BuildDestDirName(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())
{
@@ -167,7 +178,7 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo)
if (sep)
{
// end of token
int len = p - start;
int len = (int)(p - start);
if (len > 0)
{
char* token = (char*)malloc(len + 1);
@@ -234,14 +245,14 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo)
pFileInfo->MakeValidFilename();
}
void NZBFile::BuildDestDirName(FileInfo* pFileInfo)
void NZBFile::BuildDestDirName()
{
char szBuffer[1024];
if (g_pOptions->GetAppendNZBDir())
{
char szNiceNZBName[1024];
pFileInfo->GetNiceNZBName(szNiceNZBName, 1024);
m_pNZBInfo->GetNiceNZBName(szNiceNZBName, 1024);
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
szBuffer[1024-1] = '\0';
}
@@ -251,7 +262,7 @@ void NZBFile::BuildDestDirName(FileInfo* pFileInfo)
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
}
pFileInfo->SetDestDir(szBuffer);
m_pNZBInfo->SetDestDir(szBuffer);
}
/**
@@ -388,7 +399,6 @@ 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);
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
@@ -493,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))
{

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,13 +38,14 @@ public:
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
NZBFile(const char* szFileName);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo);
void BuildDestDirName(FileInfo* pFileInfo);
void BuildDestDirName();
void CheckFilenames();
#ifdef WIN32
bool ParseNZB(IUnknown* nzb);

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>
@@ -51,7 +51,6 @@
#include "NewsServer.h"
#include "MessageBase.h"
extern float g_fDownloadRate;
extern ServerPool* g_pServerPool;
#ifdef HAVE_GETOPT_LONG
@@ -77,11 +76,13 @@ static struct option long_options[] =
{"edit", required_argument, 0, 'E'},
{"connect", no_argument, 0, 'C'},
{"quit", no_argument, 0, 'Q'},
{"post", no_argument, 0, 'O'},
{"write", required_argument, 0, 'W'},
{0, 0, 0, 0}
};
#endif
static char short_options[] = "c:hno:psvABDCG:LPUR:TE:QV";
static char short_options[] = "c:hno:psvABDCE:G:LOPR:TUQVW:";
// Program options
static const char* OPTION_DESTDIR = "DestDir";
@@ -103,9 +104,10 @@ 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_DECODER = "Decoder";
static const char* OPTION_DECODE = "Decode";
static const char* OPTION_RETRIES = "Retries";
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
@@ -115,6 +117,7 @@ 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";
@@ -132,6 +135,9 @@ 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";
#ifndef WIN32
const char* PossibleConfigLocations[] =
@@ -159,7 +165,8 @@ 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;
@@ -169,6 +176,7 @@ Options::Options(int argc, char* argv[])
m_iEditQueueIDCount = 0;
m_iEditQueueOffset = 0;
m_szArgFilename = NULL;
m_szLastArg = NULL;
m_iConnectionTimeout = 0;
m_iTerminateTimeout = 0;
m_bServerMode = false;
@@ -190,11 +198,13 @@ Options::Options(int argc, char* argv[])
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_szPostProcess = NULL;
@@ -212,6 +222,9 @@ Options::Options(int argc, char* argv[])
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
@@ -220,7 +233,7 @@ Options::Options(int argc, char* argv[])
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);
@@ -295,6 +308,10 @@ Options::~Options()
{
free(m_szArgFilename);
}
if (m_szLastArg)
{
free(m_szLastArg);
}
if (m_szServerIP)
{
free(m_szServerIP);
@@ -372,10 +389,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");
@@ -385,6 +403,7 @@ 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");
@@ -403,6 +422,9 @@ void Options::InitDefault()
SetOption(OPTION_WRITEBUFFERSIZE, "0");
SetOption(OPTION_NZBDIRINTERVAL, "5");
SetOption(OPTION_NZBDIRFILEAGE, "60");
SetOption(OPTION_PARCLEANUPQUEUE, "no");
SetOption(OPTION_DISKSPACE, "0");
SetOption(OPTION_POSTLOGKIND, "none");
}
void Options::InitOptFile()
@@ -419,13 +441,12 @@ void Options::InitOptFile()
char szFilename[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);
}
@@ -437,8 +458,7 @@ void Options::InitOptFile()
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");
@@ -476,14 +496,14 @@ 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
if (!ForceDirectories(usedir))
if (!Util::ForceDirectories(usedir))
{
abort("FATAL ERROR: Directory \"%s\" (option \"%s\") does not exist and could not be created\n", usedir, szOptionName);
}
@@ -516,15 +536,16 @@ void Options::InitOptions()
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" };
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
const int BoolCount = 10;
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);
@@ -537,25 +558,23 @@ void Options::InitOptions()
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);
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
const int OutputModeCount = 7;
m_eOutputMode = (EOutputMode)ParseOptionValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, OutputModeValues);
const char* DecoderNames[] = { "uulib", "yenc", "none", "ydec", "ydecoder" };
const int DecoderValues[] = { dcUulib, dcYenc, dcNone, dcYenc, dcYenc };
const int DecoderCount = 5;
m_eDecoder = (EDecoder)ParseOptionValue(OPTION_DECODER, DecoderCount, DecoderNames, DecoderValues);
const char* LoadParsNames[] = { "none", "one", "all", "1", "0" };
const int 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, LoadParsValues);
@@ -566,6 +585,12 @@ void Options::InitOptions()
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[])
@@ -734,6 +759,31 @@ void Options::InitCommandLine(int argc, char* argv[])
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;
case '?':
exit(-1);
break;
@@ -746,7 +796,7 @@ void Options::InitCommandLine(int argc, char* argv[])
void Options::PrintUsage(char* com)
{
printf("Usage:\n"
" %s [switches] [<nzb-file>]\n\n"
" %s [switches]\n\n"
"Switches:\n"
" -h, --help Print this help-message\n"
" -v, --version Print version and exit\n"
@@ -761,7 +811,7 @@ void Options::PrintUsage(char* com)
#endif
" -V, --serverversion Print server's version and exit\n"
" -Q, --quit Shutdown the server\n"
" -A, --append Send file to the server's download queue\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"
@@ -770,6 +820,8 @@ void Options::PrintUsage(char* com)
" -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"
@@ -784,7 +836,7 @@ void Options::PrintUsage(char* com)
" 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",
BaseFileName(com));
Util::BaseFileName(com));
}
void Options::InitFileArg(int argc, char* argv[])
@@ -794,9 +846,17 @@ 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);
}
}
@@ -806,6 +866,8 @@ void Options::InitFileArg(int argc, char* argv[])
}
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];
@@ -829,10 +891,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);
}
}
@@ -1049,7 +1112,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);
@@ -1122,7 +1185,7 @@ void Options::CheckOptions()
}
#endif
if (m_eDecoder != dcYenc)
if (!m_bDecode)
{
m_bDirectWrite = false;
}
@@ -1146,7 +1209,7 @@ void Options::ParseFileIDList(int argc, char* argv[], int optind)
if (p)
{
char buf[101];
int maxlen = p - optarg < 100 ? p - optarg : 100;
int maxlen = (int)(p - optarg < 100 ? p - optarg : 100);
strncpy(buf, optarg, maxlen);
buf[maxlen] = '\0';
iEditQueueIDFrom = atoi(buf);

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,42 +34,48 @@ class Options
public:
enum EClientOperation
{
opClientNoOperation,
opClientRequestDownload,
opClientRequestList,
opClientRequestPause,
opClientRequestUnpause,
opClientRequestSetRate,
opClientRequestDumpDebug,
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestVersion
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:
@@ -93,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;
@@ -112,6 +119,7 @@ private:
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadPostQueue;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
@@ -133,6 +141,9 @@ private:
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EPostLogKind m_ePostLogKind;
// Parsed command-line parameters
bool m_bServerMode;
@@ -143,10 +154,12 @@ private:
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
char* m_szArgFilename;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
float m_fSetRate;
int m_iLogLines;
int m_iWriteLogKind;
// Current state
bool m_bPause;
@@ -187,9 +200,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; }
@@ -204,6 +218,7 @@ public:
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; }
@@ -224,20 +239,25 @@ public:
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; }
// 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* 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; }
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>
*
@@ -47,11 +47,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 +62,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 +78,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 +107,7 @@ ParChecker::~ParChecker()
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
@@ -144,12 +151,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,17 +177,23 @@ void ParChecker::Run()
}
Result res;
Repairer* repairer = new Repairer();
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
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;
}
@@ -180,15 +205,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();
@@ -198,6 +233,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();
@@ -222,18 +264,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;
}
@@ -247,8 +290,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)
{
@@ -276,214 +328,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;
@@ -494,17 +342,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)
@@ -522,9 +372,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());
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"
@@ -38,24 +39,6 @@
class PrePostProcessor : public Thread
{
public:
class ParJob
{
private:
char* m_szNZBFilename;
char* m_szParFilename;
char* m_szInfoName;
public:
ParJob(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
~ParJob();
const char* GetNZBFilename() { return m_szNZBFilename; }
const char* GetParFilename() { return m_szParFilename; }
const char* GetInfoName() { return m_szInfoName; }
};
typedef std::deque<ParJob*> ParQueue;
private:
typedef std::deque<char*> FileList;
@@ -73,31 +56,68 @@ private:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
{
private:
PrePostProcessor* m_Owner;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
friend class PrePostProcessor;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::deque<BlockInfo*> Blocks;
#endif
private:
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
void CheckIncomingNZBs();
bool WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, bool bIgnorePaused);
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, int iParStatus);
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_mutexParChecker;
ParQueue m_ParQueue;
Mutex m_mutexQueue;
PostQueue m_PostQueue;
PostQueue m_CompletedJobs;
#ifndef DISABLE_PARCHECK
ParChecker m_ParChecker;
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:
@@ -107,8 +127,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
ParQueue* LockParQueue();
void UnlockParQueue();
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>
@@ -107,15 +107,15 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Lock();
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->Exists())
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
{
if (g_pOptions->GetReloadQueue())
{
g_pDiskState->Load(&m_DownloadQueue);
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
}
else
{
g_pDiskState->Discard();
g_pDiskState->DiscardDownloadQueue();
}
}
@@ -212,8 +212,10 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
DownloadQueue DupeList;
DupeList.clear();
int index1 = 0;
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
index1++;
FileInfo* pFileInfo = *it;
if (g_pOptions->GetDupeCheck())
@@ -224,11 +226,15 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
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 (!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
(pFileInfo->GetSize() < pFileInfo2->GetSize()))
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;
@@ -267,7 +273,10 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
{
FileInfo* pFileInfo = *it;
g_pDiskState->DiscardFile(NULL, pFileInfo);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(NULL, pFileInfo);
}
delete pFileInfo;
}
@@ -278,7 +287,7 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->Save(&m_DownloadQueue);
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
@@ -315,7 +324,7 @@ float QueueCoordinator::CalcCurrentDownloadSpeed()
iTotal += m_iSpeedBytes[i];
}
float fSpeed = iTotal / 1024.0 / SPEEDMETER_SECONDS;
float fSpeed = iTotal / 1024.0f / SPEEDMETER_SECONDS;
return fSpeed;
}
@@ -331,7 +340,7 @@ float QueueCoordinator::CalcCurrentDownloadSpeed()
*/
void QueueCoordinator::AddSpeedReading(int iBytes)
{
int iIndex = time(NULL);
int iIndex = (int)time(NULL);
if (iIndex - m_iSpeedBytesIndex > SPEEDMETER_SECONDS)
{
@@ -475,7 +484,7 @@ void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloade
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';
@@ -588,10 +597,11 @@ 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, fileCompleted);
@@ -659,7 +669,7 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
{
FileInfo* pQueueEntry = *it;
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
pFileInfo != pQueueEntry)
{
@@ -719,7 +729,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
@@ -761,7 +771,7 @@ void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAl
m_mutexStat.Lock();
if (m_tStartServer > 0)
{
*iUpTimeSec = time(NULL) - m_tStartServer;
*iUpTimeSec = (int)(time(NULL) - m_tStartServer);
}
else
{
@@ -770,11 +780,11 @@ void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAl
*bStandBy = m_bStandBy;
if (m_bStandBy)
{
*iDnTimeSec = m_tPausedFrom - m_tStartDownload;
*iDnTimeSec = (int)(m_tPausedFrom - m_tStartDownload);
}
else
{
*iDnTimeSec = time(NULL) - m_tStartDownload;
*iDnTimeSec = (int)(time(NULL) - m_tStartDownload);
}
*iAllBytes = m_iAllBytes;
m_mutexStat.Unlock();

View File

@@ -106,6 +106,7 @@ public:
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
bool AddFileToQueue(const char* szFileName);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }

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>
*
@@ -169,7 +169,7 @@ bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eActio
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->Save(pDownloadQueue);
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
@@ -353,7 +353,7 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
{
FileInfo* pFileInfo2 = *it;
if (!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
}
@@ -368,7 +368,7 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pGroupInfo = *it;
if (!strcmp(pGroupInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
break;
}
@@ -421,7 +421,7 @@ void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroup
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
{
FileInfo* pGroupInfo1 = *itg;
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
pGroupInfo = pGroupInfo1;
break;
@@ -465,7 +465,7 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
{
FileInfo* pFileInfo = *it;
if (!strcmp(pItem->m_pFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (!ItemExists(&cAffectedGroupList, pFileInfo))
{
@@ -519,7 +519,7 @@ void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFile
while (iNum < pDownloadQueue->size())
{
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
if (!strcmp(pFirstFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (pLastFileInfo && iNum - iLastNum > 1)
{
@@ -549,7 +549,7 @@ void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
{
EditItem* pItem = *it;
if (!pFirstFileInfo ||
!strcmp(pFirstFileInfo->GetNZBFilename(), pItem->m_pFileInfo->GetNZBFilename()))
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
{
GroupFileList.push_back(pItem->m_pFileInfo);
if (!pFirstFileInfo)

75
README
View File

@@ -41,7 +41,7 @@ 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.1) should run at least on:
The current version (0.4.0) 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;
@@ -87,9 +87,6 @@ And the following libraries are optional:
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- for support of encoding-formats other than yEnc (disabled by default):
- libuu (http://www.fpx.de/fp/Software/UUDeview)
All these libraries are included in modern Linux distributions and
should be available as installable packages. Please note that you also
need the developer packages for these libraries too, they package names
@@ -110,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
@@ -124,10 +120,6 @@ 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.
@@ -136,63 +128,18 @@ You may run configure with additional arguments:
--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,8 +263,7 @@ 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:

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>
@@ -131,7 +131,7 @@ void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest,
bool RemoteClient::ReceiveBoolResponse()
{
printf("request sent\n");
printf("Request sent\n");
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
SNZBDownloadResponse BoolResponse;
@@ -142,7 +142,14 @@ bool RemoteClient::ReceiveBoolResponse()
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
{
printf("invaid response received: either not nzbget-server or wrong server version\n");
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;
}
@@ -151,7 +158,14 @@ bool RemoteClient::ReceiveBoolResponse()
iResponseLen = m_pConnection->Recv(buf, iTextLen);
if (iResponseLen != iTextLen)
{
printf("invaid response received: either not nzbget-server or wrong server version\n");
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;
}
@@ -168,7 +182,7 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
// Read the file into the buffer
char* szBuffer = NULL;
int iLength = 0;
if (!LoadFileIntoBuffer(szName, &szBuffer, &iLength))
if (!Util::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
{
printf("Could not load file %s\n", szName);
return false;
@@ -222,6 +236,8 @@ bool RemoteClient::RequestServerList()
return false;
}
printf("Request sent\n");
// Now listen for the returned list
SNZBListResponse ListResponse;
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
@@ -229,7 +245,14 @@ bool RemoteClient::RequestServerList()
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
printf("invaid response received: either not nzbget-server or wrong server version\n");
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;
}
@@ -262,14 +285,14 @@ bool RemoteClient::RequestServerList()
{
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
long long lFileSize = JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
long long lRemainingSize = JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
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 (lRemainingSize < lFileSize)
{
sprintf(szCompleted, ", %i%s", (int)(100 - lRemainingSize * 100.0 / lFileSize), "%");
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(lRemainingSize) * 100.0 / Util::Int64ToFloat(lFileSize)), "%");
}
char szStatus[100];
if (ntohl(pListAnswer->m_bPaused))
@@ -286,9 +309,10 @@ bool RemoteClient::RequestServerList()
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
char szNZBNiceName[1024];
FileInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename, lFileSize / 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(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
@@ -298,19 +322,20 @@ bool RemoteClient::RequestServerList()
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("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
free(pBuf);
}
long long iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
float fAverageSpeed = ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0;
long long iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
float fAverageSpeed = Util::Int64ToFloat(ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0);
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
@@ -330,13 +355,13 @@ bool RemoteClient::RequestServerList()
s = sec % 60;
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
printf("Downloaded: %.2f MB\n", iAllBytes / 1024.0 / 1024.0);
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_iParJobCount) > 0)
if (ntohl(ListResponse.m_iPostJobCount) > 0)
{
printf("Par-jobs: %i\n", (int)ntohl(ListResponse.m_iParJobCount));
printf("Post-jobs: %i\n", (int)ntohl(ListResponse.m_iPostJobCount));
}
if (ntohl(ListResponse.m_bServerStandBy))
@@ -366,6 +391,8 @@ bool RemoteClient::RequestServerLog(int iLines)
return false;
}
printf("Request sent\n");
// Now listen for the returned log
SNZBLogResponse LogResponse;
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
@@ -373,7 +400,14 @@ bool RemoteClient::RequestServerLog(int iLines)
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
printf("invaid response received: either not nzbget-server or wrong server version\n");
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;
}
@@ -419,6 +453,9 @@ 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(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
@@ -580,3 +617,113 @@ bool RemoteClient::RequestServerVersion()
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), "%");
}
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

@@ -57,6 +57,8 @@ public:
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,34 +43,12 @@
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "XmlRpc.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"
};
const unsigned int g_iMessageRequestSizes[] =
{ 0,
sizeof(SNZBDownloadRequest),
sizeof(SNZBPauseUnpauseRequest),
sizeof(SNZBListRequest),
sizeof(SNZBSetDownloadRateRequest),
sizeof(SNZBDumpDebugRequest),
sizeof(SNZBEditQueueRequest),
sizeof(SNZBLogRequest),
sizeof(SNZBRequestBase)
};
//*****************************************************************
// RemoteServer
@@ -160,546 +138,89 @@ void RemoteServer::Stop()
void RequestProcessor::Run()
{
int iBytesReceived = 0;
// Read the first package which needs to be a request
iBytesReceived = recv(m_iSocket, (char*) & m_MessageBase, sizeof(m_MessageBase), 0);
// Read the first 4 bytes to determine request type
bool bOK = false;
int iSignature = 0;
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
if (iBytesReceived < 0)
{
return;
}
// 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", g_pOptions->GetServerPort());
if (m_iSocket > -1)
{
closesocket(m_iSocket);
}
return;
}
if (strcmp(m_MessageBase.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_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], ip);
}
Dispatch();
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
{
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
{
// XML-RPC or JSON-RPC request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
eHttpMethod = XmlRpcProcessor::hmPost;
szUrl++;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
{
eProtocol = XmlRpcProcessor::rpXmlRpc;
}
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
{
eProtocol = XmlRpcProcessor::rpJsonRpc;
}
if (eProtocol != XmlRpcProcessor::rpUndefined)
{
XmlRpcProcessor processor;
processor.SetConnection(&con);
processor.SetClientIP(ip);
processor.SetProtocol(eProtocol);
processor.SetHttpMethod(eHttpMethod);
processor.SetUrl(szUrl);
processor.Execute();
bOK = true;
}
}
}
if (!bOK)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
}
// Close the socket
closesocket(m_iSocket);
}
void RequestProcessor::Dispatch()
{
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestShutdown &&
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;
}
MessageCommand* command = NULL;
switch (ntohl(m_MessageBase.m_iType))
{
case eRemoteRequestDownload:
{
command = new DownloadCommand();
break;
}
case eRemoteRequestList:
{
command = new ListCommand();
break;
}
case eRemoteRequestLog:
{
command = new LogCommand();
break;
}
case eRemoteRequestPauseUnpause:
{
command = new PauseUnpauseCommand();
break;
}
case eRemoteRequestEditQueue:
{
command = new EditQueueCommand();
break;
}
case eRemoteRequestSetDownloadRate:
{
command = new SetDownloadRateCommand();
break;
}
case eRemoteRequestDumpDebug:
{
command = new DumpDebugCommand();
break;
}
case eRemoteRequestShutdown:
{
command = new ShutdownCommand();
break;
}
case eRemoteRequestVersion:
{
command = new VersionCommand();
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 MessageCommand::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 MessageCommand::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 PauseUnpauseCommand::Execute()
{
SNZBPauseUnpauseRequest PauseUnpauseRequest;
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
{
return;
}
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
}
void SetDownloadRateCommand::Execute()
{
SNZBSetDownloadRateRequest SetDownloadRequest;
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
{
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0);
SendBoolResponse(true, "Rate-Command completed successfully");
}
void DumpDebugCommand::Execute()
{
SNZBDumpDebugRequest DumpDebugRequest;
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
{
return;
}
g_pQueueCoordinator->LogDebugInfo();
SendBoolResponse(true, "Debug-Command completed successfully");
}
void ShutdownCommand::Execute()
{
SNZBShutdownRequest ShutdownRequest;
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
{
return;
}
SendBoolResponse(true, "Stopping server");
ExitProc();
}
void VersionCommand::Execute()
{
SNZBVersionRequest VersionRequest;
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
{
return;
}
SendBoolResponse(true, VERSION);
}
void DownloadCommand::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", BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(false, tmp);
}
}
free(pRecvBuffer);
}
void ListCommand::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->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++)
{
unsigned int iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
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->GetNZBFilename()) + 1);
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetDestDir()) + 1);
bufptr += sizeof(SNZBListResponseEntry);
strcpy(bufptr, pFileInfo->GetNZBFilename());
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->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
}
g_pQueueCoordinator->UnlockQueue();
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
ListResponse.m_iTrailingDataLength = htonl(bufsize);
}
if (htonl(ListRequest.m_bServerState))
{
unsigned int iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
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
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
ListResponse.m_iParJobCount = htonl(pParQueue->size());
g_pPrePostProcessor->UnlockParQueue();
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);
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 LogCommand::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;
}
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(pMessage->GetTime());
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
bufptr += sizeof(SNZBLogResponseEntry);
strcpy(bufptr, pMessage->GetText());
bufptr += ntohl(pLogAnswer->m_iTextLen);
}
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 EditQueueCommand::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]));
}
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
free(pIDs);
if (bOK)
{
SendBoolResponse(true, "Edit-Command completed successfully");
}
else
{
SendBoolResponse(false, "Edit-Command failed");
}
}

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,12 +27,9 @@
#ifndef REMOTESERVER_H
#define REMOTESERVER_H
#include <list>
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
#include "MessageBase.h"
class RemoteServer : public Thread
{
@@ -51,83 +48,10 @@ class RequestProcessor : public Thread
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
void Dispatch();
public:
virtual void Run();
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
class MessageCommand
{
protected:
SOCKET m_iSocket;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
void SendBoolResponse(bool bSuccess, const char* szText);
public:
virtual ~MessageCommand() {};
virtual void Execute() = 0;
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; };
};
class DownloadCommand: public MessageCommand
{
public:
virtual void Execute();
};
class ListCommand: public MessageCommand
{
public:
virtual void Execute();
};
class LogCommand: public MessageCommand
{
public:
virtual void Execute();
};
class PauseUnpauseCommand: public MessageCommand
{
public:
virtual void Execute();
};
class EditQueueCommand: public MessageCommand
{
public:
virtual void Execute();
};
class SetDownloadRateCommand: public MessageCommand
{
public:
virtual void Execute();
};
class DumpDebugCommand: public MessageCommand
{
public:
virtual void Execute();
};
class ShutdownCommand: public MessageCommand
{
public:
virtual void Execute();
};
class VersionCommand: public MessageCommand
{
public:
virtual void Execute();
};
#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

@@ -211,7 +211,7 @@ void ServerPool::CloseUnusedConnections()
PooledConnection* pConnection = *it;
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = curtime - pConnection->GetFreeTime();
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());

View File

@@ -36,7 +36,7 @@
#include <stdio.h>
#ifdef WIN32
#include <process.h>
#include <process.h>
#endif
#include "Log.h"
@@ -299,4 +299,3 @@ int Thread::GetThreadCount()
m_mutexThread.Unlock();
return iThreadCount;
}

664
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>
*
@@ -35,36 +35,17 @@
#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:
@@ -217,7 +198,28 @@ const char* DirBrowser::Next()
#endif
void NormalizePathSeparators(char* szPath)
char* Util::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;
}
}
void Util::NormalizePathSeparators(char* szPath)
{
for (char* p = szPath; *p; p++)
{
@@ -228,7 +230,7 @@ void NormalizePathSeparators(char* szPath)
}
}
bool ForceDirectories(const char* szPath)
bool Util::ForceDirectories(const char* szPath)
{
char* szNormPath = strdup(szPath);
NormalizePathSeparators(szNormPath);
@@ -281,7 +283,7 @@ bool ForceDirectories(const char* szPath)
return bOK;
}
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
bool Util::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
{
FILE* pFile = fopen(szFileName, "r");
if (!pFile)
@@ -313,7 +315,7 @@ bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLeng
return true;
}
bool SetFileSize(const char* szFilename, int iSize)
bool Util::SetFileSize(const char* szFilename, int iSize)
{
bool bOK = false;
#ifdef WIN32
@@ -330,7 +332,7 @@ bool SetFileSize(const char* szFilename, int iSize)
{
fclose(pFile);
}
// there no reliable function to expand file on POSIX, so we must try different approaches,
// 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);
@@ -357,7 +359,7 @@ bool SetFileSize(const char* szFilename, int iSize)
}
//replace bad chars in filename
void MakeValidFilename(char* szFilename, char cReplaceChar)
void Util::MakeValidFilename(char* szFilename, char cReplaceChar)
{
char* p = szFilename;
while (*p)
@@ -377,18 +379,25 @@ void MakeValidFilename(char* szFilename, char cReplaceChar)
}
}
long long JoinInt64(unsigned int Hi, unsigned int Lo)
long long Util::JoinInt64(unsigned long Hi, unsigned long Lo)
{
return (((long long)Hi) << 32) + Lo;
}
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo)
void Util::SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo)
{
*Hi = (unsigned int)(Int64 >> 32);
*Lo = (unsigned int)Int64;
*Hi = (unsigned long)(Int64 >> 32);
*Lo = (unsigned long)Int64;
}
float EqualTime(_timeval* t1, _timeval* t2)
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;
@@ -397,7 +406,7 @@ float EqualTime(_timeval* t1, _timeval* t2)
#endif
}
bool EmptyTime(_timeval* t)
bool Util::EmptyTime(_timeval* t)
{
#ifdef WIN32
return t->time == 0 && t->millitm == 0;
@@ -406,11 +415,594 @@ bool EmptyTime(_timeval* t)
#endif
}
float DiffTime(_timeval* t1, _timeval* t2)
float Util::DiffTime(_timeval* t1, _timeval* t2)
{
#ifdef WIN32
return ((t1->time - t2->time) + (t1->millitm - t2->millitm) / 1000.0);
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;
char* temp = (char*) &buffer;
szOutputBuffer [0] = temp [3];
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;
char* temp = (char*) &buffer;
szOutputBuffer [0] = temp [3];
szOutputBuffer [1] = temp [2];
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;
char* temp = (char*) &buffer;
szOutputBuffer [0] = temp [3];
szOutputBuffer [1] = temp [2];
szOutputBuffer [2] = temp [1];
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;
}

91
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>
*
@@ -58,18 +58,85 @@ public:
const char* Next();
};
char* BaseFileName(const char* filename);
void NormalizePathSeparators(char* szPath);
bool ForceDirectories(const char* szPath);
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
bool SetFileSize(const char* szFilename, int iSize);
void MakeValidFilename(char* szFilename, char cReplaceChar);
class Util
{
public:
long long JoinInt64(unsigned int Hi, unsigned int Lo);
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo);
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);
float EqualTime(_timeval* t1, _timeval* t2);
bool EmptyTime(_timeval* t);
float DiffTime(_timeval* t1, _timeval* t2);
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

1442
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

View File

@@ -9,9 +9,6 @@
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* 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
@@ -46,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

444
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 0.3.1.
# Generated by GNU Autoconf 2.61 for nzbget 0.4.0.
#
# Report bugs to <hugbug@users.sourceforge.net>.
#
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='nzbget'
PACKAGE_TARNAME='nzbget'
PACKAGE_VERSION='0.3.1'
PACKAGE_STRING='nzbget 0.3.1'
PACKAGE_VERSION='0.4.0'
PACKAGE_STRING='nzbget 0.4.0'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="nzbget.cpp"
@@ -1238,7 +1238,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures nzbget 0.3.1 to adapt to many kinds of systems.
\`configure' configures nzbget 0.4.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1308,7 +1308,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 0.3.1:";;
short | recursive ) echo "Configuration of nzbget 0.4.0:";;
esac
cat <<\_ACEOF
@@ -1318,7 +1318,6 @@ Optional Features:
--disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors
--disable-curses do not use curses (removes dependency from curses-library and makes executable smaller)
--enable-uulib use uulib for decoding and joining
--enable-parcheck include code for par-checking
--enable-debug enable debugging
@@ -1329,8 +1328,6 @@ Optional Packages:
--with-libxml2-libraries=DIR libxml2 library directory
--with-libcurses-includes=DIR libcurses include directory
--with-libcurses-libraries=DIR libcurses library directory
--with-uulib-includes=DIR uulib include directory
--with-uulib-libraries=DIR uulib library directory
--with-libsigc-includes=DIR libsigc++-2.0 include directory
--with-libsigc-libraries=DIR libsigc++-2.0 library directory
--with-libpar2-includes=DIR libpar2 include directory
@@ -1413,7 +1410,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 0.3.1
nzbget configure 0.4.0
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1427,7 +1424,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by nzbget $as_me 0.3.1, which was
It was created by nzbget $as_me 0.4.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2117,7 +2114,7 @@ fi
# Define the identity of the package.
PACKAGE=nzbget
VERSION=0.3.1
VERSION=0.4.0
cat >>confdefs.h <<_ACEOF
@@ -5566,6 +5563,104 @@ fi
{ echo "$as_me:$LINENO: checking for stat64" >&5
echo $ECHO_N "checking for stat64... $ECHO_C" >&6; }
if test "${ac_cv_func_stat64+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Define stat64 to an innocuous variant, in case <limits.h> declares stat64.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define stat64 innocuous_stat64
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char stat64 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef stat64
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char stat64 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_stat64 || defined __stub___stat64
choke me
#endif
int
main ()
{
return stat64 ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_func_stat64=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_func_stat64=no
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
fi
{ echo "$as_me:$LINENO: result: $ac_cv_func_stat64" >&5
echo "${ECHO_T}$ac_cv_func_stat64" >&6; }
if test $ac_cv_func_stat64 = yes; then
cat >>confdefs.h <<\_ACEOF
#define HAVE_STAT64 1
_ACEOF
else
case " $LIBOBJS " in
*" stat64.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS stat64.$ac_objext"
;;
esac
fi
{ echo "$as_me:$LINENO: checking for ctime_r" >&5
echo $ECHO_N "checking for ctime_r... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
@@ -5923,59 +6018,6 @@ _ACEOF
fi
{ echo "$as_me:$LINENO: checking for pragma pack" >&5
echo $ECHO_N "checking for pragma pack... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#pragma pack(1)
#pragma pack()
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
{ echo "$as_me:$LINENO: result: yes" >&5
echo "${ECHO_T}yes" >&6; }
cat >>confdefs.h <<\_ACEOF
#define HAVE_PRAGMA_PACK 1
_ACEOF
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
{ echo "$as_me:$LINENO: result: no" >&5
echo "${ECHO_T}no" >&6; }
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
INCVAL="${LIBPREF}/include/libxml2"
LIBVAL="${LIBPREF}/lib"
@@ -7025,276 +7067,6 @@ _ACEOF
fi
{ echo "$as_me:$LINENO: checking whether to use uulib for decoding and joining" >&5
echo $ECHO_N "checking whether to use uulib for decoding and joining... $ECHO_C" >&6; }
# Check whether --enable-uulib was given.
if test "${enable_uulib+set}" = set; then
enableval=$enable_uulib; ENABLEUULIB=$enableval
else
ENABLEUULIB=no
fi
{ echo "$as_me:$LINENO: result: $ENABLEUULIB" >&5
echo "${ECHO_T}$ENABLEUULIB" >&6; }
if test "$ENABLEUULIB" = "yes"; then
cat >>confdefs.h <<\_ACEOF
#define ENABLE_UULIB 1
_ACEOF
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
# Check whether --with-uulib_includes was given.
if test "${with_uulib_includes+set}" = set; then
withval=$with_uulib_includes; INCVAL="$withval"
fi
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
as_ac_Header=`echo "ac_cv_header_$INCVAL/uudeview.h" | $as_tr_sh`
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
{ echo "$as_me:$LINENO: checking for $INCVAL/uudeview.h" >&5
echo $ECHO_N "checking for $INCVAL/uudeview.h... $ECHO_C" >&6; }
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
echo $ECHO_N "(cached) $ECHO_C" >&6
fi
ac_res=`eval echo '${'$as_ac_Header'}'`
{ echo "$as_me:$LINENO: result: $ac_res" >&5
echo "${ECHO_T}$ac_res" >&6; }
else
# Is the header compilable?
{ echo "$as_me:$LINENO: checking $INCVAL/uudeview.h usability" >&5
echo $ECHO_N "checking $INCVAL/uudeview.h usability... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
$ac_includes_default
#include <$INCVAL/uudeview.h>
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_header_compiler=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_compiler=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
echo "${ECHO_T}$ac_header_compiler" >&6; }
# Is the header present?
{ echo "$as_me:$LINENO: checking $INCVAL/uudeview.h presence" >&5
echo $ECHO_N "checking $INCVAL/uudeview.h presence... $ECHO_C" >&6; }
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <$INCVAL/uudeview.h>
_ACEOF
if { (ac_try="$ac_cpp conftest.$ac_ext"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } >/dev/null && {
test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
test ! -s conftest.err
}; then
ac_header_preproc=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_header_preproc=no
fi
rm -f conftest.err conftest.$ac_ext
{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
echo "${ECHO_T}$ac_header_preproc" >&6; }
# So? What about this header?
case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
yes:no: )
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: accepted by the compiler, rejected by the preprocessor!" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: proceeding with the compiler's result" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: proceeding with the compiler's result" >&2;}
ac_header_preproc=yes
;;
no:yes:* )
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: present but cannot be compiled" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: present but cannot be compiled" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: check for missing prerequisite headers?" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: check for missing prerequisite headers?" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: see the Autoconf documentation" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: see the Autoconf documentation" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: section \"Present But Cannot Be Compiled\"" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: section \"Present But Cannot Be Compiled\"" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: proceeding with the preprocessor's result" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: proceeding with the preprocessor's result" >&2;}
{ echo "$as_me:$LINENO: WARNING: $INCVAL/uudeview.h: in the future, the compiler will take precedence" >&5
echo "$as_me: WARNING: $INCVAL/uudeview.h: in the future, the compiler will take precedence" >&2;}
( cat <<\_ASBOX
## ------------------------------------------- ##
## Report this to hugbug@users.sourceforge.net ##
## ------------------------------------------- ##
_ASBOX
) | sed "s/^/$as_me: WARNING: /" >&2
;;
esac
{ echo "$as_me:$LINENO: checking for $INCVAL/uudeview.h" >&5
echo $ECHO_N "checking for $INCVAL/uudeview.h... $ECHO_C" >&6; }
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
eval "$as_ac_Header=\$ac_header_preproc"
fi
ac_res=`eval echo '${'$as_ac_Header'}'`
{ echo "$as_me:$LINENO: result: $ac_res" >&5
echo "${ECHO_T}$ac_res" >&6; }
fi
if test `eval echo '${'$as_ac_Header'}'` = yes; then
:
else
{ { echo "$as_me:$LINENO: error: \"uulib header files were not found in $INCVAL.\"" >&5
echo "$as_me: error: \"uulib header files were not found in $INCVAL.\"" >&2;}
{ (exit 1); exit 1; }; }
fi
# Check whether --with-uulib_libraries was given.
if test "${with_uulib_libraries+set}" = set; then
withval=$with_uulib_libraries; LIBVAL="$withval"
fi
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
{ echo "$as_me:$LINENO: checking for library containing UUInitialize" >&5
echo $ECHO_N "checking for library containing UUInitialize... $ECHO_C" >&6; }
if test "${ac_cv_search_UUInitialize+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_func_search_save_LIBS=$LIBS
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char UUInitialize ();
int
main ()
{
return UUInitialize ();
;
return 0;
}
_ACEOF
for ac_lib in '' uu; do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
rm -f conftest.$ac_objext conftest$ac_exeext
if { (ac_try="$ac_link"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_search_UUInitialize=$ac_res
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext
if test "${ac_cv_search_UUInitialize+set}" = set; then
break
fi
done
if test "${ac_cv_search_UUInitialize+set}" = set; then
:
else
ac_cv_search_UUInitialize=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
{ echo "$as_me:$LINENO: result: $ac_cv_search_UUInitialize" >&5
echo "${ECHO_T}$ac_cv_search_UUInitialize" >&6; }
ac_res=$ac_cv_search_UUInitialize
if test "$ac_res" != no; then
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
else
{ { echo "$as_me:$LINENO: error: \"uulib library not found in $LIBVAL.\"" >&5
echo "$as_me: error: \"uulib library not found in $LIBVAL.\"" >&2;}
{ (exit 1); exit 1; }; }
fi
fi
{ echo "$as_me:$LINENO: checking whether to include code for par-checking" >&5
echo $ECHO_N "checking whether to include code for par-checking... $ECHO_C" >&6; }
# Check whether --enable-parcheck was given.
@@ -8386,7 +8158,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by nzbget $as_me 0.3.1, which was
This file was extended by nzbget $as_me 0.4.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -8439,7 +8211,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 0.3.1
nzbget config.status 0.4.0
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

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.1, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.3.1)
AC_INIT(nzbget, 0.4.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.4.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -87,6 +87,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 +174,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 +242,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

View File

@@ -425,7 +425,7 @@ diff -urN libpar2-0.2-original/par2cmdline.h libpar2-0.2-modified/par2cmdline.h
#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-01-31 14:35:11.110360000 +0100
+++ libpar2-0.2-modified/par2repairer.cpp 2008-02-13 15:37:59.899314300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
@@ -434,3 +434,21 @@ diff -urN libpar2-0.2-original/par2repairer.cpp libpar2-0.2-modified/par2repaire
}
@@ -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

@@ -158,15 +158,12 @@ DupeCheck=no
# Do not activate this option if par-check is enabled.
RenameBroken=no
# Determine how the articles should be decoded (yenc, uulib, none)
# yenc - use internal yEnc-Decoder. Supports only yEnc-format, but is
# very fast and does not create temporary files with articles' text
# decoding them on the fly.
# uulib - use uulib to decode files. Supports many encoding formats,
# but is slow. Not available in Windows-version.
# none - the articles will not be decoded and joined. External programs
# (like "uudeview") can be used to decode and join downloaded articles.
Decoder=yEnc
# 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
@@ -257,6 +254,12 @@ DownloadRate=0
# 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 ###
@@ -268,11 +271,12 @@ CreateLog=yes
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-messages can be printed only if the programm was compiled in
# debug-mode: "./configure --enable-debug"
InfoTarget=both
WarningTarget=both
ErrorTarget=both
WarningTarget=both
InfoTarget=both
DetailTarget=both
DebugTarget=both
# Number of messages stored in buffer and available for remote clients
@@ -337,7 +341,12 @@ ServerPassword=tegbzn6789
##############################################################################
### PAR CHECK AND REPAIR ###
### 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
@@ -379,38 +388,73 @@ ParRepair=yes
# 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
##############################################################################
### POSTPROCESSING ###
# Set path to program, that must be executed after the download of
# nzb-file or one collection in nzb-file (if par-check enabled)
# 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.
# 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:
# 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;
# - state of nzb-job:
# 5 - state of nzb-job:
# 0 - there are more collections 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
# 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 clean up the directory (delete par-files,
# etc.) there are two possibilities, when you can do this:
# 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;
# 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 the next line
# 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
# 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 ###

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>
@@ -46,6 +46,9 @@
#include <signal.h>
#include <stdio.h>
#include <fcntl.h>
#ifndef DISABLE_PARCHECK
#include <iostream>
#endif
#include "nzbget.h"
#include "ServerPool.h"
@@ -74,6 +77,9 @@ void ProcessClientRequest();
void InstallSignalHandlers();
void Daemonize();
#endif
#ifndef DISABLE_PARCHECK
void DisableCout();
#endif
Thread* g_pFrontend = NULL;
Options* g_pOptions = NULL;
@@ -91,11 +97,27 @@ DiskState* g_pDiskState = NULL;
*/
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();
@@ -137,6 +159,13 @@ int main(int argc, char *argv[])
}
Run();
#ifdef WIN32
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif
#endif
return 0;
}
@@ -171,11 +200,10 @@ void Run()
g_pRemoteServer->Start();
}
// Starting a thread with the PrePostProcessor
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pPrePostProcessor = new PrePostProcessor();
g_pPrePostProcessor->Start();
}
// Create the frontend
@@ -203,7 +231,7 @@ void Run()
g_pFrontend->Start();
}
// Starting QueueCoordinator
// Starting QueueCoordinator and PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
// Standalone-mode
@@ -219,6 +247,7 @@ void Run()
}
g_pQueueCoordinator->Start();
g_pPrePostProcessor->Start();
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
@@ -325,6 +354,14 @@ void ProcessClientRequest()
{
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;
}
@@ -366,19 +403,21 @@ 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:
@@ -387,7 +426,7 @@ void SignalProc(int iSignal)
break;
default:
debug("Signal %i received", iSignal);
// printf("Signal %i received\n", iSignal);
if (SignalProcList[iSignal - 1])
{
SignalProcList[iSignal - 1](iSignal);
@@ -531,3 +570,19 @@ void Daemonize()
signal(SIGTTIN, SIG_IGN);
}
#endif
#ifndef DISABLE_PARCHECK
class NullStreamBuf : public std::streambuf
{
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,16 +31,18 @@
// 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)

View File

@@ -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"
>
@@ -363,6 +379,14 @@
RelativePath=".\RemoteServer.h"
>
</File>
<File
RelativePath=".\ScriptController.cpp"
>
</File>
<File
RelativePath=".\ScriptController.h"
>
</File>
<File
RelativePath=".\ServerPool.cpp"
>
@@ -391,6 +415,14 @@
RelativePath=".\win32.h"
>
</File>
<File
RelativePath=".\XmlRpc.cpp"
>
</File>
<File
RelativePath=".\XmlRpc.h"
>
</File>
</Files>
<Globals>
</Globals>

17
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,9 +30,6 @@
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
/* 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__
@@ -46,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.1"
#define VERSION "0.4.0"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE
@@ -62,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>