Compare commits

..

1 Commits

Author SHA1 Message Date
Andrey Prygunkov
ba24945bcd version 0.7.0 2010-04-26 21:21:23 +00:00
167 changed files with 11388 additions and 67210 deletions

View File

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

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <direct.h>
#else
@@ -77,10 +77,22 @@ ArticleDownloader::~ArticleDownloader()
{
debug("Destroying ArticleDownloader");
free(m_szTempFilename);
free(m_szArticleFilename);
free(m_szInfoName);
free(m_szOutputFilename);
if (m_szTempFilename)
{
free(m_szTempFilename);
}
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
}
void ArticleDownloader::SetTempFilename(const char* v)
@@ -98,35 +110,17 @@ void ArticleDownloader::SetInfoName(const char * v)
m_szInfoName = strdup(v);
}
/*
* How server management (for one particular article) works:
- there is a list of failed servers which is initially empty;
- level is initially 0;
void ArticleDownloader::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
<loop>
- request a connection from server pool for current level;
Exception: this step is skipped for the very first download attempt, because a
level-0 connection is initially passed from queue manager;
- try to download from server;
- if connection to server cannot be established or download fails due to interrupted connection,
try again (as many times as needed without limit) the same server until connection is OK;
- if download fails with error "Not-Found" (article or group not found) or with CRC error,
add the server to failed server list;
- if download fails with general failure error (article incomplete, other unknown error
codes), try the same server again as many times as defined by option <Retries>; if all attempts
fail, add the server to failed server list;
- if all servers from current level were tried, increase level;
- if all servers from all levels were tried, break the loop with failure status.
<end-loop>
*/
void ArticleDownloader::Run()
{
debug("Entering ArticleDownloader-loop");
SetStatus(adRunning);
BuildOutputFilename();
m_szResultFilename = m_pArticleInfo->GetResultFilename();
if (g_pOptions->GetContinuePartial())
@@ -135,61 +129,68 @@ void ArticleDownloader::Run()
{
// file exists from previous program's start
detail("Article %s already downloaded, skipping", m_szInfoName);
FreeConnection(true);
SetStatus(adFinished);
Notify(NULL);
FreeConnection(true);
return;
}
}
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
#ifdef THREADCONNECT_WORKAROUND
// NOTE: about iRemainedConnectRetries:
// Sometimes connections just do not want to work in a particular thread,
// regardless of retry count. However they work in other threads.
// If ArticleDownloader can't start download after many attempts, it terminates
// and let QueueCoordinator retry the article in a new thread.
// It wasn't confirmed that this workaround actually helps.
// Therefore it is disabled by default. Define symbol "THREADCONNECT_WORKAROUND"
// to activate the workaround.
int iRemainedConnectRetries = iRemainedDownloadRetries > 5 ? iRemainedDownloadRetries * 2 : 10;
#endif
EStatus Status = adFailed;
int iRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedRetries = iRetries;
Servers failedServers;
failedServers.reserve(g_pServerPool->GetServers()->size());
NewsServer* pWantServer = NULL;
NewsServer* pLastServer = NULL;
int iLevel = 0;
int iServerConfigGeneration = g_pServerPool->GetGeneration();
while (!IsStopped())
int iMaxLevel = g_pServerPool->GetMaxLevel();
int* LevelStatus = (int*)malloc((iMaxLevel + 1) * sizeof(int));
for (int i = 0; i <= iMaxLevel; i++)
{
LevelStatus[i] = 0;
}
int level = 0;
while (!IsStopped() && iRemainedDownloadRetries > 0)
{
SetLastUpdateTimeNow();
Status = adFailed;
SetStatus(adWaiting);
while (!m_pConnection && !(IsStopped() || iServerConfigGeneration != g_pServerPool->GetGeneration()))
while (!IsStopped() && !m_pConnection)
{
m_pConnection = g_pServerPool->GetConnection(iLevel, pWantServer, &failedServers);
m_pConnection = g_pServerPool->GetConnection(level);
usleep(5 * 1000);
}
SetLastUpdateTimeNow();
SetStatus(adRunning);
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration())
if (IsStopped())
{
Status = adFailed;
break;
}
if (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
}
pLastServer = m_pConnection->GetNewsServer();
m_pConnection->SetSuppressErrors(false);
// test connection
bool bConnected = m_pConnection && m_pConnection->Connect();
if (bConnected && !IsStopped())
{
NewsServer* pNewsServer = m_pConnection->GetNewsServer();
detail("Downloading %s @ %s (%s)", m_szInfoName, pNewsServer->GetName(), m_pConnection->GetHost());
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s @ %s", m_szInfoName, m_pConnection->GetServer()->GetHost());
Status = Download();
if (Status == adFinished || Status == adFailed || Status == adNotFound || Status == adCrcError)
{
m_ServerStats.SetStat(pNewsServer->GetID(), Status == adFinished ? 1 : 0, Status == adFinished ? 0 : 1, false);
}
}
if (bConnected)
@@ -199,6 +200,9 @@ void ArticleDownloader::Run()
m_pConnection->Disconnect();
bConnected = false;
Status = adFailed;
#ifdef THREADCONNECT_WORKAROUND
iRemainedConnectRetries--;
#endif
}
else
{
@@ -208,109 +212,89 @@ void ArticleDownloader::Run()
// free the connection, to prevent starting of thousands of threads
// (cause each of them will also free it's connection after the
// same connect-error).
FreeConnection(Status == adFinished || Status == adNotFound);
FreeConnection(Status == adFinished);
}
}
if (Status == adFinished || Status == adFatalError)
#ifdef THREADCONNECT_WORKAROUND
else
{
iRemainedConnectRetries--;
}
if (iRemainedConnectRetries == 0)
{
debug("Can't connect from this thread, retry later from another");
Status = adRetry;
break;
}
#endif
pWantServer = NULL;
if (bConnected && Status == adFailed)
{
iRemainedRetries--;
}
if (!bConnected || (Status == adFailed && iRemainedRetries > 0))
{
pWantServer = pLastServer;
}
if (pWantServer &&
!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration()))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
SetStatus(adWaiting);
int msec = 0;
while (!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration()) &&
msec < g_pOptions->GetRetryInterval() * 1000)
{
usleep(100 * 1000);
msec += 100;
}
SetLastUpdateTimeNow();
SetStatus(adRunning);
}
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration())
if (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
}
if (!pWantServer)
if (((Status == adFailed) || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
(iRemainedDownloadRetries > 1 || !bConnected) && !IsStopped())
{
failedServers.push_back(pLastServer);
// if all servers from current level were tried, increase level
// if all servers from all levels were tried, break the loop with failure status
bool bAllServersOnLevelFailed = true;
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
{
NewsServer* pCandidateServer = *it;
if (pCandidateServer->GetNormLevel() == iLevel)
{
bool bServerFailed = !pCandidateServer->GetActive() || pCandidateServer->GetMaxConnections() == 0;
if (!bServerFailed)
{
for (Servers::iterator it = failedServers.begin(); it != failedServers.end(); it++)
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
{
bServerFailed = true;
break;
}
}
}
if (!bServerFailed)
{
bAllServersOnLevelFailed = false;
break;
}
}
usleep(100 * 1000);
msec += 100;
}
}
if (bAllServersOnLevelFailed)
if (IsStopped())
{
Status = adFailed;
break;
}
if ((Status == adFinished) || (Status == adFatalError) ||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
{
break;
}
LevelStatus[level] = Status;
bool bAllLevelNotFound = true;
for (int lev = 0; lev <= iMaxLevel; lev++)
{
if (LevelStatus[lev] != adNotFound)
{
if (iLevel < g_pServerPool->GetMaxNormLevel())
{
detail("Article %s @ all level %i servers failed, increasing level", m_szInfoName, iLevel);
iLevel++;
}
else
{
detail("Article %s @ all servers failed", m_szInfoName);
Status = adFailed;
break;
}
bAllLevelNotFound = false;
break;
}
iRemainedRetries = iRetries;
}
if (bAllLevelNotFound)
{
if (iMaxLevel > 0)
{
warn("Article %s @ all servers failed: Article not found", m_szInfoName);
}
break;
}
// do not count connect-errors, only article- and group-errors
if (bConnected)
{
level++;
if (level > iMaxLevel)
{
level = 0;
}
iRemainedDownloadRetries--;
}
}
FreeConnection(Status == adFinished);
free(LevelStatus);
if (m_bDuplicate)
{
Status = adFinished;
@@ -321,19 +305,19 @@ void ArticleDownloader::Run()
Status = adFailed;
}
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
Status = adRetry;
}
if (Status == adFailed)
{
detail("Download %s failed", m_szInfoName);
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
}
else
{
warn("Download %s failed", m_szInfoName);
}
}
SetStatus(Status);
Notify(NULL);
debug("Exiting ArticleDownloader-loop");
}
@@ -370,7 +354,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
for (int retry = 3; retry > 0; retry--)
{
szResponse = m_pConnection->Request(tmp);
if ((szResponse && !strncmp(szResponse, "2", 1)) || m_pConnection->GetAuthError())
if (szResponse && !strncmp(szResponse, "2", 1))
{
break;
}
@@ -421,8 +405,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
{
if (!IsStopped())
{
detail("Article %s @ %s (%s) failed: Unexpected end of article", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
}
Status = adFailed;
break;
@@ -456,8 +439,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
{
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
detail("Article %s @ %s (%s) failed: Wrong message-id, expected %s, returned %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), m_pArticleInfo->GetMessageID(), p);
Status = adFailed;
break;
}
@@ -485,8 +467,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (!bEnd && Status == adRunning && !IsStopped())
{
detail("Article %s @ %s (%s) failed: article incomplete", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetServer()->GetHost());
Status = adFailed;
}
@@ -513,21 +494,18 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
{
if (!IsStopped())
{
detail("Article %s @ %s (%s) failed, %s: Connection closed by remote host", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment);
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))
{
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
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) || !strncmp(szResponse, "43", 2))
{
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
return adNotFound;
}
else if (!strncmp(szResponse, "2", 1))
@@ -538,8 +516,7 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
else
{
// unknown error, no special handling
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
return adFailed;
}
}
@@ -564,13 +541,13 @@ bool ArticleDownloader::Write(char* szLine, int iLen)
}
else
{
detail("Decoding %s failed: unsupported encoding", m_szInfoName);
warn("Decoding %s failed: unsupported encoding", m_szInfoName);
return false;
}
if (!bOK)
{
debug("Failed line: %s", szLine);
detail("Decoding %s failed", m_szInfoName);
warn("Decoding %s failed", m_szInfoName);
}
return bOK;
}
@@ -589,13 +566,10 @@ bool ArticleDownloader::PrepareFile(char* szLine)
{
if (!strncmp(szLine, "=ybegin ", 8))
{
if (g_pOptions->GetDupeCheck() &&
m_pFileInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
!m_pFileInfo->GetNZBInfo()->GetManyDupeFiles())
if (g_pOptions->GetDupeCheck())
{
m_pFileInfo->LockOutputFile();
bool bOutputInitialized = m_pFileInfo->GetOutputInitialized();
if (!bOutputInitialized)
if (!m_pFileInfo->GetOutputInitialized())
{
char* pb = strstr(szLine, " name=");
if (pb)
@@ -609,6 +583,11 @@ bool ArticleDownloader::PrepareFile(char* szLine)
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
}
if (m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
}
if (!g_pOptions->GetDirectWrite())
@@ -616,12 +595,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pFileInfo->SetOutputInitialized(true);
}
m_pFileInfo->UnlockOutputFile();
if (!bOutputInitialized && m_szArticleFilename &&
Util::FileExists(m_pFileInfo->GetNZBInfo()->GetDestDir(), m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
if (g_pOptions->GetDirectWrite())
@@ -634,9 +607,9 @@ bool ArticleDownloader::PrepareFile(char* szLine)
{
pb += 6; //=strlen(" size=")
long iArticleFilesize = atol(pb);
if (!CreateOutputFile(iArticleFilesize))
if (!Util::SetFileSize(m_szOutputFilename, iArticleFilesize))
{
m_pFileInfo->UnlockOutputFile();
error("Could not create file %s!", m_szOutputFilename);
return false;
}
m_pFileInfo->SetOutputInitialized(true);
@@ -663,9 +636,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pOutFile = fopen(szFilename, bDirectWrite ? "rb+" : "wb");
if (!m_pOutFile)
{
char szSysErrStr[256];
error("Could not %s file %s! Errcode: %i, %s", bDirectWrite ? "open" : "create", szFilename,
errno, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not %s file %s", bDirectWrite ? "open" : "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() == -1)
@@ -681,77 +652,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
return true;
}
/* creates output file and subdirectores */
bool ArticleDownloader::CreateOutputFile(int iSize)
{
if (g_pOptions->GetDirectWrite() && Util::FileExists(m_szOutputFilename) &&
Util::FileSize(m_szOutputFilename) == iSize)
{
// keep existing old file from previous program session
return true;
}
// delete eventually existing old file from previous program session
remove(m_szOutputFilename);
// ensure the directory exist
char szDestDir[1024];
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
strncpy(szDestDir, m_szOutputFilename, iMaxlen);
szDestDir[iMaxlen] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", szDestDir, szErrBuf);
return false;
}
if (!Util::CreateSparseFile(m_szOutputFilename, iSize))
{
error("Could not create file %s", m_szOutputFilename);
return false;
}
return true;
}
void ArticleDownloader::BuildOutputFilename()
{
char szFilename[1024];
snprintf(szFilename, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), m_pFileInfo->GetID(), m_pArticleInfo->GetPartNumber());
szFilename[1024-1] = '\0';
m_pArticleInfo->SetResultFilename(szFilename);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", szFilename);
tmpname[1024-1] = '\0';
SetTempFilename(tmpname);
if (g_pOptions->GetDirectWrite())
{
m_pFileInfo->LockOutputFile();
if (m_pFileInfo->GetOutputFilename())
{
strncpy(szFilename, m_pFileInfo->GetOutputFilename(), 1024);
szFilename[1024-1] = '\0';
}
else
{
snprintf(szFilename, 1024, "%s%c%i.out.tmp", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetID());
szFilename[1024-1] = '\0';
m_pFileInfo->SetOutputFilename(szFilename);
}
m_pFileInfo->UnlockOutputFile();
SetOutputFilename(szFilename);
}
}
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
{
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
@@ -772,8 +672,8 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
}
else
{
detail("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFailed;
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFatalError;
}
Decoder::EStatus eStatus = pDecoder->Check();
@@ -817,27 +717,27 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
remove(m_szResultFilename);
if (eStatus == Decoder::eCrcError)
{
detail("Decoding %s failed: CRC-Error", m_szInfoName);
warn("Decoding %s failed: CRC-Error", m_szInfoName);
return adCrcError;
}
else if (eStatus == Decoder::eArticleIncomplete)
{
detail("Decoding %s failed: article incomplete", m_szInfoName);
warn("Decoding %s failed: article incomplete", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eInvalidSize)
{
detail("Decoding %s failed: size mismatch", m_szInfoName);
warn("Decoding %s failed: size mismatch", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eNoBinaryData)
{
detail("Decoding %s failed: no binary data found", m_szInfoName);
warn("Decoding %s failed: no binary data found", m_szInfoName);
return adFailed;
}
else
{
detail("Decoding %s failed", m_szInfoName);
warn("Decoding %s failed", m_szInfoName);
return adFailed;
}
}
@@ -923,18 +823,11 @@ void ArticleDownloader::CompleteFileParts()
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();
char szNZBName[1024];
char szNZBDestDir[1024];
// the locking is needed for accessing the memebers of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(szNZBName, m_pFileInfo->GetNZBInfo()->GetName(), 1024);
strncpy(szNZBDestDir, m_pFileInfo->GetNZBInfo()->GetDestDir(), 1024);
g_pDownloadQueueHolder->UnlockQueue();
szNZBName[1024-1] = '\0';
szNZBDestDir[1024-1] = '\0';
char szNZBNiceName[1024];
m_pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
char InfoFilename[1024];
snprintf(InfoFilename, 1024, "%s%c%s", szNZBName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
InfoFilename[1024-1] = '\0';
if (!g_pOptions->GetDecode())
@@ -950,17 +843,33 @@ void ArticleDownloader::CompleteFileParts()
detail("Joining articles for %s", InfoFilename);
}
char szNZBDestDir[1024];
// the locking is needed for accessing the memebers of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(szNZBDestDir, m_pFileInfo->GetNZBInfo()->GetDestDir(), 1024);
g_pDownloadQueueHolder->UnlockQueue();
szNZBDestDir[1024-1] = '\0';
// Ensure the DstDir is created
char szErrBuf[1024];
if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
if (!Util::ForceDirectories(szNZBDestDir))
{
error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
error("Could not create directory %s! Errcode: %i", szNZBDestDir, errno);
SetStatus(adJoined);
return;
}
char ofn[1024];
Util::MakeUniqueFilename(ofn, 1024, szNZBDestDir, m_pFileInfo->GetFilename());
snprintf(ofn, 1024, "%s%c%s", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
ofn[1024-1] = '\0';
// prevent overwriting existing files
int dupcount = 0;
while (Util::FileExists(ofn))
{
dupcount++;
snprintf(ofn, 1024, "%s%c%s_duplicate%d", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
ofn[1024-1] = '\0';
}
FILE* outfile = NULL;
char tmpdestfile[1024];
@@ -1054,7 +963,10 @@ void ArticleDownloader::CompleteFileParts()
}
}
free(buffer);
if (buffer)
{
free(buffer);
}
if (outfile)
{
@@ -1071,24 +983,6 @@ void ArticleDownloader::CompleteFileParts()
{
error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
}
// if destination directory was changed delete the old directory (if empty)
int iLen = strlen(szNZBDestDir);
if (!(!strncmp(szNZBDestDir, m_szOutputFilename, iLen) &&
(m_szOutputFilename[iLen] == PATH_SEPARATOR || m_szOutputFilename[iLen] == ALT_PATH_SEPARATOR)))
{
debug("Checking old dir for: %s", m_szOutputFilename);
char szOldDestDir[1024];
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
strncpy(szOldDestDir, m_szOutputFilename, iMaxlen);
szOldDestDir[iMaxlen] = '\0';
if (Util::DirEmpty(szOldDestDir))
{
debug("Deleting old dir: %s", szOldDestDir);
rmdir(szOldDestDir);
}
}
}
if (!bDirectWrite || g_pOptions->GetContinuePartial())
@@ -1106,9 +1000,28 @@ void ArticleDownloader::CompleteFileParts()
}
else
{
warn("%i of %i article downloads failed for \"%s\"",
iBrokenCount + m_pFileInfo->GetMissedArticles(),
m_pFileInfo->GetTotalArticles(), InfoFilename);
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
if (g_pOptions->GetRenameBroken())
{
char brokenfn[1024];
snprintf(brokenfn, 1024, "%s_broken", ofn);
brokenfn[1024-1] = '\0';
if (Util::MoveFile(ofn, brokenfn))
{
detail("Renaming broken file from %s to %s", ofn, brokenfn);
}
else
{
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
}
strncpy(ofn, brokenfn, 1024);
ofn[1024-1] = '\0';
}
else
{
detail("Not renaming broken file %s", ofn);
}
if (g_pOptions->GetCreateBrokenLog())
{
@@ -1116,14 +1029,12 @@ void ArticleDownloader::CompleteFileParts()
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szNZBDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
FILE* file = fopen(szBrokenLogName, "ab");
fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(),
m_pFileInfo->GetTotalArticles() - iBrokenCount - m_pFileInfo->GetMissedArticles(),
m_pFileInfo->GetTotalArticles(), LINE_ENDING);
fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size(), LINE_ENDING);
fclose(file);
}
}
// the locking is needed for accessing the members of NZBInfo
// the locking is needed for accessing the memebers of NZBInfo
g_pDownloadQueueHolder->LockQueue();
m_pFileInfo->GetNZBInfo()->GetCompletedFiles()->push_back(strdup(ofn));
if (strcmp(m_pFileInfo->GetNZBInfo()->GetDestDir(), szNZBDestDir))
@@ -1144,10 +1055,9 @@ bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldD
}
// Ensure the DstDir is created
char szErrBuf[1024];
if (!Util::ForceDirectories(pNZBInfo->GetDestDir(), szErrBuf, sizeof(szErrBuf)))
if (!Util::ForceDirectories(pNZBInfo->GetDestDir()))
{
error("Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
error("Could not create directory %s! Errcode: %i", pNZBInfo->GetDestDir(), errno);
return false;
}
@@ -1163,7 +1073,13 @@ bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldD
if (strcmp(szFileName, szNewFileName))
{
// prevent overwriting of existing files
Util::MakeUniqueFilename(szNewFileName, 1024, pNZBInfo->GetDestDir(), Util::BaseFileName(szFileName));
int dupcount = 0;
while (Util::FileExists(szNewFileName))
{
dupcount++;
snprintf(szNewFileName, 1024, "%s%c%s_duplicate%d", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, Util::BaseFileName(szFileName), dupcount);
szNewFileName[1024-1] = '\0';
}
detail("Moving file %s to %s", szFileName, szNewFileName);
if (Util::MoveFile(szFileName, szNewFileName))

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -42,10 +42,10 @@ public:
{
adUndefined,
adRunning,
adWaiting,
adFinished,
adFailed,
adRetry,
adDecodeError,
adCrcError,
adDecoding,
adJoining,
@@ -72,20 +72,13 @@ private:
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
ServerStatList m_ServerStats;
EStatus Download();
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
bool CreateOutputFile(int iSize);
void BuildOutputFilename();
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
public:
ArticleDownloader();
@@ -94,13 +87,16 @@ public:
FileInfo* GetFileInfo() { return m_pFileInfo; }
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
ServerStatList* GetServerStats() { return &m_ServerStats; }
virtual void Run();
virtual void Stop();
bool Terminate();
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
@@ -115,7 +111,7 @@ class DownloadSpeedMeter
{
public:
virtual ~DownloadSpeedMeter() {};
virtual int CalcCurrentDownloadSpeed() = 0;
virtual float CalcCurrentDownloadSpeed() = 0;
virtual void AddSpeedReading(int iBytes) = 0;
};

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -34,7 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
@@ -47,25 +48,19 @@
#include "Log.h"
#include "Options.h"
#include "QueueCoordinator.h"
#include "UrlCoordinator.h"
#include "QueueEditor.h"
#include "PrePostProcessor.h"
#include "Util.h"
#include "DownloadInfo.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern UrlCoordinator* g_pUrlCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Scanner* g_pScanner;
extern void ExitProc();
extern void Reload();
const char* g_szMessageRequestNames[] =
{ "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug",
"Edit queue", "Log", "Quit", "Reload", "Version", "Post-queue", "Write log", "Scan",
"Pause/Unpause postprocessor", "History", "Download URL", "URL-queue" };
"Edit queue", "Log", "Quit", "Version", "Post-queue", "Write log", "Scan",
"Pause/Unpause postprocessor", "History" };
const unsigned int g_iMessageRequestSizes[] =
{ 0,
@@ -77,41 +72,39 @@ const unsigned int g_iMessageRequestSizes[] =
sizeof(SNZBEditQueueRequest),
sizeof(SNZBLogRequest),
sizeof(SNZBShutdownRequest),
sizeof(SNZBReloadRequest),
sizeof(SNZBVersionRequest),
sizeof(SNZBPostQueueRequest),
sizeof(SNZBWriteLogRequest),
sizeof(SNZBScanRequest),
sizeof(SNZBHistoryRequest),
sizeof(SNZBDownloadUrlRequest),
sizeof(SNZBUrlQueueRequest)
sizeof(SNZBHistoryRequest)
};
//*****************************************************************
// BinProcessor
BinRpcProcessor::BinRpcProcessor()
{
m_MessageBase.m_iSignature = (int)NZBMESSAGE_SIGNATURE;
}
void BinRpcProcessor::Execute()
{
// Read the first package which needs to be a request
if (!m_pConnection->Recv(((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature)))
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)
{
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(m_MessageBase.m_szUsername, g_pOptions->GetControlUsername())) ||
strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
// Make sure this is a nzbget request from a client
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
{
warn("nzbget request received on port %i from %s, but username or password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), m_szClientIP);
return;
}
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_pConnection->GetRemoteAddr());
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();
}
@@ -163,10 +156,6 @@ void BinRpcProcessor::Dispatch()
command = new ShutdownBinCommand();
break;
case eRemoteRequestReload:
command = new ReloadBinCommand();
break;
case eRemoteRequestVersion:
command = new VersionBinCommand();
break;
@@ -187,14 +176,6 @@ void BinRpcProcessor::Dispatch()
command = new HistoryBinCommand();
break;
case eRemoteRequestDownloadUrl:
command = new DownloadUrlBinCommand();
break;
case eRemoteRequestUrlQueue:
command = new UrlQueueBinCommand();
break;
default:
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
break;
@@ -202,7 +183,7 @@ void BinRpcProcessor::Dispatch()
if (command)
{
command->SetConnection(m_pConnection);
command->SetSocket(m_iSocket);
command->SetMessageBase(&m_MessageBase);
command->Execute();
delete command;
@@ -224,8 +205,8 @@ void BinCommand::SendBoolResponse(bool bSuccess, const char* szText)
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
// Send the request answer
m_pConnection->Send((char*) &BoolResponse, sizeof(BoolResponse));
m_pConnection->Send((char*)szText, iTextLen);
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
send(m_iSocket, (char*)szText, iTextLen, 0);
}
bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
@@ -234,7 +215,8 @@ bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
iSize -= sizeof(SNZBRequestBase);
if (iSize > 0)
{
if (!m_pConnection->Recv(((char*)pBuffer) + sizeof(SNZBRequestBase), iSize))
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
if (iBytesReceived != iSize)
{
error("invalid request");
return false;
@@ -251,8 +233,6 @@ void PauseUnpauseBinCommand::Execute()
return;
}
g_pOptions->SetResumeTime(0);
switch (ntohl(PauseUnpauseRequest.m_iAction))
{
case eRemotePauseUnpauseActionDownload:
@@ -283,7 +263,7 @@ void SetDownloadRateBinCommand::Execute()
return;
}
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate));
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
SendBoolResponse(true, "Rate-Command completed successfully");
}
@@ -296,7 +276,6 @@ void DumpDebugBinCommand::Execute()
}
g_pQueueCoordinator->LogDebugInfo();
g_pUrlCoordinator->LogDebugInfo();
SendBoolResponse(true, "Debug-Command completed successfully");
}
@@ -312,18 +291,6 @@ void ShutdownBinCommand::Execute()
ExitProc();
}
void ReloadBinCommand::Execute()
{
SNZBReloadRequest ReloadRequest;
if (!ReceiveRequest(&ReloadRequest, sizeof(ReloadRequest)))
{
return;
}
SendBoolResponse(true, "Reloading server");
Reload();
}
void VersionBinCommand::Execute()
{
SNZBVersionRequest VersionRequest;
@@ -343,29 +310,48 @@ void DownloadBinCommand::Execute()
return;
}
int iBufLen = ntohl(DownloadRequest.m_iTrailingDataLength);
char* pRecvBuffer = (char*)malloc(iBufLen);
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
char* pBufPtr = pRecvBuffer;
if (!m_pConnection->Recv(pRecvBuffer, iBufLen))
// Read from the socket until nothing remains
int iResult = 0;
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
while (NeedBytes > 0)
{
error("invalid request");
free(pRecvBuffer);
return;
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
int iPriority = ntohl(DownloadRequest.m_iPriority);
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
bool bAddTop = ntohl(DownloadRequest.m_bAddFirst);
bool bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory,
iPriority, NULL, 0, dmScore, NULL, bAddTop, bAddPaused, NULL, pRecvBuffer, iBufLen) != Scanner::asFailed;
if (NeedBytes == 0)
{
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
char tmp[1024];
snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s",
Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
if (pNZBFile)
{
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
delete pNZBFile;
SendBoolResponse(bOK, tmp);
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);
}
@@ -383,24 +369,12 @@ void ListBinCommand::Execute()
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseFileEntry));
ListResponse.m_bRegExValid = 0;
char* buf = NULL;
int bufsize = 0;
if (ntohl(ListRequest.m_bFileList))
{
eRemoteMatchMode eMatchMode = (eRemoteMatchMode)ntohl(ListRequest.m_iMatchMode);
bool bMatchGroup = ntohl(ListRequest.m_bMatchGroup);
const char* szPattern = ListRequest.m_szPattern;
RegEx *pRegEx = NULL;
if (eMatchMode == eRemoteMatchModeRegEx)
{
pRegEx = new RegEx(szPattern);
ListResponse.m_bRegExValid = pRegEx->IsValid();
}
// Make a data structure and copy all the elements of the list into it
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
@@ -412,7 +386,6 @@ void ListBinCommand::Execute()
{
NZBInfo* pNZBInfo = *it;
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
bufsize += strlen(pNZBInfo->GetName()) + 1;
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
@@ -456,17 +429,13 @@ void ListBinCommand::Execute()
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_bMatch = htonl(bMatchGroup && (!pRegEx || pRegEx->Match(pNZBInfo->GetName())));
pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1);
pListAnswer->m_iNameLen = htonl(strlen(pNZBInfo->GetName()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1);
pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1);
pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1);
bufptr += sizeof(SNZBListResponseNZBEntry);
strcpy(bufptr, pNZBInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pNZBInfo->GetName());
bufptr += ntohl(pListAnswer->m_iNameLen);
strcpy(bufptr, pNZBInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
strcpy(bufptr, pNZBInfo->GetCategory());
@@ -515,7 +484,7 @@ void ListBinCommand::Execute()
unsigned long iSizeHi, iSizeLo;
FileInfo* pFileInfo = *it;
SNZBListResponseFileEntry* pListAnswer = (SNZBListResponseFileEntry*) bufptr;
pListAnswer->m_iID = htonl(pFileInfo->GetID());
pListAnswer->m_iID = htonl(pFileInfo->GetID());
int iNZBIndex = 0;
for (unsigned int i = 0; i < pDownloadQueue->GetNZBInfoList()->size(); i++)
@@ -528,13 +497,6 @@ void ListBinCommand::Execute()
}
pListAnswer->m_iNZBIndex = htonl(iNZBIndex);
if (pRegEx && !bMatchGroup)
{
char szFilename[MAX_PATH];
snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename()));
pListAnswer->m_bMatch = htonl(pRegEx->Match(szFilename));
}
Util::SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
@@ -543,8 +505,6 @@ void ListBinCommand::Execute()
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
pListAnswer->m_iActiveDownloads = htonl(pFileInfo->GetActiveDownloads());
pListAnswer->m_iPriority = htonl(pFileInfo->GetPriority());
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
bufptr += sizeof(SNZBListResponseFileEntry);
@@ -563,8 +523,6 @@ void ListBinCommand::Execute()
g_pQueueCoordinator->UnlockQueue();
delete pRegEx;
ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries);
ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries);
ListResponse.m_iNrTrailingFileEntries = htonl(iNrFileEntries);
@@ -574,11 +532,11 @@ void ListBinCommand::Execute()
if (htonl(ListRequest.m_bServerState))
{
unsigned long iSizeHi, iSizeLo;
ListResponse.m_iDownloadRate = htonl(g_pQueueCoordinator->CalcCurrentDownloadSpeed());
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(g_pOptions->GetDownloadRate());
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
ListResponse.m_bDownloadPaused = htonl(g_pOptions->GetPauseDownload());
ListResponse.m_bDownload2Paused = htonl(g_pOptions->GetPauseDownload2());
ListResponse.m_bPostPaused = htonl(g_pOptions->GetPausePostProcess());
@@ -601,15 +559,18 @@ void ListBinCommand::Execute()
}
// Send the request answer
m_pConnection->Send((char*) &ListResponse, sizeof(ListResponse));
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
// Send the data
if (bufsize > 0)
{
m_pConnection->Send(buf, bufsize);
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
if (buf)
{
free(buf);
}
}
void LogBinCommand::Execute()
@@ -689,12 +650,12 @@ void LogBinCommand::Execute()
LogResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
m_pConnection->Send((char*) &LogResponse, sizeof(LogResponse));
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
// Send the data
if (bufsize > 0)
{
m_pConnection->Send(buf, bufsize);
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
@@ -708,75 +669,67 @@ void EditQueueBinCommand::Execute()
return;
}
int iNrIDEntries = ntohl(EditQueueRequest.m_iNrTrailingIDEntries);
int iNrNameEntries = ntohl(EditQueueRequest.m_iNrTrailingNameEntries);
int iNameEntriesLen = ntohl(EditQueueRequest.m_iTrailingNameEntriesLen);
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
int iAction = ntohl(EditQueueRequest.m_iAction);
int iMatchMode = ntohl(EditQueueRequest.m_iMatchMode);
int iOffset = ntohl(EditQueueRequest.m_iOffset);
int iTextLen = ntohl(EditQueueRequest.m_iTextLen);
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
if (iNrIDEntries * sizeof(int32_t) + iTextLen + iNameEntriesLen != iBufLength)
if (iNrEntries * sizeof(int32_t) + iTextLen != iBufLength)
{
error("Invalid struct size");
return;
}
if (iNrEntries <= 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
return;
}
char* pBuf = (char*)malloc(iBufLength);
if (!m_pConnection->Recv(pBuf, iBufLength))
{
error("invalid request");
free(pBuf);
return;
}
char* szText = NULL;
int32_t* pIDs = NULL;
if (iNrIDEntries <= 0 && iNrNameEntries <= 0)
// Read from the socket until nothing remains
char* pBufPtr = pBuf;
int NeedBytes = iBufLength;
int iResult = 0;
while (NeedBytes > 0)
{
SendBoolResponse(false, "Edit-Command failed: no IDs/Names specified");
return;
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
bool bOK = NeedBytes == 0;
char* szText = iTextLen > 0 ? pBuf : NULL;
int32_t* pIDs = (int32_t*)(pBuf + iTextLen);
char* pNames = (pBuf + iTextLen + iNrIDEntries * sizeof(int32_t));
if (bOK)
{
szText = iTextLen > 0 ? pBuf : NULL;
pIDs = (int32_t*)(pBuf + iTextLen);
}
IDList cIDList;
NameList cNameList;
if (iNrIDEntries > 0)
cIDList.reserve(iNrEntries);
for (int i = 0; i < iNrEntries; i++)
{
cIDList.reserve(iNrIDEntries);
for (int i = 0; i < iNrIDEntries; i++)
{
cIDList.push_back(ntohl(pIDs[i]));
}
cIDList.push_back(ntohl(pIDs[i]));
}
if (iNrNameEntries > 0)
{
cNameList.reserve(iNrNameEntries);
for (int i = 0; i < iNrNameEntries; i++)
{
cNameList.push_back(pNames);
pNames += strlen(pNames) + 1;
}
}
bool bOK = false;
if (iAction < eRemoteEditActionPostMoveOffset)
{
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(
iNrIDEntries > 0 ? &cIDList : NULL,
iNrNameEntries > 0 ? &cNameList : NULL,
(QueueEditor::EMatchMode)iMatchMode, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset, szText);
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
}
free(pBuf);
@@ -787,13 +740,6 @@ void EditQueueBinCommand::Execute()
}
else
{
#ifndef HAVE_REGEX_H
if ((QueueEditor::EMatchMode)iMatchMode == QueueEditor::mmRegEx)
{
SendBoolResponse(false, "Edit-Command failed: the program was compiled without RegEx-support");
return;
}
#endif
SendBoolResponse(false, "Edit-Command failed");
}
}
@@ -826,6 +772,7 @@ void PostQueueBinCommand::Execute()
{
PostInfo* pPostInfo = *it;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1;
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1;
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
@@ -848,12 +795,15 @@ void PostQueueBinCommand::Execute()
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->GetNZBInfo()->GetFilename()) + 1);
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1);
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
bufptr += sizeof(SNZBPostQueueResponseEntry);
strcpy(bufptr, pPostInfo->GetNZBInfo()->GetFilename());
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->GetNZBInfo()->GetDestDir());
@@ -875,12 +825,12 @@ void PostQueueBinCommand::Execute()
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
m_pConnection->Send((char*) &PostQueueResponse, sizeof(PostQueueResponse));
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
// Send the data
if (bufsize > 0)
{
m_pConnection->Send(buf, bufsize);
send(m_iSocket, buf, bufsize, 0);
}
free(buf);
@@ -895,36 +845,50 @@ void WriteLogBinCommand::Execute()
}
char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1);
if (!m_pConnection->Recv(pRecvBuffer, ntohl(WriteLogRequest.m_iTrailingDataLength)))
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)
{
error("invalid request");
free(pRecvBuffer);
return;
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
// Did the recv succeed?
if (iResult <= 0)
{
error("invalid request");
break;
}
pBufPtr += iResult;
NeedBytes -= iResult;
}
bool OK = true;
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
if (NeedBytes == 0)
{
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;
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");
}
SendBoolResponse(OK, OK ? "Message added to log" : "Invalid message-kind");
free(pRecvBuffer);
}
@@ -937,10 +901,8 @@ void ScanBinCommand::Execute()
return;
}
bool bSyncMode = ntohl(ScanRequest.m_bSyncMode);
g_pScanner->ScanNZBDir(bSyncMode);
SendBoolResponse(true, bSyncMode ? "Scan-Command completed" : "Scan-Command scheduled successfully");
g_pPrePostProcessor->ScanNZBDir();
SendBoolResponse(true, "Scan-Command scheduled successfully");
}
void HistoryBinCommand::Execute()
@@ -964,14 +926,15 @@ void HistoryBinCommand::Execute()
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// calculate required buffer size for nzbs
int iNrEntries = pDownloadQueue->GetHistoryList()->size();
bufsize += iNrEntries * sizeof(SNZBHistoryResponseEntry);
int iNrNZBEntries = pDownloadQueue->GetHistoryList()->size();
bufsize += iNrNZBEntries * sizeof(SNZBHistoryResponseEntry);
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
bufsize += strlen(szNicename) + 1;
NZBInfo* pNZBInfo = *it;
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
// align struct to 4-bytes, needed by ARM-processor (and may be others)
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
}
@@ -980,42 +943,36 @@ void HistoryBinCommand::Execute()
char* bufptr = buf;
// write nzb entries
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
for (NZBInfoList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
unsigned long iSizeHi, iSizeLo;
NZBInfo* pNZBInfo = *it;
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr;
pListAnswer->m_iID = htonl(pHistoryInfo->GetID());
pListAnswer->m_iKind = htonl((int)pHistoryInfo->GetKind());
pListAnswer->m_tTime = htonl((int)pHistoryInfo->GetTime());
char szNicename[1024];
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
pListAnswer->m_iNicenameLen = htonl(strlen(szNicename) + 1);
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
unsigned long iSizeHi, iSizeLo;
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus());
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
{
UrlInfo* pUrlInfo = pHistoryInfo->GetUrlInfo();
pListAnswer->m_iUrlStatus = htonl(pUrlInfo->GetStatus());
}
Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo);
pListAnswer->m_iID = htonl(pNZBInfo->GetID());
pListAnswer->m_tTime = htonl((int)pNZBInfo->GetHistoryTime());
pListAnswer->m_iSizeLo = htonl(iSizeLo);
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatus());
pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1);
pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1);
pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1);
pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1);
bufptr += sizeof(SNZBHistoryResponseEntry);
strcpy(bufptr, szNicename);
bufptr += ntohl(pListAnswer->m_iNicenameLen);
strcpy(bufptr, pNZBInfo->GetFilename());
bufptr += ntohl(pListAnswer->m_iFilenameLen);
strcpy(bufptr, pNZBInfo->GetDestDir());
bufptr += ntohl(pListAnswer->m_iDestDirLen);
strcpy(bufptr, pNZBInfo->GetCategory());
bufptr += ntohl(pListAnswer->m_iCategoryLen);
strcpy(bufptr, pNZBInfo->GetQueuedFilename());
bufptr += ntohl(pListAnswer->m_iQueuedFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pListAnswer->m_iNicenameLen = htonl(ntohl(pListAnswer->m_iNicenameLen) + 4 - (size_t)bufptr % 4);
pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
@@ -1023,126 +980,16 @@ void HistoryBinCommand::Execute()
g_pQueueCoordinator->UnlockQueue();
HistoryResponse.m_iNrTrailingEntries = htonl(iNrEntries);
HistoryResponse.m_iNrTrailingEntries = htonl(iNrNZBEntries);
HistoryResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
m_pConnection->Send((char*) &HistoryResponse, sizeof(HistoryResponse));
send(m_iSocket, (char*) &HistoryResponse, sizeof(HistoryResponse), 0);
// Send the data
if (bufsize > 0)
{
m_pConnection->Send(buf, bufsize);
}
free(buf);
}
void DownloadUrlBinCommand::Execute()
{
SNZBDownloadUrlRequest DownloadUrlRequest;
if (!ReceiveRequest(&DownloadUrlRequest, sizeof(DownloadUrlRequest)))
{
return;
}
URL url(DownloadUrlRequest.m_szURL);
if (!url.IsValid())
{
char tmp[1024];
snprintf(tmp, 1024, "Url %s is not valid", DownloadUrlRequest.m_szURL);
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
return;
}
UrlInfo* pUrlInfo = new UrlInfo();
pUrlInfo->SetURL(DownloadUrlRequest.m_szURL);
pUrlInfo->SetNZBFilename(DownloadUrlRequest.m_szNZBFilename);
pUrlInfo->SetCategory(DownloadUrlRequest.m_szCategory);
pUrlInfo->SetPriority(ntohl(DownloadUrlRequest.m_iPriority));
pUrlInfo->SetAddTop(ntohl(DownloadUrlRequest.m_bAddFirst));
pUrlInfo->SetAddPaused(ntohl(DownloadUrlRequest.m_bAddPaused));
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, ntohl(DownloadUrlRequest.m_bAddFirst));
info("Request: Queue url %s", DownloadUrlRequest.m_szURL);
char tmp[1024];
snprintf(tmp, 1024, "Url %s added to queue", DownloadUrlRequest.m_szURL);
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
void UrlQueueBinCommand::Execute()
{
SNZBUrlQueueRequest UrlQueueRequest;
if (!ReceiveRequest(&UrlQueueRequest, sizeof(UrlQueueRequest)))
{
return;
}
SNZBUrlQueueResponse UrlQueueResponse;
memset(&UrlQueueResponse, 0, sizeof(UrlQueueResponse));
UrlQueueResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
UrlQueueResponse.m_MessageBase.m_iStructSize = htonl(sizeof(UrlQueueResponse));
UrlQueueResponse.m_iEntrySize = htonl(sizeof(SNZBUrlQueueResponseEntry));
char* buf = NULL;
int bufsize = 0;
// Make a data structure and copy all the elements of the list into it
UrlQueue* pUrlQueue = g_pQueueCoordinator->LockQueue()->GetUrlQueue();
int NrEntries = pUrlQueue->size();
// calculate required buffer size
bufsize = NrEntries * sizeof(SNZBUrlQueueResponseEntry);
for (UrlQueue::iterator it = pUrlQueue->begin(); it != pUrlQueue->end(); it++)
{
UrlInfo* pUrlInfo = *it;
bufsize += strlen(pUrlInfo->GetURL()) + 1;
bufsize += strlen(pUrlInfo->GetNZBFilename()) + 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 (UrlQueue::iterator it = pUrlQueue->begin(); it != pUrlQueue->end(); it++)
{
UrlInfo* pUrlInfo = *it;
SNZBUrlQueueResponseEntry* pUrlQueueAnswer = (SNZBUrlQueueResponseEntry*) bufptr;
pUrlQueueAnswer->m_iID = htonl(pUrlInfo->GetID());
pUrlQueueAnswer->m_iURLLen = htonl(strlen(pUrlInfo->GetURL()) + 1);
pUrlQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pUrlInfo->GetNZBFilename()) + 1);
bufptr += sizeof(SNZBUrlQueueResponseEntry);
strcpy(bufptr, pUrlInfo->GetURL());
bufptr += ntohl(pUrlQueueAnswer->m_iURLLen);
strcpy(bufptr, pUrlInfo->GetNZBFilename());
bufptr += ntohl(pUrlQueueAnswer->m_iNZBFilenameLen);
// align struct to 4-bytes, needed by ARM-processor (and may be others)
if ((size_t)bufptr % 4 > 0)
{
pUrlQueueAnswer->m_iNZBFilenameLen = htonl(ntohl(pUrlQueueAnswer->m_iNZBFilenameLen) + 4 - (size_t)bufptr % 4);
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
bufptr += 4 - (size_t)bufptr % 4;
}
}
g_pQueueCoordinator->UnlockQueue();
UrlQueueResponse.m_iNrTrailingEntries = htonl(NrEntries);
UrlQueueResponse.m_iTrailingDataLength = htonl(bufsize);
// Send the request answer
m_pConnection->Send((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
// Send the data
if (bufsize > 0)
{
m_pConnection->Send(buf, bufsize);
send(m_iSocket, buf, bufsize, 0);
}
free(buf);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -33,21 +33,23 @@
class BinRpcProcessor
{
private:
SOCKET m_iSocket;
SNZBRequestBase m_MessageBase;
Connection* m_pConnection;
const char* m_szClientIP;
void Dispatch();
public:
BinRpcProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
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:
Connection* m_pConnection;
SOCKET m_iSocket;
SNZBRequestBase* m_pMessageBase;
bool ReceiveRequest(void* pBuffer, int iSize);
@@ -56,7 +58,7 @@ protected:
public:
virtual ~BinCommand() {}
virtual void Execute() = 0;
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
};
@@ -108,12 +110,6 @@ public:
virtual void Execute();
};
class ReloadBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class VersionBinCommand: public BinCommand
{
public:
@@ -144,16 +140,4 @@ public:
virtual void Execute();
};
class DownloadUrlBinCommand: public BinCommand
{
public:
virtual void Execute();
};
class UrlQueueBinCommand: public BinCommand
{
public:
virtual void Execute();
};
#endif

899
ChangeLog
View File

@@ -1,900 +1,3 @@
nzbget-12.0:
- added RSS feeds support:
- new options "FeedX.Name", "FeedX.URL", "FeedX.Filter",
"FeedX.Interval", "FeedX.PauseNzb", "FeedX.Category",
"FeedX.Priority" (section "Rss Feeds");
- new option "FeedHistory" (section "Download Queue");
- button "Preview Feed" on settings tab near each feed definition;
- new toolbar button "Feeds" on downloads tab with menu to
view feeds or fetch new nzbs from all feeds (the button is
visible only if there are feeds defined in settings);
- new dialog to see feed content showing status of each item (new,
fetched, backlog) with ability to manually fetch selected items;
- powerful filters for RSS feeds;
- new dialog to build filters in web-interface with instant preview;
- added download health monitoring:
- health indicates download status, whether the file is damaged
and how much;
- 100% health means no download errors occurred; 0% means all
articles failed;
- there is also a critical health which is calculated for each
nzb-file based on number and size of par-files;
- if during download the health goes down below 100% a health
badge appears near download name indicating the necessity of
par-repair; the indicator can be orange (repair may be possible)
or red (unrepairable) if the health goes down below critical health;
- new option "HealthCheck" to define what to do with unhealthy
(unrepairable) downloads (pause, delete, none);
- health and critical health are displayed in download-edit dialog;
health is displayed in history dialog; if download was aborted
(HealthCheck=delete) this is indicated in history dialog;
- health allows to determine download status for downloads which
have unpack and/or par-check disabled; for such downloads the
status in history is shown based on health: success (health=100%),
damaged (health > critical) or failure (health < critical);
- par-check is now automatically started for downloads having
health below 100%; this works independently of unpack (even if
unpack is disabled);
- for downloads having health less than critical health no par-check
is performed (it would fail); Instead the par-check status is
set to "failure" automatically saving time of actual par-check;
- new fields "Health" and "CriticalHealth" are returned by
RPC-Method "listgroups";
- new fields "Health", "CriticalHealth", "Deleted" and "HealthDeleted"
are returned by RPC-Method "history";
- new parameters "NZBPP_HEALTH" and "NZBPP_CRITICALHEALTH" are passed
to pp-scripts;
- added collecting of server usage statistical data for each download:
- number of successful and failed article downloads per news server;
- new page in history dialog shows collected statistics;
- new fields in RPC-method "history": ServerStats (array),
TotalArticles, SuccessArticles, FailedArticles;
- new env. vars passed to pp-scripts: NZBPP_TOTALARTICLES,
NZBPP_SUCCESSARTICLES, NZBPP_FAILEDARTICLES and per used news
server: NZBPP_SERVERX_SUCCESSARTICLES, NZBPP_SERVERX_FAILEDARTICLES;
- also new env.var HEALTHDELETED;
- added smart duplicates feature:
- mostly for use with RSS feeds;
- automatic detection of duplicate nzb-files to avoid download of
duplicates;
- nzb-files can be also manually marked as duplicates;
- if download fails - automatically choose another release (duplicate);
- if download succeeds all remaining duplicates are skipped (not downloaded);
- download items have new properties to tune duplicate handling
behavior: duplicate key, duplicate score and duplicate mode;
- if download was deleted by duplicate check its status in the
history is shown as "DUPE";
- new actions "GroupSetDupeKey", "GroupSetDupeScore", "GroupSetDupeMode",
"HistorySetDupeKey", "HistorySetDupeScore", "HistorySetDupeMode",
"HistoryMarkBad" and "HistoryMarkGood" of RPC-command "editqueue";
new actions "B" and "G" of command "--edit/-E" for history items
(subcommand "H");
- when deleting downloads from queue there are three options now:
"move to history", "move to history as duplicate" and "delete
without history tracking";
- new actions "GroupDupeDelete", "GroupFinalDelete" and
"HistorySetDupeBackup" in RPC-method "editqueue";
- RPC-commands "listgroups", "postqueue" and "history" now return
more info about nzb-item (many new fields);
- removed option "MergeNzb" because it conflicts with duplicate
handling, items can be merged manually if necessary;
- automatic detection of exactly same nzb-files (same content)
coming from different sources (different RSS feeds etc.);
individual files (inside nzb-file) having extensions listed in
option "ExtCleanupDisk" are excluded from content comparison
(unless these are par2-files, which are never excluded);
- when history item expires (as defined by option "KeepHistory")
and the duplicate check is active (option "DupeCheck") the item
is not completely deleted from history; instead the amount of
stored data reduces to minimum required for duplicate check
(about 200 bytes vs 2000 bytes for full history item);
- such old history items are not shown in web-interface by default
(to avoid transferring of large amount of history items);
- new button "Hidden" in web-interface to show hidden history items;
the items are marked with badge "hidden";
- RPC-method "editqueue" has now two actions to delete history
records: "HistoryDelete", "HistoryFinal"; action "HistoryDelete"
which has existed before now hides records, already hidden records
are ignored;
- added functions "Mark as Bad" and "Mark as Good" for history
items;
- duplicate properties (dupekey, dupescore and dupemode) can now
be viewed and changed in download-edit-dialog and
history-edit-dialog via new button "Dupe";
- for full documentation see http://nzbget.sourceforge.net/RSS#Duplicates;
- created NZBGet.app - NZBGet is now a user friendly Mac OSX application
with easy installation and seamless integration into OS UI:
works in background, is controlled from a web-browser, few
important functions are accessible via menubar icon;
- better Windows package:
- unrar is included;
- several options are set to better defaults;
- all paths are set as relative paths to program directory;
the program can be started after installation without editing
anything in config;
- included two new batch-files:
- nzbget-start.bat - starts program in normal mode (dos box);
- nzbget-recovery-mode.bat - starts with empty password (dos box);
- both batch files open browser window with correct address;
- config-file template is stored in nzbget.conf.template;
- nzbget.conf is not included in the package. When the program is
started for the first time (using one of batch files) the template
config file is copied into nzbget.conf;
- updates will be easy in the future: to update the program all
files from newer archive must be extracted over old files. Since
the archive doesn't have nzbget.conf, the existing config is kept
unmodified. The template config file will be updated;
- added file README-WINDOWS.txt with small instructions;
- version string now includes revision number (like "r789");
- added automatic updates:
- new button "Check for updates" on settings tab of web-interface,
in section "SYSTEM", initiates check and shows dialog allowing to
install new version;
- it is possible to choose between stable, testing and development
branches;
- this feature is for end-users using binary packages created and
updated by maintainers, who need to write an update script specific
for platform;
- the script is then called by NZBGet when user clicks on install-button;
- the script must download and install new version;
- for more info visit http://nzbget.sourceforge.net/Packaging;
- news servers can now be temporarily disabled via speed limit dialog
without reloading of the program:
- new option "ServerX.Active" to disable servers via settings;
- new option "ServerX.Name" to use for logging and in UI;
- changed the way how option "Unpack" works:
- instead of enabling/disabling the unpacker as a whole, it now
defines the initial value of post-processing parameter "Unpack"
for nzb-file when it is added to queue;
- this makes it now possible to disable Unpack globally but still
enable it for selected nzb-files;
- new option "CategoryX.Unpack" to set unpack on a per category basis;
- combined all footer buttons into one button "Actions" with menu:
- in download-edit-dialog: "Pause/Resume", "Delete" and "Cancel
Post-Processing";
- in history-dialog: "Delete", "Post-Process Again" and "Download
Remaining Files (Return to Queue)";
- DirectNZB headers X-DNZB-MoreInfo and X-DNZB-Details are now processed
when downloading URLs and the links "More Info" and "Details" are shown
in download-edit-dialog and in history-dialog in Actions menu;
- program can now be stopped via web-interface: new button "shutdown"
in section "SYSTEM";
- added menu "View" to settings page which allows to switch to "Compact Mode"
when option descriptions are hidden;
- added confirmation dialog by leaving settings page if there are unsaved
changes;
- downloads manually deleted from queue are shown with status "deleted"
in the history (instead of "unknown");
- all table columns except "Name" now have fixed widths to avoid annoying
layout changes especially during post-processing when long status messages
are displayed in the name-column;
- added filter buttons to messages tab (info, warning, etc.);
- added automatic par-renaming of extracted files if archive includes
par-files;
- added support for http redirects when fetching URLs;
- added new command "Download again" for history items; new action
"HistoryRedownload" of RPC-method "editqueue"; for controlling via command
line: new action "A" of subcommand "H" of command "--edit/-E";
- download queue is now saved in a more safe way to avoid potential loss
of queue if the program crashes during saving of queue;
- destination directory for option "CategoryX.DestDir" is not checked/created
on program start anymore (only when a download starts for that category);
this helps when certain categories are configured for external disks,
which are not always connected;
- added new option "CategoryX.Aliases" to configure category name matching
with nzb-sites; especially useful with rss feeds;
- in RPC-Method "appendurl" parameter "addtop" adds nzb to the top of
the main download queue (not only to the top of the URL queue);
- new logo (thanks to dogzipp for the logo);
- added support for metatag "password" in nzb-files;
- pp-scripts which move files can now inform the program about new
location by printing text "[NZB] FINALDIR=/path/to/files"; the final
path is then shown in history dialog instead of download path;
- new env-var "NZBPP_FINALDIR" passed to pp-scripts;
- pp-scripts can now set post-processing parameters by printing
command "[NZB] NZBPR_varname=value"; this allows scripts which are
executed sooner to pass data for scripts executed later;
- added new option "AuthorizedIP" to set the list of IP-addresses which
may connect without authorization;
- new option "ParRename" to force par-renaming as a first post-processing
step (active by default); this saves an unpack attempt and is even more
useful if unpack is disabled;
- post-processing progress label is now automatically trimmed if it
doesn't fill into one line; this avoids layout breaking if the text
is too long;
- reversed the order of priorities in comboboxes in dialogs: the highest
priority - at the top, the lowest - at the bottom;
- small changes in button captions: edit dialogs called from settings
page (choose script, choose order, build rss filter) now have buttons
"Discard/Apply" instead of "Close/Save"; in all other dialogs button
"Close" renamed to "Cancel" unless it was the only button in dialog;
- small change in css: slightly reduced the max height of modal dialogs
to better work on notebooks;
- options "DeleteCleanupDisk" and "NzbCleanupDisk" are now active by
default (in the example config file);
- extended add-dialog with options "Add paused" and "Disable duplicate check";
- source nzb-files are now deleted when download-item leaves queue and
history (option "NzbCleanupDisk");
- when deleting downloads from queue the messages about deleted
individual files are now printed as "detail" instead of "info";
- failed article downloads are now logged as "detail" instead of
"warning" to reduce number of warnings for downloads removed from
server (DMCA); one warning is printed for a file with a summary of
number of failed downloads for the file;
- tuned algorithm calculating maximum threads limit to allow more
threads for backup server connections (related to option "TreadLimit"
removed in v11); this may sometimes increase speed when backup servers
were used;
- by adding nzb-file to queue via RPC-methods "append" and "appendurl"
the actual format of the file is checked and if nzb-format is detected
the file is added even if it does not have .nzb extension;
- added new option "UrlForce" to allow URL-downloads (including fetching
of RSS feeds and nzb-files from feeds) even if download is paused;
the option is active by default;
- destination directory for option "DestDir" is not checked/created on
program start anymore (only when a download starts); this helps when
DestDir is mounted to a network drive which is not available on program start;
- added special handling for files ".AppleDouble" and ".DS_Store" during
unpack to avoid problems on NAS having support for AFP protocol (used
on Mac OSX);
- history records with failed script status are now shown as "PP-FAILURE"
in history list (instead of just "FAILURE");
- option "DiskSpace" now checks space on "InterDir" in addition to
"DestDir";
- support for rar-archives with non-standard extensions is now limited
to file extensions consisting of digits; this is to avoid extracting
of rar-archives having non-rar extensions on purpose (example: .cbr);
- if option "ParRename" is disabled (not recommended) unpacker does
not initiate par-rename anymore, instead the full par-verify is
performed then;
- for external script the exec-permissions are now added automatically;
this makes the installation of pp-scripts and other scripts easier;
- option "InterDir" is now active by default;
- when option "InterDir" is used the intermediate destination directory
names now include unique numbers to avoid several downloads with same
name to use the same directory and interfere with each other;
- when option "UnpackCleanupDisk" is active all archive files are now
deleted from download directory without relying on output printed by
unrar; this solves issues with non-ascii-characters in archive file
names on some platforms and especially in combination with rar5;
- improved handling of non-ascii characters in file names on windows;
- added support for rar5-format when checking signatures of archives
with non-standard file extensions;
- small restructure in settings order:
- combined sections "REMOTE CONTROL" and "PERMISSIONS" into one
section with name "SECURITY";
- moved sections "CATEGORIES" and "RSS FEEDS" higher in the
section list;
- improved par-check: if main par2-file is corrupted and can not be
loaded other par2-files are downloaded and then used as replacement
for main par2-file;
- if unpack did not find archive files the par-check is not requested
anymore if par-rename was already done;
- better handling of obfuscated nzb-files containing multiple files
with same names; removed option "StrictParName" which was not working
good with obfuscated files; if more par-files are required for repair
the files with strict names are tried first and then other par-files;
- added new scheduler commands "ActivateServer", "DeactivateServer" and
"FetchFeed"; combined options "TaskX.DownloadRate" and "TaskX.Process"
into one option "TaskX.Param", also used by new commands;
- added status filter buttons to history page;
- if unpack fails with write error (usually because of not enough space
on disk) this is shown as status "Unpack: space" in web-interface;
this unpack-status is handled as "success" by duplicate handling
(no download of other duplicate); also added new unpack-status "wrong
password" (only for rar5-archives); env.var. NZBPP_UNPACKSTATUS has
two new possible values: 3 (write error) and 4 (wrong password);
updated pp-script "EMail.py" to support new unpack-statuses;
- fixed a potential seg. fault in a commonly used function;
- added new option "TimeCorrection" to adjust conversion from system
time to local time (solves issues with scheduler when using a
binary compiled for other platform);
- NZBIDs are now generated with more care avoiding numbering holes
possible in previous versions;
- fixed: invalid "Offset" passed to RPC-method "editqueue" or command
line action "-E/--edit" could crash the program;
- fixed: crash after downloading of an URL (happen only on certain systems);
- fixed: restoring of settings didn't work for multi-sections (servers,
categories, etc.) if they were empty;
- fixed: choosing local files didn't work in Opera;
- fixed: certain characters printed by pp-scripts could crash the
program;
- fixed: malformed nzb-file could cause a memory leak;
- fixed: when a duplicate file was detected in collection it was
automatically deleted (if option DupeCheck is active) but the
total size of collection was not updated;
- when deleting individual files the total count of files in collection
was not updated;
- fixed: when multiple nzb-files were added via URL (rss including) at
the same time the info about category and priority could get lost for
some of files;
- fixed: if unpack fails the created destination directory was not
automatically removed (only if option "InterDir" was active);
- fixed scrolling to the top of page happening by clicking on items in
downloads/history lists and on action-buttons in edit-download and
history dialogs;
- fixed potential buffer overflow in remote client;
- improved error reporting when creation of temporary output file fails;
- fixed: when deleting download, if all remaining queued files are
par2-files the disk cleanup should not be performed, but it was
sometimes;
- fixed a potential problem in incorrect using of one library function.
nzbget-11.0:
- reworked concept of post-processing scripts:
- multiple scripts can be assigned to each nzb-file;
- all assigned scripts are executed after the nzb-file is
downloaded and internally processed (unpack, repair);
- option <PostProcess> is obsolete;
- new option <ScriptDir> sets directory where all pp-scripts must
be stored;
- new option <DefScript> sets the default list of pp-scripts to
be assigned to nzb-file when it's added to queue;
- new option <CategoryX.DefScript> to set the default list of
pp-scripts on a category basis;
- the execution order of pp-scripts can be set using new option
<ScriptOrder>;
- there are no separate configuration files for pp-scripts;
- configuration options and pp-parameters are defined in the
pp-scripts;
- script configuration options are saved in nzbget configuration
file (nzbget.conf);
- changed parameters list of RPC-methods <loadconfig> and
<saveconfig>;
- new RPC-method <configtemplates> returns configuration
descriptions for the program and for all pp-scripts;
- configuration of all scripts can be done in web-interface;
- the pp-scripts assigned to a particular nzb-file can be viewed
and changed in web-interface on page <pp-parameters> in the
edit download dialog;
- option <PostPauseQueue> renamed to <ScriptPauseQueue> (the old
name is still recognized);
- new option <ConfigTemplate> to define the location of template
configuration file (in previous versions it must be always
stored in <WebDir>);
- history dialog shows status of every script;
- the old example post-processing script replaced with two new scripts:
- EMail.py - sends E-Mail notification;
- Logger.py - saves the full post-processing log of the job into
file _postprocesslog.txt;
- both pp-scripts are written in python and work on Windows too
(in addition to Linux, Mac, etc.);
- added possibility to set post-processing parameters for history items:
- pp-parameters can now be viewed and changed in history dialog
in web-interface;
- useful before post-processing again;
- new action <HistorySetParameter> in RPC-method <editqueue>;
- new action <O> in remote command <--edit/-E> for history items
(subcommand <H>);
- added new feature <split download> which creates new download from
selected files of source download;
- new command <Split> in web-interface in edit download dialog
on page <Files>;
- new action <S> in remote command <--edit/-E>;
- new action <FileSplit> in JSON-/XML-RPC method <editqueue>;
- added support for manual par-check:
- if option <ParCheck> is set to <Manual> and a damaged download
is detected the program downloads all par2-files but doesn't
perform par-check; the user must perform par-check/repair
manually then (possibly on another, faster computer);
- old values <yes/no> of option <ParCheck> renamed to <Force>
and <Auto> respectively;
- when set to <Force> all par2-files are always downloaded;
- removed option <LoadPars> since its functionality is now
covered by option <ParCheck>;
- result of par-check can now have new value <Manual repair
necessary>;
- field <ParStatus> in RPC-method <history> can have new value
<MANUAL>;
- parameter <NZBPP_PARSTATUS> for pp-script can have new value
<4 = manual repair necessary>;
- when download is resumed in web-interface the option <ParCheck=Force>
is respected and all par2-files are resumed (not only main par2-file);
- automatic deletion of backup-source files after successful par-repair;
important when repairing renamed rar-files since this could cause
failure during unpack;
- par-checker and renamer now add messages into the log of pp-item
(like unpack- and pp-scripts-messages); these message now appear in
the log created by scripts Logger.py and EMail.py;
- when a nzb-file is added via web-interface or via remote call the
file is now put into incoming nzb-directory (option "NzbDir") and
then scanned; this has two advantages over the old behavior when the
file was parsed directly in memory:
- the file serves as a backup for troubleshootings;
- the file is processed by nzbprocess-script (if defined in
option "NzbProcess") making the pre-processing much easier;
- new env-var parameters are passed to NzbProcess-script: NZBNP_NZBNAME,
NZBNP_CATEGORY, NZBNP_PRIORITY, NZBNP_TOP, NZBNP_PAUSED;
- new commands for use in NzbProcess-scripts: "[NZB] TOP=1" to add nzb
to the top of queue and "[NZB] PAUSED=1" to add nzb-file in paused state;
- reworked post-processor queue:
- only one job is created for each nzb-file; no more separate
jobs are created for par-collections within one nzb-file;
- option <AllowReProcess> removed; a post-processing script is
called only once per nzb-file, this behavior cannot be altered
anymore;
- with a new feature <Split> individual par-collections can be
processed separately in a more effective way than before
- improved unicode (utf8) support:
- non-ascii characters are now correctly transferred via JSON-RPC;
- correct displaying of nzb-names and paths in web-interface;
- it is now possible to use non-ascii characters on settings page
for option values (such as paths or category names);
- improved unicode support in XML-RPC and JSON-RPC;
- if username and password are defined for a news-server the
authentication is now forced (in previous versions the authentication
was performed only if requested by server); needed for servers
supporting both anonymous (restricted) and authorized (full access)
accounts;
- added option <ExtCleanupDisk> to automatically delete unwanted files
(with specified extensions or names) after successful par-check or unpack;
- improvement in JSON-/XML-RPC:
- all ID fields including NZBID are now persistent and remain
their values after restart;
- this allows for third-party software to identify nzb-files by
ID;
- method <history> now returns ID of NZB-file in the field
<NZBID>;
- in versions up to 0.8.0 the field <NZBID> was used to identify
history items in the edit-commands <HistoryDelete>,
<HistoryReturn>, <HistoryProcess>; since version 9 field <ID>
is used for this purpose; in versions 9-10 field <NZBID> still
existed and had the same value as field <ID> for compatibility
with version 0.8.0; the compatibility is not provided anymore;
this change was needed to provide a consistent using of field
<NZBID> across all RPC-methods;
- added support for rar-files with non-standard extensions (such as
.001, etc.);
- added functions to backup and restore settings from web-interface;
when restoring it's possible to choose what sections to restore
(for example only news servers settings or only settings of a
certain pp-script) or restore the whole configuration;
- new option "ControlUsername" to define login user name (if you don't
like default username "nzbget");
- if a communication error occurs in web-interface, it retries multiple
times before giving up with an error message;
- the maximum number of download threads are now managed automatically
taking into account the number of allowed connections to news servers;
removed option <ThreadLimit>;
- pp-scripts terminated with unknown status are now considered failed
(status=FAILURE instead of status=UNKNOWN);
- new parameter (env. var) <NZBPP_NZBID> is passed to pp_scripts and
contains an internal ID of NZB-file;
- improved thread synchronisation to avoid (short-time) lockings of
the program during creation of destination files;
- more detailed error message if a directory could not be created
(<DstDir>, <NzbDir>, etc.); the message includes error text reported
by OS such as <permission denied> or similar;
- when unpacking the unpack start time is now measured after receiving
of unrar copyright message; this provides better unpack time
estimation in a case when user uses unpack-script to do some things
before executing unrar (for example sending Wake-On-Lan message to
the destination NAS); it works with unrar only, it's not possible
with 7-Zip because it buffers printed messages;
- when the program is reloaded, a message with version number is
printed like on start;
- configuration can now be saved in web-interface even if there were
no changes made but if obsolete or invalid options were detected in
the config file; the saving removes invalid entries from config file;
- option <ControlPassword> can now be set to en empty value to disable
authentication; useful if nzbget works behind other web-server with
its own authentication;
- when deleting downloads via web-interface a proper hint regarding
deleting of already downloaded files from disk depending on option
<DeleteCleanupDisk> is displayed;
- if a news-server returns empty or bad article (this may be caused
by errors on the news server), the program tries again from the same
or other servers (in previous versions the article was marked as
failed without other download attempts);
- when a nzb-file whose name ends with ".queued" is added via web-
interface the ".queued"-part is automatically removed;
- small improvement in multithread synchronization of download queue;
- added link to catalog of pp-scripts to web-interface;
- updated forum URL in about dialog in web-interface;
- small correction in a log-message: removed <Request:> from message
<Request: Queue collection...>;
- removed option "ProcessLogKind"; scripts should use prefixes ([INFO],
[DETAIL], etc); messages printed without prefixes are added as [INFO];
- removed option "AppendNzbDir"; if it was disabled that caused problems
in par-checker and unpacker; the option is now assumed always active;
- removed option "RenameBroken"; it caused problems in par-checker
(the option existed since early program versions before the par-check
was added);
- configure-script now defines "SIGCHLD_HANDLER" by default on all
systems including BSD; this eliminates the need of configure-
parameter "--enable-sigchld-handler" on 64-Bit BSD; the trade-off:
32-Bit BSD now requires "--disable-sigchld-handler";
- improved configure-script: defining of symbol "FILE_OFFSET_BITS=64",
required on some systems, is not necessary anymore;
- fixed: in the option "NzbAddedProcess" the env-var parameter with
nzb-name was passed in "NZBNA_NAME", should be "NZBNA_NZBNAME";
the old parameter name "NZBNA_NAME" is still supported for
compatibility;
- fixed: download time in statistics were incorrect if the computer
was put into standby (thanks Frank Kuypers for the patch);
- fixed: when option <InterDir> was active and the download after
unpack contained rar-file with the same name as one of original
files (sometimes happen with included subtitles) the original
rar-file was kept with name <.rar_duplicate1> even if the option
<UnpackCleanupDisk> was active;
- fixed: failed to read download queue from disk if post-processing
queue was not empty;
- fixed: when a duplicate file was detected during download the
program could hang;
- fixed: symbol <DISABLE_TLS> must be defined in project settings;
defining it in <win32.h> didn't work properly (Windows only);
- fixed: crash when adding malformed nzb-files with certain
structure (Windows only);
- fixed: by deleting of a partially downloaded nzb-file from queue,
when the option <DeleteCleanupDisk> was active, the file
<_brokenlog.txt> was not deleted preventing the directory from
automatic deletion;
- fixed: if an error occurs when a RPC-client or web-browser
communicates with nzbget the program could crash;
- fixed: if the last file of collection was detected as duplicate
after the download of the first article the file was deleted from
queue (that's OK) but the post-processing was not triggered
(that's a bug);
- fixed: support for splitted files (.001, .002, etc.) were broken.
nzbget-10.2:
- fixed potential segfault which could happen with file paths longer
than 1024 characters;
- fixed: when options <DirectWrite> and <ContinuePartial> were both
active, a restart or reload of the program during download may cause
damaged files in the active download;
- increased width of speed indication ui-element to avoid layout
breaking on some linux-browsers;
- fixed a race condition in unpacker which could lead to a segfault
(although the chances were low because the code wasn't executed often).
nzbget-10.1:
- fixed: articles with decoding errors (incomplete or damaged posts)
caused infinite retry-loop in downloader.
nzbget-10.0:
- added built-in unpack:
- rar and 7-zip formats are supported (via external Unrar and
7-Zip executables);
- new options <Unpack>, <UnpackPauseQueue>, <UnpackCleanupDisk>,
<UnrarCmd>, <SevenZipCmd>;
- web-interface now shows progress and estimated time during
unpack (rar only; for 7-Zip progress is not available due to
limitations of 7-Zip);
- when built-in unpack is enabled, the post-processing script is
called after unpack and possibly par-check/repair (if needed);
- for nzb-files containing multiple collections (par-sets) the
post-processing script is called only once, after the last
par-set;
- new parameter <NZBPP_UNPACKSTATUS> passed to post-processing
script;
- if the option <AllowReProcess> is enabled the post-processing-
script is called after each par-set (as in previous versions);
- example post-processing script updated: removed unrar-code,
added check for unpack status;
- new field <UnpackStatus> in result of RPC-method <history>;
- history-dialog in web-interface shows three status: par-status,
unpack-status, script-status;
- with two built-in special post-processing parameters <*Unpack:>
and <*Unpack:Password> the unpack can be disabled for individual
nzb-file or the password can be set;
- built-in special post-processing parameters can be set via web-
interface on page <PP-Parameters> (when built-in unpack is
enabled);
- added support for HTTPS to the built-in web-server (web-interface and
XML/JSON-RPC):
- new options <SecureControl>, <SecurePort>, <SecureCert> and
<SecureKey>;
- module <TLS.c/h> completely rewritten with support for server-
side sockets, newer versions of GnuTLS, proper thread lockings
in OpenSSL;
- improved the automatic par-scan (option <ParScan=auto>) to
significantly reduce the verify-time in some common cases with renamed
rar-files:
- the extra files are scanned in an optimized order;
- the scan stops when all missings files are found;
- added fast renaming of intentionally misnamed (rar-) files:
- the new renaming algorithm doesn't require full par-scan and
restores original filenames in just a few seconds, even on very
slow computers (NAS, media players, etc.);
- the fast renaming is performed automatically when requested by
the built-in unpacker (option <Unpack> must be active);
- added new option <InterDir> to put intermediate files during download
into a separate directory (instead of storing them directly in
destination directory (option <DestDir>):
- when nzb-file is completely (successfully) downloaded, repaired
(if neccessary) and unpacked the files are moved to destination
directory (option <DestDir> or <CategoryX.DestDir>);
- intermediate directory can significantly improve unpack
performance if it is located on a separate physical hard drive;
- added new option <ServerX.Cipher> to manually select cipher for
encrypted communication with news server:
- manually choosing a faster cipher (such as <RC4>) can
significantly improve performance (if CPU is a limiting factor);
- major improvements in news-server/connection management (main and fill
servers):
- if download of article fails, the program tries all servers of
the same level before trying higher level servers;
- this ensures that fill servers are used only if all main servers
fail;
- this makes the configuring of multiple servers much easier than
before: in most cases the simple configuration of level 0 for
all main servers and level 1 for all fill servers suffices;
- in previous versions the level was increased immediately after
the first tried server of the level failed; to make sure all
main servers were tried before downloading from fill servers it
was required to create complex server configurations with
duplicates; these configurations were still not as effective as
now;
- do not reconnect on <article/group not found> errors since this
doesn't help but unnecessary increases CPU load and network
traffic;
- removed option <RetryOnCrcError>; it's not required anymore;
- new option <ServerX.Group> allows more flexible configuration
of news servers when using multiple accounts on the same server;
with this option it's also possible to imitate the old server
management behavior regarding levels;
- news servers configuration is now less error-prone:
- the option <ServerX.Level> is not required to start from <0> and
when several news servers are configured the Levels can be any
integers - the program sorts the servers and corrects the Levels
to 0,1,2,etc. automatically if needed;
- when option <ServerX.Connections> is set to <0> the server is
ignored (in previous version such a server could cause hanging
when the program was trying to go to the next level);
- if no news servers are defined (or all definitions are invalid)
a warning is printed to inform that the download is not
possible;
- categories can now have their own destination directories; new option
<CategoryX.DestDir>;
- new feature <Pause for X Minutes> in web-interface; new XML-/JSON-RPC
method <scheduleresume>;
- improved the handling of hanging connections: if a connection hangs
longer than defined by option <ConnectionTimeout> the program tries to
gracefully close connection first (this is new); if it still hangs
after <TerminateTimeout> the download thread is terminated as a last
resort (as in previous versions);
- added automatic speed meter recalibration to recover after possible
synchronization errors which can occur when the option <AccurateRate>
is not active; this makes the default (less accurate but fast) speed
meter almost as good as the accurate one; important when speed
throttling is active;
- when the par-checked requests more par-files, they get an extra
priority and are downloaded before other files regardless of their
priorities; this is needed to avoid hanging of par-checker-job if a
file with a higher priority gets added to queue during par-check;
- when post-processing-parameters are passed to the post-processing
script a second version of each parameter with a normalized parameter-
name is passed in addition to the original parameter name; in the
normalized name the special characters <*> and <:> are replaced with
<_> and all characters are passed in upper case; this is important for
internal post-processing-parameters (*Unpack:=yes/no) which include
special characters;
- warning <Non-nzbget request received> now is not printed when the
connection was aborted before the request signature was read;
- changed formatting of remaining time for post-processing to short
format (as used for remaining download time);
- added link to article <Performance tips> to settings tab on web-
interface;
- removed hint <Post-processing script may have moved files elsewhere>
from history dialog since it caused more questions than helped;
- changed default value for option <ServerX.JoinGroup> to <no>; most
news servers nowadays do not require joining the group and many
servers do not keep headers for many groups making the join-command
fail even if the articles still can be successfully downloaded;
- small change in example post-processing script: message <Deleting
source ts-files> are now printed only if ts-files really existed;
- improved configure-script:
- libs which are added via pkgconfig are now put into LIBS instead
of LDFLAGS - improves compatibility with newer Linux linkers;
- OpenSSL libs/includes are now added using pkgconfig to better
handle dependencies;
- additional check for libcrypto (part of OpenSSL) ensures the
library is added to linker command even if pkgconfig is not
used;
- adding of local files via web-interface now works in IE10;
- if an obsolete option is found in the config file a warning is printed
instead of an error and the program is not paused anymore;
- fixed: the reported line numbers for configuration errors were
sometimes inaccurate;
- fixed warning <file glyphicons-halflings.png not found>;
- fixed: some XML-/JSON-RPC methods may return negative values for file
sizes between 2-4GB; this had also influence on web-interface.
- fixed: if an external program (unrar, pp-script, etc.) could not be
started, the execute-function has returned code 255 although the code
-1 were expected in this case; this could break designed post-
processing flow;
- fixed: some characters with codes below 32 were not properly encoded
in JSON-RPC; sometimes output from unrar contained such characters
and could break web-interface;
- fixed: special characters (quotation marks, etc.) in unpack password
and in configuration options were not displayed properly and could be
discarded on saving;
nzbget-9.1:
- added full par-scan feature needed to par-check/repair files which
were renamed after creation of par-files:
- new option <ParScan> to activate full par-scan (always or automatic);
the automatic full par-scan activates if missing files are detected
during par-check, this avoids unnecessary full scan for normal
(not renamed) par sets;
- improved the post-processing script to better handle renamed rar-files;
- replaced a browser error message when trying to add local files in
IE9 with a better message dialog;
nzbget-9.0:
- changed version naming scheme by removing the leading zero: current
version is now called 9.0 instead of 0.9.0 (it's really the 9th major
version of the program);
- added built-in web-interface:
- completely new designed and written from scratch;
- doesn't require a separate web-server;
- doesn't require PHP;
- 100% Javascript application; the built-in web-server hosts only
static files; the javascript app communicates with NZBGet via
JSON-RPC;
- very efficient usage of server resources (CPU and memory);
- easy installation. Since neither a separate web-server nor PHP
are needed the installation of new web-interface is very easy.
Actually it is performed automatically when you "make install"
or "ipkg install nzbget";
- modern look: better layout, popup dialogs, nice animations,
hi-def icons;
- built-in phone-theme (activates automatically);
- combined view for "currently downloading", "queued", "currently
processing" and "queued for processing";
- renaming of nzb-files;
- multiselect with multiedit or merge of downloads;
- fast paging in the lists (downloads, history, messages);
- search box for filtering in the lists (downloads, history, messages)
and in settings;
- adding nzb-files to download queue was improved in several ways:
- add multiple files at once. The "select files dialog" allows
to select multiple files;
- add files using drag and drop. Just drop the files from your
file manager directly into the web-browser;
- add files via URLs. Put the URL and NZBGet downloads the
nzb-file and adds it to download queue automatically;
- the priority of nzb-file can now be set when adding local-files
or URLs;
- the history can be cleared completely or selected items can be removed;
- file mode is now nzb-file related;
- added the ability to queue URLs:
- the program automatically downloads nzb-files from given URLs
and put them to download queue.
- when multiple URLs are added in a short time, they are put
into a special URL-queue.
- the number of simultaneous URL-downloads are controlled via
new option UrlConnections.
- with the new option ReloadUrlQueue can be controlled if the URL-queue
should be reloaded after the program is restarted (if the URL-queue
was not empty).
- new switch <-U> for remote-command <--append/-A> to queue an URL.
- new subcommand <-U> in the remote command <--list/-L> prints the
current URL-queue.
- if URL-download fails, the URL is moved into history.
- with subcommand <-R> of command <--edit> the failed URL can be
returned to URL-queue for redownload.
- the remote command <--list/-L> for history can now print the infos
for URL history items.
- new XML/JSON-RPC command <appendurl> to add an URL or multiple
URLs for download.
- new XML/JSON-RPC command <urlqueue> returns the items from the
URL-queue.
- the XML/JSON-RPC command <history> was extended to provide
infos about URL history items.
- the URL-queue obeys the pause-state of download queue.
- the URL-downloads support HTTP and HTTPS protocols;
- added new field <name> to nzb-info-object.
- it is initially set to the cleaned up name of the nzb-file.
- the renaming of the group changes this field.
- all RPC-methods related to nzb-object return the new field, the
old field <NZBNicename> is now deprecated.
- the option <MergeNZB> now checks the <name>-field instead of
<nzbfilename> (the latter is not changed when the nzb is renamed).
- new env-var-parameter <NZBPP_NZBNAME> for post-processing script;
- added options <GN> and <FN> for remote command <--edit/-E>. With these
options the name of group or file can be used in edit-command instead
of file ID;
- added support for regular expressions (POSIX ERE Syntax) in remote
commands <--list/-L> and <--edit/-E> using new subcommands <GR> and <FR>;
- improved performance of RPC-command <listgroups>;
- added new command <FileReorder> to RPC-method <editqueue> to set the
order of individual files in the group;
- added gzip-support to built-in web-server (including RPC);
- added processing of http-request <OPTIONS> in RPC-server for better
support of cross domain requests;
- renamed example configuration file and postprocessing script to make
the installation easier;
- improved the automatic installation (<make install>) to install all
necessary files (not only the binary as it was before);
- improved handling of configuration errors: the program now does not
terminate on errors but rather logs all of them and uses default option values;
- added new XML/JSON-RPC methods <config>, <loadconfig> and <saveconfig>;
- with active option <AllowReProcess> the NZB considered completed even if
there are paused non-par-files (the paused non-par-files are treated the
same way as paused par-files): as a result the reprocessable script is called;
- added subcommand <W> to remote command <-S/--scan> to scan synchronously
(wait until scan completed);
- added parameter <SyncMode> to XML/JSON-RPC method <scan>;
- the command <Scan> in web-interface now waits for completing of scan
before reporting the status;
- added remote command <--reload/-O> and JSON/XML-RPC method <reload> to
reload configuration from disk and reintialize the program; the reload
can be performed from web-interface;
- JSON/XML-RPC method <append> extended with parameter <priority>;
- categories available in web-interface are now configured in program
configuration file (nzbget.conf) and can be managed via web-interface
on settings page;
- updated descriptions in example configuration file;
- changes in configuration file:
- renamed options <ServerIP>, <ServerPort> and <ServerPassword> to
<ControlIP>, <ControlPort> and <ControlPassword> to avoid confusion
with news-server options <ServerX.Host>, <ServerX.Port> and
<ServerX.Password>;
- the old option names are still recognized and are automatically
renamed when the configuration is saved from web-interface;
- also renamed option <$MAINDIR> to <MainDir>;
- extended remote command <--append/-A> with optional parameters:
- <T> - adds the file/URL to the top of queue;
- <P> - pauses added files;
- <C category-name> - sets category for added nzb-file/URL;
- <N nzb-name> - sets nzb filename for added URL;
- the old switches <--category/-K> and <--top/-T> are deprecated
but still supported for compatibility;
- renamed subcommand <K> of command <--edit/-E> to <C> (the old
subcommand is still supported for compatibility);
- added new option <NzbAddedProcess> to setup a script called after
a nzb-file is added to queue;
- added debug messages for speed meter;
- improved the startup script <nzbgetd> so it can be directly used in
</etc/init.d> without modifications;
- fixed: after renaming of a group, the new name was not displayed
by remote commands <-L G> and <-C in curses mode>;
- fixed incompatibility with OpenSLL 1.0 (thanks to OpenWRT team
for the patch);
- fixed: RPC-method <log(0, IdFrom)> could return wrong results if
the log was filtered with options <XXXTarget>;
- fixed: free disk space calculated incorrectly on some OSes;
- fixed: unrar failure was not always properly detected causing the
post-processing to delete not yet unpacked rar-files;
- fixed compilation error on recent linux versions;
- fixed compilation error on older systems;
nzbget-0.8.0:
- added priorities; new action <I> for remote command <--edit/-E> to set
priorities for groups or individual files; new actions <SetGroupPriority>
and <SetFilePriority> of RPC-command <editqueue>; remote command
<--list/-L> prints priorities and indicates files or groups being
downloaded; ncurses-frontend prints priorities and indicates files or
groups being download; new command <PRIORITY> to set priority of nzb-file
from nzbprocess-script; RPC-commands <listgroups> and <listfiles> return
priorities and indicate files or groups being downloaded;
- added renaming of groups; new subcommand <N> for command <--edit/-E>; new
action <SetName> for RPC-method <editqueue>;
- added new option <AccurateRate>, which enables syncronisation in speed
meter; that makes the indicated speed more accurate by eliminating
measurement errors possible due thread conflicts; thanks to anonymous
nzbget user for the patch;
- improved the parsing of filename from article subject;
- option <DirectWrite> now efficiently works on Windows with NTFS partitions;
- added URL-based-authentication as alternative to HTTP-header authentication
for XML- and JSON-RPC;
- fixed: nzb-files containing umlauts and other special characters could not
be parsed - replaced XML-Reader with SAX-Parser - only on POSIX (not on
Windows);
- fixed incorrect displaying of group sizes bigger than 4GB on many 64-bit
OSes;
- fixed a bug causing error on decoding of input data in JSON-RPC;
- fixed a compilation error on some windows versions;
- fixed: par-repair could fail when the filenames were not correctly parsed
from article subjects;
- fixed a compatibility issue with OpenBSD (and possibly other BSD based
systems); added the automatic configuring of required signal handling logic
to better support BSD without breaking the compatibility with certain Linux
systems;
- corrected the address of Free Software Foundation in copyright notice.
nzbget-0.7.0:
- added history: new option <KeepHistory>, new remote subcommand <H> for
commands <L> (list history entries) and <E> (delete history entries,
@@ -1438,7 +541,7 @@ nzbget-0.3.0:
of completing and state (paused or not) for every file is printed.
The header of queue shows number of total files, number of unpaused
files and size for all and unpaused files. Better using of screen estate
space - no more empty lines and separate header for status (total seven
space <EFBFBD> no more empty lines and separate header for status (total seven
lines gain). The messages are printed on several lines (if they not fill
in one line) without trimming now;
- configure.ac-file updated to work with recent versions of autoconf/automake.

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -74,11 +74,11 @@ void ColoredFrontend::PrintStatus()
char tmp[1024];
char timeString[100];
timeString[0] = '\0';
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
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);
@@ -86,9 +86,9 @@ void ColoredFrontend::PrintStatus()
}
char szDownloadLimit[128];
if (m_iDownloadLimit > 0)
if (m_fDownloadLimit > 0.0f)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
}
else
{
@@ -113,7 +113,7 @@ void ColoredFrontend::PrintStatus()
#endif
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
szDownloadLimit, szControlSeq);

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -37,7 +37,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -54,16 +54,19 @@
#include "nzbget.h"
#include "Connection.h"
#include "Log.h"
#include "TLS.h"
static const int CONNECTION_READBUFFER_SIZE = 1024;
#ifndef DISABLE_TLS
bool Connection::bTLSLibInitialized = false;
#endif
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
Mutex* Connection::m_pMutexGetHostByName = NULL;
#endif
#endif
void Connection::Init()
void Connection::Init(bool bTLS)
{
debug("Initializing global connection data");
@@ -84,7 +87,21 @@ void Connection::Init()
#endif
#ifndef DISABLE_TLS
TLSSocket::Init();
if (bTLS)
{
debug("Initializing TLS library");
char* szErrStr;
int iRes = tls_lib_init(&szErrStr);
bTLSLibInitialized = iRes == TLS_EOK;
if (!bTLSLibInitialized)
{
error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error");
if (szErrStr)
{
free(szErrStr);
}
}
}
#endif
#ifndef HAVE_GETADDRINFO
@@ -103,7 +120,11 @@ void Connection::Final()
#endif
#ifndef DISABLE_TLS
TLSSocket::Final();
if (bTLSLibInitialized)
{
debug("Finalizing TLS library");
tls_lib_deinit();
}
#endif
#ifndef HAVE_GETADDRINFO
@@ -113,48 +134,38 @@ void Connection::Final()
#endif
}
Connection::Connection(const char* szHost, int iPort, bool bTLS)
Connection::Connection(NetAddress* pNetAddress)
{
debug("Creating Connection");
m_szHost = NULL;
m_iPort = iPort;
m_bTLS = bTLS;
m_szCipher = NULL;
m_pNetAddress = pNetAddress;
m_eStatus = csDisconnected;
m_iSocket = INVALID_SOCKET;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = true;
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_pTLS = NULL;
m_bTLSError = false;
#endif
if (szHost)
{
m_szHost = strdup(szHost);
}
}
Connection::Connection(SOCKET iSocket, bool bTLS)
Connection::Connection(SOCKET iSocket, bool bAutoClose)
{
debug("Creating Connection");
m_szHost = NULL;
m_iPort = 0;
m_bTLS = bTLS;
m_szCipher = NULL;
m_pNetAddress = NULL;
m_eStatus = csConnected;
m_iSocket = iSocket;
m_iBufAvail = 0;
m_iTimeout = 60;
m_bSuppressErrors = true;
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
m_bAutoClose = bAutoClose;
#ifndef DISABLE_TLS
m_pTLSSocket = NULL;
m_bTLSError = false;
m_pTLS = NULL;
#endif
}
@@ -162,52 +173,32 @@ Connection::~Connection()
{
debug("Destroying Connection");
Disconnect();
free(m_szHost);
free(m_szCipher);
if (m_eStatus == csConnected && m_bAutoClose)
{
Disconnect();
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
delete m_pTLSSocket;
#endif
}
void Connection::SetSuppressErrors(bool bSuppressErrors)
{
m_bSuppressErrors = bSuppressErrors;
#ifndef DISABLE_TLS
if (m_pTLSSocket)
if (m_pTLS)
{
m_pTLSSocket->SetSuppressErrors(bSuppressErrors);
free(m_pTLS);
}
#endif
}
void Connection::SetCipher(const char* szCipher)
{
free(m_szCipher);
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
bool Connection::Connect()
{
debug("Connecting");
if (m_eStatus == csConnected)
{
return true;
}
bool bRes = DoConnect();
if (bRes)
{
m_eStatus = csConnected;
}
else
{
DoDisconnect();
}
Connection::DoDisconnect();
return bRes;
}
@@ -217,9 +208,7 @@ bool Connection::Disconnect()
debug("Disconnecting");
if (m_eStatus == csDisconnected)
{
return true;
}
bool bRes = DoDisconnect();
@@ -230,145 +219,51 @@ bool Connection::Disconnect()
return bRes;
}
bool Connection::Bind()
int Connection::Bind()
{
debug("Binding");
if (m_eStatus == csListening)
{
return true;
return 0;
}
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error("Could not resolve hostname %s", m_szHost);
return false;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return false;
}
}
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return false;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return false;
}
if (listen(m_iSocket, 100) < 0)
{
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return false;
}
m_eStatus = csListening;
int iRes = DoBind();
return true;
if (iRes == 0)
{
m_eStatus = csListening;
}
return iRes;
}
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::WriteLine");
//debug("Connection::write(char* line)");
if (m_eStatus != csConnected)
{
return -1;
}
int iRes = send(m_iSocket, pBuffer, strlen(pBuffer), 0);
int iRes = DoWriteLine(pBuffer);
return iRes;
}
bool Connection::Send(const char* pBuffer, int iSize)
int Connection::Send(const char* pBuffer, int iSize)
{
debug("Sending data");
if (m_eStatus != csConnected)
{
return false;
return -1;
}
int iBytesSent = 0;
while (iBytesSent < iSize)
{
int iRes = send(m_iSocket, pBuffer + iBytesSent, iSize-iBytesSent, 0);
if (iRes <= 0)
{
return false;
}
iBytesSent += iRes;
}
int iRes = send(m_iSocket, pBuffer, iSize, 0);
return true;
return iRes;
}
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
@@ -378,100 +273,26 @@ char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
return NULL;
}
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
}
return pBuffer;
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
}
Connection* Connection::Accept()
SOCKET Connection::Accept()
{
debug("Accepting connection");
if (m_eStatus != csListening)
{
return NULL;
return INVALID_SOCKET;
}
SOCKET iSocket = accept(m_iSocket, NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
if (iSocket == INVALID_SOCKET)
{
return NULL;
}
Connection* pCon = new Connection(iSocket, m_bTLS);
SOCKET iRes = DoAccept();
return pCon;
return iRes;
}
int Connection::TryRecv(char* pBuffer, int iSize)
int Connection::Recv(char* pBuffer, int iSize)
{
debug("Receiving data");
@@ -487,7 +308,7 @@ int Connection::TryRecv(char* pBuffer, int iSize)
return iReceived;
}
bool Connection::Recv(char * pBuffer, int iSize)
bool Connection::RecvAll(char * pBuffer, int iSize)
{
debug("Receiving data (full buffer)");
@@ -536,18 +357,17 @@ bool Connection::DoConnect()
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
addr_hints.ai_socktype = SOCK_STREAM,
sprintf(iPortStr, "%d", m_iPort);
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
ReportError("Could not resolve hostname %s", m_szHost, true, 0);
ReportError("Could not resolve hostname %s", m_pNetAddress->GetHost(), true, 0);
return false;
}
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
bool bLastAddr = !addr->ai_next;
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
@@ -558,33 +378,20 @@ bool Connection::DoConnect()
break;
}
// Connection failed
if (bLastAddr)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
}
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
else if (bLastAddr)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
}
}
freeaddrinfo(addr_list);
if (m_iSocket == INVALID_SOCKET)
{
return false;
}
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
sSocketAddress.sin_port = htons(m_iPort);
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return false;
@@ -593,20 +400,25 @@ bool Connection::DoConnect()
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
ReportError("Socket creation failed for %s", m_pNetAddress->GetHost(), true, 0);
return false;
}
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
ReportError("Connection to %s failed", m_szHost, true, 0);
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
return false;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Connection to %s failed", m_pNetAddress->GetHost(), true, 0);
return false;
}
#ifdef WIN32
int MSecVal = m_iTimeout * 1000;
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
@@ -618,16 +430,9 @@ bool Connection::DoConnect()
#endif
if (err != 0)
{
ReportError("Socket initialization failed for %s", m_szHost, true, 0);
ReportError("setsockopt failed", NULL, true, 0);
}
#ifndef DISABLE_TLS
if (m_bTLS && !StartTLS(true, NULL, NULL))
{
return false;
}
#endif
return true;
}
@@ -637,23 +442,205 @@ bool Connection::DoDisconnect()
if (m_iSocket != INVALID_SOCKET)
{
#ifndef DISABLE_TLS
CloseTLS();
#endif
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
#ifndef DISABLE_TLS
if (m_pTLS)
{
CloseTLS();
}
#endif
}
m_eStatus = csDisconnected;
return true;
}
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
int Connection::DoWriteLine(const char* pBuffer)
{
*iBufLen = m_iBufAvail;
*pBuffer = m_szBufPtr;
m_iBufAvail = 0;
};
//debug("Connection::doWrite()");
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
//debug( "Connection::DoReadLine()" );
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
}
return pBuffer;
}
int Connection::DoBind()
{
debug("Do binding");
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error( "Could not resolve hostname %s", m_pNetAddress->GetHost() );
return -1;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
if (listen(m_iSocket, 10) < 0)
{
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), true, 0);
return -1;
}
return 0;
}
SOCKET Connection::DoAccept()
{
SOCKET iSocket = accept(GetSocket(), NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
}
void Connection::Cancel()
{
@@ -688,9 +675,14 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
#ifdef WIN32
int ErrCode = WSAGetLastError();
char szErrMsg[1024];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ErrCode, 0, szErrMsg, 1024, NULL);
szErrMsg[1024-1] = '\0';
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
}
else
{
error("%s: ErrNo %i", szErrPrefix, ErrCode);
}
#else
const char *szErrMsg = NULL;
int ErrCode = herrno;
@@ -703,7 +695,7 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
szErrMsg = hstrerror(ErrCode);
}
#endif
if (m_bSuppressErrors)
{
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
@@ -712,6 +704,7 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
{
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
}
#endif
}
else
{
@@ -727,36 +720,71 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool
}
#ifndef DISABLE_TLS
bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile)
bool Connection::CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix)
{
bool bOK = iResultCode == TLS_EOK;
if (!bOK)
{
ReportError(szErrMsgPrefix, szErrStr ? szErrStr : "unknown error", false, 0);
if (szErrStr)
{
free(szErrStr);
}
}
return bOK;
}
bool Connection::StartTLS()
{
debug("Starting TLS");
delete m_pTLSSocket;
m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher);
m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors);
if (m_pTLS)
{
free(m_pTLS);
}
return m_pTLSSocket->Start();
m_pTLS = malloc(sizeof(tls_t));
tls_t* pTLS = (tls_t*)m_pTLS;
memset(pTLS, 0, sizeof(tls_t));
tls_clear(pTLS);
char* szErrStr;
int iRes;
iRes = tls_init(pTLS, NULL, NULL, NULL, 0, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not initialize TLS-object: %s"))
{
return false;
}
debug("tls_start...");
iRes = tls_start(pTLS, (int)m_iSocket, NULL, 1, NULL, &szErrStr);
debug("tls_start...%i", iRes);
if (!CheckTLSResult(iRes, szErrStr, "Could not establish secure connection: %s"))
{
return false;
}
return true;
}
void Connection::CloseTLS()
{
if (m_pTLSSocket)
{
m_pTLSSocket->Close();
delete m_pTLSSocket;
m_pTLSSocket = NULL;
}
tls_close((tls_t*)m_pTLS);
free(m_pTLS);
m_pTLS = NULL;
}
int Connection::recv(SOCKET s, char* buf, int len, int flags)
{
int iReceived = 0;
size_t iReceived = 0;
if (m_pTLSSocket)
if (m_pTLS)
{
m_bTLSError = false;
iReceived = m_pTLSSocket->Recv(buf, len);
if (iReceived < 0)
char* szErrStr;
int iRes = tls_getbuf((tls_t*)m_pTLS, buf, len, &iReceived, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not read from TLS-socket: %s"))
{
m_bTLSError = true;
return -1;
@@ -771,23 +799,22 @@ int Connection::recv(SOCKET s, char* buf, int len, int flags)
int Connection::send(SOCKET s, const char* buf, int len, int flags)
{
int iSent = 0;
if (m_pTLSSocket)
if (m_pTLS)
{
m_bTLSError = false;
iSent = m_pTLSSocket->Send(buf, len);
if (iSent < 0)
char* szErrStr;
int iRes = tls_putbuf((tls_t*)m_pTLS, buf, len, &szErrStr);
if (!CheckTLSResult(iRes, szErrStr, "Could not send to TLS-socket: %s"))
{
m_bTLSError = true;
return -1;
}
return iSent;
return 0;
}
else
{
iSent = ::send(s, buf, len, flags);
return iSent;
int iRet = ::send(s, buf, len, flags);
return iRet;
}
}
#endif
@@ -841,20 +868,3 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
return uaddr;
}
#endif
const char* Connection::GetRemoteAddr()
{
struct sockaddr_in PeerName;
int iPeerNameLength = sizeof(PeerName);
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0)
{
#ifdef WIN32
strncpy(m_szRemoteAddr, inet_ntoa(PeerName.sin_addr), sizeof(m_szRemoteAddr));
#else
inet_ntop(AF_INET, &PeerName.sin_addr, m_szRemoteAddr, sizeof(m_szRemoteAddr));
#endif
}
m_szRemoteAddr[sizeof(m_szRemoteAddr)-1] = '\0';
return m_szRemoteAddr;
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,14 +27,12 @@
#ifndef CONNECTION_H
#define CONNECTION_H
#include "NetAddress.h"
#ifndef HAVE_GETADDRINFO
#ifndef HAVE_GETHOSTBYNAME_R
#include "Thread.h"
#endif
#endif
#ifndef DISABLE_TLS
#include "TLS.h"
#endif
class Connection
{
@@ -46,22 +44,20 @@ public:
csListening,
csCancelled
};
protected:
char* m_szHost;
int m_iPort;
NetAddress* m_pNetAddress;
SOCKET m_iSocket;
bool m_bTLS;
char* m_szCipher;
char* m_szReadBuf;
int m_iBufAvail;
char* m_szBufPtr;
EStatus m_eStatus;
int m_iTimeout;
bool m_bSuppressErrors;
char m_szRemoteAddr[20];
bool m_bAutoClose;
#ifndef DISABLE_TLS
TLSSocket* m_pTLSSocket;
void* m_pTLS;
static bool bTLSLibInitialized;
bool m_bTLSError;
#endif
#ifndef HAVE_GETADDRINFO
@@ -70,47 +66,47 @@ protected:
#endif
#endif
Connection(SOCKET iSocket, bool bTLS);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
bool DoConnect();
bool DoDisconnect();
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
#endif
#ifndef DISABLE_TLS
bool CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix);
int recv(SOCKET s, char* buf, int len, int flags);
int send(SOCKET s, const char* buf, int len, int flags);
void CloseTLS();
#endif
public:
Connection(const char* szHost, int iPort, bool bTLS);
Connection(NetAddress* pNetAddress);
Connection(SOCKET iSocket, bool bAutoClose);
virtual ~Connection();
static void Init();
static void Init(bool bTLS);
static void Final();
virtual bool Connect();
virtual bool Disconnect();
bool Bind();
bool Send(const char* pBuffer, int iSize);
bool Recv(char* pBuffer, int iSize);
int TryRecv(char* pBuffer, int iSize);
bool Connect();
bool Disconnect();
int Bind();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
bool RecvAll(char* pBuffer, int iSize);
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
void ReadBuffer(char** pBuffer, int *iBufLen);
int WriteLine(const char* pBuffer);
Connection* Accept();
SOCKET Accept();
void Cancel();
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
void SetCipher(const char* szCipher);
NetAddress* GetServer() { return m_pNetAddress; }
SOCKET GetSocket() { return m_iSocket; }
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
EStatus GetStatus() { return m_eStatus; }
void SetSuppressErrors(bool bSuppressErrors);
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
bool GetSuppressErrors() { return m_bSuppressErrors; }
const char* GetRemoteAddr();
#ifndef DISABLE_TLS
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
bool StartTLS();
#endif
};

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -60,12 +60,18 @@ Decoder::~ Decoder()
{
debug("Destroying Decoder");
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
}
void Decoder::Clear()
{
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
@@ -262,7 +268,10 @@ BreakLoop:
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
@@ -395,7 +404,10 @@ unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
// extracting filename
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,13 +27,10 @@
#define DISKSTATE_H
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "NewsServer.h"
class DiskState
{
private:
int fscanf(FILE* infile, const char* Format, ...);
int ParseFormatVersion(const char* szFormatSignature);
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
@@ -42,38 +39,21 @@ private:
void SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* outfile);
bool LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* infile, int iFormatVersion);
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile);
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
void SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile);
bool LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion);
void SaveDupInfo(DupInfo* pDupInfo, FILE* outfile);
bool LoadDupInfo(DupInfo* pDupInfo, FILE* infile, int iFormatVersion);
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile);
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool SaveFeedStatus(Feeds* pFeeds, FILE* outfile);
bool LoadFeedStatus(Feeds* pFeeds, FILE* infile, int iFormatVersion);
bool SaveFeedHistory(FeedHistory* pFeedHistory, FILE* outfile);
bool LoadFeedHistory(FeedHistory* pFeedHistory, FILE* infile, int iFormatVersion);
void CalcCriticalHealth(DownloadQueue* pDownloadQueue);
bool SaveServerStats(Servers* pServers, FILE* outfile);
bool LoadServerStats(Servers* pServers, FILE* infile, int iFormatVersion);
void ConvertDupeKey(char* buf, int bufsize);
public:
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
void DiscardDownloadQueue();
bool DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
bool SaveFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
bool LoadFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
bool SaveStats(Servers* pServers);
bool LoadStats(Servers* pServers);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -85,36 +85,21 @@ private:
char* m_szFilename;
long long m_lSize;
long long m_lRemainingSize;
long long m_lSuccessSize;
long long m_lFailedSize;
long long m_lMissedSize;
int m_iTotalArticles;
int m_iMissedArticles;
int m_iFailedArticles;
int m_iSuccessArticles;
time_t m_tTime;
bool m_bPaused;
bool m_bDeleted;
bool m_bFilenameConfirmed;
bool m_bParFile;
int m_iCompleted;
bool m_bOutputInitialized;
char* m_szOutputFilename;
Mutex* m_pMutexOutputFile;
int m_iPriority;
bool m_bExtraPriority;
int m_iActiveDownloads;
bool m_bAutoDeleted;
Mutex m_mutexOutputFile;
static int m_iIDGen;
static int m_iIDMax;
public:
FileInfo();
~FileInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
void SetID(int s);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
@@ -126,24 +111,10 @@ public:
void MakeValidFilename();
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
void SetSize(long long lSize) { m_lSize = lSize; m_lRemainingSize = lSize; }
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
long long GetSize() { return m_lSize; }
long long GetRemainingSize() { return m_lRemainingSize; }
void SetRemainingSize(long long lRemainingSize) { m_lRemainingSize = lRemainingSize; }
long long GetMissedSize() { return m_lMissedSize; }
void SetMissedSize(long long lMissedSize) { m_lMissedSize = lMissedSize; }
long long GetSuccessSize() { return m_lSuccessSize; }
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
long long GetFailedSize() { return m_lFailedSize; }
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
int GetTotalArticles() { return m_iTotalArticles; }
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
int GetMissedArticles() { return m_iMissedArticles; }
void SetMissedArticles(int iMissedArticles) { m_iMissedArticles = iMissedArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
bool GetPaused() { return m_bPaused; }
@@ -151,24 +122,13 @@ public:
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int iCompleted) { m_iCompleted = iCompleted; }
bool GetParFile() { return m_bParFile; }
void SetParFile(bool bParFile) { m_bParFile = bParFile; }
void SetCompleted(int s) { m_iCompleted = s; }
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* szOutputFilename);
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
bool GetExtraPriority() { return m_bExtraPriority; }
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads);
bool GetAutoDeleted() { return m_bAutoDeleted; }
void SetAutoDeleted(bool bAutoDeleted) { m_bAutoDeleted = bAutoDeleted; }
bool IsDupe(const char* szFilename);
};
typedef std::deque<FileInfo*> FileQueue;
@@ -186,9 +146,6 @@ private:
int m_iRemainingParCount;
time_t m_tMinTime;
time_t m_tMaxTime;
int m_iMinPriority;
int m_iMaxPriority;
int m_iActiveDownloads;
friend class DownloadQueue;
@@ -205,20 +162,9 @@ public:
int GetRemainingParCount() { return m_iRemainingParCount; }
time_t GetMinTime() { return m_tMinTime; }
time_t GetMaxTime() { return m_tMaxTime; }
int GetMinPriority() { return m_iMinPriority; }
int GetMaxPriority() { return m_iMaxPriority; }
int GetActiveDownloads() { return m_iActiveDownloads; }
};
typedef std::deque<GroupInfo*> GroupQueueBase;
class GroupQueue : public GroupQueueBase
{
public:
~GroupQueue();
void Clear();
};
typedef std::deque<GroupInfo*> GroupQueue;
class NZBParameter
{
@@ -242,79 +188,7 @@ typedef std::deque<NZBParameter*> NZBParameterListBase;
class NZBParameterList : public NZBParameterListBase
{
public:
~NZBParameterList();
void SetParameter(const char* szName, const char* szValue);
NZBParameter* Find(const char* szName, bool bCaseSensitive);
void Clear();
void CopyFrom(NZBParameterList* pSourceParameters);
};
class ScriptStatus
{
public:
enum EStatus
{
srNone,
srFailure,
srSuccess
};
private:
char* m_szName;
EStatus m_eStatus;
friend class ScriptStatusList;
public:
ScriptStatus(const char* szName, EStatus eStatus);
~ScriptStatus();
const char* GetName() { return m_szName; }
EStatus GetStatus() { return m_eStatus; }
};
typedef std::deque<ScriptStatus*> ScriptStatusListBase;
class ScriptStatusList : public ScriptStatusListBase
{
public:
~ScriptStatusList();
void Add(const char* szScriptName, ScriptStatus::EStatus eStatus);
void Clear();
ScriptStatus::EStatus CalcTotalStatus();
};
class ServerStat
{
private:
int m_iServerID;
int m_iSuccessArticles;
int m_iFailedArticles;
public:
ServerStat(int iServerID);
int GetServerID() { return m_iServerID; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
};
typedef std::vector<ServerStat*> ServerStatListBase;
class ServerStatList : public ServerStatListBase
{
public:
~ServerStatList();
void SetStat(int iServerID, int iSuccessArticles, int iFailedArticles, bool bAdd);
void Add(ServerStatList* pServerStats);
void Clear();
};
enum EDupeMode
{
dmScore,
dmAll,
dmForce
};
class NZBInfoList;
@@ -322,61 +196,20 @@ class NZBInfoList;
class NZBInfo
{
public:
enum ERenameStatus
{
rsNone,
rsSkipped,
rsFailure,
rsSuccess
};
enum EParStatus
{
psNone,
psSkipped,
psFailure,
psSuccess,
psRepairPossible,
psManual
prNone,
prFailure,
prRepairPossible,
prSuccess
};
enum EUnpackStatus
enum EScriptStatus
{
usNone,
usSkipped,
usFailure,
usSuccess,
usSpace,
usPassword
};
enum ECleanupStatus
{
csNone,
csFailure,
csSuccess
};
enum EMoveStatus
{
msNone,
msFailure,
msSuccess
};
enum EDeleteStatus
{
dsNone,
dsManual,
dsHealth,
dsDupe
};
enum EMarkStatus
{
ksNone,
ksBad,
ksGood
srNone,
srUnknown,
srFailure,
srSuccess
};
typedef std::vector<char*> Files;
@@ -386,164 +219,71 @@ private:
int m_iID;
int m_iRefCount;
char* m_szFilename;
char* m_szName;
char* m_szDestDir;
char* m_szFinalDir;
char* m_szCategory;
int m_iFileCount;
int m_iParkedFileCount;
long long m_lSize;
long long m_lSuccessSize;
long long m_lFailedSize;
long long m_lCurrentSuccessSize;
long long m_lCurrentFailedSize;
long long m_lParSize;
long long m_lParSuccessSize;
long long m_lParFailedSize;
long long m_lParCurrentSuccessSize;
long long m_lParCurrentFailedSize;
int m_iTotalArticles;
int m_iSuccessArticles;
int m_iFailedArticles;
Files m_completedFiles;
bool m_bPostProcess;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
ECleanupStatus m_eCleanupStatus;
EMoveStatus m_eMoveStatus;
EDeleteStatus m_eDeleteStatus;
EMarkStatus m_eMarkStatus;
bool m_bDeletePaused;
bool m_bManyDupeFiles;
EScriptStatus m_eScriptStatus;
char* m_szQueuedFilename;
bool m_bDeleting;
bool m_bAvoidHistory;
bool m_bHealthPaused;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bParManual;
bool m_bCleanupDisk;
bool m_bUnpackCleanedUpDisk;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
unsigned int m_iFullContentHash;
unsigned int m_iFilteredContentHash;
time_t m_tHistoryTime;
NZBInfoList* m_Owner;
NZBParameterList m_ppParameters;
ScriptStatusList m_scriptStatuses;
ServerStatList m_ServerStats;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
static int m_iIDGen;
static int m_iIDMax;
friend class NZBInfoList;
public:
NZBInfo(bool bPersistent = true);
NZBInfo();
~NZBInfo();
void Retain();
void AddReference();
void Release();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
void GetNiceNZBName(char* szBuffer, int iSize);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
const char* GetFinalDir() { return m_szFinalDir; } // needs locking (for shared objects)
void SetFinalDir(const char* szFinalDir); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
const char* GetName() { return m_szName; } // needs locking (for shared objects)
void SetName(const char* szName); // needs locking (for shared objects)
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
int GetFileCount() { return m_iFileCount; }
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
int GetParkedFileCount() { return m_iParkedFileCount; }
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
long long GetSuccessSize() { return m_lSuccessSize; }
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
long long GetFailedSize() { return m_lFailedSize; }
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
long long GetCurrentSuccessSize() { return m_lCurrentSuccessSize; }
void SetCurrentSuccessSize(long long lCurrentSuccessSize) { m_lCurrentSuccessSize = lCurrentSuccessSize; }
long long GetCurrentFailedSize() { return m_lCurrentFailedSize; }
void SetCurrentFailedSize(long long lCurrentFailedSize) { m_lCurrentFailedSize = lCurrentFailedSize; }
long long GetParSize() { return m_lParSize; }
void SetParSize(long long lParSize) { m_lParSize = lParSize; }
long long GetParSuccessSize() { return m_lParSuccessSize; }
void SetParSuccessSize(long long lParSuccessSize) { m_lParSuccessSize = lParSuccessSize; }
long long GetParFailedSize() { return m_lParFailedSize; }
void SetParFailedSize(long long lParFailedSize) { m_lParFailedSize = lParFailedSize; }
long long GetParCurrentSuccessSize() { return m_lParCurrentSuccessSize; }
void SetParCurrentSuccessSize(long long lParCurrentSuccessSize) { m_lParCurrentSuccessSize = lParCurrentSuccessSize; }
long long GetParCurrentFailedSize() { return m_lParCurrentFailedSize; }
void SetParCurrentFailedSize(long long lParCurrentFailedSize) { m_lParCurrentFailedSize = lParCurrentFailedSize; }
int GetTotalArticles() { return m_iTotalArticles; }
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
void BuildDestDirName();
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
void ClearCompletedFiles();
bool GetPostProcess() { return m_bPostProcess; }
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ECleanupStatus GetCleanupStatus() { return m_eCleanupStatus; }
void SetCleanupStatus(ECleanupStatus eCleanupStatus) { m_eCleanupStatus = eCleanupStatus; }
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
EDeleteStatus GetDeleteStatus() { return m_eDeleteStatus; }
void SetDeleteStatus(EDeleteStatus eDeleteStatus) { m_eDeleteStatus = eDeleteStatus; }
EMarkStatus GetMarkStatus() { return m_eMarkStatus; }
void SetMarkStatus(EMarkStatus eMarkStatus) { m_eMarkStatus = eMarkStatus; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleting() { return m_bDeleting; }
void SetDeleting(bool bDeleting) { m_bDeleting = bDeleting; }
bool GetDeletePaused() { return m_bDeletePaused; }
void SetDeletePaused(bool bDeletePaused) { m_bDeletePaused = bDeletePaused; }
bool GetManyDupeFiles() { return m_bManyDupeFiles; }
void SetManyDupeFiles(bool bManyDupeFiles) { m_bManyDupeFiles = bManyDupeFiles; }
bool GetAvoidHistory() { return m_bAvoidHistory; }
void SetAvoidHistory(bool bAvoidHistory) { m_bAvoidHistory = bAvoidHistory; }
bool GetHealthPaused() { return m_bHealthPaused; }
void SetHealthPaused(bool bHealthPaused) { m_bHealthPaused = bHealthPaused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetParCleanup() { return m_bParCleanup; }
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
bool GetCleanupDisk() { return m_bCleanupDisk; }
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
bool GetUnpackCleanedUpDisk() { return m_bUnpackCleanedUpDisk; }
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
time_t GetHistoryTime() { return m_tHistoryTime; }
void SetHistoryTime(time_t tHistoryTime) { m_tHistoryTime = tHistoryTime; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
ScriptStatusList* GetScriptStatuses() { return &m_scriptStatuses; } // needs locking (for shared objects)
ServerStatList* GetServerStats() { return &m_ServerStats; }
int CalcHealth();
int CalcCriticalHealth();
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
unsigned int GetFullContentHash() { return m_iFullContentHash; }
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
@@ -569,36 +309,59 @@ public:
ptVerifyingSources,
ptRepairing,
ptVerifyingRepaired,
ptRenaming,
ptUnpacking,
ptMoving,
ptExecutingScript,
ptFinished
};
enum EParStatus
{
psNone,
psFailure,
psSuccess,
psRepairPossible
};
enum ERequestParCheck
{
rpNone,
rpCurrent,
rpAll
};
enum EScriptStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
};
typedef std::deque<Message*> Messages;
private:
int m_iID;
NZBInfo* m_pNZBInfo;
char* m_szParFilename;
char* m_szInfoName;
bool m_bWorking;
bool m_bDeleted;
bool m_bRequestParCheck;
bool m_bParCheck;
EParStatus m_eParStatus;
EScriptStatus m_eScriptStatus;
ERequestParCheck m_eRequestParCheck;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
int m_iStageProgress;
time_t m_tStartTime;
time_t m_tStageTime;
Thread* m_pPostThread;
Thread* m_pScriptThread;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
static int m_iIDGen;
static int m_iIDMax;
public:
PostInfo();
@@ -606,6 +369,8 @@ public:
int GetID() { return m_iID; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
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; }
@@ -624,11 +389,17 @@ public:
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetRequestParCheck() { return m_bRequestParCheck; }
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
bool GetParCheck() { return m_bParCheck; }
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
Thread* GetScriptThread() { return m_pScriptThread; }
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
Messages* LockMessages();
void UnlockMessages();
};
@@ -637,157 +408,7 @@ typedef std::deque<PostInfo*> PostQueue;
typedef std::vector<int> IDList;
typedef std::vector<char*> NameList;
class UrlInfo
{
public:
enum EStatus
{
aiUndefined,
aiRunning,
aiFinished,
aiFailed,
aiRetry,
aiScanSkipped,
aiScanFailed
};
private:
int m_iID;
char* m_szURL;
char* m_szNZBFilename;
char* m_szCategory;
int m_iPriority;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
bool m_bAddTop;
bool m_bAddPaused;
bool m_bForce;
EStatus m_eStatus;
static int m_iIDGen;
static int m_iIDMax;
public:
UrlInfo();
~UrlInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
void SetURL(const char* szURL); // needs locking (for shared objects)
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
void SetNZBFilename(const char* szNZBFilename); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
const char* GetDupeKey() { return m_szDupeKey; }
void SetDupeKey(const char* szDupeKey);
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
bool GetAddTop() { return m_bAddTop; }
void SetAddTop(bool bAddTop) { m_bAddTop = bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
void SetAddPaused(bool bAddPaused) { m_bAddPaused = bAddPaused; }
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
static void MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize);
bool GetForce() { return m_bForce; }
void SetForce(bool bForce) { m_bForce = bForce; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
};
typedef std::deque<UrlInfo*> UrlQueue;
class DupInfo
{
public:
enum EStatus
{
dsUndefined,
dsSuccess,
dsFailed,
dsDeleted,
dsDupe,
dsBad,
dsGood
};
private:
char* m_szName;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
long long m_lSize;
unsigned int m_iFullContentHash;
unsigned int m_iFilteredContentHash;
EStatus m_eStatus;
public:
DupInfo();
~DupInfo();
const char* GetName() { return m_szName; } // needs locking (for shared objects)
void SetName(const char* szName); // needs locking (for shared objects)
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
unsigned int GetFullContentHash() { return m_iFullContentHash; }
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
};
class HistoryInfo
{
public:
enum EKind
{
hkUnknown,
hkNZBInfo,
hkUrlInfo,
hkDupInfo
};
private:
int m_iID;
EKind m_eKind;
void* m_pInfo;
time_t m_tTime;
static int m_iIDGen;
static int m_iIDMax;
public:
HistoryInfo(NZBInfo* pNZBInfo);
HistoryInfo(UrlInfo* pUrlInfo);
HistoryInfo(DupInfo* pDupInfo);
~HistoryInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
EKind GetKind() { return m_eKind; }
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }
DupInfo* GetDupInfo() { return (DupInfo*)m_pInfo; }
void DiscardUrlInfo() { m_pInfo = NULL; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
};
typedef std::deque<HistoryInfo*> HistoryList;
typedef std::deque<NZBInfo*> HistoryList;
class DownloadQueue
{
@@ -797,7 +418,6 @@ protected:
PostQueue m_PostQueue;
HistoryList m_HistoryList;
FileQueue m_ParkedFiles;
UrlQueue m_UrlQueue;
public:
NZBInfoList* GetNZBInfoList() { return &m_NZBInfoList; }
@@ -805,7 +425,6 @@ public:
PostQueue* GetPostQueue() { return &m_PostQueue; }
HistoryList* GetHistoryList() { return &m_HistoryList; }
FileQueue* GetParkedFiles() { return &m_ParkedFiles; }
UrlQueue* GetUrlQueue() { return &m_UrlQueue; }
void BuildGroups(GroupQueue* pGroupQueue);
};

View File

@@ -1,583 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <set>
#include <algorithm>
#include "nzbget.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "DiskState.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
#include "DupeCoordinator.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
bool DupeCoordinator::IsDupeSuccess(NZBInfo* pNZBInfo)
{
bool bFailure =
pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone ||
pNZBInfo->GetMarkStatus() == NZBInfo::ksBad ||
pNZBInfo->GetParStatus() == NZBInfo::psFailure ||
pNZBInfo->GetUnpackStatus() == NZBInfo::usFailure ||
pNZBInfo->GetUnpackStatus() == NZBInfo::usPassword ||
(pNZBInfo->GetParStatus() == NZBInfo::psSkipped &&
pNZBInfo->GetUnpackStatus() == NZBInfo::usSkipped &&
pNZBInfo->CalcHealth() < pNZBInfo->CalcCriticalHealth());
return !bFailure;
}
bool DupeCoordinator::SameNameOrKey(const char* szName1, const char* szDupeKey1,
const char* szName2, const char* szDupeKey2)
{
bool bHasDupeKeys = !Util::EmptyStr(szDupeKey1) && !Util::EmptyStr(szDupeKey2);
return (bHasDupeKeys && !strcmp(szDupeKey1, szDupeKey2)) ||
(!bHasDupeKeys && !strcmp(szName1, szName2));
}
/**
Check if the title was already downloaded or is already queued:
- if there is a duplicate with exactly same content (via hash-check)
in queue or in history - the new item is skipped;
- if there is a duplicate marked as good in history - the new item is skipped;
- if there is a duplicate with success-status in dup-history but
there are no duplicates in recent history - the new item is skipped;
- if queue has a duplicate with the same or higher score - the new item
is moved to history as dupe-backup;
- if queue has a duplicate with lower score - the existing item is moved
to history as dupe-backup (unless it is in post-processing stage) and
the new item is added to queue;
- if queue doesn't have duplicates - the new item is added to queue.
*/
void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("Checking duplicates for %s", pNZBInfo->GetName());
GroupQueue groupQueue;
pDownloadQueue->BuildGroups(&groupQueue);
// find duplicates in download queue with exactly same content
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pGroupNZBInfo->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pGroupNZBInfo->GetFilteredContentHash());
// if there is a duplicate with exactly same content (via hash-check)
// in queue - the new item is skipped
if (pGroupNZBInfo != pNZBInfo && bSameContent)
{
if (!strcmp(pNZBInfo->GetName(), pGroupNZBInfo->GetName()))
{
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
}
else
{
warn("Skipping duplicate %s, already queued as %s",
pNZBInfo->GetName(), pGroupNZBInfo->GetName());
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
}
// find duplicates in post queue with exactly same content
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pPostInfo->GetNZBInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pPostInfo->GetNZBInfo()->GetFilteredContentHash());
// if there is a duplicate with exactly same content (via hash-check)
// in queue - the new item is skipped;
if (bSameContent)
{
if (!strcmp(pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName()))
{
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
}
else
{
warn("Skipping duplicate %s, already queued as %s",
pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
}
// find duplicates in history
bool bSkip = false;
bool bGood = false;
bool bSameContent = false;
const char* szDupeName = NULL;
// find duplicates in queue having exactly same content
// also: nzb-files having duplicates marked as good are skipped
// also (only in score mode): nzb-files having success-duplicates in dup-history but don't having duplicates in recent history are skipped
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
((pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetNZBInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetNZBInfo()->GetFilteredContentHash())))
{
bSkip = true;
bSameContent = true;
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
((pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetDupInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetDupInfo()->GetFilteredContentHash())))
{
bSkip = true;
bSameContent = true;
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
bSkip = true;
bGood = true;
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood ||
(pNZBInfo->GetDupeMode() == dmScore &&
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess &&
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetDupInfo()->GetDupeScore())) &&
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
bSkip = true;
bGood = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
break;
}
}
if (!bSameContent && !bGood && pNZBInfo->GetDupeMode() == dmScore)
{
// nzb-files having success-duplicates in recent history (with different content) are added to history for backup
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()) &&
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetNZBInfo()->GetDupeScore() &&
IsDupeSuccess(pHistoryInfo->GetNZBInfo()))
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pHistoryInfo->GetNZBInfo()->GetName());
return;
}
}
}
if (bSkip)
{
if (!strcmp(pNZBInfo->GetName(), szDupeName))
{
warn("Skipping duplicate %s, found in history with %s", pNZBInfo->GetName(),
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
else
{
warn("Skipping duplicate %s, found in history %s with %s",
pNZBInfo->GetName(), szDupeName,
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
// find duplicates in download queue and post-queue and handle both items according to their scores:
// only one item remains in queue and another one is moved to history as dupe-backup
if (pNZBInfo->GetDupeMode() == dmScore)
{
// find duplicates in download queue
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
if (pGroupNZBInfo != pNZBInfo &&
pGroupNZBInfo->GetDupeMode() != dmForce &&
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
// if queue has a duplicate with the same or higher score - the new item
// is moved to history as dupe-backup
if (pNZBInfo->GetDupeScore() <= pGroupNZBInfo->GetDupeScore())
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pGroupNZBInfo->GetName());
return;
}
// if queue has a duplicate with lower score - the existing item is moved
// to history as dupe-backup (unless it is in post-processing stage) and
// the new item is added to queue
else
{
// unless it is in post-processing stage
bool bPostProcess = false;
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
if (pPostInfo->GetNZBInfo() == pGroupNZBInfo)
{
bPostProcess = true;
break;
}
}
if (!bPostProcess)
{
// the existing queue item is moved to history as dupe-backup
info("Moving collection %s with lower duplicate score to history", pGroupNZBInfo->GetName());
pGroupNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pGroupInfo->GetLastID(), false, QueueEditor::eaGroupDelete, 0, NULL);
}
}
}
}
// find duplicates in post queue
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
// if queue has a duplicate with the same or higher score - the new item
// is moved to history as dupe-backup;
if (pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pNZBInfo->GetDupeScore() <= pPostInfo->GetNZBInfo()->GetDupeScore() &&
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
return;
}
}
}
}
/**
- if download of an item fails and there are duplicates in history -
return the best duplicate from historyto queue for download;
- if download of an item completes successfully - nothing extra needs to be done;
*/
void DupeCoordinator::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("Processing duplicates for %s", pNZBInfo->GetName());
if (pNZBInfo->GetDupeMode() == dmScore && !IsDupeSuccess(pNZBInfo))
{
ReturnBestDupe(pDownloadQueue, pNZBInfo, pNZBInfo->GetName(), pNZBInfo->GetDupeKey());
}
}
/**
Returns the best duplicate from history to download queue.
*/
void DupeCoordinator::ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey)
{
// check if history (recent or dup) has other success-duplicates or good-duplicates
bool bHistoryDupe = false;
int iHistoryScore = 0;
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
bool bGoodDupe = false;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
(IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood) &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
if (!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore)
{
iHistoryScore = pHistoryInfo->GetNZBInfo()->GetDupeScore();
}
bHistoryDupe = true;
bGoodDupe = pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess ||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood) &&
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
if (!bHistoryDupe || pHistoryInfo->GetDupInfo()->GetDupeScore() > iHistoryScore)
{
iHistoryScore = pHistoryInfo->GetDupInfo()->GetDupeScore();
}
bHistoryDupe = true;
bGoodDupe = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
}
if (bGoodDupe)
{
// another duplicate with good-status exists - exit without moving other dupes to queue
return;
}
}
// check if duplicates exist in post-processing queue
bool bPostDupe = false;
int iPostScore = 0;
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
if (pPostInfo->GetNZBInfo() != pNZBInfo &&
pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey) &&
(!bPostDupe || pPostInfo->GetNZBInfo()->GetDupeScore() > iPostScore))
{
iPostScore = pPostInfo->GetNZBInfo()->GetDupeScore();
bPostDupe = true;
}
}
// check if duplicates exist in download queue
GroupQueue groupQueue;
pDownloadQueue->BuildGroups(&groupQueue);
bool bQueueDupe = false;
int iQueueScore = 0;
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
if (pGroupNZBInfo != pNZBInfo &&
pGroupNZBInfo->GetDupeMode() != dmForce &&
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(), szNZBName, szDupeKey) &&
(!bQueueDupe || pGroupNZBInfo->GetDupeScore() > iQueueScore))
{
iQueueScore = pGroupNZBInfo->GetDupeScore();
bQueueDupe = true;
}
}
// find dupe-backup with highest score, whose score is also higher than other
// success-duplicates and higher than already queued items
HistoryInfo* pHistoryDupe = NULL;
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
pHistoryInfo->GetNZBInfo()->CalcHealth() >= pHistoryInfo->GetNZBInfo()->CalcCriticalHealth() &&
pHistoryInfo->GetNZBInfo()->GetMarkStatus() != NZBInfo::ksBad &&
(!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore) &&
(!bPostDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iPostScore) &&
(!bQueueDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iQueueScore) &&
(!pHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > pHistoryDupe->GetNZBInfo()->GetDupeScore()) &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
pHistoryDupe = pHistoryInfo;
}
}
// move that dupe-backup from history to download queue
if (pHistoryDupe)
{
info("Found duplicate %s for %s", pHistoryDupe->GetNZBInfo()->GetName(), szNZBName);
HistoryRedownload(pDownloadQueue, pHistoryDupe);
}
}
void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood)
{
char szNZBName[1024];
pHistoryInfo->GetName(szNZBName, 1024);
info("Marking %s as %s", szNZBName, (bGood ? "good" : "bad"));
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
pHistoryInfo->GetNZBInfo()->SetMarkStatus(bGood ? NZBInfo::ksGood : NZBInfo::ksBad);
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo)
{
pHistoryInfo->GetDupInfo()->SetStatus(bGood ? DupInfo::dsGood : DupInfo::dsBad);
}
else
{
error("Could not mark %s as bad: history item has wrong type", szNZBName);
return;
}
if (!g_pOptions->GetDupeCheck() ||
(pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() == dmForce) ||
(pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() == dmForce))
{
return;
}
if (bGood)
{
// mark as good
// moving all duplicates from history to dup-history
HistoryCleanup(pDownloadQueue, pHistoryInfo);
}
else
{
// mark as bad
const char* szDupeKey = pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pHistoryInfo->GetNZBInfo()->GetDupeKey() :
pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pHistoryInfo->GetDupInfo()->GetDupeKey() :
NULL;
ReturnBestDupe(pDownloadQueue, NULL, szNZBName, szDupeKey);
}
}
void DupeCoordinator::HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo)
{
const char* szDupeKey = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetDupeKey() :
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetDupeKey() :
NULL;
const char* szNZBName = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetName() :
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetName() :
NULL;
bool bChanged = false;
int index = 0;
// traversing in a reverse order to delete items in order they were added to history
// (just to produce the log-messages in a more logical order)
for (HistoryList::reverse_iterator it = pDownloadQueue->GetHistoryList()->rbegin(); it != pDownloadQueue->GetHistoryList()->rend(); )
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
pHistoryInfo != pMarkHistoryInfo &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
HistoryTransformToDup(pDownloadQueue, pHistoryInfo, index);
index++;
it = pDownloadQueue->GetHistoryList()->rbegin() + index;
bChanged = true;
}
else
{
it++;
index++;
}
}
if (bChanged && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
}
void DupeCoordinator::HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
// replace history element
DupInfo* pDupInfo = new DupInfo();
pDupInfo->SetName(pHistoryInfo->GetNZBInfo()->GetName());
pDupInfo->SetDupeKey(pHistoryInfo->GetNZBInfo()->GetDupeKey());
pDupInfo->SetDupeScore(pHistoryInfo->GetNZBInfo()->GetDupeScore());
pDupInfo->SetDupeMode(pHistoryInfo->GetNZBInfo()->GetDupeMode());
pDupInfo->SetSize(pHistoryInfo->GetNZBInfo()->GetSize());
pDupInfo->SetFullContentHash(pHistoryInfo->GetNZBInfo()->GetFullContentHash());
pDupInfo->SetFilteredContentHash(pHistoryInfo->GetNZBInfo()->GetFilteredContentHash());
pDupInfo->SetStatus(
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood ? DupInfo::dsGood :
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad ? DupInfo::dsBad :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe ? DupInfo::dsDupe :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsManual ? DupInfo::dsDeleted :
IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ? DupInfo::dsSuccess :
DupInfo::dsFailed);
HistoryInfo* pNewHistoryInfo = new HistoryInfo(pDupInfo);
pNewHistoryInfo->SetTime(pHistoryInfo->GetTime());
(*pDownloadQueue->GetHistoryList())[pDownloadQueue->GetHistoryList()->size() - 1 - rindex] = pNewHistoryInfo;
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
delete pHistoryInfo;
info("Collection %s removed from history", szNiceName);
}

View File

@@ -1,53 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef DUPECOORDINATOR_H
#define DUPECOORDINATOR_H
#include <deque>
#include "DownloadInfo.h"
class DupeCoordinator
{
private:
bool IsDupeSuccess(NZBInfo* pNZBInfo);
void ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey);
void HistoryReturnDupe(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
void HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo);
bool SameNameOrKey(const char* szName1, const char* szDupeKey1, const char* szName2, const char* szDupeKey2);
protected:
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo) = 0;
virtual void DeleteQueuedFile(const char* szQueuedFile) = 0;
public:
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood);
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
};
#endif

View File

@@ -1,742 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "FeedCoordinator.h"
#include "Options.h"
#include "WebDownloader.h"
#include "Log.h"
#include "Util.h"
#include "FeedFile.h"
#include "FeedFilter.h"
#include "UrlCoordinator.h"
#include "DiskState.h"
extern Options* g_pOptions;
extern UrlCoordinator* g_pUrlCoordinator;
extern DiskState* g_pDiskState;
FeedCoordinator::FeedCacheItem::FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
time_t tLastUsage, FeedItemInfos* pFeedItemInfos)
{
m_szUrl = strdup(szUrl);
m_iCacheTimeSec = iCacheTimeSec;
m_szCacheId = strdup(szCacheId);
m_tLastUsage = tLastUsage;
m_pFeedItemInfos = pFeedItemInfos;
m_pFeedItemInfos->Retain();
}
FeedCoordinator::FeedCacheItem::~FeedCacheItem()
{
free(m_szUrl);
free(m_szCacheId);
m_pFeedItemInfos->Release();
}
FeedCoordinator::FeedCoordinator()
{
debug("Creating FeedCoordinator");
m_bForce = false;
m_bSave = false;
m_UrlCoordinatorObserver.m_pOwner = this;
g_pUrlCoordinator->Attach(&m_UrlCoordinatorObserver);
}
FeedCoordinator::~FeedCoordinator()
{
debug("Destroying FeedCoordinator");
// Cleanup
debug("Deleting FeedDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
delete *it;
}
m_ActiveDownloads.clear();
debug("Deleting Feeds");
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
delete *it;
}
m_Feeds.clear();
debug("Deleting FeedCache");
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
{
delete *it;
}
m_FeedCache.clear();
debug("FeedCoordinator destroyed");
}
void FeedCoordinator::AddFeed(FeedInfo* pFeedInfo)
{
m_Feeds.push_back(pFeedInfo);
}
void FeedCoordinator::Run()
{
debug("Entering FeedCoordinator-loop");
m_mutexDownloads.Lock();
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pOptions->GetReloadQueue())
{
g_pDiskState->LoadFeeds(&m_Feeds, &m_FeedHistory);
}
m_mutexDownloads.Unlock();
int iSleepInterval = 100;
int iUpdateCounter = 0;
int iCleanupCounter = 60000;
while (!IsStopped())
{
usleep(iSleepInterval * 1000);
iUpdateCounter += iSleepInterval;
if (iUpdateCounter >= 1000)
{
// this code should not be called too often, once per second is OK
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
{
m_mutexDownloads.Lock();
time_t tCurrent = time(NULL);
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
{
m_bForce = false;
// check feed list and update feeds
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (((pFeedInfo->GetInterval() > 0 &&
(tCurrent - pFeedInfo->GetLastUpdate() >= pFeedInfo->GetInterval() * 60 ||
tCurrent < pFeedInfo->GetLastUpdate())) ||
pFeedInfo->GetFetch()) &&
pFeedInfo->GetStatus() != FeedInfo::fsRunning)
{
StartFeedDownload(pFeedInfo, pFeedInfo->GetFetch());
}
else if (pFeedInfo->GetFetch())
{
m_bForce = true;
}
}
}
m_mutexDownloads.Unlock();
}
CheckSaveFeeds();
ResetHangingDownloads();
iUpdateCounter = 0;
}
iCleanupCounter += iSleepInterval;
if (iCleanupCounter >= 60000)
{
// clean up feed history once a minute
CleanupHistory();
CleanupCache();
CheckSaveFeeds();
iCleanupCounter = 0;
}
}
// waiting for downloads
debug("FeedCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
m_mutexDownloads.Lock();
completed = m_ActiveDownloads.size() == 0;
m_mutexDownloads.Unlock();
CheckSaveFeeds();
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("FeedCoordinator: Downloads are completed");
debug("Exiting FeedCoordinator-loop");
}
void FeedCoordinator::Stop()
{
Thread::Stop();
debug("Stopping UrlDownloads");
m_mutexDownloads.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
(*it)->Stop();
}
m_mutexDownloads.Unlock();
debug("UrlDownloads are notified");
}
void FeedCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
{
return;
}
m_mutexDownloads.Lock();
time_t tm = ::time(NULL);
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
FeedDownloader* pFeedDownloader = *it;
if (tm - pFeedDownloader->GetLastUpdateTime() > TimeOut &&
pFeedDownloader->GetStatus() == FeedDownloader::adRunning)
{
debug("Terminating hanging download %s", pFeedDownloader->GetInfoName());
if (pFeedDownloader->Terminate())
{
error("Terminated hanging download %s", pFeedDownloader->GetInfoName());
pFeedDownloader->GetFeedInfo()->SetStatus(FeedInfo::fsUndefined);
}
else
{
error("Could not terminate hanging download %s", pFeedDownloader->GetInfoName());
}
m_ActiveDownloads.erase(it);
// it's not safe to destroy pFeedDownloader, because the state of object is unknown
delete pFeedDownloader;
it = m_ActiveDownloads.begin();
continue;
}
it++;
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::LogDebugInfo()
{
debug(" FeedCoordinator");
debug(" ----------------");
m_mutexDownloads.Lock();
debug(" Active Downloads: %i", m_ActiveDownloads.size());
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
FeedDownloader* pFeedDownloader = *it;
pFeedDownloader->LogDebugInfo();
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::StartFeedDownload(FeedInfo* pFeedInfo, bool bForce)
{
debug("Starting new FeedDownloader for %", pFeedInfo->GetName());
FeedDownloader* pFeedDownloader = new FeedDownloader();
pFeedDownloader->SetAutoDestroy(true);
pFeedDownloader->Attach(this);
pFeedDownloader->SetFeedInfo(pFeedInfo);
pFeedDownloader->SetURL(pFeedInfo->GetUrl());
if (strlen(pFeedInfo->GetName()) > 0)
{
pFeedDownloader->SetInfoName(pFeedInfo->GetName());
}
else
{
char szUrlName[1024];
UrlInfo::MakeNiceName(pFeedInfo->GetUrl(), "", szUrlName, sizeof(szUrlName));
pFeedDownloader->SetInfoName(szUrlName);
}
pFeedDownloader->SetForce(bForce || g_pOptions->GetUrlForce());
char tmp[1024];
if (pFeedInfo->GetID() > 0)
{
snprintf(tmp, 1024, "%sfeed-%i.tmp", g_pOptions->GetTempDir(), pFeedInfo->GetID());
}
else
{
snprintf(tmp, 1024, "%sfeed-%i-%i.tmp", g_pOptions->GetTempDir(), (int)time(NULL), rand());
}
tmp[1024-1] = '\0';
pFeedDownloader->SetOutputFilename(tmp);
pFeedInfo->SetStatus(FeedInfo::fsRunning);
pFeedInfo->SetForce(bForce);
pFeedInfo->SetFetch(false);
m_ActiveDownloads.push_back(pFeedDownloader);
pFeedDownloader->Start();
}
void FeedCoordinator::Update(Subject* pCaller, void* pAspect)
{
debug("Notification from FeedDownloader received");
FeedDownloader* pFeedDownloader = (FeedDownloader*) pCaller;
if ((pFeedDownloader->GetStatus() == WebDownloader::adFinished) ||
(pFeedDownloader->GetStatus() == WebDownloader::adFailed) ||
(pFeedDownloader->GetStatus() == WebDownloader::adRetry))
{
FeedCompleted(pFeedDownloader);
}
}
void FeedCoordinator::FeedCompleted(FeedDownloader* pFeedDownloader)
{
debug("Feed downloaded");
FeedInfo* pFeedInfo = pFeedDownloader->GetFeedInfo();
bool bStatusOK = pFeedDownloader->GetStatus() == WebDownloader::adFinished;
if (bStatusOK)
{
pFeedInfo->SetOutputFilename(pFeedDownloader->GetOutputFilename());
}
// delete Download from Queue
m_mutexDownloads.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
FeedDownloader* pa = *it;
if (pa == pFeedDownloader)
{
m_ActiveDownloads.erase(it);
break;
}
}
m_mutexDownloads.Unlock();
if (bStatusOK)
{
if (!pFeedInfo->GetPreview())
{
FeedFile* pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
remove(pFeedInfo->GetOutputFilename());
m_mutexDownloads.Lock();
if (pFeedFile)
{
ProcessFeed(pFeedInfo, pFeedFile->GetFeedItemInfos());
delete pFeedFile;
}
pFeedInfo->SetLastUpdate(time(NULL));
pFeedInfo->SetForce(false);
m_bSave = true;
m_mutexDownloads.Unlock();
}
pFeedInfo->SetStatus(FeedInfo::fsFinished);
}
else
{
pFeedInfo->SetStatus(FeedInfo::fsFailed);
}
}
void FeedCoordinator::FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
{
debug("Filtering feed %s", pFeedInfo->GetName());
FeedFilter* pFeedFilter = NULL;
if (pFeedInfo->GetFilter() && strlen(pFeedInfo->GetFilter()) > 0)
{
pFeedFilter = new FeedFilter(pFeedInfo->GetFilter());
}
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
pFeedItemInfo->SetMatchStatus(FeedItemInfo::msAccepted);
pFeedItemInfo->SetMatchRule(0);
pFeedItemInfo->SetPauseNzb(pFeedInfo->GetPauseNzb());
pFeedItemInfo->SetPriority(pFeedInfo->GetPriority());
pFeedItemInfo->SetAddCategory(pFeedInfo->GetCategory());
pFeedItemInfo->SetDupeScore(0);
pFeedItemInfo->SetDupeMode(dmScore);
pFeedItemInfo->BuildDupeKey(NULL, NULL);
if (pFeedFilter)
{
pFeedFilter->Match(pFeedItemInfo);
}
}
delete pFeedFilter;
}
void FeedCoordinator::ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
{
debug("Process feed %s", pFeedInfo->GetName());
FilterFeed(pFeedInfo, pFeedItemInfos);
bool bFirstFetch = pFeedInfo->GetLastUpdate() == 0;
int iAdded = 0;
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
if (pFeedItemInfo->GetMatchStatus() == FeedItemInfo::msAccepted)
{
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
FeedHistoryInfo::EStatus eStatus = FeedHistoryInfo::hsUnknown;
if (bFirstFetch)
{
eStatus = FeedHistoryInfo::hsBacklog;
}
else if (!pFeedHistoryInfo)
{
DownloadItem(pFeedInfo, pFeedItemInfo);
eStatus = FeedHistoryInfo::hsFetched;
iAdded++;
}
if (pFeedHistoryInfo)
{
pFeedHistoryInfo->SetLastSeen(time(NULL));
}
else
{
m_FeedHistory.Add(pFeedItemInfo->GetUrl(), eStatus, time(NULL));
}
}
}
if (iAdded)
{
info("%s has %i new item(s)", pFeedInfo->GetName(), iAdded);
}
else
{
detail("%s has no new items", pFeedInfo->GetName());
}
}
void FeedCoordinator::DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo)
{
debug("Download %s from %s", pFeedItemInfo->GetUrl(), pFeedInfo->GetName());
UrlInfo* pUrlInfo = new UrlInfo();
pUrlInfo->SetURL(pFeedItemInfo->GetUrl());
// add .nzb-extension if not present
char szNZBName[1024];
strncpy(szNZBName, pFeedItemInfo->GetFilename(), 1024);
szNZBName[1024-1] = '\0';
char* ext = strrchr(szNZBName, '.');
if (ext && !strcasecmp(ext, ".nzb"))
{
*ext = '\0';
}
char szNZBName2[1024];
snprintf(szNZBName2, 1024, "%s.nzb", szNZBName);
Util::MakeValidFilename(szNZBName2, '_', false);
if (strlen(szNZBName) > 0)
{
pUrlInfo->SetNZBFilename(szNZBName2);
}
pUrlInfo->SetCategory(pFeedItemInfo->GetAddCategory());
pUrlInfo->SetPriority(pFeedItemInfo->GetPriority());
pUrlInfo->SetAddPaused(pFeedItemInfo->GetPauseNzb());
pUrlInfo->SetDupeKey(pFeedItemInfo->GetDupeKey());
pUrlInfo->SetDupeScore(pFeedItemInfo->GetDupeScore());
pUrlInfo->SetDupeMode(pFeedItemInfo->GetDupeMode());
pUrlInfo->SetForce(pFeedInfo->GetForce() || g_pOptions->GetUrlForce());
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, false);
}
bool FeedCoordinator::ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos)
{
if (iID < 1 || iID > (int)m_Feeds.size())
{
return false;
}
FeedInfo* pFeedInfo = m_Feeds.at(iID - 1);
return PreviewFeed(pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(), pFeedInfo->GetPriority(),
0, NULL, ppFeedItemInfos);
}
bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos)
{
debug("Preview feed %s", szName);
FeedInfo* pFeedInfo = new FeedInfo(0, szName, szUrl, 0, szFilter, bPauseNzb, szCategory, iPriority);
pFeedInfo->SetPreview(true);
FeedItemInfos* pFeedItemInfos = NULL;
bool bHasCache = false;
if (iCacheTimeSec > 0 && *szCacheId != '\0')
{
m_mutexDownloads.Lock();
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
{
FeedCacheItem* pFeedCacheItem = *it;
if (!strcmp(pFeedCacheItem->GetCacheId(), szCacheId))
{
pFeedCacheItem->SetLastUsage(time(NULL));
pFeedItemInfos = pFeedCacheItem->GetFeedItemInfos();
pFeedItemInfos->Retain();
bHasCache = true;
break;
}
}
m_mutexDownloads.Unlock();
}
if (!bHasCache)
{
m_mutexDownloads.Lock();
bool bFirstFetch = true;
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo2 = *it;
if (!strcmp(pFeedInfo2->GetUrl(), pFeedInfo->GetUrl()) &&
!strcmp(pFeedInfo2->GetFilter(), pFeedInfo->GetFilter()) &&
pFeedInfo2->GetLastUpdate() > 0)
{
bFirstFetch = false;
break;
}
}
StartFeedDownload(pFeedInfo, true);
m_mutexDownloads.Unlock();
// wait until the download in a separate thread completes
while (pFeedInfo->GetStatus() == FeedInfo::fsRunning)
{
usleep(100 * 1000);
}
// now can process the feed
FeedFile* pFeedFile = NULL;
if (pFeedInfo->GetStatus() == FeedInfo::fsFinished)
{
pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
}
remove(pFeedInfo->GetOutputFilename());
if (!pFeedFile)
{
delete pFeedInfo;
return false;
}
pFeedItemInfos = pFeedFile->GetFeedItemInfos();
pFeedItemInfos->Retain();
delete pFeedFile;
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
pFeedItemInfo->SetStatus(bFirstFetch ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
if (pFeedHistoryInfo)
{
pFeedItemInfo->SetStatus((FeedItemInfo::EStatus)pFeedHistoryInfo->GetStatus());
}
}
}
FilterFeed(pFeedInfo, pFeedItemInfos);
delete pFeedInfo;
if (iCacheTimeSec > 0 && *szCacheId != '\0' && !bHasCache)
{
FeedCacheItem* pFeedCacheItem = new FeedCacheItem(szUrl, iCacheTimeSec, szCacheId, time(NULL), pFeedItemInfos);
m_mutexDownloads.Lock();
m_FeedCache.push_back(pFeedCacheItem);
m_mutexDownloads.Unlock();
}
*ppFeedItemInfos = pFeedItemInfos;
return true;
}
void FeedCoordinator::FetchFeed(int iID)
{
debug("FetchFeeds");
m_mutexDownloads.Lock();
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (pFeedInfo->GetID() == iID || iID == 0)
{
pFeedInfo->SetFetch(true);
m_bForce = true;
}
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::UrlCoordinatorUpdate(Subject* pCaller, void* pAspect)
{
debug("Notification from URL-Coordinator received");
UrlCoordinator::Aspect* pUrlAspect = (UrlCoordinator::Aspect*)pAspect;
if (pUrlAspect->eAction == UrlCoordinator::eaUrlCompleted)
{
m_mutexDownloads.Lock();
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pUrlAspect->pUrlInfo->GetURL());
if (pFeedHistoryInfo)
{
pFeedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
}
else
{
m_FeedHistory.Add(pUrlAspect->pUrlInfo->GetURL(), FeedHistoryInfo::hsFetched, time(NULL));
}
m_bSave = true;
m_mutexDownloads.Unlock();
}
}
bool FeedCoordinator::HasActiveDownloads()
{
m_mutexDownloads.Lock();
bool bActive = !m_ActiveDownloads.empty();
m_mutexDownloads.Unlock();
return bActive;
}
void FeedCoordinator::CheckSaveFeeds()
{
debug("CheckSaveFeeds");
m_mutexDownloads.Lock();
if (m_bSave)
{
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFeeds(&m_Feeds, &m_FeedHistory);
}
m_bSave = false;
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::CleanupHistory()
{
debug("CleanupHistory");
m_mutexDownloads.Lock();
time_t tOldestUpdate = time(NULL);
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (pFeedInfo->GetLastUpdate() < tOldestUpdate)
{
tOldestUpdate = pFeedInfo->GetLastUpdate();
}
}
time_t tBorderDate = tOldestUpdate - g_pOptions->GetFeedHistory();
int i = 0;
for (FeedHistory::iterator it = m_FeedHistory.begin(); it != m_FeedHistory.end(); )
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (pFeedHistoryInfo->GetLastSeen() < tBorderDate)
{
detail("Deleting %s from feed history", pFeedHistoryInfo->GetUrl());
delete pFeedHistoryInfo;
m_FeedHistory.erase(it);
it = m_FeedHistory.begin() + i;
m_bSave = true;
}
else
{
it++;
i++;
}
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::CleanupCache()
{
debug("CleanupCache");
m_mutexDownloads.Lock();
time_t tCurTime = time(NULL);
int i = 0;
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); )
{
FeedCacheItem* pFeedCacheItem = *it;
if (pFeedCacheItem->GetLastUsage() + pFeedCacheItem->GetCacheTimeSec() < tCurTime ||
pFeedCacheItem->GetLastUsage() > tCurTime)
{
debug("Deleting %s from feed cache", pFeedCacheItem->GetUrl());
delete pFeedCacheItem;
m_FeedCache.erase(it);
it = m_FeedCache.begin() + i;
}
else
{
it++;
i++;
}
}
m_mutexDownloads.Unlock();
}

View File

@@ -1,127 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDCOORDINATOR_H
#define FEEDCOORDINATOR_H
#include <deque>
#include <list>
#include <time.h>
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "Observer.h"
class FeedDownloader;
class FeedCoordinator : public Thread, public Observer, public Subject
{
public:
typedef std::list<FeedDownloader*> ActiveDownloads;
private:
class UrlCoordinatorObserver: public Observer
{
public:
FeedCoordinator* m_pOwner;
virtual void Update(Subject* pCaller, void* pAspect) { m_pOwner->UrlCoordinatorUpdate(pCaller, pAspect); }
};
class FeedCacheItem
{
private:
char* m_szUrl;
int m_iCacheTimeSec;
char* m_szCacheId;
time_t m_tLastUsage;
FeedItemInfos* m_pFeedItemInfos;
public:
FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
time_t tLastUsage, FeedItemInfos* pFeedItemInfos);
~FeedCacheItem();
const char* GetUrl() { return m_szUrl; }
int GetCacheTimeSec() { return m_iCacheTimeSec; }
const char* GetCacheId() { return m_szCacheId; }
time_t GetLastUsage() { return m_tLastUsage; }
void SetLastUsage(time_t tLastUsage) { m_tLastUsage = tLastUsage; }
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
};
typedef std::deque<FeedCacheItem*> FeedCache;
private:
Feeds m_Feeds;
ActiveDownloads m_ActiveDownloads;
FeedHistory m_FeedHistory;
Mutex m_mutexDownloads;
UrlCoordinatorObserver m_UrlCoordinatorObserver;
bool m_bForce;
bool m_bSave;
FeedCache m_FeedCache;
void StartFeedDownload(FeedInfo* pFeedInfo, bool bForce);
void FeedCompleted(FeedDownloader* pFeedDownloader);
void FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
void ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
void DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo);
void ResetHangingDownloads();
void UrlCoordinatorUpdate(Subject* pCaller, void* pAspect);
void CleanupHistory();
void CleanupCache();
void CheckSaveFeeds();
public:
FeedCoordinator();
virtual ~FeedCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* pCaller, void* pAspect);
void AddFeed(FeedInfo* pFeedInfo);
bool PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos);
bool ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos);
void FetchFeed(int iID);
bool HasActiveDownloads();
Feeds* GetFeeds() { return &m_Feeds; }
void LogDebugInfo();
};
class FeedDownloader : public WebDownloader
{
private:
FeedInfo* m_pFeedInfo;
public:
void SetFeedInfo(FeedInfo* pFeedInfo) { m_pFeedInfo = pFeedInfo; }
FeedInfo* GetFeedInfo() { return m_pFeedInfo; }
};
#endif

View File

@@ -1,609 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <string.h>
#include <list>
#ifdef WIN32
#include <comutil.h>
#import <msxml.tlb> named_guids
using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlerror.h>
#include <libxml/entities.h>
#endif
#include "nzbget.h"
#include "FeedFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
FeedFile::FeedFile(const char* szFileName)
{
debug("Creating FeedFile");
m_szFileName = strdup(szFileName);
m_pFeedItemInfos = new FeedItemInfos();
m_pFeedItemInfos->Retain();
#ifndef WIN32
m_pFeedItemInfo = NULL;
m_szTagContent = NULL;
m_iTagContentLen = 0;
#endif
}
FeedFile::~FeedFile()
{
debug("Destroying FeedFile");
// Cleanup
free(m_szFileName);
m_pFeedItemInfos->Release();
#ifndef WIN32
delete m_pFeedItemInfo;
free(m_szTagContent);
#endif
}
void FeedFile::LogDebugInfo()
{
debug(" FeedFile %s", m_szFileName);
}
void FeedFile::AddItem(FeedItemInfo* pFeedItemInfo)
{
m_pFeedItemInfos->Add(pFeedItemInfo);
}
void FeedFile::ParseSubject(FeedItemInfo* pFeedItemInfo)
{
// if title has quatation marks we use only part within quatation marks
char* p = (char*)pFeedItemInfo->GetTitle();
char* start = strchr(p, '\"');
if (start)
{
start++;
char* end = strchr(start + 1, '\"');
if (end)
{
int len = (int)(end - start);
char* point = strchr(start + 1, '.');
if (point && point < end)
{
char* filename = (char*)malloc(len + 1);
strncpy(filename, start, len);
filename[len] = '\0';
char* ext = strrchr(filename, '.');
if (ext && !strcasecmp(ext, ".par2"))
{
*ext = '\0';
}
pFeedItemInfo->SetFilename(filename);
free(filename);
return;
}
}
}
pFeedItemInfo->SetFilename(pFeedItemInfo->GetTitle());
}
#ifdef WIN32
FeedFile* FeedFile::Create(const char* szFileName)
{
CoInitialize(NULL);
HRESULT hr;
MSXML::IXMLDOMDocumentPtr doc;
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
if (FAILED(hr))
{
return NULL;
}
// Load the XML document file...
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
VARIANT_BOOL success = doc->load(v);
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
const char* szErrMsg = r;
error("Error parsing rss feed: %s", szErrMsg);
return NULL;
}
FeedFile* pFile = new FeedFile(szFileName);
if (!pFile->ParseFeed(doc))
{
delete pFile;
pFile = NULL;
}
return pFile;
}
void FeedFile::EncodeURL(const char* szFilename, char* szURL)
{
while (char ch = *szFilename++)
{
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') )
{
*szURL++ = ch;
}
else
{
*szURL++ = '%';
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
}
}
*szURL = NULL;
}
bool FeedFile::ParseFeed(IUnknown* nzb)
{
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
MSXML::IXMLDOMNodeListPtr itemList = root->selectNodes("/rss/channel/item");
for (int i = 0; i < itemList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
FeedItemInfo* pFeedItemInfo = new FeedItemInfo();
AddItem(pFeedItemInfo);
MSXML::IXMLDOMNodePtr tag;
MSXML::IXMLDOMNodePtr attr;
// <title>Debian 6</title>
tag = node->selectSingleNode("title");
if (!tag)
{
// bad rss feed
return false;
}
_bstr_t title(tag->Gettext());
pFeedItemInfo->SetTitle(title);
ParseSubject(pFeedItemInfo);
// <pubDate>Wed, 26 Jun 2013 00:02:54 -0600</pubDate>
tag = node->selectSingleNode("pubDate");
if (tag)
{
_bstr_t time(tag->Gettext());
time_t unixtime = WebUtil::ParseRfc822DateTime(time);
if (unixtime > 0)
{
pFeedItemInfo->SetTime(unixtime);
}
}
// <category>Movies &gt; HD</category>
tag = node->selectSingleNode("category");
if (tag)
{
_bstr_t category(tag->Gettext());
pFeedItemInfo->SetCategory(category);
}
// <description>long text</description>
tag = node->selectSingleNode("description");
if (tag)
{
_bstr_t description(tag->Gettext());
pFeedItemInfo->SetDescription(description);
}
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
tag = node->selectSingleNode("enclosure");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("url");
if (attr)
{
_bstr_t url(attr->Gettext());
pFeedItemInfo->SetUrl(url);
}
attr = tag->Getattributes()->getNamedItem("length");
if (attr)
{
_bstr_t size(attr->Gettext());
long long lSize = atoll(size);
pFeedItemInfo->SetSize(lSize);
}
}
if (!pFeedItemInfo->GetUrl())
{
// <link>https://nzb.org/fetch/334534ce/4364564564</link>
tag = node->selectSingleNode("link");
if (!tag)
{
// bad rss feed
return false;
}
_bstr_t link(tag->Gettext());
pFeedItemInfo->SetUrl(link);
}
// newznab special
//<newznab:attr name="size" value="5423523453534" />
if (pFeedItemInfo->GetSize() == 0)
{
tag = node->selectSingleNode("newznab:attr[@name='size']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t size(attr->Gettext());
long long lSize = atoll(size);
pFeedItemInfo->SetSize(lSize);
}
}
}
//<newznab:attr name="imdb" value="1588173"/>
tag = node->selectSingleNode("newznab:attr[@name='imdb']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
int iVal = atoi(val);
pFeedItemInfo->SetImdbId(iVal);
}
}
//<newznab:attr name="rageid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='rageid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
int iVal = atoi(val);
pFeedItemInfo->SetRageId(iVal);
}
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
tag = node->selectSingleNode("newznab:attr[@name='episode']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
pFeedItemInfo->SetEpisode(val);
}
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
tag = node->selectSingleNode("newznab:attr[@name='season']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
pFeedItemInfo->SetSeason(val);
}
}
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr");
for (int i = 0; i < itemList->Getlength(); i++)
{
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
MSXML::IXMLDOMNodePtr name = node->Getattributes()->getNamedItem("name");
MSXML::IXMLDOMNodePtr value = node->Getattributes()->getNamedItem("value");
if (name && value)
{
_bstr_t name(name->Gettext());
_bstr_t val(value->Gettext());
pFeedItemInfo->GetAttributes()->Add(name, val);
}
}
}
return true;
}
#else
FeedFile* FeedFile::Create(const char* szFileName)
{
FeedFile* pFile = new FeedFile(szFileName);
xmlSAXHandler SAX_handler = {0};
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
pFile->m_bIgnoreNextError = false;
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (ret != 0)
{
error("Failed to parse rss feed");
delete pFile;
pFile = NULL;
}
return pFile;
}
void FeedFile::Parse_StartElement(const char *name, const char **atts)
{
ResetTagContent();
if (!strcmp("item", name))
{
delete m_pFeedItemInfo;
m_pFeedItemInfo = new FeedItemInfo();
}
else if (!strcmp("enclosure", name) && m_pFeedItemInfo)
{
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
for (; *atts; atts+=2)
{
if (!strcmp("url", atts[0]))
{
char* szUrl = strdup(atts[1]);
WebUtil::XmlDecode(szUrl);
m_pFeedItemInfo->SetUrl(szUrl);
free(szUrl);
}
else if (!strcmp("length", atts[0]))
{
long long lSize = atoll(atts[1]);
m_pFeedItemInfo->SetSize(lSize);
}
}
}
else if (m_pFeedItemInfo && !strcmp("newznab:attr", name) &&
atts[0] && atts[1] && atts[2] && atts[3] &&
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
{
m_pFeedItemInfo->GetAttributes()->Add(atts[1], atts[3]);
//<newznab:attr name="size" value="5423523453534" />
if (m_pFeedItemInfo->GetSize() == 0 &&
!strcmp("size", atts[1]))
{
long long lSize = atoll(atts[3]);
m_pFeedItemInfo->SetSize(lSize);
}
//<newznab:attr name="imdb" value="1588173"/>
else if (!strcmp("imdb", atts[1]))
{
m_pFeedItemInfo->SetImdbId(atoi(atts[3]));
}
//<newznab:attr name="rageid" value="33877"/>
else if (!strcmp("rageid", atts[1]))
{
m_pFeedItemInfo->SetRageId(atoi(atts[3]));
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
else if (!strcmp("episode", atts[1]))
{
m_pFeedItemInfo->SetEpisode(atts[3]);
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
else if (!strcmp("season", atts[1]))
{
m_pFeedItemInfo->SetSeason(atts[3]);
}
}
}
void FeedFile::Parse_EndElement(const char *name)
{
if (!strcmp("item", name))
{
// Close the file element, add the new file to file-list
AddItem(m_pFeedItemInfo);
m_pFeedItemInfo = NULL;
}
else if (!strcmp("title", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetTitle(m_szTagContent);
ResetTagContent();
ParseSubject(m_pFeedItemInfo);
}
else if (!strcmp("link", name) && m_pFeedItemInfo &&
(!m_pFeedItemInfo->GetUrl() || strlen(m_pFeedItemInfo->GetUrl()) == 0))
{
m_pFeedItemInfo->SetUrl(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("category", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetCategory(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("description", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetDescription(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("pubDate", name) && m_pFeedItemInfo)
{
time_t unixtime = WebUtil::ParseRfc822DateTime(m_szTagContent);
if (unixtime > 0)
{
m_pFeedItemInfo->SetTime(unixtime);
}
ResetTagContent();
}
}
void FeedFile::Parse_Content(const char *buf, int len)
{
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
m_iTagContentLen += len;
m_szTagContent[m_iTagContentLen] = '\0';
}
void FeedFile::ResetTagContent()
{
free(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
void FeedFile::SAX_StartElement(FeedFile* pFile, const char *name, const char **atts)
{
pFile->Parse_StartElement(name, atts);
}
void FeedFile::SAX_EndElement(FeedFile* pFile, const char *name)
{
pFile->Parse_EndElement(name);
}
void FeedFile::SAX_characters(FeedFile* pFile, const char * xmlstr, int len)
{
char* str = (char*)xmlstr;
// trim starting blanks
int off = 0;
for (int i = 0; i < len; i++)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
off++;
}
else
{
break;
}
}
int newlen = len - off;
// trim ending blanks
for (int i = len - 1; i >= off; i--)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
newlen--;
}
else
{
break;
}
}
if (newlen > 0)
{
// interpret tag content
pFile->Parse_Content(str + off, newlen);
}
}
void* FeedFile::SAX_getEntity(FeedFile* pFile, const char * name)
{
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
if (!e)
{
warn("entity not found");
pFile->m_bIgnoreNextError = true;
}
return e;
}
void FeedFile::SAX_error(FeedFile* pFile, const char *msg, ...)
{
if (pFile->m_bIgnoreNextError)
{
pFile->m_bIgnoreNextError = false;
return;
}
va_list argp;
va_start(argp, msg);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing rss feed: %s", szErrMsg);
}
#endif

View File

@@ -1,70 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDFILE_H
#define FEEDFILE_H
#include <vector>
#include "FeedInfo.h"
class FeedFile
{
private:
FeedItemInfos* m_pFeedItemInfos;
char* m_szFileName;
FeedFile(const char* szFileName);
void AddItem(FeedItemInfo* pFeedItemInfo);
void ParseSubject(FeedItemInfo* pFeedItemInfo);
#ifdef WIN32
bool ParseFeed(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
FeedItemInfo* m_pFeedItemInfo;
char* m_szTagContent;
int m_iTagContentLen;
bool m_bIgnoreNextError;
static void SAX_StartElement(FeedFile* pFile, const char *name, const char **atts);
static void SAX_EndElement(FeedFile* pFile, const char *name);
static void SAX_characters(FeedFile* pFile, const char * xmlstr, int len);
static void* SAX_getEntity(FeedFile* pFile, const char * name);
static void SAX_error(FeedFile* pFile, const char *msg, ...);
void Parse_StartElement(const char *name, const char **atts);
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
void ResetTagContent();
#endif
public:
virtual ~FeedFile();
static FeedFile* Create(const char* szFileName);
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
void LogDebugInfo();
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,186 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDFILTER_H
#define FEEDFILTER_H
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "Util.h"
class FeedFilter
{
private:
typedef std::deque<char*> RefValues;
enum ETermCommand
{
fcText,
fcRegex,
fcEqual,
fcLess,
fcLessEqual,
fcGreater,
fcGreaterEqual,
fcOpeningBrace,
fcClosingBrace,
fcOrOperator
};
class Term
{
private:
bool m_bPositive;
char* m_szField;
ETermCommand m_eCommand;
char* m_szParam;
long long m_iIntParam;
double m_fFloatParam;
bool m_bFloat;
RegEx* m_pRegEx;
RefValues* m_pRefValues;
bool GetFieldData(const char* szField, FeedItemInfo* pFeedItemInfo,
const char** StrValue, long long* IntValue);
bool ParseParam(const char* szField, const char* szParam);
bool ParseSizeParam(const char* szParam);
bool ParseAgeParam(const char* szParam);
bool ParseNumericParam(const char* szParam);
bool MatchValue(const char* szStrValue, long long iIntValue);
bool MatchText(const char* szStrValue);
bool MatchRegex(const char* szStrValue);
void FillWildMaskRefValues(const char* szStrValue, WildMask* pMask, int iRefOffset);
void FillRegExRefValues(const char* szStrValue, RegEx* pRegEx);
public:
Term();
~Term();
void SetRefValues(RefValues* pRefValues) { m_pRefValues = pRefValues; }
bool Compile(char* szToken);
bool Match(FeedItemInfo* pFeedItemInfo);
ETermCommand GetCommand() { return m_eCommand; }
};
typedef std::deque<Term*> TermList;
enum ERuleCommand
{
frAccept,
frReject,
frRequire,
frOptions,
frComment
};
class Rule
{
private:
bool m_bIsValid;
ERuleCommand m_eCommand;
char* m_szCategory;
int m_iPriority;
int m_iAddPriority;
bool m_bPause;
int m_iDupeScore;
int m_iAddDupeScore;
char* m_szDupeKey;
char* m_szAddDupeKey;
EDupeMode m_eDupeMode;
char* m_szSeries;
char* m_szRageId;
bool m_bHasCategory;
bool m_bHasPriority;
bool m_bHasAddPriority;
bool m_bHasPause;
bool m_bHasDupeScore;
bool m_bHasAddDupeScore;
bool m_bHasDupeKey;
bool m_bHasAddDupeKey;
bool m_bHasDupeMode;
bool m_bPatCategory;
bool m_bPatDupeKey;
bool m_bPatAddDupeKey;
bool m_bHasSeries;
bool m_bHasRageId;
char* m_szPatCategory;
char* m_szPatDupeKey;
char* m_szPatAddDupeKey;
TermList m_Terms;
RefValues m_RefValues;
char* CompileCommand(char* szRule);
char* CompileOptions(char* szRule);
bool CompileTerm(char* szTerm);
bool MatchExpression(FeedItemInfo* pFeedItemInfo);
public:
Rule();
~Rule();
void Compile(char* szRule);
bool IsValid() { return m_bIsValid; }
ERuleCommand GetCommand() { return m_eCommand; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
int GetAddPriority() { return m_iAddPriority; }
bool GetPause() { return m_bPause; }
const char* GetDupeKey() { return m_szDupeKey; }
const char* GetAddDupeKey() { return m_szAddDupeKey; }
int GetDupeScore() { return m_iDupeScore; }
int GetAddDupeScore() { return m_iAddDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
const char* GetRageId() { return m_szRageId; }
const char* GetSeries() { return m_szSeries; }
bool HasCategory() { return m_bHasCategory; }
bool HasPriority() { return m_bHasPriority; }
bool HasAddPriority() { return m_bHasAddPriority; }
bool HasPause() { return m_bHasPause; }
bool HasDupeScore() { return m_bHasDupeScore; }
bool HasAddDupeScore() { return m_bHasAddDupeScore; }
bool HasDupeKey() { return m_bHasDupeKey; }
bool HasAddDupeKey() { return m_bHasAddDupeKey; }
bool HasDupeMode() { return m_bHasDupeMode; }
bool HasRageId() { return m_bHasRageId; }
bool HasSeries() { return m_bHasSeries; }
bool Match(FeedItemInfo* pFeedItemInfo);
void ExpandRefValues(FeedItemInfo* pFeedItemInfo, char** pDestStr, char* pPatStr);
const char* GetRefValue(FeedItemInfo* pFeedItemInfo, const char* szVarName);
};
typedef std::deque<Rule*> RuleList;
private:
RuleList m_Rules;
void Compile(const char* szFilter);
void CompileRule(char* szRule);
void ApplyOptions(Rule* pRule, FeedItemInfo* pFeedItemInfo);
public:
FeedFilter(const char* szFilter);
~FeedFilter();
void Match(FeedItemInfo* pFeedItemInfo);
};
#endif

View File

@@ -1,440 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision: 0 $
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include "nzbget.h"
#include "FeedInfo.h"
#include "Util.h"
FeedInfo::FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority)
{
m_iID = iID;
m_szName = strdup(szName ? szName : "");
m_szUrl = strdup(szUrl ? szUrl : "");
m_szFilter = strdup(szFilter ? szFilter : "");
m_iFilterHash = Util::HashBJ96(szFilter, strlen(szFilter), 0);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iInterval = iInterval;
m_bPauseNzb = bPauseNzb;
m_iPriority = iPriority;
m_tLastUpdate = 0;
m_bPreview = false;
m_eStatus = fsUndefined;
m_szOutputFilename = NULL;
m_bFetch = false;
m_bForce = false;
}
FeedInfo::~FeedInfo()
{
free(m_szName);
free(m_szUrl);
free(m_szFilter);
free(m_szCategory);
free(m_szOutputFilename);
}
void FeedInfo::SetOutputFilename(const char* szOutputFilename)
{
free(m_szOutputFilename);
m_szOutputFilename = strdup(szOutputFilename);
}
FeedItemInfo::Attr::Attr(const char* szName, const char* szValue)
{
m_szName = strdup(szName ? szName : "");
m_szValue = strdup(szValue ? szValue : "");
}
FeedItemInfo::Attr::~Attr()
{
free(m_szName);
free(m_szValue);
}
FeedItemInfo::Attributes::~Attributes()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
void FeedItemInfo::Attributes::Add(const char* szName, const char* szValue)
{
push_back(new Attr(szName, szValue));
}
FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* szName)
{
for (iterator it = begin(); it != end(); it++)
{
Attr* pAttr = *it;
if (!strcasecmp(pAttr->GetName(), szName))
{
return pAttr;
}
}
return NULL;
}
FeedItemInfo::FeedItemInfo()
{
m_pSharedFeedData = NULL;
m_szTitle = NULL;
m_szFilename = NULL;
m_szUrl = NULL;
m_szCategory = strdup("");
m_lSize = 0;
m_tTime = 0;
m_iImdbId = 0;
m_iRageId = 0;
m_szDescription = strdup("");
m_szSeason = NULL;
m_szEpisode = NULL;
m_iSeasonNum = 0;
m_iEpisodeNum = 0;
m_bSeasonEpisodeParsed = false;
m_szAddCategory = strdup("");
m_bPauseNzb = false;
m_iPriority = 0;
m_eStatus = isUnknown;
m_eMatchStatus = msIgnored;
m_iMatchRule = 0;
m_szDupeKey = NULL;
m_iDupeScore = 0;
m_eDupeMode = dmScore;
}
FeedItemInfo::~FeedItemInfo()
{
free(m_szTitle);
free(m_szFilename);
free(m_szUrl);
free(m_szCategory);
free(m_szDescription);
free(m_szSeason);
free(m_szEpisode);
free(m_szAddCategory);
free(m_szDupeKey);
}
void FeedItemInfo::SetTitle(const char* szTitle)
{
free(m_szTitle);
m_szTitle = szTitle ? strdup(szTitle) : NULL;
}
void FeedItemInfo::SetFilename(const char* szFilename)
{
free(m_szFilename);
m_szFilename = szFilename ? strdup(szFilename) : NULL;
}
void FeedItemInfo::SetUrl(const char* szUrl)
{
free(m_szUrl);
m_szUrl = szUrl ? strdup(szUrl) : NULL;
}
void FeedItemInfo::SetCategory(const char* szCategory)
{
free(m_szCategory);
m_szCategory = strdup(szCategory ? szCategory: "");
}
void FeedItemInfo::SetDescription(const char* szDescription)
{
free(m_szDescription);
m_szDescription = strdup(szDescription ? szDescription: "");
}
void FeedItemInfo::SetSeason(const char* szSeason)
{
free(m_szSeason);
m_szSeason = szSeason ? strdup(szSeason) : NULL;
m_iSeasonNum = szSeason ? ParsePrefixedInt(szSeason) : 0;
}
void FeedItemInfo::SetEpisode(const char* szEpisode)
{
free(m_szEpisode);
m_szEpisode = szEpisode ? strdup(szEpisode) : NULL;
m_iEpisodeNum = szEpisode ? ParsePrefixedInt(szEpisode) : 0;
}
int FeedItemInfo::ParsePrefixedInt(const char *szValue)
{
const char* szVal = szValue;
if (!strchr("0123456789", *szVal))
{
szVal++;
}
return atoi(szVal);
}
void FeedItemInfo::SetAddCategory(const char* szAddCategory)
{
free(m_szAddCategory);
m_szAddCategory = strdup(szAddCategory ? szAddCategory : "");
}
void FeedItemInfo::SetDupeKey(const char* szDupeKey)
{
free(m_szDupeKey);
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
}
void FeedItemInfo::AppendDupeKey(const char* szExtraDupeKey)
{
if (!m_szDupeKey || *m_szDupeKey == '\0' || !szExtraDupeKey || *szExtraDupeKey == '\0')
{
return;
}
int iLen = (m_szDupeKey ? strlen(m_szDupeKey) : 0) + 1 + strlen(szExtraDupeKey) + 1;
char* szNewKey = (char*)malloc(iLen);
snprintf(szNewKey, iLen, "%s-%s", m_szDupeKey, szExtraDupeKey);
szNewKey[iLen - 1] = '\0';
free(m_szDupeKey);
m_szDupeKey = szNewKey;
}
void FeedItemInfo::BuildDupeKey(const char* szRageId, const char* szSeries)
{
int iRageId = szRageId && *szRageId ? atoi(szRageId) : m_iRageId;
free(m_szDupeKey);
if (m_iImdbId != 0)
{
m_szDupeKey = (char*)malloc(20);
snprintf(m_szDupeKey, 20, "imdb=%i", m_iImdbId);
}
else if (szSeries && *szSeries && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
{
int iLen = strlen(szSeries) + 50;
m_szDupeKey = (char*)malloc(iLen);
snprintf(m_szDupeKey, iLen, "series=%s-%s-%s", szSeries, m_szSeason, m_szEpisode);
m_szDupeKey[iLen-1] = '\0';
}
else if (iRageId != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
{
m_szDupeKey = (char*)malloc(100);
snprintf(m_szDupeKey, 100, "rageid=%i-%s-%s", iRageId, m_szSeason, m_szEpisode);
m_szDupeKey[100-1] = '\0';
}
else
{
m_szDupeKey = strdup("");
}
}
int FeedItemInfo::GetSeasonNum()
{
if (!m_szSeason && !m_bSeasonEpisodeParsed)
{
ParseSeasonEpisode();
}
return m_iSeasonNum;
}
int FeedItemInfo::GetEpisodeNum()
{
if (!m_szEpisode && !m_bSeasonEpisodeParsed)
{
ParseSeasonEpisode();
}
return m_iEpisodeNum;
}
void FeedItemInfo::ParseSeasonEpisode()
{
m_bSeasonEpisodeParsed = true;
RegEx* pRegEx = m_pSharedFeedData->GetSeasonEpisodeRegEx();
if (pRegEx->Match(m_szTitle))
{
char szRegValue[100];
char szValue[100];
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + pRegEx->GetMatchStart(1)));
szValue[100-1] = '\0';
SetSeason(szValue);
int iLen = pRegEx->GetMatchLen(2);
iLen = iLen < 99 ? iLen : 99;
strncpy(szRegValue, m_szTitle + pRegEx->GetMatchStart(2), pRegEx->GetMatchLen(2));
szRegValue[iLen] = '\0';
snprintf(szValue, 100, "E%s", szRegValue);
szValue[100-1] = '\0';
Util::ReduceStr(szValue, "-", "");
for (char* p = szValue; *p; p++) *p = toupper(*p); // convert string to uppercase e02 -> E02
SetEpisode(szValue);
}
}
FeedHistoryInfo::FeedHistoryInfo(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
{
m_szUrl = szUrl ? strdup(szUrl) : NULL;
m_eStatus = eStatus;
m_tLastSeen = tLastSeen;
}
FeedHistoryInfo::~FeedHistoryInfo()
{
free(m_szUrl);
}
FeedHistory::~FeedHistory()
{
Clear();
}
void FeedHistory::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void FeedHistory::Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
{
push_back(new FeedHistoryInfo(szUrl, eStatus, tLastSeen));
}
void FeedHistory::Remove(const char* szUrl)
{
for (iterator it = begin(); it != end(); it++)
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
{
delete pFeedHistoryInfo;
erase(it);
break;
}
}
}
FeedHistoryInfo* FeedHistory::Find(const char* szUrl)
{
for (iterator it = begin(); it != end(); it++)
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
{
return pFeedHistoryInfo;
}
}
return NULL;
}
FeedItemInfos::FeedItemInfos()
{
debug("Creating FeedItemInfos");
m_iRefCount = 0;
}
FeedItemInfos::~FeedItemInfos()
{
debug("Destroing FeedItemInfos");
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
void FeedItemInfos::Retain()
{
m_iRefCount++;
}
void FeedItemInfos::Release()
{
m_iRefCount--;
if (m_iRefCount <= 0)
{
delete this;
}
}
void FeedItemInfos::Add(FeedItemInfo* pFeedItemInfo)
{
push_back(pFeedItemInfo);
pFeedItemInfo->SetSharedFeedData(&m_SharedFeedData);
}
SharedFeedData::SharedFeedData()
{
m_pSeasonEpisodeRegEx = NULL;
}
SharedFeedData::~SharedFeedData()
{
delete m_pSeasonEpisodeRegEx;
}
RegEx* SharedFeedData::GetSeasonEpisodeRegEx()
{
if (!m_pSeasonEpisodeRegEx)
{
m_pSeasonEpisodeRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
}
return m_pSeasonEpisodeRegEx;
}

View File

@@ -1,278 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision: 0 $
* $Date$
*
*/
#ifndef FEEDINFO_H
#define FEEDINFO_H
#include <deque>
#include <time.h>
#include "Util.h"
#include "DownloadInfo.h"
class FeedInfo
{
public:
enum EStatus
{
fsUndefined,
fsRunning,
fsFinished,
fsFailed
};
private:
int m_iID;
char* m_szName;
char* m_szUrl;
int m_iInterval;
char* m_szFilter;
unsigned int m_iFilterHash;
bool m_bPauseNzb;
char* m_szCategory;
int m_iPriority;
time_t m_tLastUpdate;
bool m_bPreview;
EStatus m_eStatus;
char* m_szOutputFilename;
bool m_bFetch;
bool m_bForce;
public:
FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority);
~FeedInfo();
int GetID() { return m_iID; }
const char* GetName() { return m_szName; }
const char* GetUrl() { return m_szUrl; }
int GetInterval() { return m_iInterval; }
const char* GetFilter() { return m_szFilter; }
unsigned int GetFilterHash() { return m_iFilterHash; }
bool GetPauseNzb() { return m_bPauseNzb; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
time_t GetLastUpdate() { return m_tLastUpdate; }
void SetLastUpdate(time_t tLastUpdate) { m_tLastUpdate = tLastUpdate; }
bool GetPreview() { return m_bPreview; }
void SetPreview(bool bPreview) { m_bPreview = bPreview; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* szOutputFilename);
bool GetFetch() { return m_bFetch; }
void SetFetch(bool bFetch) { m_bFetch = bFetch; }
bool GetForce() { return m_bForce; }
void SetForce(bool bForce) { m_bForce = bForce; }
};
typedef std::deque<FeedInfo*> Feeds;
class SharedFeedData
{
private:
RegEx* m_pSeasonEpisodeRegEx;
public:
SharedFeedData();
~SharedFeedData();
RegEx* GetSeasonEpisodeRegEx();
};
class FeedItemInfo
{
public:
enum EStatus
{
isUnknown,
isBacklog,
isFetched,
isNew
};
enum EMatchStatus
{
msIgnored,
msAccepted,
msRejected
};
class Attr
{
private:
char* m_szName;
char* m_szValue;
public:
Attr(const char* szName, const char* szValue);
~Attr();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
typedef std::deque<Attr*> AttributesBase;
class Attributes: public AttributesBase
{
public:
~Attributes();
void Add(const char* szName, const char* szValue);
Attr* Find(const char* szName);
};
private:
char* m_szTitle;
char* m_szFilename;
char* m_szUrl;
time_t m_tTime;
long long m_lSize;
char* m_szCategory;
int m_iImdbId;
int m_iRageId;
char* m_szDescription;
char* m_szSeason;
char* m_szEpisode;
int m_iSeasonNum;
int m_iEpisodeNum;
bool m_bSeasonEpisodeParsed;
char* m_szAddCategory;
bool m_bPauseNzb;
int m_iPriority;
EStatus m_eStatus;
EMatchStatus m_eMatchStatus;
int m_iMatchRule;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
SharedFeedData* m_pSharedFeedData;
Attributes m_Attributes;
int ParsePrefixedInt(const char *szValue);
void ParseSeasonEpisode();
public:
FeedItemInfo();
~FeedItemInfo();
void SetSharedFeedData(SharedFeedData* pSharedFeedData) { m_pSharedFeedData = pSharedFeedData; }
const char* GetTitle() { return m_szTitle; }
void SetTitle(const char* szTitle);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
const char* GetUrl() { return m_szUrl; }
void SetUrl(const char* szUrl);
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
const char* GetCategory() { return m_szCategory; }
void SetCategory(const char* szCategory);
int GetImdbId() { return m_iImdbId; }
void SetImdbId(int iImdbId) { m_iImdbId = iImdbId; }
int GetRageId() { return m_iRageId; }
void SetRageId(int iRageId) { m_iRageId = iRageId; }
const char* GetDescription() { return m_szDescription; }
void SetDescription(const char* szDescription);
const char* GetSeason() { return m_szSeason; }
void SetSeason(const char* szSeason);
const char* GetEpisode() { return m_szEpisode; }
void SetEpisode(const char* szEpisode);
int GetSeasonNum();
int GetEpisodeNum();
const char* GetAddCategory() { return m_szAddCategory; }
void SetAddCategory(const char* szAddCategory);
bool GetPauseNzb() { return m_bPauseNzb; }
void SetPauseNzb(bool bPauseNzb) { m_bPauseNzb = bPauseNzb; }
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
EMatchStatus GetMatchStatus() { return m_eMatchStatus; }
void SetMatchStatus(EMatchStatus eMatchStatus) { m_eMatchStatus = eMatchStatus; }
int GetMatchRule() { return m_iMatchRule; }
void SetMatchRule(int iMatchRule) { m_iMatchRule = iMatchRule; }
const char* GetDupeKey() { return m_szDupeKey; }
void SetDupeKey(const char* szDupeKey);
void AppendDupeKey(const char* szExtraDupeKey);
void BuildDupeKey(const char* szRageId, const char* szSeries);
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
Attributes* GetAttributes() { return &m_Attributes; }
};
typedef std::deque<FeedItemInfo*> FeedItemInfosBase;
class FeedItemInfos : public FeedItemInfosBase
{
private:
int m_iRefCount;
SharedFeedData m_SharedFeedData;
public:
FeedItemInfos();
~FeedItemInfos();
void Retain();
void Release();
void Add(FeedItemInfo* pFeedItemInfo);
};
class FeedHistoryInfo
{
public:
enum EStatus
{
hsUnknown,
hsBacklog,
hsFetched
};
private:
char* m_szUrl;
EStatus m_eStatus;
time_t m_tLastSeen;
public:
FeedHistoryInfo(const char* szUrl, EStatus eStatus, time_t tLastSeen);
~FeedHistoryInfo();
const char* GetUrl() { return m_szUrl; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
time_t GetLastSeen() { return m_tLastSeen; }
void SetLastSeen(time_t tLastSeen) { m_tLastSeen = tLastSeen; }
};
typedef std::deque<FeedHistoryInfo*> FeedHistoryBase;
class FeedHistory : public FeedHistoryBase
{
public:
~FeedHistory();
void Clear();
void Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen);
void Remove(const char* szUrl);
FeedHistoryInfo* Find(const char* szUrl);
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -34,7 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
@@ -62,11 +63,11 @@ Frontend::Frontend()
m_iNeededLogEntries = 0;
m_bSummary = false;
m_bFileList = false;
m_iCurrentDownloadSpeed = 0;
m_fCurrentDownloadSpeed = 0;
m_lRemainingSize = 0;
m_bPauseDownload = false;
m_bPauseDownload2 = false;
m_iDownloadLimit = 0;
m_fDownloadLimit = 0;
m_iThreadCount = 0;
m_iPostJobCount = 0;
m_iUpTimeSec = 0;
@@ -86,7 +87,7 @@ bool Frontend::PrepareData()
}
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
{
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Stop();
return false;
}
@@ -95,11 +96,11 @@ bool Frontend::PrepareData()
{
if (m_bSummary)
{
m_iCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
m_bPauseDownload = g_pOptions->GetPauseDownload();
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
m_iDownloadLimit = g_pOptions->GetDownloadRate();
m_fDownloadLimit = g_pOptions->GetDownloadRate();
m_iThreadCount = Thread::GetThreadCount();
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
m_iPostJobCount = pPostQueue->size();
@@ -181,7 +182,6 @@ void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
}
else
{
g_pOptions->SetResumeTime(0);
if (bSecondRegister)
{
g_pOptions->SetPauseDownload2(bPause);
@@ -193,15 +193,15 @@ void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
}
}
void Frontend::ServerSetDownloadRate(int iRate)
void Frontend::ServerSetDownloadRate(float fRate)
{
if (IsRemoteMode())
{
RequestSetDownloadRate(iRate);
RequestSetDownloadRate(fRate);
}
else
{
g_pOptions->SetDownloadRate(iRate);
g_pOptions->SetDownloadRate(fRate);
}
}
@@ -235,17 +235,14 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
bool Frontend::RequestMessages()
{
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect();
if (!OK)
@@ -265,15 +262,15 @@ bool Frontend::RequestMessages()
LogRequest.m_iIDFrom = 0;
}
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
{
return false;
}
// Now listen for the returned log
SNZBLogResponse LogResponse;
bool bRead = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (!bRead ||
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
@@ -284,7 +281,7 @@ bool Frontend::RequestMessages()
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!connection.Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -316,7 +313,8 @@ bool Frontend::RequestMessages()
bool Frontend::RequestFileList()
{
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
Connection connection(&netAddress);
bool OK = connection.Connect();
if (!OK)
@@ -329,15 +327,15 @@ bool Frontend::RequestFileList()
ListRequest.m_bFileList = htonl(m_bFileList);
ListRequest.m_bServerState = htonl(m_bSummary);
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
return false;
}
// Now listen for the returned list
SNZBListResponse ListResponse;
bool bRead = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (!bRead ||
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
@@ -348,7 +346,7 @@ bool Frontend::RequestFileList()
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!connection.Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -362,8 +360,8 @@ bool Frontend::RequestFileList()
m_bPauseDownload = ntohl(ListResponse.m_bDownloadPaused);
m_bPauseDownload2 = ntohl(ListResponse.m_bDownload2Paused);
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
m_iCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate);
m_iDownloadLimit = ntohl(ListResponse.m_iDownloadLimit);
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
@@ -394,11 +392,11 @@ bool Frontend::RequestPauseUnpause(bool bPause, bool bSecondRegister)
return client.RequestServerPauseUnpause(bPause, bSecondRegister ? eRemotePauseUnpauseActionDownload2 : eRemotePauseUnpauseActionDownload);
}
bool Frontend::RequestSetDownloadRate(int iRate)
bool Frontend::RequestSetDownloadRate(float fRate)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerSetDownloadRate(iRate);
return client.RequestServerSetDownloadRate(fRate);
}
bool Frontend::RequestDumpDebug()
@@ -412,5 +410,5 @@ bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
{
RemoteClient client;
client.SetVerbose(false);
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID, false);
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, false);
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -50,11 +50,11 @@ protected:
int m_iUpdateInterval;
// summary
int m_iCurrentDownloadSpeed;
float m_fCurrentDownloadSpeed;
long long m_lRemainingSize;
bool m_bPauseDownload;
bool m_bPauseDownload2;
int m_iDownloadLimit;
float m_fDownloadLimit;
int m_iThreadCount;
int m_iPostJobCount;
int m_iUpTimeSec;
@@ -72,8 +72,8 @@ protected:
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
void ServerPauseUnpause(bool bPause, bool bSecondRegister);
bool RequestPauseUnpause(bool bPause, bool bSecondRegister);
void ServerSetDownloadRate(int iRate);
bool RequestSetDownloadRate(int iRate);
void ServerSetDownloadRate(float fRate);
bool RequestSetDownloadRate(float fRate);
void ServerDumpDebug();
bool RequestDumpDebug();
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);

39
Log.cpp
View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -38,7 +38,7 @@
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdio.h>
#include <cstdio>
#include "nzbget.h"
#include "Options.h"
@@ -59,8 +59,15 @@ Log::Log()
Log::~Log()
{
Clear();
free(m_szLogFilename);
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
if (m_szLogFilename)
{
free(m_szLogFilename);
}
}
void Log::Filelog(const char* msg, ...)
@@ -77,7 +84,6 @@ void Log::Filelog(const char* msg, ...)
time_t rawtime;
time(&rawtime);
rawtime += g_pOptions->GetTimeCorrection();
char szTime[50];
#ifdef HAVE_CTIME_R_3
@@ -303,18 +309,10 @@ Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText
Message::~ Message()
{
free(m_szText);
}
void Log::Clear()
{
m_mutexLog.Lock();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
if (m_szText)
{
delete *it;
free(m_szText);
}
m_Messages.clear();
m_mutexLog.Unlock();
}
void Log::AppendMessage(Message::EKind eKind, const char * szText)
@@ -353,10 +351,9 @@ void Log::ResetLog()
* During intializing stage (when options were not read yet) all messages
* are saved in screen log, even if they shouldn't (according to options).
* Method "InitOptions()" check all messages added to screen log during
* intializing stage and does three things:
* intializing stage and does two things:
* 1) save the messages to log-file (if they should according to options);
* 2) delete messages from screen log (if they should not be saved in screen log).
* 3) renumerate IDs
*/
void Log::InitOptions()
{
@@ -365,13 +362,8 @@ void Log::InitOptions()
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
#ifdef WIN32
WebUtil::Utf8ToAnsi(m_szLogFilename, strlen(m_szLogFilename) + 1);
#endif
}
m_iIDGen = 0;
for (unsigned int i = 0; i < m_Messages.size(); )
{
Message* pMessage = m_Messages.at(i);
@@ -407,7 +399,6 @@ void Log::InitOptions()
}
else
{
pMessage->m_iID = ++m_iIDGen;
i++;
}
}

11
Log.h
View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -64,11 +64,9 @@ private:
time_t m_tTime;
char* m_szText;
friend class Log;
public:
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
~Message();
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
~Message();
unsigned int GetID() { return m_iID; }
EKind GetKind() { return m_eKind; }
time_t GetTime() { return m_tTime; }
@@ -110,7 +108,6 @@ public:
~Log();
Messages* LockMessages();
void UnlockMessages();
void Clear();
void ResetLog();
void InitOptions();
};

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -1,325 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Log.h"
#include "Util.h"
#include "Maintenance.h"
extern Options* g_pOptions;
extern Maintenance* g_pMaintenance;
Maintenance::Maintenance()
{
m_iIDMessageGen = 0;
m_UpdateScriptController = NULL;
m_szUpdateScript = NULL;
}
Maintenance::~Maintenance()
{
m_mutexController.Lock();
if (m_UpdateScriptController)
{
m_UpdateScriptController->Detach();
m_mutexController.Unlock();
while (m_UpdateScriptController)
{
usleep(20*1000);
}
}
ClearMessages();
free(m_szUpdateScript);
}
void Maintenance::ResetUpdateController()
{
m_mutexController.Lock();
m_UpdateScriptController = NULL;
m_mutexController.Unlock();
}
void Maintenance::ClearMessages()
{
for (Log::Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
}
Log::Messages* Maintenance::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void Maintenance::UnlockMessages()
{
m_mutexLog.Unlock();
}
void Maintenance::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
{
if (tTime == 0)
{
tTime = time(NULL);
}
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_Messages.push_back(pMessage);
m_mutexLog.Unlock();
}
bool Maintenance::StartUpdate(EBranch eBranch)
{
m_mutexController.Lock();
bool bAlreadyUpdating = m_UpdateScriptController != NULL;
m_mutexController.Unlock();
if (bAlreadyUpdating)
{
error("Could not start update-script: update-script is already running");
return false;
}
if (m_szUpdateScript)
{
free(m_szUpdateScript);
m_szUpdateScript = NULL;
}
if (!ReadPackageInfoStr("install-script", &m_szUpdateScript))
{
return false;
}
ClearMessages();
m_UpdateScriptController = new UpdateScriptController();
m_UpdateScriptController->SetScript(m_szUpdateScript);
m_UpdateScriptController->SetBranch(eBranch);
m_UpdateScriptController->SetAutoDestroy(true);
m_UpdateScriptController->Start();
return true;
}
bool Maintenance::CheckUpdates(char** pUpdateInfo)
{
char* szUpdateInfoScript;
if (!ReadPackageInfoStr("update-info-script", &szUpdateInfoScript))
{
return false;
}
*pUpdateInfo = NULL;
UpdateInfoScriptController::ExecuteScript(szUpdateInfoScript, pUpdateInfo);
free(szUpdateInfoScript);
return *pUpdateInfo;
}
bool Maintenance::ReadPackageInfoStr(const char* szKey, char** pValue)
{
char szFileName[1024];
snprintf(szFileName, 1024, "%s%cpackage-info.json", g_pOptions->GetWebDir(), PATH_SEPARATOR);
szFileName[1024-1] = '\0';
char* szPackageInfo;
int iPackageInfoLen;
if (!Util::LoadFileIntoBuffer(szFileName, &szPackageInfo, &iPackageInfoLen))
{
error("Could not load file %s", szFileName);
return false;
}
char szKeyStr[100];
snprintf(szKeyStr, 100, "\"%s\"", szKey);
szKeyStr[100-1] = '\0';
char* p = strstr(szPackageInfo, szKeyStr);
if (!p)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
p = strchr(p + strlen(szKeyStr), '"');
if (!p)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
p++;
char* pend = strchr(p, '"');
if (!pend)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
int iLen = pend - p;
if (iLen >= sizeof(szFileName))
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
*pValue = (char*)malloc(iLen+1);
strncpy(*pValue, p, iLen);
(*pValue)[iLen] = '\0';
WebUtil::JsonDecode(*pValue);
free(szPackageInfo);
return true;
}
void UpdateScriptController::Run()
{
m_iPrefixLen = 0;
PrintMessage(Message::mkInfo, "Executing update-script %s", GetScript());
char szInfoName[1024];
snprintf(szInfoName, 1024, "update-script %s", Util::BaseFileName(GetScript()));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
const char* szBranchName[] = { "STABLE", "TESTING", "DEVEL" };
SetEnvVar("NZBUP_BRANCH", szBranchName[m_eBranch]);
char szProcessID[20];
#ifdef WIN32
int pid = (int)GetCurrentProcessId();
#else
int pid = (int)getppid();
#endif
snprintf(szProcessID, 20, "%i", pid);
szProcessID[20-1] = '\0';
SetEnvVar("NZBUP_PROCESSID", szProcessID);
char szLogPrefix[100];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 100);
szLogPrefix[100-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
Execute();
g_pMaintenance->ResetUpdateController();
}
void UpdateScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
szText = szText + m_iPrefixLen;
g_pMaintenance->AppendMessage(eKind, time(NULL), szText);
ScriptController::AddMessage(eKind, szText);
}
void UpdateInfoScriptController::ExecuteScript(const char* szScript, char** pUpdateInfo)
{
detail("Executing update-info-script %s", Util::BaseFileName(szScript));
UpdateInfoScriptController* pScriptController = new UpdateInfoScriptController();
pScriptController->SetScript(szScript);
char szInfoName[1024];
snprintf(szInfoName, 1024, "update-info-script %s", Util::BaseFileName(szScript));
szInfoName[1024-1] = '\0';
pScriptController->SetInfoName(szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
pScriptController->SetLogPrefix(szLogPrefix);
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
pScriptController->Execute();
if (pScriptController->m_UpdateInfo.GetBuffer())
{
int iLen = strlen(pScriptController->m_UpdateInfo.GetBuffer());
*pUpdateInfo = (char*)malloc(iLen + 1);
strncpy(*pUpdateInfo, pScriptController->m_UpdateInfo.GetBuffer(), iLen);
(*pUpdateInfo)[iLen] = '\0';
}
delete pScriptController;
}
void UpdateInfoScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
szText = szText + m_iPrefixLen;
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strncmp(szText + 6, "[UPDATEINFO]", 12))
{
m_UpdateInfo.Append(szText + 6 + 12);
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
}
}
else
{
ScriptController::AddMessage(eKind, szText);
}
}

View File

@@ -1,94 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef MAINTENANCE_H
#define MAINTENANCE_H
#include "Thread.h"
#include "ScriptController.h"
#include "Log.h"
#include "Util.h"
class UpdateScriptController;
class Maintenance
{
private:
Log::Messages m_Messages;
Mutex m_mutexLog;
Mutex m_mutexController;
int m_iIDMessageGen;
UpdateScriptController* m_UpdateScriptController;
char* m_szUpdateScript;
bool ReadPackageInfoStr(const char* szKey, char** pValue);
public:
enum EBranch
{
brStable,
brTesting,
brDevel
};
Maintenance();
~Maintenance();
void ClearMessages();
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Log::Messages* LockMessages();
void UnlockMessages();
bool StartUpdate(EBranch eBranch);
void ResetUpdateController();
bool CheckUpdates(char** pUpdateInfo);
};
class UpdateScriptController : public Thread, public ScriptController
{
private:
Maintenance::EBranch m_eBranch;
int m_iPrefixLen;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
void SetBranch(Maintenance::EBranch eBranch) { m_eBranch = eBranch; }
};
class UpdateInfoScriptController : public ScriptController
{
private:
int m_iPrefixLen;
StringBuilder m_UpdateInfo;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, char** pUpdateInfo);
};
#endif

View File

@@ -1,136 +1,23 @@
#
# This file if part of nzbget
#
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
bin_PROGRAMS = nzbget
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
NCursesFrontend.h NNTPConnection.cpp \
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NetAddress.cpp NetAddress.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h \
PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp \
Util.h XmlRpc.cpp XmlRpc.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES) $(osx_FILES)
patches_FILES = \
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
win32.h NTService.cpp NTService.h \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
osx_FILES = \
osx/App_Prefix.pch osx/NZBGet-Info.plist \
osx/DaemonController.h osx/DaemonController.m \
osx/MainApp.h osx/MainApp.m osx/MainApp.xib \
osx/PFMoveApplication.h osx/PFMoveApplication.m \
osx/PreferencesDialog.h osx/PreferencesDialog.m osx/PreferencesDialog.xib \
osx/RPC.h osx/RPC.m osx/WebClient.h osx/WebClient.m \
osx/WelcomeDialog.h osx/WelcomeDialog.m osx/WelcomeDialog.xib \
osx/NZBGet.xcodeproj/project.pbxproj \
osx/Resources/Images/mainicon.icns osx/Resources/Images/statusicon.png \
osx/Resources/Images/statusicon@2x.png osx/Resources/Images/statusicon-inv.png \
osx/Resources/Images/statusicon-inv@2x.png osx/Resources/licenses/license-bootstrap.txt \
osx/Resources/licenses/license-jquery-GPL.txt osx/Resources/licenses/license-jquery-MIT.txt \
osx/Resources/Credits.rtf osx/Resources/Localizable.strings osx/Resources/Welcome.rtf
doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
webui/history.js webui/messages.js webui/status.js webui/style.css webui/upload.js \
webui/util.js webui/config.js webui/feed.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Note about "sed":
# We need to make some changes in installed files.
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
# 1) copy the original file to original.temp (delete existing original.temp, if any);
# 2) sed < original.temp > original
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
# (only if they do not exist there to prevent override by update)
install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
nzbgetd nzbget-shell.bat
# Determining subversion revision:
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
@@ -169,15 +56,4 @@ svn_version.cpp: FORCE
fi
FORCE:
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
clean-bak: rm *~
# Fix premissions
dist-hook:
chmod -x $(distdir)/*.cpp $(distdir)/*.h
find $(distdir)/webui -type f -print -exec chmod -x {} \;

View File

@@ -14,29 +14,6 @@
@SET_MAKE@
#
# This file if part of nzbget
#
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
@@ -59,11 +36,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
bin_PROGRAMS = nzbget$(EXEEXT)
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
$(dist_exampleconf_DATA) $(nobase_dist_ppscripts_SCRIPTS) \
$(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
config.guess config.sub depcomp install-sh missing
@@ -77,41 +51,24 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"
am__installdirs = "$(DESTDIR)$(bindir)"
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
DupeCoordinator.$(OBJEXT) Frontend.$(OBJEXT) \
FeedCoordinator.$(OBJEXT) FeedFile.$(OBJEXT) \
FeedFilter.$(OBJEXT) FeedInfo.$(OBJEXT) Log.$(OBJEXT) \
LoggableFrontend.$(OBJEXT) Maintenance.$(OBJEXT) \
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
Options.$(OBJEXT) ParChecker.$(OBJEXT) ParRenamer.$(OBJEXT) \
ParCoordinator.$(OBJEXT) PrePostProcessor.$(OBJEXT) \
QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \
RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \
Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
RemoteServer.$(OBJEXT) Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) Unpack.$(OBJEXT) \
nzbget.$(OBJEXT)
Util.$(OBJEXT) XmlRpc.$(OBJEXT) nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
nobase_dist_ppscriptsSCRIPT_INSTALL = $(install_sh_SCRIPT)
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(nobase_dist_ppscripts_SCRIPTS) $(sbin_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -126,11 +83,6 @@ CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(nzbget_SOURCES)
DIST_SOURCES = $(nzbget_SOURCES)
dist_docDATA_INSTALL = $(INSTALL_DATA)
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
$(nobase_dist_webui_DATA)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -143,14 +95,18 @@ am__remove_distdir = \
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
distuninstallcheck_listfiles = find . -type f -print
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
ADDSRCS = @ADDSRCS@
AMDEP_FALSE = @AMDEP_FALSE@
AMDEP_TRUE = @AMDEP_TRUE@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CFLAGS = @CFLAGS@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
@@ -228,8 +184,6 @@ localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
openssl_CFLAGS = @openssl_CFLAGS@
openssl_LIBS = @openssl_LIBS@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
@@ -237,89 +191,26 @@ psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
sysconfdir = @sysconfdir@
target = @target@
target_alias = @target_alias@
target_cpu = @target_cpu@
target_os = @target_os@
target_vendor = @target_vendor@
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
NCursesFrontend.h NNTPConnection.cpp \
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NetAddress.cpp NetAddress.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h \
PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h Util.cpp \
Util.h XmlRpc.cpp XmlRpc.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES) $(osx_FILES)
patches_FILES = \
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
win32.h NTService.cpp NTService.h \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
osx_FILES = \
osx/App_Prefix.pch osx/NZBGet-Info.plist \
osx/DaemonController.h osx/DaemonController.m \
osx/MainApp.h osx/MainApp.m osx/MainApp.xib \
osx/PFMoveApplication.h osx/PFMoveApplication.m \
osx/PreferencesDialog.h osx/PreferencesDialog.m osx/PreferencesDialog.xib \
osx/RPC.h osx/RPC.m osx/WebClient.h osx/WebClient.m \
osx/WelcomeDialog.h osx/WelcomeDialog.m osx/WelcomeDialog.xib \
osx/NZBGet.xcodeproj/project.pbxproj \
osx/Resources/Images/mainicon.icns osx/Resources/Images/statusicon.png \
osx/Resources/Images/statusicon@2x.png osx/Resources/Images/statusicon-inv.png \
osx/Resources/Images/statusicon-inv@2x.png osx/Resources/licenses/license-bootstrap.txt \
osx/Resources/licenses/license-jquery-GPL.txt osx/Resources/licenses/license-jquery-MIT.txt \
osx/Resources/Credits.rtf osx/Resources/Localizable.strings osx/Resources/Welcome.rtf
doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
webui/history.js webui/messages.js webui/status.js webui/style.css webui/upload.js \
webui/util.js webui/config.js webui/feed.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
dist_doc_DATA = $(doc_FILES)
exampleconfdir = $(datadir)/nzbget
dist_exampleconf_DATA = $(exampleconf_FILES)
webuidir = $(datadir)/nzbget
nobase_dist_webui_DATA = $(webui_FILES)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
sh '{}' ';'
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
nzbgetd nzbget-shell.bat
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -402,50 +293,6 @@ clean-binPROGRAMS:
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
@rm -f nzbget$(EXEEXT)
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
install-nobase_dist_ppscriptsSCRIPTS: $(nobase_dist_ppscripts_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(ppscriptsdir)" || $(mkdir_p) "$(DESTDIR)$(ppscriptsdir)"
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " $(nobase_dist_ppscriptsSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
$(nobase_dist_ppscriptsSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ppscriptsdir)/$$f"; \
else :; fi; \
done
uninstall-nobase_dist_ppscriptsSCRIPTS:
@$(NORMAL_UNINSTALL)
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " rm -f '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
rm -f "$(DESTDIR)$(ppscriptsdir)/$$f"; \
done
install-sbinSCRIPTS: $(sbin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
if test -f $$d$$p; then \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
$(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \
else :; fi; \
done
uninstall-sbinSCRIPTS:
@$(NORMAL_UNINSTALL)
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
rm -f "$(DESTDIR)$(sbindir)/$$f"; \
done
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -460,24 +307,17 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DiskState.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadInfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DupeCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedInfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Frontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Log.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoggableFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Maintenance.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NNTPConnection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NZBFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetAddress.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NewsServer.Po@am__quote@
@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)/ParCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamer.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@
@@ -489,11 +329,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Unpack.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebServer.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@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svn_version.Po@am__quote@
@@ -512,59 +348,6 @@ distclean-compile:
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
uninstall-info-am:
install-dist_docDATA: $(dist_doc_DATA)
@$(NORMAL_INSTALL)
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
@list='$(dist_doc_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \
$(dist_docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \
done
uninstall-dist_docDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_doc_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \
rm -f "$(DESTDIR)$(docdir)/$$f"; \
done
install-dist_exampleconfDATA: $(dist_exampleconf_DATA)
@$(NORMAL_INSTALL)
test -z "$(exampleconfdir)" || $(mkdir_p) "$(DESTDIR)$(exampleconfdir)"
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(dist_exampleconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(exampleconfdir)/$$f'"; \
$(dist_exampleconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(exampleconfdir)/$$f"; \
done
uninstall-dist_exampleconfDATA:
@$(NORMAL_UNINSTALL)
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(exampleconfdir)/$$f'"; \
rm -f "$(DESTDIR)$(exampleconfdir)/$$f"; \
done
install-nobase_dist_webuiDATA: $(nobase_dist_webui_DATA)
@$(NORMAL_INSTALL)
test -z "$(webuidir)" || $(mkdir_p) "$(DESTDIR)$(webuidir)"
@$(am__vpath_adj_setup) \
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
$(am__vpath_adj) \
echo " $(nobase_dist_webuiDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuidir)/$$f'"; \
$(nobase_dist_webuiDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuidir)/$$f"; \
done
uninstall-nobase_dist_webuiDATA:
@$(NORMAL_UNINSTALL)
@$(am__vpath_adj_setup) \
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
$(am__vpath_adj) \
echo " rm -f '$(DESTDIR)$(webuidir)/$$f'"; \
rm -f "$(DESTDIR)$(webuidir)/$$f"; \
done
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
@@ -617,7 +400,6 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \
@@ -644,9 +426,6 @@ distdir: $(DISTFILES)
|| exit 1; \
fi; \
done
$(MAKE) $(AM_MAKEFLAGS) \
top_distdir="$(top_distdir)" distdir="$(distdir)" \
dist-hook
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
@@ -746,9 +525,9 @@ distcleancheck: distclean
exit 1; } >&2
check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
all-am: Makefile $(PROGRAMS) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; do \
for dir in "$(DESTDIR)$(bindir)"; do \
test -z "$$dir" || $(mkdir_p) "$$dir"; \
done
install: install-am
@@ -796,15 +575,9 @@ info: info-am
info-am:
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-data-am:
install-exec-am: install-binPROGRAMS install-sbinSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
install-exec-am: install-binPROGRAMS
install-info: install-info-am
@@ -831,70 +604,23 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-info-am \
uninstall-nobase_dist_ppscriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
uninstall-am: uninstall-binPROGRAMS uninstall-info-am
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
dist-gzip dist-hook dist-shar dist-tarZ dist-zip distcheck \
distclean distclean-compile distclean-generic distclean-hdr \
dist-gzip dist-shar dist-tarZ dist-zip distcheck distclean \
distclean-compile distclean-generic distclean-hdr \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am \
install-data-hook install-dist_docDATA \
install-dist_exampleconfDATA install-exec install-exec-am \
install-exec-hook install-info install-info-am install-man \
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA install-sbinSCRIPTS \
install-binPROGRAMS install-data install-data-am install-exec \
install-exec-am install-info install-info-am install-man \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
uninstall-info-am uninstall-nobase_dist_ppscriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
uninstall-info-am
# Note about "sed":
# We need to make some changes in installed files.
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
# 1) copy the original file to original.temp (delete existing original.temp, if any);
# 2) sed < original.temp > original
# 3) delete original.temp
# These steps ensure that the output file has the same permissions as the original file.
# Configure installed script
install-exec-hook:
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
install-data-hook:
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
# Install configuration files into /etc
# (only if they do not exist there to prevent override by update)
install-conf:
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
fi
uninstall-conf:
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
# Determining subversion revision:
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
# File is recreated only if revision number was changed.
@@ -933,11 +659,6 @@ svn_version.cpp: FORCE
FORCE:
clean-bak: rm *~
# Fix premissions
dist-hook:
chmod -x $(distdir)/*.cpp $(distdir)/*.h
find $(distdir)/webui -type f -print -exec chmod -x {} \;
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,7 +27,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A621B; // = "nzb-XX" (protocol version)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6208; // = "nzb8" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -56,14 +56,11 @@ enum eRemoteRequest
eRemoteRequestEditQueue,
eRemoteRequestLog,
eRemoteRequestShutdown,
eRemoteRequestReload,
eRemoteRequestVersion,
eRemoteRequestPostQueue,
eRemoteRequestWriteLog,
eRemoteRequestScan,
eRemoteRequestHistory,
eRemoteRequestDownloadUrl,
eRemoteRequestUrlQueue
eRemoteRequestHistory
};
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
@@ -79,43 +76,24 @@ enum eRemoteEditAction
eRemoteEditActionFileDelete, // delete files
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionFileSetPriority, // set priority for files
eRemoteEditActionFileReorder, // (not supported)
eRemoteEditActionFileSplit, // split - create new group from selected files
eRemoteEditActionGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
eRemoteEditActionGroupMoveTop, // move group to the top of download-queue
eRemoteEditActionGroupMoveBottom, // move group to the bottom of download-queue
eRemoteEditActionGroupPause, // pause group
eRemoteEditActionGroupResume, // resume (unpause) group
eRemoteEditActionGroupDelete, // delete group
eRemoteEditActionGroupDupeDelete, // delete group
eRemoteEditActionGroupFinalDelete, // delete group
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files) in group
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars in group, except main par-file (does not affect other files)
eRemoteEditActionGroupSetPriority, // set priority for groups
eRemoteEditActionGroupSetCategory, // set or change category for a group
eRemoteEditActionGroupMerge, // merge group
eRemoteEditActionGroupSetParameter, // set post-process parameter for group
eRemoteEditActionGroupSetName, // set group name (rename group)
eRemoteEditActionGroupSetDupeKey, // (reserved)
eRemoteEditActionGroupSetDupeScore, // (reserved)
eRemoteEditActionGroupSetDupeMode, // (reserved)
eRemoteEditActionPostMoveOffset = 51, // move post-job to m_iOffset relative to the current position in post-queue
eRemoteEditActionPostMoveTop, // move post-job to the top of post-queue
eRemoteEditActionPostMoveBottom, // move post-job to the bottom of post-queue
eRemoteEditActionPostDelete, // delete post-job
eRemoteEditActionHistoryDelete, // hide history-item
eRemoteEditActionHistoryFinalDelete, // delete history-item
eRemoteEditActionHistoryDelete, // delete history-item
eRemoteEditActionHistoryReturn, // move history-item back to download queue
eRemoteEditActionHistoryProcess, // move history-item back to download queue and start postprocessing
eRemoteEditActionHistoryRedownload, // move history-item back to download queue for redownload
eRemoteEditActionHistorySetParameter, // set post-process parameter for history-item
eRemoteEditActionHistorySetDupeKey, // (reserved)
eRemoteEditActionHistorySetDupeScore, // (reserved)
eRemoteEditActionHistorySetDupeMode, // (reserved)
eRemoteEditActionHistorySetDupeBackup, // (reserved)
eRemoteEditActionHistoryMarkBad, // mark history-item as bad (and download other duplicate)
eRemoteEditActionHistoryMarkGood // mark history-item as good (and push it into dup-history)
eRemoteEditActionHistoryProcess // move history-item back to download queue and start postprocessing
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
@@ -127,22 +105,13 @@ enum eRemotePauseUnpauseAction
eRemotePauseUnpauseActionScan // pause/unpause scan of incoming nzb-directory
};
// Possible values for field "m_iMatchMode" of struct "SNZBEditQueueRequest":
enum eRemoteMatchMode
{
eRemoteMatchModeID = 1, // ID
eRemoteMatchModeName, // Name
eRemoteMatchModeRegEx, // RegEx
};
// The basic SNZBRequestBase struct, used in all requests
struct SNZBRequestBase
{
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
char m_szUsername[NZBREQUESTPASSWORDSIZE]; // User name
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
};
// The basic SNZBResposneBase struct, used in all responses
@@ -157,10 +126,8 @@ struct SNZBDownloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, be empty
int32_t m_bAddFirst; // 1 - add file to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
//char m_szContent[m_iTrailingDataLength]; // variable sized
};
@@ -180,9 +147,6 @@ struct SNZBListRequest
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bFileList; // 1 - return file list
int32_t m_bServerState; // 1 - return server state
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode (only values eRemoteMatchModeID (no filter) and eRemoteMatchModeRegEx are allowed)
int32_t m_bMatchGroup; // 0 - match files; 1 - match nzbs (when m_iMatchMode == eRemoteMatchModeRegEx)
char m_szPattern[NZBREQUESTFILENAMESIZE]; // RegEx Pattern (when m_iMatchMode == eRemoteMatchModeRegEx)
};
// A list response
@@ -205,7 +169,6 @@ struct SNZBListResponse
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
int32_t m_bRegExValid; // 0 - error in RegEx-pattern, 1 - RegEx-pattern is valid (only when Request has eRemoteMatchModeRegEx)
int32_t m_iNrTrailingNZBEntries; // Number of List-NZB-entries, following to this structure
int32_t m_iNrTrailingPPPEntries; // Number of List-PPP-entries, following to this structure
int32_t m_iNrTrailingFileEntries; // Number of List-File-entries, following to this structure
@@ -220,14 +183,11 @@ struct SNZBListResponseNZBEntry
{
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
int32_t m_bMatch; // 1 - group matches the pattern (only when Request has eRemoteMatchModeRegEx)
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szName[m_iNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szCategory[m_iCategoryLen]; // variable sized
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
@@ -254,9 +214,6 @@ struct SNZBListResponseFileEntry
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
int32_t m_bPaused; // 1 - file is paused
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
int32_t m_iPriority; // Download priority
int32_t m_iActiveDownloads; // Number of active downloads for this file
int32_t m_bMatch; // 1 - file matches the pattern (only when Request has eRemoteMatchModeRegEx)
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
//char m_szSubject[m_iSubjectLen]; // variable sized
@@ -324,7 +281,7 @@ struct SNZBSetDownloadRateResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// edit queue request
// An edit queue request
struct SNZBEditQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
@@ -332,15 +289,11 @@ struct SNZBEditQueueRequest
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode
int32_t m_iNrTrailingIDEntries; // Number of ID-entries, following to this structure
int32_t m_iNrTrailingNameEntries; // Number of Name-entries, following to this structure
int32_t m_iTrailingNameEntriesLen; // Length of all Name-entries, following to this structure
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
//char m_szText[m_iTextLen]; // variable sized
//int32_t m_iIDs[m_iNrTrailingIDEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
//char* m_szNames[m_iNrTrailingNameEntries]; // variable sized array of strings. For File-Actions - name of file incl. nzb-name as path, for Group-Actions - name of group
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
//char m_szText[m_iTextLen]; // variable sized
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
};
// An edit queue response
@@ -382,21 +335,6 @@ struct SNZBShutdownResponse
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Reload server request
struct SNZBReloadRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// Reload server response
struct SNZBReloadResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// Server version request
struct SNZBVersionRequest
{
@@ -438,10 +376,12 @@ struct SNZBPostQueueResponseEntry
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
@@ -469,7 +409,6 @@ struct SNZBWriteLogResponse
struct SNZBScanRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
int32_t m_bSyncMode; // 0 - asynchronous Scan (the command returns immediately), 1 - synchronous Scan (the command returns when the scan is completed)
};
// Scan nzb directory response
@@ -487,80 +426,34 @@ struct SNZBHistoryRequest
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// history response
// A history response
struct SNZBHistoryResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBHistoryResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of History-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all History-entries, following to this structure
// SNZBHistoryResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
// SNZBHistoryResponseEntry m_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
};
// history entry
// A list response nzb entry
struct SNZBHistoryResponseEntry
{
int32_t m_iID; // History-ID
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
int32_t m_iID; // NZBID
int32_t m_tTime; // When the item was added to history. time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
int32_t m_iNicenameLen; // Length of Nicename-string (m_szNicename), following to this record
// for Collection items (m_iKind = 1)
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
int32_t m_iFileCount; // Initial number of files included in NZB-file
int32_t m_iParStatus; // See NZBInfo::EParStatus
int32_t m_iScriptStatus; // See NZBInfo::EScriptStatus
// for URL items (m_iKind = 2)
int32_t m_iUrlStatus; // See UrlInfo::EStatus
// trailing data
//char m_szNicename[m_iNicenameLen]; // variable sized
};
// download url request
struct SNZBDownloadUrlRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
char m_szURL[NZBREQUESTFILENAMESIZE]; // url to nzb-file
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. Can be empty, then the filename is read from URL download response
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
int32_t m_bAddFirst; // 1 - add url to the top of download queue
int32_t m_bAddPaused; // 1 - pause added files
int32_t m_iPriority; // Priority for files (0 - default)
};
// download url response
struct SNZBDownloadUrlResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
//char m_szText[m_iTrailingDataLength]; // variable sized
};
// UrlQueue request
struct SNZBUrlQueueRequest
{
SNZBRequestBase m_MessageBase; // Must be the first in the struct
};
// UrlQueue response
struct SNZBUrlQueueResponse
{
SNZBResponseBase m_MessageBase; // Must be the first in the struct
int32_t m_iEntrySize; // Size of the SNZBUrlQueueResponseEntry-struct
int32_t m_iNrTrailingEntries; // Number of UrlQueue-entries, following to this structure
int32_t m_iTrailingDataLength; // Length of all UrlQueue-entries, following to this structure
// SNZBUrlQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
};
// UrlQueue response entry
struct SNZBUrlQueueResponseEntry
{
int32_t m_iID; // ID of Url-entry
int32_t m_iURLLen; // Length of URL-string (m_szURL), following to this record
int32_t m_iNZBFilenameLen; // Length of NZBFilename-string (m_szNZBFilename), following to this record
//char m_szURL[m_iURLLen]; // variable sized
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
//char m_szFilename[m_iFilenameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szCategory[m_iCategoryLen]; // variable sized
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -202,9 +202,14 @@ NCursesFrontend::NCursesFrontend()
NCursesFrontend::~NCursesFrontend()
{
#ifdef WIN32
free(m_pScreenBuffer);
free(m_pOldScreenBuffer);
if (m_pScreenBuffer)
{
free(m_pScreenBuffer);
}
if (m_pOldScreenBuffer)
{
free(m_pOldScreenBuffer);
}
m_ColorAttr.clear();
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -545,8 +550,6 @@ int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
szText = (char*)malloc(iLen);
time_t rawtime = Msg->GetTime();
rawtime += g_pOptions->GetTimeCorrection();
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&rawtime, szTime, 50);
@@ -610,10 +613,10 @@ void NCursesFrontend::PrintStatus()
char timeString[100];
timeString[0] = '\0';
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
@@ -621,9 +624,9 @@ void NCursesFrontend::PrintStatus()
}
char szDownloadLimit[128];
if (m_iDownloadLimit > 0)
if (m_fDownloadLimit > 0.0f)
{
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
}
else
{
@@ -643,7 +646,7 @@ void NCursesFrontend::PrintStatus()
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s, Avg. %.*f KB/s",
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
m_bPauseDownload || m_bPauseDownload2 ?
@@ -720,8 +723,11 @@ void NCursesFrontend::PrintKeyInputBar()
void NCursesFrontend::SetHint(const char* szHint)
{
free(m_szHint);
m_szHint = NULL;
if (m_szHint)
{
free(m_szHint);
m_szHint = NULL;
}
if (szHint)
{
m_szHint = strdup(szHint);
@@ -786,8 +792,8 @@ void NCursesFrontend::PrintFileQueue()
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
m_bUseColor ? "" : "*** ", (int)pDownloadQueue->GetFileQueue()->size(),
(int)pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
m_bUseColor ? "" : "*** ", pDownloadQueue->GetFileQueue()->size(),
pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
}
@@ -813,19 +819,6 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
color = NCURSES_COLORPAIR_TEXT;
}
const char* szDownloading = "";
if (pFileInfo->GetActiveDownloads() > 0)
{
szDownloading = " *";
}
char szPriority[100];
szPriority[0] = '\0';
if (pFileInfo->GetPriority() != 0)
{
sprintf(szPriority, " [%+i]", pFileInfo->GetPriority());
}
char szCompleted[20];
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
@@ -836,7 +829,7 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
char szNZBNiceName[1024];
if (m_bShowNZBname)
{
strncpy(szNZBNiceName, pFileInfo->GetNZBInfo()->GetName(), 1023);
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
int len = strlen(szNZBNiceName);
szNZBNiceName[len] = PATH_SEPARATOR;
szNZBNiceName[len + 1] = '\0';
@@ -847,9 +840,8 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
}
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.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';
@@ -920,7 +912,6 @@ void NCursesFrontend::PrintGroupQueue()
{
int iLineNr = m_iQueueWinTop;
LockQueue();
GroupQueue* pGroupQueue = &m_groupQueue;
if (pGroupQueue->empty())
{
@@ -971,11 +962,10 @@ void NCursesFrontend::PrintGroupQueue()
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
m_bUseColor ? "" : "*** ", (int)pGroupQueue->size(), szRemaining, szUnpaused);
m_bUseColor ? "" : "*** ", pGroupQueue->size(), szRemaining, szUnpaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, false);
}
UnlockQueue();
}
void NCursesFrontend::ResetColWidths()
@@ -987,23 +977,21 @@ void NCursesFrontend::ResetColWidths()
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth)
{
int color = NCURSES_COLORPAIR_TEXT;
char chBrace1 = '[';
char chBrace2 = ']';
int color = 0;
const char* Brace1 = "[";
const char* Brace2 = "]";
if (m_eInputMode == eEditQueue && bSelected)
{
color = NCURSES_COLORPAIR_TEXTHIGHL;
if (!m_bUseColor)
{
chBrace1 = '<';
chBrace2 = '>';
Brace1 = "<";
Brace2 = ">";
}
}
const char* szDownloading = "";
if (pGroupInfo->GetActiveDownloads() > 0)
else
{
szDownloading = " *";
color = NCURSES_COLORPAIR_TEXT;
}
long long lUnpausedRemainingSize = pGroupInfo->GetRemainingSize() - pGroupInfo->GetPausedSize();
@@ -1011,19 +999,8 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
char szPriority[100];
szPriority[0] = '\0';
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
{
if (pGroupInfo->GetMinPriority() == pGroupInfo->GetMaxPriority())
{
sprintf(szPriority, " [%+i]", pGroupInfo->GetMinPriority());
}
else
{
sprintf(szPriority, " [%+i..%+i]", pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority());
}
}
char szNZBNiceName[1024];
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
char szBuffer[MAX_SCREEN_WIDTH];
@@ -1053,21 +1030,20 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
Util::FormatFileSize(szTotal, sizeof(szTotal), pGroupInfo->GetNZBInfo()->GetSize());
char szNameWithIds[1024];
snprintf(szNameWithIds, 1024, "%c%i-%i%c%s%s %s", chBrace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), chBrace2,
szPriority, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
snprintf(szNameWithIds, 1024, "%s%i-%i%s %s", Brace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), Brace2, szNZBNiceName);
szNameWithIds[iNameLen] = '\0';
char szTime[100];
szTime[0] = '\0';
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
if (pGroupInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
{
snprintf(szTime, 100, "[paused]");
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pGroupInfo->GetRemainingSize());
}
else if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
else if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
{
long long remain_sec = (long long)(lUnpausedRemainingSize / iCurrentDownloadSpeed);
long long remain_sec = (long long)(lUnpausedRemainingSize / (fCurrentDownloadSpeed * 1024));
int h = (int)(remain_sec / 3600);
int m = (int)((remain_sec % 3600) / 60);
int s = (int)(remain_sec % 60);
@@ -1099,8 +1075,7 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
}
else
{
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%c%i-%i%c%s %s", chBrace1, pGroupInfo->GetFirstID(),
pGroupInfo->GetLastID(), chBrace2, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i-%i%s %s", Brace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), Brace2, szNZBNiceName);
}
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
@@ -1122,7 +1097,11 @@ void NCursesFrontend::PrepareGroupQueue()
void NCursesFrontend::ClearGroupQueue()
{
m_groupQueue.Clear();
for (GroupQueue::iterator it = m_groupQueue.begin(); it != m_groupQueue.end(); it++)
{
delete *it;
}
m_groupQueue.clear();
}
bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
@@ -1438,7 +1417,7 @@ void NCursesFrontend::UpdateInput(int initialKey)
// Enter
else if (iKey == 10 || iKey == 13)
{
ServerSetDownloadRate(m_iInputValue * 1024);
ServerSetDownloadRate((float)m_iInputValue);
m_eInputMode = eNormal;
return;
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -34,8 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
@@ -45,18 +44,20 @@
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer->GetHost(), pNewsServer->GetPort(), pNewsServer->GetTLS())
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
{
m_pNewsServer = pNewsServer;
m_szActiveGroup = NULL;
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
m_bAuthError = false;
SetCipher(pNewsServer->GetCipher());
}
NNTPConnection::~NNTPConnection()
{
free(m_szActiveGroup);
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
free(m_szLineBuf);
}
@@ -80,16 +81,19 @@ const char* NNTPConnection::Request(const char* req)
if (!strncmp(answer, "480", 3))
{
debug("%s requested authorization", GetHost());
debug("%s requested authorization", m_pNetAddress->GetHost());
//authentication required!
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
@@ -97,16 +101,13 @@ const char* NNTPConnection::Request(const char* req)
bool NNTPConnection::Authenticate()
{
if (strlen(m_pNewsServer->GetUser()) == 0 || strlen(m_pNewsServer->GetPassword()) == 0)
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
!((NewsServer*)m_pNetAddress)->GetPassword())
{
error("%c%s (%s) requested authorization but username/password are not set in settings",
toupper(m_pNewsServer->GetName()[0]), m_pNewsServer->GetName() + 1, m_pNewsServer->GetHost());
m_bAuthError = true;
return false;
return true;
}
m_bAuthError = !AuthInfoUser(0);
return !m_bAuthError;
return AuthInfoUser();
}
bool NNTPConnection::AuthInfoUser(int iRecur)
@@ -117,7 +118,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", m_pNewsServer->GetUser());
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
tmp[1024-1] = '\0';
WriteLine(tmp);
@@ -125,13 +126,13 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (!strncmp(answer, "281", 3))
{
debug("Authorization for %s successful", GetHost());
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
@@ -147,7 +148,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
@@ -160,7 +161,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
}
char tmp[1024];
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", m_pNewsServer->GetPassword());
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
tmp[1024-1] = '\0';
WriteLine(tmp);
@@ -168,12 +169,12 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
else if (!strncmp(answer, "2", 1))
{
debug("Authorization for %s successful", GetHost());
debug("Authorization for %s successful", m_pNetAddress->GetHost());
return true;
}
else if (!strncmp(answer, "381", 3))
@@ -185,7 +186,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
@@ -204,77 +205,86 @@ const char* NNTPConnection::JoinGroup(const char* grp)
tmp[1024-1] = '\0';
const char* answer = Request(tmp);
if (m_bAuthError)
{
return answer;
}
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetHost());
free(m_szActiveGroup);
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
if (m_szActiveGroup)
{
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
}
else
{
debug("Error changing group on %s to %s: %s.", GetHost(), grp, answer);
debug("Error changing group on %s to %s: %s.",
GetServer()->GetHost(), grp, answer);
}
return answer;
}
bool NNTPConnection::Connect()
bool NNTPConnection::DoConnect()
{
debug("Opening connection to %s", GetHost());
if (m_eStatus == csConnected)
debug("Opening connection to %s", GetServer()->GetHost());
bool res = Connection::DoConnect();
if (!res)
{
return true;
return res;
}
if (!Connection::Connect())
#ifndef DISABLE_TLS
if (GetNewsServer()->GetTLS())
{
return false;
if (!StartTLS())
{
return false;
}
}
#endif
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Connection to server%i (%s) failed: Connection closed by remote host", NULL);
Disconnect();
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to server%i (%s) failed (Answer: %s)", answer);
Disconnect();
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
return false;
}
if ((strlen(m_pNewsServer->GetUser()) > 0 && strlen(m_pNewsServer->GetPassword()) > 0) &&
!Authenticate())
{
return false;
}
debug("Connection to %s established", GetHost());
debug("Connection to %s established", GetServer()->GetHost());
return true;
}
bool NNTPConnection::Disconnect()
bool NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
Request("quit\r\n");
free(m_szActiveGroup);
m_szActiveGroup = NULL;
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
}
return Connection::Disconnect();
return Connection::DoDisconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetID(), m_pNewsServer->GetHost(), szAnswer);
snprintf(szErrStr, 1024, szMsgPrefix, m_pNetAddress->GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -33,27 +33,27 @@
class NNTPConnection : public Connection
{
private:
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
bool Authenticate();
bool AuthInfoUser(int iRecur);
bool AuthInfoPass(int iRecur);
virtual bool DoConnect();
virtual bool DoDisconnect();
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
public:
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
virtual bool Connect();
virtual bool Disconnect();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
NNTPConnection(NewsServer* server);
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

@@ -1,8 +1,8 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -34,16 +34,14 @@
#include <string.h>
#include <list>
#include <ctype.h>
#ifdef WIN32
#include <comutil.h>
#import <msxml.tlb> named_guids
#import "MSXML.dll" named_guids
using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlerror.h>
#include <libxml/entities.h>
#endif
#include "nzbget.h"
@@ -57,26 +55,34 @@ using namespace MSXML;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#ifndef WIN32
static void libxml_errorhandler(void *ebuf, const char *fmt, ...)
{
va_list argp;
va_start(argp, fmt);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), fmt, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing nzb-file: %s", szErrMsg);
}
#endif
NZBFile::NZBFile(const char* szFileName, const char* szCategory)
{
debug("Creating NZBFile");
m_szFileName = strdup(szFileName);
m_szPassword = NULL;
m_pNZBInfo = new NZBInfo();
m_pNZBInfo->Retain();
m_pNZBInfo->AddReference();
m_pNZBInfo->SetFilename(szFileName);
m_pNZBInfo->SetCategory(szCategory);
m_pNZBInfo->BuildDestDirName();
#ifndef WIN32
m_bPassword = false;
m_pFileInfo = NULL;
m_pArticle = NULL;
m_szTagContent = NULL;
m_iTagContentLen = 0;
#endif
m_FileInfos.clear();
}
@@ -85,8 +91,10 @@ NZBFile::~NZBFile()
debug("Destroying NZBFile");
// Cleanup
free(m_szFileName);
free(m_szPassword);
if (m_szFileName)
{
free(m_szFileName);
}
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
@@ -98,11 +106,6 @@ NZBFile::~NZBFile()
{
m_pNZBInfo->Release();
}
#ifndef WIN32
delete m_pFileInfo;
free(m_szTagContent);
#endif
}
void NZBFile::LogDebugInfo()
@@ -115,106 +118,66 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
// make Article-List big enough
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
pFileInfo->GetArticles()->push_back(NULL);
int index = pArticleInfo->GetPartNumber() - 1;
if ((*pFileInfo->GetArticles())[index])
{
delete (*pFileInfo->GetArticles())[index];
}
(*pFileInfo->GetArticles())[index] = pArticleInfo;
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
}
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
// calculate file size and delete empty articles
long long lSize = 0;
long long lMissedSize = 0;
long long lOneSize = 0;
int iUncountedArticles = 0;
int iMissedArticles = 0;
// deleting empty articles
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
int iTotalArticles = (int)pArticles->size();
int i = 0;
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end(); )
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
{
ArticleInfo* pArticle = *it;
if (!pArticle)
if (*it == NULL)
{
pArticles->erase(it);
it = pArticles->begin() + i;
iMissedArticles++;
if (lOneSize > 0)
{
lMissedSize += lOneSize;
}
else
{
iUncountedArticles++;
}
}
else
{
lSize += pArticle->GetSize();
if (lOneSize == 0)
{
lOneSize = pArticle->GetSize();
}
it++;
i++;
}
}
if (pArticles->empty())
if (!pArticles->empty())
{
delete pFileInfo;
return;
}
ParseSubject(pFileInfo);
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
lMissedSize += iUncountedArticles * lOneSize;
lSize += lMissedSize;
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
pFileInfo->SetSize(lSize);
pFileInfo->SetRemainingSize(lSize - lMissedSize);
pFileInfo->SetMissedSize(lMissedSize);
pFileInfo->SetTotalArticles(iTotalArticles);
pFileInfo->SetMissedArticles(iMissedArticles);
}
void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
{
if (TryQuotes)
{
// try to use the filename in quatation marks
char* p = (char*)pFileInfo->GetSubject();
char* start = strchr(p, '\"');
if (start)
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
start++;
char* end = strchr(start + 1, '\"');
if (end)
{
int len = (int)(end - start);
char* point = strchr(start + 1, '.');
if (point && point < end)
{
char* filename = (char*)malloc(len + 1);
strncpy(filename, start, len);
filename[len] = '\0';
pFileInfo->SetFilename(filename);
free(filename);
return;
}
}
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
else
{
delete pFileInfo;
}
}
void NZBFile::ParseSubject(FileInfo* pFileInfo)
{
// tokenize subject, considering spaces as separators and quotation
// marks as non separatable token delimiters.
// then take the last token containing dot (".") as a filename
@@ -297,14 +260,19 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
pFileInfo->MakeValidFilename();
}
bool NZBFile::HasDuplicateFilenames()
/**
* Check if the parsing of subject was correct
*/
void NZBFile::CheckFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo1 = *it;
int iDupe = 1;
int iDupe = 0;
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
{
FileInfo* pFileInfo2 = *it2;
@@ -323,218 +291,25 @@ bool NZBFile::HasDuplicateFilenames()
// an often case by posting-errors to repost bad files
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
{
return true;
}
}
return false;
}
/**
* Generate filenames from subjects and check if the parsing of subject was correct
*/
void NZBFile::BuildFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
ParseSubject(pFileInfo, true);
}
if (HasDuplicateFilenames())
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
ParseSubject(pFileInfo, false);
}
}
if (HasDuplicateFilenames())
{
m_pNZBInfo->SetManyDupeFiles(true);
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
}
}
bool CompareFileInfo(FileInfo* pFirst, FileInfo* pSecond)
{
return strcmp(pFirst->GetFilename(), pSecond->GetFilename()) > 0;
}
void NZBFile::CalcHashes()
{
FileInfoList fileList;
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
fileList.push_back(*it);
}
fileList.sort(CompareFileInfo);
// split ExtCleanupDisk into tokens and create a list
ExtList extList;
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
char* saveptr;
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
while (szExt)
{
extList.push_back(szExt);
szExt = strtok_r(NULL, ",; ", &saveptr);
}
unsigned int iFullContentHash = 0;
unsigned int iFilteredContentHash = 0;
int iUseForFilteredCount = 0;
for (FileInfoList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
FileInfo* pFileInfo = *it;
// check file extension
int iFilenameLen = strlen(pFileInfo->GetFilename());
bool bSkip = false;
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
{
const char* szExt = *it;
int iExtLen = strlen(szExt);
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, pFileInfo->GetFilename() + iFilenameLen - iExtLen))
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
{
bSkip = true;
break;
FileInfo* pFileInfo2 = *it2;
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
pFileInfo2->MakeValidFilename();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->LoadArticles(pFileInfo2);
g_pDiskState->SaveFile(pFileInfo2);
pFileInfo2->ClearArticles();
}
}
}
bSkip = bSkip && !pFileInfo->GetParFile();
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pArticle = *it;
int iLen = strlen(pArticle->GetMessageID());
iFullContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFullContentHash);
if (!bSkip)
{
iFilteredContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFilteredContentHash);
iUseForFilteredCount++;
}
}
}
free(szExtCleanupDisk);
// if filtered hash is based on less than a half of files - do not use filtered hash at all
if (iUseForFilteredCount < (int)fileList.size() / 2)
{
iFilteredContentHash = 0;
}
m_pNZBInfo->SetFullContentHash(iFullContentHash);
m_pNZBInfo->SetFilteredContentHash(iFilteredContentHash);
}
void NZBFile::ProcessFiles()
{
BuildFilenames();
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->MakeValidFilename();
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
bool bParFile = strstr(szLoFileName, ".par2");
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
m_pNZBInfo->SetTotalArticles(m_pNZBInfo->GetTotalArticles() + pFileInfo->GetTotalArticles());
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFailedSize(m_pNZBInfo->GetFailedSize() + pFileInfo->GetMissedSize());
m_pNZBInfo->SetCurrentFailedSize(m_pNZBInfo->GetFailedSize());
pFileInfo->SetParFile(bParFile);
if (bParFile)
{
m_pNZBInfo->SetParSize(m_pNZBInfo->GetParSize() + pFileInfo->GetSize());
m_pNZBInfo->SetParFailedSize(m_pNZBInfo->GetParFailedSize() + pFileInfo->GetMissedSize());
m_pNZBInfo->SetParCurrentFailedSize(m_pNZBInfo->GetParFailedSize());
}
}
CalcHashes();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
if (m_szPassword)
{
ReadPassword();
}
}
/**
* Password read using XML-parser may have special characters (such as TAB) stripped.
* This function rereads password directly from file to keep all characters intact.
*/
void NZBFile::ReadPassword()
{
FILE* pFile = fopen(m_szFileName, "rb");
if (!pFile)
{
return;
}
// obtain file size.
fseek(pFile , 0 , SEEK_END);
int iSize = ftell(pFile);
rewind(pFile);
// reading first 4KB of the file
// allocate memory to contain the whole file.
char* buf = (char*)malloc(4096);
iSize = iSize < 4096 ? iSize : 4096;
// copy the file into the buffer.
fread(buf, 1, iSize, pFile);
fclose(pFile);
buf[iSize-1] = '\0';
char* szMetaPassword = strstr(buf, "<meta type=\"password\">");
if (szMetaPassword)
{
szMetaPassword += 22; // length of '<meta type="password">'
char* szEnd = strstr(szMetaPassword, "</meta>");
if (szEnd)
{
*szEnd = '\0';
WebUtil::XmlDecode(szMetaPassword);
free(m_szPassword);
m_szPassword = strdup(szMetaPassword);
}
}
free(buf);
}
#ifdef WIN32
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
CoInitialize(NULL);
@@ -551,15 +326,21 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
VARIANT_BOOL success = doc->load(v);
VARIANT_BOOL success;
if (bFromBuffer)
{
success = doc->loadXML(szBuffer);
}
else
{
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
success = doc->load(v);
}
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
@@ -571,7 +352,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->ProcessFiles();
pFile->CheckFilenames();
}
else
{
@@ -595,7 +376,7 @@ void NZBFile::EncodeURL(const char* szFilename, char* szURL)
else
{
*szURL++ = '%';
int a = (unsigned char)ch >> 4;
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
@@ -609,17 +390,10 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
MSXML::IXMLDOMNodePtr node = root->selectSingleNode("/nzb/head/meta[@type='password']");
if (node)
{
_bstr_t password(node->Gettext());
m_szPassword = strdup(password);
}
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
for (int i = 0; i < fileList->Getlength(); i++)
{
node = fileList->Getitem(i);
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
if (!attribute) return false;
_bstr_t subject(attribute->Gettext());
@@ -660,14 +434,16 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
int partNumber = atoi(number);
int lsize = atoi(bytes);
if (partNumber > 0)
{
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
}
AddFileInfo(pFileInfo);
@@ -677,233 +453,160 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
#else
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
NZBFile* pFile = new NZBFile(szFileName, szCategory);
xmlSAXHandler SAX_handler = {0};
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
pFile->m_bIgnoreNextError = false;
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (ret == 0)
xmlSetGenericErrorFunc(NULL, libxml_errorhandler);
xmlTextReaderPtr doc;
if (bFromBuffer)
{
pFile->ProcessFiles();
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
}
else
{
doc = xmlReaderForFile(szFileName, NULL, 0);
}
if (!doc)
{
error("Could not create XML-Reader");
return NULL;
}
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->CheckFilenames();
}
else
{
error("Failed to parse nzb-file");
delete pFile;
pFile = NULL;
}
return pFile;
xmlFreeTextReader(doc);
return pFile;
}
void NZBFile::Parse_StartElement(const char *name, const char **atts)
bool NZBFile::ParseNZB(void* nzb)
{
if (m_szTagContent)
{
free(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
if (!strcmp("file", name))
{
m_pFileInfo = new FileInfo();
m_pFileInfo->SetFilename(m_szFileName);
FileInfo* pFileInfo = NULL;
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
// walk through whole doc and search for segments-tags
int ret = xmlTextReaderRead(node);
while (ret == 1)
{
if (node)
{
xmlChar *name, *value;
for (int i = 0; atts[i]; i += 2)
{
const char* attrname = atts[i];
const char* attrvalue = atts[i + 1];
if (!strcmp("subject", attrname))
{
m_pFileInfo->SetSubject(attrvalue);
}
if (!strcmp("date", attrname))
{
m_pFileInfo->SetTime(atoi(attrvalue));
}
}
}
else if (!strcmp("segment", name))
{
if (!m_pFileInfo)
{
// error: bad nzb-file
return;
}
long long lsize = -1;
int partNumber = -1;
name = xmlTextReaderName(node);
if (name == NULL)
{
name = xmlStrdup(BAD_CAST "--");
}
value = xmlTextReaderValue(node);
for (int i = 0; atts[i]; i += 2)
{
const char* attrname = atts[i];
const char* attrvalue = atts[i + 1];
if (!strcmp("bytes", attrname))
{
lsize = atol(attrvalue);
}
if (!strcmp("number", attrname))
{
partNumber = atol(attrvalue);
}
}
if (xmlTextReaderNodeType(node) == 1)
{
if (!strcmp("file", (char*)name))
{
pFileInfo = new FileInfo();
pFileInfo->SetFilename(m_szFileName);
if (partNumber > 0)
{
// new segment, add it!
m_pArticle = new ArticleInfo();
m_pArticle->SetPartNumber(partNumber);
m_pArticle->SetSize(lsize);
AddArticle(m_pFileInfo, m_pArticle);
}
}
else if (!strcmp("meta", name))
{
m_bPassword = atts[0] && atts[1] && !strcmp("type", atts[0]) && !strcmp("password", atts[1]);
}
}
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
if (!strcmp("subject",(char*)name))
{
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetSubject((char*)value);
}
if (!strcmp("date",(char*)name))
{
xmlFree(value);
value = xmlTextReaderValue(node);
pFileInfo->SetTime(atoi((char*)value));
}
}
}
else if (!strcmp("segment",(char*)name))
{
long long lsize = -1;
int partNumber = -1;
void NZBFile::Parse_EndElement(const char *name)
{
if (!strcmp("file", name))
{
// Close the file element, add the new file to file-list
AddFileInfo(m_pFileInfo);
m_pFileInfo = NULL;
m_pArticle = NULL;
}
else if (!strcmp("group", name))
{
if (!m_pFileInfo)
{
// error: bad nzb-file
return;
}
m_pFileInfo->GetGroups()->push_back(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
else if (!strcmp("segment", name))
{
if (!m_pFileInfo || !m_pArticle)
{
// error: bad nzb-file
return;
}
while (xmlTextReaderMoveToNextAttribute(node))
{
xmlFree(name);
name = xmlTextReaderName(node);
xmlFree(value);
value = xmlTextReaderValue(node);
if (!strcmp("bytes",(char*)name))
{
lsize = atol((char*)value);
}
if (!strcmp("number",(char*)name))
{
partNumber = atol((char*)value);
}
}
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
/* Get the #text part */
ret = xmlTextReaderRead(node);
// Get the #text part
char ID[2048];
snprintf(ID, 2048, "<%s>", m_szTagContent);
m_pArticle->SetMessageID(ID);
m_pArticle = NULL;
}
else if (!strcmp("meta", name) && m_bPassword)
{
m_szPassword = strdup(m_szTagContent);
}
}
if (partNumber > 0)
{
// new segment, add it!
xmlFree(value);
value = xmlTextReaderValue(node);
char tmp[2048];
snprintf(tmp, 2048, "<%s>", (char*)value);
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(tmp);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
}
else if (!strcmp("group",(char*)name))
{
ret = xmlTextReaderRead(node);
xmlFree(value);
value = xmlTextReaderValue(node);
if (!pFileInfo)
{
// error: bad nzb-file
break;
}
pFileInfo->GetGroups()->push_back(strdup((char*)value));
}
}
void NZBFile::Parse_Content(const char *buf, int len)
{
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
m_iTagContentLen += len;
m_szTagContent[m_iTagContentLen] = '\0';
}
if (xmlTextReaderNodeType(node) == 15)
{
/* Close the file element, add the new file to file-list */
if (!strcmp("file",(char*)name))
{
AddFileInfo(pFileInfo);
}
}
void NZBFile::SAX_StartElement(NZBFile* pFile, const char *name, const char **atts)
{
pFile->Parse_StartElement(name, atts);
}
void NZBFile::SAX_EndElement(NZBFile* pFile, const char *name)
{
pFile->Parse_EndElement(name);
}
void NZBFile::SAX_characters(NZBFile* pFile, const char * xmlstr, int len)
{
char* str = (char*)xmlstr;
// trim starting blanks
int off = 0;
for (int i = 0; i < len; i++)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
off++;
}
else
{
break;
}
}
int newlen = len - off;
// trim ending blanks
for (int i = len - 1; i >= off; i--)
{
char ch = str[i];
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
{
newlen--;
}
else
{
break;
}
}
if (newlen > 0)
{
// interpret tag content
pFile->Parse_Content(str + off, newlen);
}
}
void* NZBFile::SAX_getEntity(NZBFile* pFile, const char * name)
{
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
if (!e)
{
warn("entity not found");
pFile->m_bIgnoreNextError = true;
}
return e;
}
void NZBFile::SAX_error(NZBFile* pFile, const char *msg, ...)
{
if (pFile->m_bIgnoreNextError)
{
pFile->m_bIgnoreNextError = false;
return;
}
va_list argp;
va_start(argp, msg);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing nzb-file: %s", szErrMsg);
xmlFree(name);
xmlFree(value);
}
ret = xmlTextReaderRead(node);
}
if (ret != 0)
{
error("Failed to parse nzb-file");
return false;
}
return true;
}
#endif

View File

@@ -1,8 +1,8 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -28,7 +28,6 @@
#define NZBFILE_H
#include <vector>
#include <list>
#include "DownloadInfo.h"
@@ -36,52 +35,32 @@ class NZBFile
{
public:
typedef std::vector<FileInfo*> FileInfos;
typedef std::list<FileInfo*> FileInfoList;
typedef std::list<char*> ExtList;
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
char* m_szPassword;
NZBFile(const char* szFileName, const char* szCategory);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo, bool TryQuotes);
void BuildFilenames();
void ProcessFiles();
void CalcHashes();
bool HasDuplicateFilenames();
void ReadPassword();
void ParseSubject(FileInfo* pFileInfo);
void CheckFilenames();
#ifdef WIN32
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
FileInfo* m_pFileInfo;
ArticleInfo* m_pArticle;
char* m_szTagContent;
int m_iTagContentLen;
bool m_bIgnoreNextError;
bool m_bPassword;
static void SAX_StartElement(NZBFile* pFile, const char *name, const char **atts);
static void SAX_EndElement(NZBFile* pFile, const char *name);
static void SAX_characters(NZBFile* pFile, const char * xmlstr, int len);
static void* SAX_getEntity(NZBFile* pFile, const char * name);
static void SAX_error(NZBFile* pFile, const char *msg, ...);
void Parse_StartElement(const char *name, const char **atts);
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
bool ParseNZB(void* nzb);
#endif
static NZBFile* Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer);
public:
virtual ~NZBFile();
static NZBFile* Create(const char* szFileName, const char* szCategory);
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
const char* GetPassword() { return m_szPassword; }
void DetachFileInfos();
void LogDebugInfo();

View File

@@ -1,7 +1,8 @@
/*
* This file is part of nzbget
* This file if part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,27 +16,39 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef DEBUG
# define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
# define DLog(...) do {} while (0)
#ifdef WIN32
#include "win32.h"
#endif
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)
#include <stdlib.h>
#include <string.h>
#include "nzbget.h"
#include "NetAddress.h"
NetAddress::NetAddress(const char* szHost, int iPort)
{
m_szHost = NULL;
m_iPort = iPort;
if (szHost)
m_szHost = strdup(szHost);
}
NetAddress::~NetAddress()
{
if (m_szHost)
free(m_szHost);
m_szHost = NULL;
}

View File

@@ -1,7 +1,8 @@
/*
* This file is part of nzbget
* This file if part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,24 +16,29 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
#import "WebClient.h"
@interface RPC : WebClient {
}
#ifndef NETADDRESS_H
#define NETADDRESS_H
- (id)initWithMethod:(NSString*)method
receiver:(id)receiver
success:(SEL)successCallback
failure:(SEL)failureCallback;
class NetAddress
{
private:
char* m_szHost;
int m_iPort;
+ (void)setRpcUrl:(NSString*)url;
public:
NetAddress(const char* szHost, int iPort);
virtual ~NetAddress();
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
};
@end
#endif

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -34,47 +34,38 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "nzbget.h"
#include "NewsServer.h"
#include "Log.h"
NewsServer::NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS,
const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel) : NetAddress(szHost, iPort)
{
m_iID = iID;
m_iStateID = 0;
m_bActive = bActive;
m_iPort = iPort;
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = iLevel;
m_iNormLevel = iLevel;
m_iGroup = iGroup;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
m_szHost = strdup(szHost ? szHost : "");
m_szUser = strdup(szUser ? szUser : "");
m_szPassword = strdup(szPass ? szPass : "");
m_szCipher = strdup(szCipher ? szCipher : "");
if (szName && strlen(szName) > 0)
if (szUser)
{
m_szName = strdup(szName);
m_szUser = strdup(szUser);
}
else
if (szPass)
{
m_szName = (char*)malloc(20);
snprintf(m_szName, 20, "server%i", iID);
m_szName[20-1] = '\0';
m_szPassword = strdup(szPass);
}
}
NewsServer::~NewsServer()
{
free(m_szName);
free(m_szHost);
free(m_szUser);
free(m_szPassword);
free(m_szCipher);
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
}

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,52 +27,27 @@
#ifndef NEWSSERVER_H
#define NEWSSERVER_H
#include <vector>
#include "NetAddress.h"
class NewsServer
class NewsServer : public NetAddress
{
private:
int m_iID;
int m_iStateID;
bool m_bActive;
char* m_szName;
int m_iGroup;
char* m_szHost;
int m_iPort;
char* m_szUser;
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
int m_iNormLevel;
bool m_bJoinGroup;
bool m_bTLS;
char* m_szCipher;
public:
NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
~NewsServer();
int GetID() { return m_iID; }
int GetStateID() { return m_iStateID; }
void SetStateID(int iStateID) { m_iStateID = iStateID; }
bool GetActive() { return m_bActive; }
void SetActive(bool bActive) { m_bActive = bActive; }
const char* GetName() { return m_szName; }
int GetGroup() { return m_iGroup; }
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
virtual ~NewsServer();
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
int GetNormLevel() { return m_iNormLevel; }
void SetNormLevel(int iLevel) { m_iNormLevel = iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
};
typedef std::vector<NewsServer*> Servers;
#endif

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$

View File

File diff suppressed because it is too large Load Diff

294
Options.h
View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -28,11 +28,7 @@
#define OPTIONS_H
#include <vector>
#include <list>
#include <time.h>
#include "Thread.h"
#include "Util.h"
class Options
{
@@ -49,12 +45,10 @@ public:
opClientRequestEditQueue,
opClientRequestLog,
opClientRequestShutdown,
opClientRequestReload,
opClientRequestVersion,
opClientRequestPostQueue,
opClientRequestWriteLog,
opClientRequestScanSync,
opClientRequestScanAsync,
opClientRequestScan,
opClientRequestDownloadPause,
opClientRequestDownloadUnpause,
opClientRequestDownload2Pause,
@@ -63,9 +57,7 @@ public:
opClientRequestPostUnpause,
opClientRequestScanPause,
opClientRequestScanUnpause,
opClientRequestHistory,
opClientRequestDownloadUrl,
opClientRequestUrlQueue
opClientRequestHistory
};
enum EMessageTarget
{
@@ -80,23 +72,11 @@ public:
omColored,
omNCurses
};
enum EParCheck
enum ELoadPars
{
pcAuto,
pcForce,
pcManual
};
enum EParScan
{
psLimited,
psFull,
psAuto
};
enum EHealthCheck
{
hcPause,
hcDelete,
hcNone
lpNone,
lpOne,
lpAll
};
enum EScriptLogKind
{
@@ -107,144 +87,38 @@ public:
slError,
slDebug
};
enum EMatchMode
{
mmID = 1,
mmName,
mmRegEx
};
class OptEntry
{
private:
char* m_szName;
char* m_szValue;
char* m_szDefValue;
int m_iLineNo;
void SetName(const char* szName);
void SetValue(const char* szValue);
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
friend class Options;
public:
OptEntry();
OptEntry(const char* szName, const char* szValue);
~OptEntry();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
const char* GetDefValue() { return m_szDefValue; }
int GetLineNo() { return m_iLineNo; }
};
typedef std::vector<OptEntry*> OptEntriesBase;
class OptEntries: public OptEntriesBase
{
public:
~OptEntries();
OptEntry* FindOption(const char* szName);
};
class ConfigTemplate
{
private:
char* m_szName;
char* m_szDisplayName;
char* m_szTemplate;
friend class Options;
public:
ConfigTemplate(const char* szName, const char* szDisplayName, const char* szTemplate);
~ConfigTemplate();
const char* GetName() { return m_szName; }
const char* GetDisplayName() { return m_szDisplayName; }
const char* GetTemplate() { return m_szTemplate; }
};
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
class ConfigTemplates: public ConfigTemplatesBase
{
public:
~ConfigTemplates();
};
typedef std::vector<char*> NameList;
class Category
{
private:
char* m_szName;
char* m_szDestDir;
bool m_bUnpack;
char* m_szDefScript;
NameList m_Aliases;
public:
Category(const char* szName, const char* szDestDir, bool bUnpack, const char* szDefScript);
~Category();
const char* GetName() { return m_szName; }
const char* GetDestDir() { return m_szDestDir; }
bool GetUnpack() { return m_bUnpack; }
const char* GetDefScript() { return m_szDefScript; }
NameList* GetAliases() { return &m_Aliases; }
};
typedef std::vector<Category*> CategoriesBase;
class Categories: public CategoriesBase
{
public:
~Categories();
Category* FindCategory(const char* szName, bool bSearchAliases);
};
class Script
{
private:
char* m_szName;
char* m_szLocation;
char* m_szDisplayName;
public:
Script(const char* szName, const char* szLocation);
~Script();
const char* GetName() { return m_szName; }
const char* GetLocation() { return m_szLocation; }
void SetDisplayName(const char* szDisplayName);
const char* GetDisplayName() { return m_szDisplayName; }
};
typedef std::list<Script*> ScriptListBase;
class ScriptList: public ScriptListBase
{
public:
~ScriptList();
Script* Find(const char* szName);
};
typedef std::vector<OptEntry*> OptEntries;
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
Mutex m_mutexOptEntries;
Categories m_Categories;
// Options
bool m_bConfigErrors;
int m_iConfigLine;
char* m_szConfigFilename;
char* m_szDestDir;
char* m_szInterDir;
char* m_szTempDir;
char* m_szQueueDir;
char* m_szNzbDir;
char* m_szWebDir;
char* m_szConfigTemplate;
char* m_szScriptDir;
EMessageTarget m_eInfoTarget;
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
@@ -255,40 +129,31 @@ private:
bool m_bResetLog;
int m_iConnectionTimeout;
int m_iTerminateTimeout;
bool m_bAppendNZBDir;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
bool m_bRenameBroken;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bDupeCheck;
char* m_szControlIP;
char* m_szControlUsername;
char* m_szControlPassword;
int m_iControlPort;
bool m_bSecureControl;
int m_iSecurePort;
char* m_szSecureCert;
char* m_szSecureKey;
char* m_szAuthorizedIP;
char* m_szServerIP;
char* m_szServerPassword;
int m_szServerPort;
char* m_szLockFile;
char* m_szDaemonUsername;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadUrlQueue;
bool m_bReloadPostQueue;
int m_iUrlConnections;
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
EParCheck m_eParCheck;
ELoadPars m_eLoadPars;
bool m_bParCheck;
bool m_bParRepair;
EParScan m_eParScan;
bool m_bParRename;
EHealthCheck m_eHealthCheck;
char* m_szDefScript;
char* m_szScriptOrder;
char* m_szPostProcess;
char* m_szNZBProcess;
char* m_szNZBAddedProcess;
bool m_bStrictParName;
bool m_bNoConfig;
int m_iUMask;
int m_iUpdateInterval;
@@ -296,30 +161,25 @@ private:
bool m_bCursesTime;
bool m_bCursesGroup;
bool m_bCrcCheck;
bool m_bRetryOnCrcError;
int m_iThreadLimit;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EScriptLogKind m_eProcessLogKind;
bool m_bAllowReProcess;
bool m_bTLS;
bool m_bDumpCore;
bool m_bParPauseQueue;
bool m_bScriptPauseQueue;
bool m_bPostPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
int m_iParTimeLimit;
int m_iKeepHistory;
bool m_bAccurateRate;
bool m_bUnpack;
bool m_bUnpackCleanupDisk;
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
int m_iFeedHistory;
bool m_bUrlForce;
int m_iTimeCorrection;
// Parsed command-line parameters
bool m_bServerMode;
@@ -329,18 +189,13 @@ private:
int m_iEditQueueOffset;
int* m_pEditQueueIDList;
int m_iEditQueueIDCount;
NameList m_EditQueueNameList;
EMatchMode m_EMatchMode;
char* m_szEditQueueText;
char* m_szArgFilename;
char* m_szAddCategory;
int m_iAddPriority;
bool m_bAddPaused;
char* m_szAddNZBFilename;
char* m_szCategory;
char* m_szLastArg;
bool m_bPrintOptions;
bool m_bAddTop;
int m_iSetRate;
float m_fSetRate;
int m_iLogLines;
int m_iWriteLogKind;
bool m_bTestBacktrace;
@@ -350,9 +205,8 @@ private:
bool m_bPauseDownload2;
bool m_bPausePostProcess;
bool m_bPauseScan;
int m_iDownloadRate;
float m_fDownloadRate;
EClientOperation m_eClientOperation;
time_t m_tResumeTime;
void InitDefault();
void InitOptFile();
@@ -360,56 +214,33 @@ private:
void InitOptions();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitCategories();
void InitScheduler();
void InitFeeds();
void CheckOptions();
void PrintUsage(char* com);
void Dump();
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
int ParseIntValue(const char* OptName, int iBase);
float ParseFloatValue(const char* OptName);
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
OptEntry* FindOption(const char* optname);
const char* GetOption(const char* optname);
void SetOption(const char* optname, const char* value);
bool SetOptionString(const char* option);
bool SplitOptionString(const char* option, char** pOptName, char** pOptValue);
bool ValidateOptionName(const char* optname);
void LoadConfigFile();
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
void LoadConfig(const char* configfile);
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty);
void ParseFileIDList(int argc, char* argv[], int optind);
void ParseFileNameList(int argc, char* argv[], int optind);
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
void ConfigError(const char* msg, ...);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *szOptionName);
void ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue, int iValueBufLen);
static bool CompareScripts(Script* pScript1, Script* pScript2);
void LoadScriptDir(ScriptList* pScriptList, const char* szDirectory, bool bIsSubDir);
void BuildScriptDisplayNames(ScriptList* pScriptList);
public:
Options(int argc, char* argv[]);
~Options();
bool LoadConfig(OptEntries* pOptEntries);
bool SaveConfig(OptEntries* pOptEntries);
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
void LoadScriptList(ScriptList* pScriptList);
// Options
OptEntries* LockOptEntries();
void UnlockOptEntries();
const char* GetConfigFilename() { return m_szConfigFilename; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetInterDir() { return m_szInterDir; }
const char* GetTempDir() { return m_szTempDir; }
const char* GetQueueDir() { return m_szQueueDir; }
const char* GetNzbDir() { return m_szNzbDir; }
const char* GetWebDir() { return m_szWebDir; }
const char* GetConfigTemplate() { return m_szConfigTemplate; }
const char* GetScriptDir() { return m_szScriptDir; }
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
bool GetResetLog() const { return m_bResetLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
@@ -420,72 +251,56 @@ public:
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
bool GetDecode() { return m_bDecode; };
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
bool GetContinuePartial() { return m_bContinuePartial; }
bool GetRenameBroken() { return m_bRenameBroken; }
int GetRetries() { return m_iRetries; }
int GetRetryInterval() { return m_iRetryInterval; }
bool GetSaveQueue() { return m_bSaveQueue; }
bool GetDupeCheck() { return m_bDupeCheck; }
const char* GetControlIP() { return m_szControlIP; }
const char* GetControlUsername() { return m_szControlUsername; }
const char* GetControlPassword() { return m_szControlPassword; }
int GetControlPort() { return m_iControlPort; }
bool GetSecureControl() { return m_bSecureControl; }
int GetSecurePort() { return m_iSecurePort; }
const char* GetSecureCert() { return m_szSecureCert; }
const char* GetSecureKey() { return m_szSecureKey; }
const char* GetAuthorizedIP() { return m_szAuthorizedIP; }
const char* GetServerIP() { return m_szServerIP; }
const char* GetServerPassword() { return m_szServerPassword; }
int GetServerPort() { return m_szServerPort; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUsername() { return m_szDaemonUsername; }
const char* GetDaemonUserName() { return m_szDaemonUserName; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
int GetUrlConnections() { return m_iUrlConnections; }
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
const char* GetLogFile() { return m_szLogFile; }
EParCheck GetParCheck() { return m_eParCheck; }
ELoadPars GetLoadPars() { return m_eLoadPars; }
bool GetParCheck() { return m_bParCheck; }
bool GetParRepair() { return m_bParRepair; }
EParScan GetParScan() { return m_eParScan; }
bool GetParRename() { return m_bParRename; }
EHealthCheck GetHealthCheck() { return m_eHealthCheck; }
const char* GetScriptOrder() { return m_szScriptOrder; }
const char* GetDefScript() { return m_szDefScript; }
const char* GetPostProcess() { return m_szPostProcess; }
const char* GetNZBProcess() { return m_szNZBProcess; }
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
bool GetStrictParName() { return m_bStrictParName; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
bool GetCursesNZBName() { return m_bCursesNZBName; }
bool GetCursesTime() { return m_bCursesTime; }
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
int GetThreadLimit() { return m_iThreadLimit; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
int GetDiskSpace() { return m_iDiskSpace; }
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
bool GetTLS() { return m_bTLS; }
bool GetDumpCore() { return m_bDumpCore; }
bool GetParPauseQueue() { return m_bParPauseQueue; }
bool GetScriptPauseQueue() { return m_bScriptPauseQueue; }
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
int GetParTimeLimit() { return m_iParTimeLimit; }
int GetKeepHistory() { return m_iKeepHistory; }
bool GetAccurateRate() { return m_bAccurateRate; }
bool GetUnpack() { return m_bUnpack; }
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
int GetFeedHistory() { return m_iFeedHistory; }
bool GetUrlForce() { return m_bUrlForce; }
int GetTimeCorrection() { return m_iTimeCorrection; }
Category* FindCategory(const char* szName, bool bSearchAliases) { return m_Categories.FindCategory(szName, bSearchAliases); }
// Parsed command-line parameters
bool GetServerMode() { return m_bServerMode; }
@@ -496,17 +311,12 @@ public:
int GetEditQueueOffset() { return m_iEditQueueOffset; }
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
EMatchMode GetMatchMode() { return m_EMatchMode; }
const char* GetEditQueueText() { return m_szEditQueueText; }
const char* GetArgFilename() { return m_szArgFilename; }
const char* GetAddCategory() { return m_szAddCategory; }
bool GetAddPaused() { return m_bAddPaused; }
const char* GetCategory() { return m_szCategory; }
const char* GetLastArg() { return m_szLastArg; }
int GetAddPriority() { return m_iAddPriority; }
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
bool GetAddTop() { return m_bAddTop; }
int GetSetRate() { return m_iSetRate; }
float GetSetRate() { return m_fSetRate; }
int GetLogLines() { return m_iLogLines; }
int GetWriteLogKind() { return m_iWriteLogKind; }
bool GetTestBacktrace() { return m_bTestBacktrace; }
@@ -520,10 +330,8 @@ public:
bool GetPausePostProcess() const { return m_bPausePostProcess; }
void SetPauseScan(bool bPauseScan) { m_bPauseScan = bPauseScan; }
bool GetPauseScan() const { return m_bPauseScan; }
void SetDownloadRate(int iRate) { m_iDownloadRate = iRate; }
int GetDownloadRate() const { return m_iDownloadRate; }
void SetResumeTime(time_t tResumeTime) { m_tResumeTime = tResumeTime; }
time_t GetResumeTime() const { return m_tResumeTime; }
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
float GetDownloadRate() const { return m_fDownloadRate; }
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -35,21 +35,18 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#else
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ParChecker.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
@@ -69,463 +66,152 @@ const char* Par2CmdLineErrStr[] = { "OK",
class Repairer : public Par2Repairer
{
private:
CommandLine commandLine;
public:
Result PreProcess(const char *szParFilename);
Result Process(bool dorepair);
friend class ParChecker;
};
Result Repairer::PreProcess(const char *szParFilename)
{
#ifdef HAVE_PAR2_BUGFIXES_V2
// Ensure linking against the patched version of libpar2
BugfixesPatchVersion2();
#endif
if (g_pOptions->GetParScan() == Options::psFull)
{
char szWildcardParam[1024];
strncpy(szWildcardParam, szParFilename, 1024);
szWildcardParam[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szWildcardParam);
if (szBasename != szWildcardParam && strlen(szBasename) > 0)
{
szBasename[0] = '*';
szBasename[1] = '\0';
}
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename, szWildcardParam };
if (!commandLine.Parse(6, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
else
{
const char* argv[] = { "par2", "r", "-v", "-v", szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
return eInvalidCommandLineArguments;
}
}
return Par2Repairer::PreProcess(commandLine);
}
Result Repairer::Process(bool dorepair)
{
return Par2Repairer::Process(commandLine, dorepair);
}
class MissingFilesComparator
{
private:
const char* m_szBaseParFilename;
public:
MissingFilesComparator(const char* szBaseParFilename) : m_szBaseParFilename(szBaseParFilename) {}
bool operator()(CommandLine::ExtraFile* pFirst, CommandLine::ExtraFile* pSecond) const;
};
/*
* Files with the same name as in par-file (and a differnt extension) are
* placed at the top of the list to be scanned first.
*/
bool MissingFilesComparator::operator()(CommandLine::ExtraFile* pFile1, CommandLine::ExtraFile* pFile2) const
{
char name1[1024];
strncpy(name1, Util::BaseFileName(pFile1->FileName().c_str()), 1024);
name1[1024-1] = '\0';
if (char* ext = strrchr(name1, '.')) *ext = '\0'; // trim extension
char name2[1024];
strncpy(name2, Util::BaseFileName(pFile2->FileName().c_str()), 1024);
name2[1024-1] = '\0';
if (char* ext = strrchr(name2, '.')) *ext = '\0'; // trim extension
return strcmp(name1, m_szBaseParFilename) == 0 && strcmp(name1, name2) != 0;
}
ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psFailed;
m_szDestDir = NULL;
m_szNZBName = NULL;
m_eStatus = psUndefined;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_pRepairer = NULL;
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
m_bCancelled = false;
m_eStage = ptLoadingPars;
m_QueuedParFiles.clear();
}
ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
free(m_szDestDir);
free(m_szNZBName);
free(m_szInfoName);
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szErrMsg)
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
Cleanup();
}
void ParChecker::Cleanup()
{
delete (Repairer*)m_pRepairer;
m_pRepairer = NULL;
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
}
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end() ;it++)
void ParChecker::SetParFilename(const char * szParFilename)
{
if (m_szParFilename)
{
free(*it);
free(m_szParFilename);
}
m_ProcessedFiles.clear();
m_sourceFiles.clear();
free(m_szErrMsg);
m_szErrMsg = NULL;
}
void ParChecker::SetDestDir(const char * szDestDir)
{
free(m_szDestDir);
m_szDestDir = strdup(szDestDir);
}
void ParChecker::SetNZBName(const char * szNZBName)
{
free(m_szNZBName);
m_szNZBName = strdup(szNZBName);
m_szParFilename = strdup(szParFilename);
}
void ParChecker::SetInfoName(const char * szInfoName)
{
free(m_szInfoName);
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
void ParChecker::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParChecker::Run()
{
ParCoordinator::FileList fileList;
if (!ParCoordinator::FindMainPars(m_szDestDir, &fileList))
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Could not find any par-files", m_szNZBName);
m_eStatus = psFailed;
Completed();
return;
}
m_eStatus = psRepairNotNeeded;
m_bCancelled = false;
for (ParCoordinator::FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
char* szParFilename = *it;
debug("Found par: %s", szParFilename);
if (!IsStopped() && !m_bCancelled)
{
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
char szInfoName[1024];
int iBaseLen = 0;
ParCoordinator::ParseParFilename(szParFilename, &iBaseLen, NULL);
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
strncpy(szInfoName, szParFilename, maxlen);
szInfoName[maxlen] = '\0';
char szParInfoName[1024];
snprintf(szParInfoName, 1024, "%s%c%s", m_szNZBName, (int)PATH_SEPARATOR, szInfoName);
szParInfoName[1024-1] = '\0';
SetInfoName(szParInfoName);
EStatus eStatus = RunParCheck(szFullParFilename);
// accumulate total status, the worst status has priority
if (m_eStatus > eStatus)
{
m_eStatus = eStatus;
}
if (g_pOptions->GetCreateBrokenLog())
{
WriteBrokenLog(eStatus);
}
}
free(szParFilename);
}
Completed();
}
ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
{
Cleanup();
m_szParFilename = szParFilename;
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
EStatus eStatus = psFailed;
m_bCancelled = false;
PrintMessage(Message::mkInfo, "Verifying %s", m_szInfoName);
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
CommandLine commandLine;
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
if (!commandLine.Parse(5, (char**)argv))
{
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
SetStatus(psFailed);
return;
}
Result res;
Repairer* pRepairer = new Repairer();
m_pRepairer = pRepairer;
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
UpdateProgress();
Result res = (Result)PreProcessPar();
if (IsStopped() || res != eSuccess)
res = pRepairer->PreProcess(commandLine);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
Cleanup();
return psFailed;
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
SetStatus(psFailed);
delete pRepairer;
return;
}
char BufReason[1024];
BufReason[0] = '\0';
if (m_szErrMsg)
{
free(m_szErrMsg);
}
m_szErrMsg = NULL;
m_eStage = ptVerifyingSources;
Repairer* pRepairer = (Repairer*)m_pRepairer;
res = pRepairer->Process(false);
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && pRepairer->missingfilecount > 0 && g_pOptions->GetParScan() == Options::psAuto && AddMissingFiles())
{
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
if (!IsStopped() && res == eRepairNotPossible)
{
res = (Result)ProcessMorePars();
}
if (IsStopped())
{
Cleanup();
return psFailed;
}
eStatus = psFailed;
if (res == eSuccess)
{
PrintMessage(Message::mkInfo, "Repair not needed for %s", m_szInfoName);
eStatus = psRepairNotNeeded;
}
else if (res == eRepairPossible)
{
eStatus = psRepairPossible;
if (g_pOptions->GetParRepair())
{
PrintMessage(Message::mkInfo, "Repairing %s", m_szInfoName);
SaveSourceList();
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(true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
PrintMessage(Message::mkInfo, "Successfully repaired %s", m_szInfoName);
eStatus = psRepaired;
DeleteLeftovers();
}
}
else
{
PrintMessage(Message::mkInfo, "Repair possible for %s", m_szInfoName);
}
}
if (m_bCancelled)
{
if (m_eStage >= ptRepairing)
{
PrintMessage(Message::mkWarning, "Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
eStatus = psRepairPossible;
}
else
{
PrintMessage(Message::mkWarning, "Par-check cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("par-check cancelled");
eStatus = psFailed;
}
}
else if (eStatus == psFailed)
{
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
{
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
}
PrintMessage(Message::mkError, "Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
Cleanup();
return eStatus;
}
int ParChecker::PreProcessPar()
{
Result res = eRepairFailed;
while (!IsStopped() && res != eSuccess)
{
Cleanup();
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 = pRepairer->PreProcess(m_szParFilename);
debug("ParChecker: PreProcess-result=%i", res);
if (IsStopped())
{
PrintMessage(Message::mkError, "Could not verify %s: stopping", m_szInfoName);
m_szErrMsg = strdup("par-check was stopped");
return eRepairFailed;
}
if (res == eInvalidCommandLineArguments)
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
return res;
}
if (res != eSuccess)
{
PrintMessage(Message::mkWarning, "Could not verify %s: par2-file could not be processed", m_szInfoName);
PrintMessage(Message::mkInfo, "Requesting more par2-files for %s", m_szInfoName);
bool bHasMorePars = LoadMainParBak();
if (!bHasMorePars)
{
PrintMessage(Message::mkWarning, "No more par2-files found");
break;
}
}
}
if (res != eSuccess)
{
PrintMessage(Message::mkError, "Could not verify %s: par2-file could not be processed", m_szInfoName);
m_szErrMsg = strdup("par2-file could not be processed");
return res;
}
return res;
}
bool ParChecker::LoadMainParBak()
{
while (!IsStopped())
{
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
if (hasMorePars)
{
return true;
}
int iBlockFound = 0;
bool requested = RequestMorePars(1, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_bQueuedParFilesChanged = false;
m_mutexQueuedParFiles.Unlock();
if (!requested && !hasMorePars)
{
return false;
}
if (!hasMorePars)
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool bQueuedParFilesChanged = false;
while (!bQueuedParFilesChanged && !IsStopped())
{
m_mutexQueuedParFiles.Lock();
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
m_mutexQueuedParFiles.Unlock();
usleep(100 * 1000);
}
}
}
return false;
}
int ParChecker::ProcessMorePars()
{
Result res = eRepairNotPossible;
Repairer* pRepairer = (Repairer*)m_pRepairer;
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
PrintMessage(Message::mkInfo, "Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
@@ -551,9 +237,9 @@ int ParChecker::ProcessMorePars()
if (!requested && !hasMorePars)
{
m_szErrMsg = (char*)malloc(1024);
snprintf(m_szErrMsg, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
m_szErrMsg[1024-1] = '\0';
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
BufReason[1024-1] = '\0';
m_szErrMsg = strdup(BufReason);
break;
}
@@ -580,33 +266,94 @@ int ParChecker::ProcessMorePars()
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
res = pRepairer->Process(commandLine, false);
debug("ParChecker: Process-result=%i", res);
}
}
return res;
if (IsStopped())
{
SetStatus(psFailed);
delete pRepairer;
return;
}
if (res == eSuccess)
{
info("Repair not needed for %s", m_szInfoName);
m_bRepairNotNeeded = true;
}
else if (res == eRepairPossible)
{
if (g_pOptions->GetParRepair())
{
info("Repairing %s", m_szInfoName);
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iProcessedFiles = 0;
m_eStage = ptRepairing;
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
UpdateProgress();
res = pRepairer->Process(commandLine, true);
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
info("Successfully repaired %s", m_szInfoName);
}
}
else
{
info("Repair possible for %s", m_szInfoName);
res = eSuccess;
}
}
if (m_bCancelled)
{
warn("Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
SetStatus(psFailed);
}
else if (res == eSuccess)
{
SetStatus(psFinished);
}
else
{
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
{
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
}
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
SetStatus(psFailed);
}
delete pRepairer;
}
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
FileList moreFiles;
QueuedParFiles moreFiles;
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
for (FileList::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
{
char* szParFilename = *it;
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
PrintMessage(Message::mkInfo, "File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
PrintMessage(Message::mkInfo, "Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
@@ -633,11 +380,11 @@ bool ParChecker::CheckSplittedFragments()
{
bool bFragmentsAdded = false;
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (AddSplittedFragments(sourcefile->TargetFileName().c_str()))
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
@@ -660,7 +407,7 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
szBasename[-1] = '\0';
int iBaseLen = strlen(szBasename);
std::list<CommandLine::ExtraFile> extrafiles;
list<CommandLine::ExtraFile> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
@@ -690,7 +437,7 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
if (!extrafiles.empty())
{
m_iExtraFiles += extrafiles.size();
m_iExtraFiles = extrafiles.size();
m_bVerifyingExtraFiles = true;
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
m_bVerifyingExtraFiles = false;
@@ -699,93 +446,6 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
return bFragmentsAdded;
}
bool ParChecker::AddMissingFiles()
{
PrintMessage(Message::mkInfo, "Performing extra par-scan for %s", m_szInfoName);
char szDirectory[1024];
strncpy(szDirectory, m_szParFilename, 1024);
szDirectory[1024-1] = '\0';
char* szBasename = Util::BaseFileName(szDirectory);
if (szBasename == szDirectory)
{
return false;
}
szBasename[-1] = '\0';
std::list<CommandLine::ExtraFile*> extrafiles;
DirBrowser dir(szDirectory);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && strcmp(filename, "_brokenlog.txt"))
{
bool bAlreadyScanned = false;
for (FileList::iterator it = m_ProcessedFiles.begin(); it != m_ProcessedFiles.end(); it++)
{
const char* szProcessedFilename = *it;
if (!strcasecmp(Util::BaseFileName(szProcessedFilename), filename))
{
bAlreadyScanned = true;
break;
}
}
if (!bAlreadyScanned)
{
char fullfilename[1024];
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
fullfilename[1024-1] = '\0';
extrafiles.push_back(new CommandLine::ExtraFile(fullfilename, Util::FileSize(fullfilename)));
}
}
}
// Sort the list
char* szBaseParFilename = strdup(Util::BaseFileName(m_szParFilename));
if (char* ext = strrchr(szBaseParFilename, '.')) *ext = '\0'; // trim extension
extrafiles.sort(MissingFilesComparator(szBaseParFilename));
free(szBaseParFilename);
// Scan files
bool bFilesAdded = false;
if (!extrafiles.empty())
{
m_iExtraFiles += extrafiles.size();
m_bVerifyingExtraFiles = true;
std::list<CommandLine::ExtraFile> extrafiles1;
// adding files one by one until all missing files are found
while (!IsStopped() && !m_bCancelled && extrafiles.size() > 0 && ((Repairer*)m_pRepairer)->missingfilecount > 0)
{
CommandLine::ExtraFile* pExtraFile = extrafiles.front();
extrafiles.pop_front();
extrafiles1.clear();
extrafiles1.push_back(*pExtraFile);
bFilesAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles1) || bFilesAdded;
((Repairer*)m_pRepairer)->UpdateVerificationResults();
delete pExtraFile;
}
m_bVerifyingExtraFiles = false;
// free any remaining objects
for (std::list<CommandLine::ExtraFile*>::iterator it = extrafiles.begin(); it != extrafiles.end() ;it++)
{
delete *it;
}
}
return bFilesAdded;
}
void ParChecker::signal_filename(std::string str)
{
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
@@ -795,12 +455,7 @@ void ParChecker::signal_filename(std::string str)
m_eStage = ptVerifyingRepaired;
}
PrintMessage(Message::mkInfo, "%s %s", szStageMessage[m_eStage], str.c_str());
if (m_eStage == ptLoadingPars || m_eStage == ptVerifyingSources)
{
m_ProcessedFiles.push_back(strdup(str.c_str()));
}
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';
@@ -865,7 +520,7 @@ void ParChecker::signal_done(std::string str, int available, int total)
{
bool bFileExists = true;
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
@@ -879,11 +534,11 @@ void ParChecker::signal_done(std::string str, int available, int total)
if (bFileExists)
{
PrintMessage(Message::mkWarning, "File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
}
else
{
PrintMessage(Message::mkWarning, "File %s with %i block(s) is missing", str.c_str(), total);
warn("File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
@@ -895,103 +550,8 @@ void ParChecker::Cancel()
((Repairer*)m_pRepairer)->cancelled = true;
m_bCancelled = true;
#else
PrintMessage(Message::mkError, "Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details).");
error("Could not cancel par-repair. The used version of libpar2 does not support the cancelling of par-repair. Libpar2 needs to be patched for that feature to work.");
#endif
}
void ParChecker::WriteBrokenLog(EStatus eStatus)
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_szDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (eStatus != psRepairNotNeeded || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (eStatus == psFailed)
{
if (m_bCancelled)
{
fprintf(file, "Repair cancelled for %s\n", m_szInfoName);
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
}
else if (eStatus == psRepairPossible)
{
fprintf(file, "Repair possible for %s\n", m_szInfoName);
}
else if (eStatus == psRepaired)
{
fprintf(file, "Successfully repaired %s\n", m_szInfoName);
}
else if (eStatus == psRepairNotNeeded)
{
fprintf(file, "Repair not needed for %s\n", m_szInfoName);
}
fclose(file);
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
}
void ParChecker::SaveSourceList()
{
// Buliding a list of DiskFile-objects, marked as source-files
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile* sourcefile = (Par2RepairerSourceFile*)*it;
vector<DataBlock>::iterator it2 = sourcefile->SourceBlocks();
for (int i = 0; i < (int)sourcefile->BlockCount(); i++, it2++)
{
DataBlock block = *it2;
DiskFile* pSourceFile = block.GetDiskFile();
if (pSourceFile &&
std::find(m_sourceFiles.begin(), m_sourceFiles.end(), pSourceFile) == m_sourceFiles.end())
{
m_sourceFiles.push_back(pSourceFile);
}
}
}
}
void ParChecker::DeleteLeftovers()
{
// After repairing check if all DiskFile-objects saved by "SaveSourceList()" have
// corresponding target-files. If not - the source file was replaced. In this case
// the DiskFile-object points to the renamed bak-file, which we can delete.
for (SourceList::iterator it = m_sourceFiles.begin(); it != m_sourceFiles.end(); it++)
{
DiskFile* pSourceFile = (DiskFile*)*it;
bool bFound = false;
for (std::vector<Par2RepairerSourceFile*>::iterator it2 = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it2 != ((Repairer*)m_pRepairer)->sourcefiles.end(); it2++)
{
Par2RepairerSourceFile* sourcefile = *it2;
if (sourcefile->GetTargetFile() == pSourceFile)
{
bFound = true;
break;
}
}
if (!bFound)
{
PrintMessage(Message::mkInfo, "Deleting file %s", Util::BaseFileName(pSourceFile->FileName().c_str()));
remove(pSourceFile->FileName().c_str());
}
}
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -29,20 +29,19 @@
#ifndef DISABLE_PARCHECK
#include <deque>
#include <string>
#include "Thread.h"
#include "Log.h"
#include "Observer.h"
class ParChecker : public Thread
class ParChecker : public Thread, public Subject
{
public:
enum EStatus
{
psUndefined,
psWorking,
psFailed,
psRepairPossible,
psRepaired,
psRepairNotNeeded
psFinished
};
enum EStage
@@ -53,22 +52,19 @@ public:
ptVerifyingRepaired,
};
typedef std::deque<char*> FileList;
typedef std::deque<void*> SourceList;
typedef std::deque<char*> QueuedParFiles;
private:
char* m_szInfoName;
char* m_szDestDir;
char* m_szNZBName;
const char* m_szParFilename;
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;
FileList m_QueuedParFiles;
bool m_bRepairNotNeeded;
QueuedParFiles m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
bool m_bQueuedParFilesChanged;
FileList m_ProcessedFiles;
int m_iProcessedFiles;
int m_iFilesToRepair;
int m_iExtraFiles;
@@ -77,20 +73,10 @@ private:
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
SourceList m_sourceFiles;
void Cleanup();
EStatus RunParCheck(const char* szParFilename);
int PreProcessPar();
bool LoadMainParBak();
int ProcessMorePars();
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
bool AddMissingFiles();
void WriteBrokenLog(EStatus eStatus);
void SaveSourceList();
void DeleteLeftovers();
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
@@ -103,8 +89,6 @@ protected:
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
@@ -114,12 +98,14 @@ public:
ParChecker();
virtual ~ParChecker();
virtual void Run();
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);
void SetNZBName(const char* szNZBName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
const char* GetErrMsg() { return m_szErrMsg; }
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
void Cancel();

View File

@@ -1,742 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include "nzbget.h"
#include "ParCoordinator.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "QueueCoordinator.h"
#include "DiskState.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
#ifndef DISABLE_PARCHECK
bool ParCoordinator::PostParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
{
return m_pOwner->RequestMorePars(m_pPostInfo->GetNZBInfo(), GetParFilename(), iBlockNeeded, pBlockFound);
}
void ParCoordinator::PostParChecker::UpdateProgress()
{
m_pOwner->UpdateParCheckProgress();
}
void ParCoordinator::PostParChecker::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_pOwner->UpdateParRenameProgress();
}
void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
#endif
ParCoordinator::ParCoordinator()
{
debug("Creating ParCoordinator");
#ifndef DISABLE_PARCHECK
m_bStopped = false;
m_ParChecker.m_pOwner = this;
m_ParRenamer.m_pOwner = this;
#endif
}
ParCoordinator::~ParCoordinator()
{
debug("Destroying ParCoordinator");
}
#ifndef DISABLE_PARCHECK
void ParCoordinator::Stop()
{
debug("Stopping ParCoordinator");
m_bStopped = true;
if (m_ParChecker.IsRunning())
{
m_ParChecker.Stop();
int iMSecWait = 5000;
while (m_ParChecker.IsRunning() && iMSecWait > 0)
{
usleep(50 * 1000);
iMSecWait -= 50;
}
if (m_ParChecker.IsRunning())
{
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
m_ParChecker.Kill();
}
}
}
#endif
void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("ParCoordinator: Pausing pars");
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
QueueEditor::eaGroupPauseExtraPars, 0, NULL);
break;
}
}
}
bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
{
if (pFileList)
{
pFileList->clear();
}
DirBrowser dir(szPath);
while (const char* filename = dir.Next())
{
int iBaseLen = 0;
if (ParseParFilename(filename, &iBaseLen, NULL))
{
if (!pFileList)
{
return true;
}
// check if the base file already added to list
bool exists = false;
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
const char* filename2 = *it;
exists = SameParCollection(filename, filename2);
if (exists)
{
break;
}
}
if (!exists)
{
pFileList->push_back(strdup(filename));
}
}
}
return pFileList && !pFileList->empty();
}
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
{
int iBaseLen1 = 0, iBaseLen2 = 0;
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
iBaseLen1 == iBaseLen2 &&
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
}
bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks)
{
char szFilename[1024];
strncpy(szFilename, szParFilename, 1024);
szFilename[1024-1] = '\0';
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
int iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
// find last occurence of ".par2" and trim filename after it
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
}
*(szFilename + iLen - 5) = '\0';
int blockcnt = 0;
char* p = strrchr(szFilename, '.');
if (p && !strncasecmp(p, ".vol", 4))
{
char* b = strchr(p, '+');
if (!b)
{
b = strchr(p, '-');
}
if (b)
{
blockcnt = atoi(b+1);
*p = '\0';
}
}
if (iBaseNameLen)
{
*iBaseNameLen = strlen(szFilename);
}
if (iBlocks)
{
*iBlocks = blockcnt;
}
return true;
}
#ifndef DISABLE_PARCHECK
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
{
m_eCurrentJob = jkParCheck;
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParChecker.SetNZBName(pPostInfo->GetNZBInfo()->GetName());
m_ParChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", pPostInfo->GetInfoName());
pPostInfo->SetWorking(true);
m_ParChecker.Start();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
{
const char* szDestDir = pPostInfo->GetNZBInfo()->GetDestDir();
char szFinalDir[1024];
if (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess)
{
pPostInfo->GetNZBInfo()->BuildFinalDirName(szFinalDir, 1024);
szFinalDir[1024-1] = '\0';
szDestDir = szFinalDir;
}
m_eCurrentJob = jkParRename;
m_ParRenamer.SetPostInfo(pPostInfo);
m_ParRenamer.SetDestDir(szDestDir);
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
m_ParRenamer.Start();
}
bool ParCoordinator::Cancel()
{
if (m_eCurrentJob == jkParCheck)
{
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName());
m_ParChecker.Cancel();
return true;
}
#else
warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName());
#endif
}
else if (m_eCurrentJob == jkParRename)
{
if (!m_ParRenamer.GetCancelled())
{
debug("Cancelling par-rename for %s", m_ParRenamer.GetInfoName());
m_ParRenamer.Cancel();
return true;
}
}
return false;
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
bool ParCoordinator::AddPar(FileInfo* pFileInfo, bool bDeleted)
{
bool bSameCollection = m_ParChecker.IsRunning() &&
pFileInfo->GetNZBInfo() == m_ParChecker.GetPostInfo()->GetNZBInfo() &&
SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(m_ParChecker.GetParFilename()));
if (bSameCollection && !bDeleted)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
szFullFilename[1024-1] = '\0';
m_ParChecker.AddParFile(szFullFilename);
if (g_pOptions->GetParPauseQueue())
{
PauseDownload();
}
}
else
{
m_ParChecker.QueueChanged();
}
return bSameCollection;
}
void ParCoordinator::ParCheckCompleted()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
// Update ParStatus (accumulate result)
if ((m_ParChecker.GetStatus() == ParChecker::psRepaired ||
m_ParChecker.GetStatus() == ParChecker::psRepairNotNeeded) &&
pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
else if (m_ParChecker.GetStatus() == ParChecker::psRepairPossible &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
else
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
/**
* 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 ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
Blocks blocks;
blocks.clear();
int iBlockFound = 0;
int iCurBlockFound = 0;
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, true, &iCurBlockFound);
iBlockFound += iCurBlockFound;
if (iBlockFound < iBlockNeeded)
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound < iBlockNeeded)
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, false, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound >= iBlockNeeded)
{
// 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())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
pBestBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
blocks.remove(pBestBlockInfo);
delete pBestBlockInfo;
}
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())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
pBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
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();
bool bOK = iBlockNeeded <= 0;
if (bOK && g_pOptions->GetParPauseQueue())
{
UnpauseDownload();
}
return bOK;
}
void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound)
{
*pBlockFound = 0;
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
char* szBaseParFilename = Util::BaseFileName(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 (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
int iBlocks = 0;
if (pFileInfo->GetNZBInfo() == pNZBInfo &&
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
iBlocks > 0)
{
bool bUseFile = true;
if (bExactParName)
{
bUseFile = SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
}
else 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';
bUseFile = strstr(szLoFileName, szCandidateFileName);
}
}
bool bAlreadyAdded = false;
// check if file is not in the list already
if (bUseFile)
{
for (Blocks::iterator it = pBlocks->begin(); it != pBlocks->end(); it++)
{
BlockInfo* pBlockInfo = *it;
if (pBlockInfo->m_pFileInfo == pFileInfo)
{
bAlreadyAdded = true;
break;
}
}
}
// 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
if (bUseFile && !bAlreadyAdded)
{
BlockInfo* pBlockInfo = new BlockInfo();
pBlockInfo->m_pFileInfo = pFileInfo;
pBlockInfo->m_iBlockCount = iBlocks;
pBlocks->push_back(pBlockInfo);
*pBlockFound += iBlocks;
}
}
}
}
void ParCoordinator::UpdateParCheckProgress()
{
g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
if (m_ParChecker.GetFileProgress() == 0)
{
pPostInfo->SetProgressLabel(m_ParChecker.GetProgressLabel());
}
pPostInfo->SetFileProgress(m_ParChecker.GetFileProgress());
pPostInfo->SetStageProgress(m_ParChecker.GetStageProgress());
PostInfo::EStage StageKind[] = { PostInfo::ptLoadingPars, PostInfo::ptVerifyingSources, PostInfo::ptRepairing, PostInfo::ptVerifyingRepaired };
PostInfo::EStage eStage = StageKind[m_ParChecker.GetStage()];
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != eStage)
{
pPostInfo->SetStage(eStage);
pPostInfo->SetStageTime(tCurrent);
}
bool bParCancel = false;
#ifdef HAVE_PAR2_CANCEL
if (!m_ParChecker.GetCancelled())
{
if ((g_pOptions->GetParTimeLimit() > 0) &&
m_ParChecker.GetStage() == ParChecker::ptRepairing &&
((g_pOptions->GetParTimeLimit() > 5 && tCurrent - pPostInfo->GetStageTime() > 5 * 60) ||
(g_pOptions->GetParTimeLimit() <= 5 && tCurrent - pPostInfo->GetStageTime() > 1 * 60)))
{
// first five (or one) minutes elapsed, now can check the estimated time
int iEstimatedRepairTime = (int)((tCurrent - pPostInfo->GetStartTime()) * 1000 /
(pPostInfo->GetStageProgress() > 0 ? pPostInfo->GetStageProgress() : 1));
if (iEstimatedRepairTime > g_pOptions->GetParTimeLimit() * 60)
{
debug("Estimated repair time %i seconds", iEstimatedRepairTime);
m_ParChecker.PrintMessage(Message::mkWarning, "Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
bParCancel = true;
}
}
}
#endif
if (bParCancel)
{
m_ParChecker.Cancel();
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
{
if (g_pOptions->GetPausePostProcess())
{
time_t tStageTime = pPostInfo->GetStageTime();
time_t tStartTime = pPostInfo->GetStartTime();
time_t tWaitTime = time(NULL);
// wait until Post-processor is unpaused
while (g_pOptions->GetPausePostProcess() && !m_bStopped)
{
usleep(100 * 1000);
// update time stamps
time_t tDelta = time(NULL) - tWaitTime;
if (tStageTime > 0)
{
pPostInfo->SetStageTime(tStageTime + tDelta);
}
if (tStartTime > 0)
{
pPostInfo->SetStartTime(tStartTime + tDelta);
}
}
}
}
void ParCoordinator::ParRenameCompleted()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->GetNZBInfo()->SetRenameStatus(m_ParRenamer.GetStatus() == ParRenamer::psSuccess ? NZBInfo::rsSuccess : NZBInfo::rsFailure);
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
void ParCoordinator::UpdateParRenameProgress()
{
g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->SetProgressLabel(m_ParRenamer.GetProgressLabel());
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
time_t tCurrent = time(NULL);
if (!pPostInfo->GetStartTime())
{
pPostInfo->SetStartTime(tCurrent);
}
if (pPostInfo->GetStage() != PostInfo::ptRenaming)
{
pPostInfo->SetStage(PostInfo::ptRenaming);
pPostInfo->SetStageTime(tCurrent);
}
g_pQueueCoordinator->UnlockQueue();
CheckPauseState(pPostInfo);
}
void ParCoordinator::PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
pPostInfo->AppendMessage(eKind, szText);
switch (eKind)
{
case Message::mkDetail:
detail("%s", szText);
break;
case Message::mkInfo:
info("%s", szText);
break;
case Message::mkWarning:
warn("%s", szText);
break;
case Message::mkError:
error("%s", szText);
break;
case Message::mkDebug:
debug("%s", szText);
break;
}
}
#endif

View File

@@ -1,130 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARCOORDINATOR_H
#define PARCOORDINATOR_H
#include <list>
#include <deque>
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#include "ParRenamer.h"
#endif
class ParCoordinator
{
private:
#ifndef DISABLE_PARCHECK
class PostParChecker: public ParChecker
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParCheckCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
class PostParRenamer: public ParRenamer
{
private:
ParCoordinator* m_pOwner;
PostInfo* m_pPostInfo;
protected:
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParRenameCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class ParCoordinator;
};
struct BlockInfo
{
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
enum EJobKind
{
jkParCheck,
jkParRename
};
private:
PostParChecker m_ParChecker;
bool m_bStopped;
PostParRenamer m_ParRenamer;
EJobKind m_eCurrentJob;
protected:
virtual bool PauseDownload() = 0;
virtual bool UnpauseDownload() = 0;
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void ParCheckCompleted();
void ParRenameCompleted();
void CheckPauseState(PostInfo* pPostInfo);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...);
#endif
public:
typedef std::deque<char*> FileList;
public:
ParCoordinator();
virtual ~ParCoordinator();
static bool FindMainPars(const char* szPath, FileList* pFileList);
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void StartParCheckJob(PostInfo* pPostInfo);
void StartParRenameJob(PostInfo* pPostInfo);
void Stop();
bool Cancel();
#endif
};
#endif

View File

@@ -1,345 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#ifndef DISABLE_PARCHECK
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
#include <md5.h>
#else
#include <unistd.h>
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#include <libpar2/md5.h>
#endif
#include "nzbget.h"
#include "ParRenamer.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
class ParRenamerRepairer : public Par2Repairer
{
public:
friend class ParRenamer;
};
ParRenamer::FileHash::FileHash(const char* szFilename, const char* szHash)
{
m_szFilename = strdup(szFilename);
m_szHash = strdup(szHash);
}
ParRenamer::FileHash::~FileHash()
{
free(m_szFilename);
free(m_szHash);
}
ParRenamer::ParRenamer()
{
debug("Creating ParRenamer");
m_eStatus = psFailed;
m_szDestDir = NULL;
m_szInfoName = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_iStageProgress = 0;
m_bCancelled = false;
}
ParRenamer::~ParRenamer()
{
debug("Destroying ParRenamer");
free(m_szDestDir);
free(m_szInfoName);
free(m_szProgressLabel);
Cleanup();
}
void ParRenamer::Cleanup()
{
ClearHashList();
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
{
free(*it);
}
m_DirList.clear();
}
void ParRenamer::ClearHashList()
{
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
{
delete *it;
}
m_FileHashList.clear();
}
void ParRenamer::SetDestDir(const char * szDestDir)
{
free(m_szDestDir);
m_szDestDir = strdup(szDestDir);
}
void ParRenamer::SetInfoName(const char * szInfoName)
{
free(m_szInfoName);
m_szInfoName = strdup(szInfoName);
}
void ParRenamer::Cancel()
{
m_bCancelled = true;
}
void ParRenamer::Run()
{
Cleanup();
m_bCancelled = false;
m_iFileCount = 0;
m_iCurFile = 0;
m_iRenamedCount = 0;
m_eStatus = psFailed;
snprintf(m_szProgressLabel, 1024, "Checking renamed files for %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = 0;
UpdateProgress();
BuildDirList(m_szDestDir);
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
{
char* szDestDir = *it;
debug("Checking %s", szDestDir);
ClearHashList();
LoadParFiles(szDestDir);
CheckFiles(szDestDir);
}
if (m_bCancelled)
{
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", m_szInfoName);
}
else if (m_iRenamedCount > 0)
{
PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
m_eStatus = psSuccess;
}
else
{
PrintMessage(Message::mkInfo, "No renamed files found for %s", m_szInfoName);
}
Cleanup();
Completed();
}
void ParRenamer::BuildDirList(const char* szDestDir)
{
m_DirList.push_back(strdup(szDestDir));
char* szFullFilename = (char*)malloc(1024);
DirBrowser* pDirBrowser = new DirBrowser(szDestDir);
while (const char* filename = pDirBrowser->Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (Util::DirectoryExists(szFullFilename))
{
BuildDirList(szFullFilename);
}
else
{
m_iFileCount++;
}
}
}
free(szFullFilename);
delete pDirBrowser;
}
void ParRenamer::LoadParFiles(const char* szDestDir)
{
ParCoordinator::FileList parFileList;
ParCoordinator::FindMainPars(szDestDir, &parFileList);
for (ParCoordinator::FileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
{
char* szParFilename = *it;
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
LoadParFile(szFullParFilename);
free(*it);
}
}
void ParRenamer::LoadParFile(const char* szParFilename)
{
ParRenamerRepairer* pRepairer = new ParRenamerRepairer();
if (!pRepairer->LoadPacketsFromFile(szParFilename))
{
PrintMessage(Message::mkWarning, "Could not load par2-file %s", szParFilename);
delete pRepairer;
return;
}
for (map<MD5Hash, Par2RepairerSourceFile*>::iterator it = pRepairer->sourcefilemap.begin(); it != pRepairer->sourcefilemap.end(); it++)
{
if (m_bCancelled)
{
break;
}
Par2RepairerSourceFile* sourceFile = (*it).second;
m_FileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
sourceFile->GetDescriptionPacket()->Hash16k().print().c_str()));
}
delete pRepairer;
}
void ParRenamer::CheckFiles(const char* szDestDir)
{
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (!Util::DirectoryExists(szFullFilename))
{
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = m_iCurFile * 1000 / m_iFileCount;
UpdateProgress();
m_iCurFile++;
CheckFile(szDestDir, szFullFilename);
}
}
}
}
void ParRenamer::CheckFile(const char* szDestDir, const char* szFilename)
{
debug("Computing hash for %s", szFilename);
const int iBlockSize = 16*1024;
FILE* pFile = fopen(szFilename, "rb");
if (!pFile)
{
PrintMessage(Message::mkError, "Could not open file %s", szFilename);
return;
}
// load first 16K of the file into buffer
void* pBuffer = malloc(iBlockSize);
int iReadBytes = fread(pBuffer, 1, iBlockSize, pFile);
int iError = ferror(pFile);
if (iReadBytes != iBlockSize && iError)
{
PrintMessage(Message::mkError, "Could not read file %s", szFilename);
return;
}
fclose(pFile);
MD5Hash hash16k;
MD5Context context;
context.Update(pBuffer, iReadBytes);
context.Final(hash16k);
free(pBuffer);
debug("file: %s; hash16k: %s", Util::BaseFileName(szFilename), hash16k.print().c_str());
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
{
FileHash* pFileHash = *it;
if (!strcmp(pFileHash->GetHash(), hash16k.print().c_str()))
{
debug("Found correct filename: %s", pFileHash->GetFilename());
char szDstFilename[1024];
snprintf(szDstFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
szDstFilename[1024-1] = '\0';
if (!Util::FileExists(szDstFilename))
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
if (Util::MoveFile(szFilename, szDstFilename))
{
m_iRenamedCount++;
}
else
{
PrintMessage(Message::mkError, "Could not rename %s to %s", szFilename, szDstFilename);
}
}
break;
}
}
}
#endif

View File

@@ -1,106 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef PARRENAMER_H
#define PARRENAMER_H
#ifndef DISABLE_PARCHECK
#include <deque>
#include "Thread.h"
#include "Log.h"
class ParRenamer : public Thread
{
public:
enum EStatus
{
psFailed,
psSuccess
};
class FileHash
{
private:
char* m_szFilename;
char* m_szHash;
public:
FileHash(const char* szFilename, const char* szHash);
~FileHash();
const char* GetFilename() { return m_szFilename; }
const char* GetHash() { return m_szHash; }
};
typedef std::deque<FileHash*> FileHashList;
typedef std::deque<char*> DirList;
private:
char* m_szInfoName;
char* m_szDestDir;
EStatus m_eStatus;
char* m_szProgressLabel;
int m_iStageProgress;
bool m_bCancelled;
DirList m_DirList;
FileHashList m_FileHashList;
int m_iFileCount;
int m_iCurFile;
int m_iRenamedCount;
void Cleanup();
void ClearHashList();
void BuildDirList(const char* szDestDir);
void CheckDir(const char* szDestDir);
void LoadParFiles(const char* szDestDir);
void LoadParFile(const char* szParFilename);
void CheckFiles(const char* szDestDir);
void CheckFile(const char* szDestDir, const char* szFilename);
protected:
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetStageProgress() { return m_iStageProgress; }
public:
ParRenamer();
virtual ~ParRenamer();
virtual void Run();
void SetDestDir(const char* szDestDir);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
void Cancel();
bool GetCancelled() { return m_bCancelled; }
};
#endif
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -31,13 +31,15 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "ParCoordinator.h"
#include "DupeCoordinator.h"
#include "Scanner.h"
#ifndef DISABLE_PARCHECK
#include "ParChecker.h"
#endif
class PrePostProcessor : public Thread
{
public:
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
enum EEditAction
{
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
@@ -45,91 +47,104 @@ public:
eaPostMoveBottom,
eaPostDelete,
eaHistoryDelete,
eaHistoryFinalDelete,
eaHistoryReturn,
eaHistoryProcess,
eaHistoryRedownload,
eaHistorySetParameter,
eaHistorySetDupeKey,
eaHistorySetDupeScore,
eaHistorySetDupeMode,
eaHistorySetDupeBackup,
eaHistoryMarkBad,
eaHistoryMarkGood
eaHistoryProcess
};
private:
typedef std::deque<char*> FileList;
class QueueCoordinatorObserver: public Observer
{
public:
PrePostProcessor* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->QueueCoordinatorUpdate(Caller, Aspect); }
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
};
class PostParCoordinator: public ParCoordinator
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
{
public:
PrePostProcessor* owner;
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
{
private:
PrePostProcessor* m_pOwner;
PrePostProcessor* m_Owner;
PostInfo* m_pPostInfo;
protected:
virtual bool PauseDownload() { return m_pOwner->PauseDownload(); }
virtual bool UnpauseDownload() { return m_pOwner->UnpauseDownload(); }
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
friend class PrePostProcessor;
};
class PostDupeCoordinator: public DupeCoordinator
struct BlockInfo
{
private:
PrePostProcessor* m_pOwner;
protected:
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
virtual void DeleteQueuedFile(const char* szQueuedFile) { m_pOwner->DeleteQueuedFile(szQueuedFile); }
friend class PrePostProcessor;
FileInfo* m_pFileInfo;
int m_iBlockCount;
};
typedef std::list<BlockInfo*> Blocks;
#endif
private:
PostParCoordinator m_ParCoordinator;
PostDupeCoordinator m_DupeCoordinator;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
const char* m_szPauseReason;
Scanner m_Scanner;
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bAllowOnlyOneDeleted);
bool bIgnoreFirstInPostQueue, bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
void ApplySchedulerState();
void CheckScheduledResume();
void UpdatePauseState(bool bNeedPause, const char* szReason);
bool PauseDownload();
bool UnpauseDownload();
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool FindMainPars(const char* szPath, FileList* pFileList);
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
bool SameParCollection(const char* szFilename1, const char* szFilename2);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bPostScript, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
int FindGroupID(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
bool PostQueueDelete(IDList* pIDList);
bool HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bFinal);
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bRestorePauseState);
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
void HistorySetDupeParam(HistoryInfo* pHistoryInfo, EEditAction eAction, const char* szText);
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
void CheckHistory();
bool HistoryDelete(IDList* pIDList);
bool HistoryReturn(IDList* pIDList, bool bReprocess);
void Cleanup();
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void DeletePostThread(PostInfo* pPostInfo);
void CheckHistory();
#ifndef DISABLE_PARCHECK
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
void ParCheckerUpdate(Subject* Caller, void* Aspect);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void UpdateParProgress();
void StartParJob(PostInfo* pPostInfo);
#endif
public:
PrePostProcessor();
virtual ~PrePostProcessor();
@@ -137,7 +152,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void ScanNZBDir();
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -43,15 +43,12 @@ class QueueCoordinator : public Thread, public Observer, public Subject, public
{
public:
typedef std::list<ArticleDownloader*> ActiveDownloads;
enum EAspectAction
{
eaNZBFileFound,
eaNZBFileAdded,
eaFileCompleted,
eaFileDeleted
};
struct Aspect
{
EAspectAction eAction;
@@ -66,8 +63,6 @@ private:
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
int m_iDownloadsLimit;
int m_iServerConfigGeneration;
// statistics
static const int SPEEDMETER_SLOTS = 30;
@@ -76,12 +71,6 @@ private:
int m_iSpeedTotalBytes;
int m_iSpeedTime[SPEEDMETER_SLOTS];
int m_iSpeedStartTime;
time_t m_tSpeedCorrection;
#ifdef HAVE_SPINLOCK
SpinLock m_spinlockSpeed;
#else
Mutex m_mutexSpeed;
#endif
int m_iSpeedBytesIndex;
long long m_iAllBytes;
@@ -94,15 +83,14 @@ private:
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void StatFileInfo(FileInfo* pFileInfo, bool bCompleted);
void CheckHealth(FileInfo* pFileInfo);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
void AdjustStartTime();
void AdjustDownloadsLimit();
public:
QueueCoordinator();
@@ -113,7 +101,7 @@ public:
// statistics
long long CalcRemainingSize();
virtual int CalcCurrentDownloadSpeed();
virtual float CalcCurrentDownloadSpeed();
virtual void AddSpeedReading(int iBytes);
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
@@ -121,14 +109,11 @@ public:
DownloadQueue* LockQueue();
void UnlockQueue() ;
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
void AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFileQueue, bool bAddFirst);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
bool SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo);
void DiscardDiskFile(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -33,10 +33,9 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#include <set>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
@@ -111,24 +110,17 @@ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
}
/*
* Removes entry
* Removes entry with index iEntry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
{
if (pFileInfo->GetNZBInfo()->GetDeleting())
{
detail("Deleting file %s from download queue", pFileInfo->GetFilename());
}
else
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
}
info("Deleting file %s from download queue", pFileInfo->GetFilename());
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
}
/*
* Moves entry in the queue
* Moves entry identified with iID in the queue
* returns true if successful, false if operation is not possible
*/
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
@@ -156,23 +148,12 @@ void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
}
}
/*
* Set priority for entry
* returns true if successful, false if operation is not possible
*/
void QueueEditor::SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority)
{
debug("Setting priority %s for file %s", szPriority, pFileInfo->GetFilename());
int iPriority = atoi(szPriority);
pFileInfo->SetPriority(iPriority);
}
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
IDList cIDList;
cIDList.clear();
cIDList.push_back(ID);
return EditList(&cIDList, NULL, mmID, bSmartOrder, eAction, iOffset, szText);
return EditList(&cIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
@@ -183,20 +164,11 @@ bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bS
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
}
bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder,
EEditAction eAction, int iOffset, const char* szText)
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
bool bOK = true;
if (pNameList)
{
pIDList = new IDList();
bOK = BuildIDListFromNameList(pDownloadQueue, pIDList, pNameList, eMatchMode, eAction);
}
bOK = bOK && (InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText) || eMatchMode == mmRegEx);
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
@@ -205,11 +177,6 @@ bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMat
g_pQueueCoordinator->UnlockQueue();
if (pNameList)
{
delete pIDList;
}
return bOK;
}
@@ -228,93 +195,66 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
switch (eAction)
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
{
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
break;
case eaGroupMerge:
return MergeGroups(pDownloadQueue, &cItemList);
case eaFileSplit:
return SplitGroup(pDownloadQueue, &cItemList, szText);
case eaFileReorder:
ReorderFiles(pDownloadQueue, &cItemList);
break;
default:
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
}
else if (eAction == eaGroupMerge)
{
MergeGroups(pDownloadQueue, &cItemList);
}
else
{
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
switch (eAction)
{
EditItem* pItem = *it;
switch (eAction)
{
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaFileSetPriority:
SetPriorityEntry(pItem->m_pFileInfo, szText);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetName:
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
break;
case eaGroupSetDupeKey:
case eaGroupSetDupeScore:
case eaGroupSetDupeMode:
SetNZBDupeParam(pItem->m_pFileInfo->GetNZBInfo(), eAction, szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupDupeDelete:
case eaGroupFinalDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
case eaGroupSetPriority:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
case eaFileSplit:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
}
}
return cItemList.size() > 0;
@@ -425,80 +365,10 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
}
}
bool QueueEditor::BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction)
{
#ifndef HAVE_REGEX_H
if (eMatchMode == mmRegEx)
{
return false;
}
#endif
std::set<int> uniqueIDs;
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
{
const char* szName = *it;
RegEx *pRegEx = NULL;
if (eMatchMode == mmRegEx)
{
pRegEx = new RegEx(szName);
if (!pRegEx->IsValid())
{
delete pRegEx;
return false;
}
}
bool bFound = false;
for (FileQueue::iterator it2 = pDownloadQueue->GetFileQueue()->begin(); it2 != pDownloadQueue->GetFileQueue()->end(); it2++)
{
FileInfo* pFileInfo = *it2;
if (eAction < eaGroupMoveOffset)
{
// file action
char szFilename[MAX_PATH];
snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename()));
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
(uniqueIDs.find(pFileInfo->GetID()) == uniqueIDs.end()))
{
uniqueIDs.insert(pFileInfo->GetID());
pIDList->push_back(pFileInfo->GetID());
bFound = true;
}
}
else
{
// group action
const char *szFilename = pFileInfo->GetNZBInfo()->GetName();
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
(uniqueIDs.find(pFileInfo->GetNZBInfo()->GetID()) == uniqueIDs.end()))
{
uniqueIDs.insert(pFileInfo->GetNZBInfo()->GetID());
pIDList->push_back(pFileInfo->GetID());
bFound = true;
}
}
}
delete pRegEx;
if (!bFound && (eMatchMode == mmName))
{
return false;
}
}
return true;
}
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText)
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
{
IDList cIDList;
cIDList.clear();
bool bAllPaused = true;
// collecting files belonging to group
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
@@ -507,7 +377,6 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
bAllPaused &= pFileInfo2->GetPaused();
}
}
@@ -556,25 +425,16 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
}
iOffset = iFileOffset;
}
else if (eAction == eaGroupDelete || eAction == eaGroupDupeDelete || eAction == eaGroupFinalDelete)
else if (eAction == eaGroupDelete)
{
pFileInfo->GetNZBInfo()->SetDeleting(true);
pFileInfo->GetNZBInfo()->SetAvoidHistory(eAction == eaGroupFinalDelete);
pFileInfo->GetNZBInfo()->SetDeletePaused(bAllPaused);
if (eAction == eaGroupDupeDelete)
{
pFileInfo->GetNZBInfo()->SetDeleteStatus(NZBInfo::dsDupe);
}
pFileInfo->GetNZBInfo()->SetDeleted(true);
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause,
eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder, eaFileSplit,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFileDelete, eaFileDelete,
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, (EEditAction)0, (EEditAction)0, (EEditAction)0 };
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, szText);
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, NULL);
}
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
@@ -649,7 +509,7 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
}
if (iOffset > 0)
{
for (int i = iNum + 1; i <= (int)cGroupList.size() - iOffset; i++)
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
@@ -673,11 +533,11 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
{
FileInfo* pFileInfo = *it;
AlignGroup(pDownloadQueue, pFileInfo->GetNZBInfo());
AlignGroup(pDownloadQueue, pFileInfo);
}
}
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
{
FileInfo* pLastFileInfo = NULL;
unsigned int iLastNum = 0;
@@ -685,7 +545,7 @@ void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
while (iNum < pDownloadQueue->GetFileQueue()->size())
{
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
if (pFileInfo->GetNZBInfo() == pNZBInfo)
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
if (pLastFileInfo && iNum - iLastNum > 1)
{
@@ -827,15 +687,8 @@ void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
g_pQueueCoordinator->SetQueueEntryNZBCategory(pNZBInfo, szCategory);
}
void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName)
{
debug("QueueEditor: renaming '%s' to '%s'", Util::BaseFileName(pNZBInfo->GetFilename()), szName);
g_pQueueCoordinator->SetQueueEntryNZBName(pNZBInfo, szName);
}
/**
* Check if deletion of already downloaded files is possible (when nzb is deleted from queue).
* Check if deletion of already downloaded files is possible (when nzb id deleted from queue).
* The deletion is most always possible, except the case if all remaining files in queue
* (belonging to this nzb-file) are PARS.
*/
@@ -844,33 +697,28 @@ bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInf
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
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 szLoFileName[1024];
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
szLoFileName[1024-1] = '\0';
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
if (!strstr(szLoFileName, ".par2"))
{
// non-par file found
return true;
}
if (!strstr(szLoFileName, ".par2"))
{
// non-par file found
return true;
}
}
return false;
}
bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return false;
return;
}
bool bOK = true;
EditItem* pDestItem = pItemList->front();
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
@@ -879,90 +727,23 @@ bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
{
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
if (g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo()))
{
bOK = false;
}
g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo());
}
delete pItem;
}
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
delete pDestItem;
return bOK;
}
bool QueueEditor::SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName)
{
if (pItemList->size() == 0)
// align group ("AlignGroup" needs the first file item as parameter)
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
return false;
}
FileQueue* pFileList = new FileQueue();
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
{
EditItem* pItem = *it;
pFileList->push_back(pItem->m_pFileInfo);
delete pItem;
}
NZBInfo* pNewNZBInfo = NULL;
bool bOK = g_pQueueCoordinator->SplitQueueEntries(pFileList, szName, &pNewNZBInfo);
if (bOK)
{
AlignGroup(pDownloadQueue, pNewNZBInfo);
}
delete pFileList;
return bOK;
}
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return;
}
EditItem* pFirstItem = pItemList->front();
NZBInfo* pNZBInfo = pFirstItem->m_pFileInfo->GetNZBInfo();
unsigned int iInsertPos = 0;
// find first file of the group
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pDestItem->m_pFileInfo->GetNZBInfo())
{
AlignGroup(pDownloadQueue, pFileInfo);
break;
}
iInsertPos++;
}
// now can reorder
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
{
EditItem* pItem = *it;
FileInfo* pFileInfo = pItem->m_pFileInfo;
// move file item
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo1 = *it;
if (pFileInfo1 == pFileInfo)
{
pDownloadQueue->GetFileQueue()->erase(it);
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iInsertPos, pFileInfo);
iInsertPos++;
break;
}
}
delete pItem;
}
delete pDestItem;
}
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
@@ -976,56 +757,12 @@ void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
{
*szValue = '\0';
szValue++;
pNZBInfo->GetParameters()->SetParameter(szStr, szValue);
pNZBInfo->SetParameter(szStr, szValue);
}
else
{
error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString);
error("Could not set nzb parameter for %s: invalid argument: %s", Util::BaseFileName(pNZBInfo->GetFilename()), szParamString);
}
free(szStr);
}
void QueueEditor::SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText)
{
debug("QueueEditor: setting dupe parameter %i='%s' for '%s'", (int)eAction, szText, pNZBInfo->GetName());
switch (eAction)
{
case eaGroupSetDupeKey:
pNZBInfo->SetDupeKey(szText);
break;
case eaGroupSetDupeScore:
pNZBInfo->SetDupeScore(atoi(szText));
break;
case eaGroupSetDupeMode:
{
EDupeMode eMode = dmScore;
if (!strcasecmp(szText, "SCORE"))
{
eMode = dmScore;
}
else if (!strcasecmp(szText, "ALL"))
{
eMode = dmAll;
}
else if (!strcasecmp(szText, "FORCE"))
{
eMode = dmForce;
}
else
{
error("Could not set duplicate mode for %s: incorrect mode (%s)", pNZBInfo->GetName(), szText);
return;
}
pNZBInfo->SetDupeMode(eMode);
break;
}
default:
// suppress compiler warning
break;
}
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
* This file if part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -33,7 +33,6 @@
class QueueEditor
{
public:
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
enum EEditAction
{
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
@@ -44,34 +43,17 @@ public:
eaFileDelete,
eaFilePauseAllPars,
eaFilePauseExtraPars,
eaFileSetPriority,
eaFileReorder,
eaFileSplit,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
eaGroupPause,
eaGroupResume,
eaGroupDelete,
eaGroupDupeDelete,
eaGroupFinalDelete,
eaGroupPauseAllPars,
eaGroupPauseExtraPars,
eaGroupSetPriority,
eaGroupSetCategory,
eaGroupMerge,
eaGroupSetParameter,
eaGroupSetName,
eaGroupSetDupeKey,
eaGroupSetDupeScore,
eaGroupSetDupeMode
};
enum EMatchMode
{
mmID = 1,
mmName,
mmRegEx
eaGroupSetParameter
};
private:
@@ -92,34 +74,28 @@ private:
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
bool BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction);
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText);
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
void AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
void PausePars(FileList* pFileList, bool bExtraParsOnly);
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
bool SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName);
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
void SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText);
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
void DeleteEntry(FileInfo* pFileInfo);
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
void SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority);
public:
QueueEditor();
~QueueEditor();
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
bool LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);

137
README
View File

@@ -2,10 +2,6 @@
NZBGet ReadMe
=====================================
This is a short documentation. For more information please
visit NZBGet home page at
http://nzbget.sourceforge.net
Contents
--------
1. About NZBGet
@@ -33,9 +29,6 @@ In server/client mode NZBGet runs as server in background.
Then you use client to send requests to server. The sample requests
are: download nzb-file, list files in queue, etc.
There is also a built-in web-interface. The server has RPC-support
and can be controlled from third party applications too.
Standalone-tool, server and client are all contained in only one
executable file "nzbget". The mode in which the program works
depends on command-line parameters passed to the program.
@@ -48,12 +41,13 @@ NZBGet is written in C++ and was initialy developed on Linux.
It was ported to Windows later and tested for compatibility with
several POSIX-OS'es.
It should run at least on:
- Linux Debian 5.0 on x86;
- Linux with uClibc on MIPSEL and ARM;
- OpenBSD 5.0 on x86;
- Mac OS X 10.7 Lion on x64;
- Windows XP SP3 on x86 and Windows 7 on x64.
The current version (0.7.0) should run at least on:
- Linux Debian 4.0 on x86;
- Linux with uClibc and BusyBox on MIPSEL and ARM;
- FreeBSD 7.0 on x86;
- OpenSolaris 2008.11 on x86;
- Mac OS X;
- Windows XP SP2 on x86.
Clients and servers running on different OS'es may communicate with
each other. For example, you can use NZBGet as client on Windows to
@@ -95,9 +89,6 @@ And the following libraries are optional:
or
- OpenSSL (http://www.openssl.org)
- for gzip support in web-server and web-client (enabled by default):
- zlib (http://www.zlib.net)
All these libraries are included in modern Linux distributions and
should be available as installable packages. Please note that you also
need the developer packages for these libraries too, they package names
@@ -108,41 +99,24 @@ download the libraries at the given URLs and compile them (see hints below).
4. Installation on POSIX
=====================================
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
Well, the usual stuff:
- untar the nzbget-source via
tar -zxf nzbget-VERSION.tar.gz
- change into nzbget-directory via
cd nzbget-VERSION
- configure it via
./configure
(maybe you have to tell configure, where to find some libraries.
./configure --help is your friend!
also see "Configure-options" later)
- in a case you don't have root access or want to install the program
in your home directory use the configure parameter --prefix, e. g.:
./configure --prefix ~/usr
./configure --help is your friend!)
also see "Configure-options" later.)
- compile it via
make
- to install system wide become root via:
- become root via
su
- install it via:
- install it via
make install
- install configuration files into <prefix>/etc via:
make install-conf
(you can skip this step if you intend to store configuration
files in a non-standard location)
Configure-options
-----------------
You may run configure with additional arguments:
@@ -158,9 +132,6 @@ You may run configure with additional arguments:
--disable-tls - to make without TLS/SSL support. Use this option if
you can not neither GnuTLS nor OpenSSL.
--disable-gzip - to make without gzip support. Use this option
if you can not use zlib.
--enable-debug - to build in debug-mode, if you want to see and log
debug-messages.
@@ -274,27 +245,17 @@ in MS Visual C++ 2005 you should be able to compile NZBGet.
6. Configuration
=====================================
NZBGet needs a configuration file.
NZBGet needs a configuration-file to work properly.
An example configuration file is provided in "nzbget.conf", which
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
system configuration and configure options - typically "/usr/local",
"/usr" or "/opt"). The installer adjusts the file according to your
system paths. If you have performed the installation step
"make install-conf" this file is already copied to "<prefix>/etc" and
NZBGet finds it automatically. If you install the program manually
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
to one of the locations listed below.
Open the file in a text editor and modify it accodring to your needs.
You need to set at least the option "MAINDIR" and one news server in
configuration file. The file has comments on how to use each option.
You need to set at least the option "MAINDIR" and one newsserver in
configuration file. Have a look at the example in nzbget.conf.example,
it has comments on how to use each option.
The program looks for configuration file in following standard
locations (in this order):
On POSIX systems:
~/.nzbget
/etc/nzbget.conf
/usr/etc/nzbget.conf
@@ -422,7 +383,7 @@ Running client & server on seperate machines:
Since nzbget communicates via TCP/IP it's possible to have a server running on
one computer and adding downloads via a client on another computer.
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
Do this by setting the "serverip" option in the nzbget.conf file to point to the
IP of the server (default is localhost which means client and server runs on
same computer)
@@ -441,58 +402,28 @@ nzbget-client-commands in this terminal.
Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-processing
scripts, defined in configuration file.
After the download of nzb-file is completed nzbget can call post-process-script,
defined in configuration file. See example configuration file for the
description of parameters passed to the script.
Example post-processing scripts are provided in directory "ppscripts".
To use the scripts copy them into your local directory and set options
<ScriptDir>, <DefScript> and <ScriptOrder>.
For information on writing your own post-processing scripts please
visit NZBGet web site.
Web-interface
-------------
NZBGet has a built-in web-server providing the access to the program
functions via web-interface.
To activate web-interface set the option "WebDir" to the path with
web-interface files. If you install using "make install-conf" as
described above the option is set automatically. If you install using
binary files you should check if the option is set correctly.
To access web-interface from your web-browser use the server address
and port defined in NZBGet configuration file in options "ControlIP" and
"ControlPort". For example:
http://localhost:6789/
For login credentials type username "nzbget" (predefined and not changeable)
and the password from the option "ControlPassword" (default is tegbzn6789).
In a case your browser forget credentials, to prevent typing them each
time, there is a workaround - use URL in the form:
http://localhost:6789/nzbget:password/
Please note, that in this case the password is saved in a bookmark or in
browser history in plain text and is easy to find by persons having
access to your computer.
An example script for unraring of downloaded files is provided in file
postprocess-example.sh. The usage instructions are included in the file,
please open the file in any text editor to read them.
NOTE: That example script is for POSIX systems (not for Windows).
=====================================
8. Authors
=====================================
NZBGet is developed and maintained by Andrey Prygunkov
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
Up to version 0.2.3 it was developed and maintained by Bo Cordes Petersen
(placebodk@users.sourceforge.net).
Beginning at version 0.3.0 the program is being developed by Andrei Prygounkov
(hugbug@users.sourceforge.net).
The original project was initially created by Sven Henkel
(sidddy@users.sourceforge.net) in 2004 and later developed by
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
Since then the program has been completely rewritten.
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).
=====================================
9. Copyright
@@ -519,9 +450,9 @@ libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
10. Contact
=====================================
If you encounter any problem, feel free to use the forum
If you encounter any problem, feel free to use forums on
nzbget.sourceforge.net/forum
sourceforge.net/projects/nzbget
or contact me at

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <windows.h>
#else
@@ -46,7 +46,6 @@
#include "nzbget.h"
#include "RemoteClient.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
@@ -56,6 +55,7 @@ extern Options* g_pOptions;
RemoteClient::RemoteClient()
{
m_pConnection = NULL;
m_pNetAddress = NULL;
m_bVerbose = true;
/*
@@ -76,7 +76,15 @@ RemoteClient::RemoteClient()
RemoteClient::~RemoteClient()
{
delete m_pConnection;
if (m_pConnection)
{
delete m_pConnection;
}
if (m_pNetAddress)
{
delete m_pNetAddress;
}
}
void RemoteClient::printf(const char * msg,...)
@@ -101,19 +109,13 @@ void RemoteClient::perror(const char * msg)
bool RemoteClient::InitConnection()
{
// Create a connection to the server
const char *szControlIP = g_pOptions->GetControlIP();
if (!strcmp(szControlIP, "0.0.0.0"))
{
szControlIP = "127.0.0.1";
}
m_pConnection = new Connection(szControlIP, g_pOptions->GetControlPort(), false);
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = new Connection(m_pNetAddress);
bool OK = m_pConnection->Connect();
if (!OK)
{
printf("Unable to send request to nzbserver at %s (port %i)\n", szControlIP, g_pOptions->GetControlPort());
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
}
return OK;
}
@@ -123,11 +125,7 @@ void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest,
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE - 1);
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -139,22 +137,35 @@ bool RemoteClient::ReceiveBoolResponse()
SNZBDownloadResponse BoolResponse;
memset(&BoolResponse, 0, sizeof(BoolResponse));
bool bRead = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
if (!bRead ||
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
if (iResponseLen != sizeof(BoolResponse) ||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-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;
}
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
char* buf = (char*)malloc(iTextLen);
bRead = m_pConnection->Recv(buf, iTextLen);
if (!bRead)
iResponseLen = m_pConnection->Recv(buf, iTextLen);
if (iResponseLen != iTextLen)
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
free(buf);
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;
}
@@ -166,7 +177,7 @@ bool RemoteClient::ReceiveBoolResponse()
/*
* Sends a message to the running nzbget process.
*/
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority)
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst)
{
// Read the file into the buffer
char* szBuffer = NULL;
@@ -183,9 +194,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
SNZBDownloadRequest DownloadRequest;
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
DownloadRequest.m_bAddPaused = htonl(bAddPaused);
DownloadRequest.m_iPriority = htonl(iPriority);
DownloadRequest.m_iTrailingDataLength = htonl(iLength - 1);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
@@ -196,7 +205,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
DownloadRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
if (!m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)))
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
{
perror("m_pConnection->Send");
OK = false;
@@ -210,7 +219,10 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
// Cleanup
free(szBuffer);
if (szBuffer)
{
free(szBuffer);
}
return OK;
}
@@ -227,29 +239,23 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) pBufPtr;
const char* szFileName = pBufPtr + sizeof(SNZBListResponseNZBEntry);
const char* szName = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen);
const char* szDestDir = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iNameLen);
const char* szCategory = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iNameLen) + ntohl(pListAnswer->m_iDestDirLen);
const char* m_szQueuedFilename = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iNameLen) + ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen);
const char* szDestDir = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen);
const char* szCategory = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
const char* m_szQueuedFilename = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen);
MatchedNZBInfo* pNZBInfo = new MatchedNZBInfo();
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo)));
pNZBInfo->SetFilename(szFileName);
pNZBInfo->SetName(szName);
pNZBInfo->SetDestDir(szDestDir);
pNZBInfo->SetCategory(szCategory);
pNZBInfo->SetQueuedFilename(m_szQueuedFilename);
pNZBInfo->m_bMatch = ntohl(pListAnswer->m_bMatch);
pNZBInfo->Retain();
pNZBInfo->AddReference();
pDownloadQueue->GetNZBInfoList()->Add(pNZBInfo);
pBufPtr += sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iNameLen) + ntohl(pListAnswer->m_iDestDirLen) +
ntohl(pListAnswer->m_iCategoryLen) + ntohl(pListAnswer->m_iQueuedFilenameLen);
ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen) +
ntohl(pListAnswer->m_iQueuedFilenameLen);
}
//read ppp entries
@@ -261,7 +267,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
const char* szValue = pBufPtr + sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen);
NZBInfo* pNZBInfo = pDownloadQueue->GetNZBInfoList()->at(ntohl(pListAnswer->m_iNZBIndex) - 1);
pNZBInfo->GetParameters()->SetParameter(szName, szValue);
pNZBInfo->SetParameter(szName, szValue);
pBufPtr += sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen) +
ntohl(pListAnswer->m_iValueLen);
@@ -275,7 +281,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
const char* szSubject = pBufPtr + sizeof(SNZBListResponseFileEntry);
const char* szFileName = pBufPtr + sizeof(SNZBListResponseFileEntry) + ntohl(pListAnswer->m_iSubjectLen);
MatchedFileInfo* pFileInfo = new MatchedFileInfo();
FileInfo* pFileInfo = new FileInfo();
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
pFileInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
pFileInfo->SetRemainingSize(Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
@@ -283,9 +289,6 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
pFileInfo->SetSubject(szSubject);
pFileInfo->SetFilename(szFileName);
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
pFileInfo->SetActiveDownloads(ntohl(pListAnswer->m_iActiveDownloads));
pFileInfo->SetPriority(ntohl(pListAnswer->m_iPriority));
pFileInfo->m_bMatch = ntohl(pListAnswer->m_bMatch);
NZBInfo* pNZBInfo = pDownloadQueue->GetNZBInfoList()->at(ntohl(pListAnswer->m_iNZBIndex) - 1);
@@ -301,7 +304,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
pDownloadQueue->GetNZBInfoList()->ReleaseAll();
}
bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPattern)
bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
{
if (!InitConnection()) return false;
@@ -309,15 +312,8 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
ListRequest.m_bFileList = htonl(true);
ListRequest.m_bServerState = htonl(true);
ListRequest.m_iMatchMode = htonl(szPattern ? eRemoteMatchModeRegEx : eRemoteMatchModeID);
ListRequest.m_bMatchGroup = htonl(bGroups);
if (szPattern)
{
strncpy(ListRequest.m_szPattern, szPattern, NZBREQUESTFILENAMESIZE - 1);
ListRequest.m_szPattern[NZBREQUESTFILENAMESIZE-1] = '\0';
}
if (!m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)))
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
@@ -327,12 +323,19 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
// Now listen for the returned list
SNZBListResponse ListResponse;
bool bRead = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
if (!bRead ||
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
if (iResponseLen != sizeof(ListResponse) ||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-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;
}
@@ -340,7 +343,7 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
if (!m_pConnection->Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -349,13 +352,6 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
m_pConnection->Disconnect();
if (szPattern && !ListResponse.m_bRegExValid)
{
printf("Error in regular expression\n");
free(pBuf);
return false;
}
if (bFiles)
{
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
@@ -372,33 +368,17 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
long long lRemaining = 0;
long long lPaused = 0;
int iMatches = 0;
for (FileQueue::iterator it = cRemoteQueue.GetFileQueue()->begin(); it != cRemoteQueue.GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
char szPriority[100];
szPriority[0] = '\0';
if (pFileInfo->GetPriority() != 0)
{
sprintf(szPriority, "[%+i] ", pFileInfo->GetPriority());
}
char szCompleted[100];
szCompleted[0] = '\0';
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
{
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(pFileInfo->GetRemainingSize()) * 100.0 / Util::Int64ToFloat(pFileInfo->GetSize())), "%");
}
char szThreads[100];
szThreads[0] = '\0';
if (pFileInfo->GetActiveDownloads() > 0)
{
sprintf(szThreads, ", %i thread%s", pFileInfo->GetActiveDownloads(), (pFileInfo->GetActiveDownloads() > 1 ? "s" : ""));
}
char szStatus[100];
if (pFileInfo->GetPaused())
{
@@ -410,30 +390,18 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
szStatus[0] = '\0';
lRemaining += pFileInfo->GetRemainingSize();
}
if (!szPattern || ((MatchedFileInfo*)pFileInfo)->m_bMatch)
{
printf("[%i] %s%s/%s (%.2f MB%s%s)%s\n", pFileInfo->GetID(), szPriority, pFileInfo->GetNZBInfo()->GetName(),
pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
szCompleted, szThreads, szStatus);
iMatches++;
}
char szNZBNiceName[1024];
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
printf("[%i] %s%c%s (%.2f MB%s)%s\n", pFileInfo->GetID(), szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(),
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0), szCompleted, szStatus);
delete pFileInfo;
}
if (iMatches == 0)
{
printf("No matches founds\n");
}
printf("-----------------------------------\n");
printf("Files: %i\n", cRemoteQueue.GetFileQueue()->size());
if (szPattern)
{
printf("Matches: %i\n", iMatches);
}
if (lPaused > 0)
{
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0),
@@ -465,7 +433,6 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
long long lRemaining = 0;
long long lPaused = 0;
int iMatches = 0;
for (GroupQueue::iterator it = cGroupQueue.begin(); it != cGroupQueue.end(); it++)
{
@@ -477,20 +444,6 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
char szRemaining[20];
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
char szPriority[100];
szPriority[0] = '\0';
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
{
if (pGroupInfo->GetMinPriority() == pGroupInfo->GetMaxPriority())
{
sprintf(szPriority, "[%+i] ", pGroupInfo->GetMinPriority());
}
else
{
sprintf(szPriority, "[%+i..%+i] ", pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority());
}
}
char szPaused[20];
szPaused[0] = '\0';
if (pGroupInfo->GetPausedSize() > 0)
@@ -501,6 +454,9 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
lPaused += pGroupInfo->GetPausedSize();
}
char szNZBNiceName[1024];
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
char szCategory[1024];
szCategory[0] = '\0';
if (pGroupInfo->GetNZBInfo()->GetCategory() && strlen(pGroupInfo->GetNZBInfo()->GetCategory()) > 0)
@@ -508,43 +464,33 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
sprintf(szCategory, " (%s)", pGroupInfo->GetNZBInfo()->GetCategory());
}
char szThreads[100];
szThreads[0] = '\0';
if (pGroupInfo->GetActiveDownloads() > 0)
{
sprintf(szThreads, ", %i thread%s", pGroupInfo->GetActiveDownloads(), (pGroupInfo->GetActiveDownloads() > 1 ? "s" : ""));
}
char szParameters[1024];
szParameters[0] = '\0';
for (NZBParameterList::iterator it = pGroupInfo->GetNZBInfo()->GetParameters()->begin(); it != pGroupInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
if (szParameters[0] == '\0')
{
strncat(szParameters, " (", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, " (", 1024);
}
else
{
strncat(szParameters, ", ", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, ", ", 1024);
}
NZBParameter* pNZBParameter = *it;
strncat(szParameters, pNZBParameter->GetName(), sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, "=", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, pNZBParameter->GetValue(), sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, pNZBParameter->GetName(), 1024);
strncat(szParameters, "=", 1024);
strncat(szParameters, pNZBParameter->GetValue(), 1024);
}
if (szParameters[0] != '\0')
{
strncat(szParameters, ")", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, ")", 1024);
}
if (!szPattern || ((MatchedNZBInfo*)pGroupInfo->GetNZBInfo())->m_bMatch)
{
printf("[%i-%i] %s%s (%i file%s, %s%s%s)%s%s\n", pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), szPriority,
pGroupInfo->GetNZBInfo()->GetName(), pGroupInfo->GetRemainingFileCount(),
pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining,
szPaused, szThreads, szCategory, szParameters);
iMatches++;
}
printf("[%i-%i] %s (%i file%s, %s%s)%s%s\n", pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), szNZBNiceName,
pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining,
szPaused, szCategory, szParameters);
delete pGroupInfo;
}
for (FileQueue::iterator it = cRemoteQueue.GetFileQueue()->begin(); it != cRemoteQueue.GetFileQueue()->end(); it++)
@@ -552,17 +498,8 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
delete *it;
}
if (iMatches == 0)
{
printf("No matches founds\n");
}
printf("-----------------------------------\n");
printf("Groups: %i\n", cGroupQueue.size());
if (szPattern)
{
printf("Matches: %i\n", iMatches);
}
printf("Files: %i\n", cRemoteQueue.GetFileQueue()->size());
if (lPaused > 0)
{
@@ -650,10 +587,10 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
if (ntohl(ListResponse.m_iPostJobCount) > 0 || ntohl(ListResponse.m_bPostPaused))
{
strncat(szServerState, strlen(szServerState) > 0 ? ", Post-Processing" : "Post-Processing", sizeof(szServerState) - strlen(szServerState) - 1);
strncat(szServerState, strlen(szServerState) > 0 ? ", Post-Processing" : "Post-Processing", sizeof(szServerState));
if (ntohl(ListResponse.m_bPostPaused))
{
strncat(szServerState, " paused", sizeof(szServerState) - strlen(szServerState) - 1);
strncat(szServerState, " paused", sizeof(szServerState));
}
}
@@ -676,7 +613,7 @@ bool RemoteClient::RequestServerLog(int iLines)
LogRequest.m_iLines = htonl(iLines);
LogRequest.m_iIDFrom = 0;
if (!m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)))
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
@@ -686,12 +623,19 @@ bool RemoteClient::RequestServerLog(int iLines)
// Now listen for the returned log
SNZBLogResponse LogResponse;
bool bRead = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
if (!bRead ||
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
if (iResponseLen != sizeof(LogResponse) ||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-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;
}
@@ -699,7 +643,7 @@ bool RemoteClient::RequestServerLog(int iLines)
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
if (!m_pConnection->Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -762,7 +706,7 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAct
PauseUnpauseRequest.m_bPause = htonl(bPause);
PauseUnpauseRequest.m_iAction = htonl(iAction);
if (!m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)))
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -775,15 +719,15 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAct
return OK;
}
bool RemoteClient::RequestServerSetDownloadRate(int iRate)
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
{
if (!InitConnection()) return false;
SNZBSetDownloadRateRequest SetDownloadRateRequest;
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
SetDownloadRateRequest.m_iDownloadRate = htonl(iRate);
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
if (!m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)))
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -803,7 +747,7 @@ bool RemoteClient::RequestServerDumpDebug()
SNZBDumpDebugRequest DumpDebugInfo;
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
if (!m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)))
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
{
perror("m_pConnection->Send");
m_pConnection->Disconnect();
@@ -816,10 +760,9 @@ bool RemoteClient::RequestServerDumpDebug()
return OK;
}
bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder)
bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder)
{
if ((iIDCount <= 0 || pIDList == NULL) && (pNameList == NULL || pNameList->size() == 0))
if (iIDCount <= 0 || pIDList == NULL)
{
printf("File(s) not specified\n");
return false;
@@ -827,38 +770,18 @@ bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset
if (!InitConnection()) return false;
int iIDLength = sizeof(int32_t) * iIDCount;
int iNameCount = 0;
int iNameLength = 0;
if (pNameList && pNameList->size() > 0)
{
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
{
const char *szName = *it;
iNameLength += strlen(szName) + 1;
iNameCount++;
}
// align size to 4-bytes, needed by ARM-processor (and may be others)
iNameLength += iNameLength % 4 > 0 ? 4 - iNameLength % 4 : 0;
}
int iTextLen = szText ? strlen(szText) + 1 : 0;
// align size to 4-bytes, needed by ARM-processor (and may be others)
iTextLen += iTextLen % 4 > 0 ? 4 - iTextLen % 4 : 0;
int iLength = iTextLen + iIDLength + iNameLength;
int iLength = sizeof(int32_t) * iIDCount + iTextLen;
SNZBEditQueueRequest EditQueueRequest;
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
EditQueueRequest.m_iAction = htonl(iAction);
EditQueueRequest.m_iMatchMode = htonl(iMatchMode);
EditQueueRequest.m_iOffset = htonl((int)iOffset);
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
EditQueueRequest.m_iTextLen = htonl(iTextLen);
EditQueueRequest.m_iNrTrailingIDEntries = htonl(iIDCount);
EditQueueRequest.m_iNrTrailingNameEntries = htonl(iNameCount);
EditQueueRequest.m_iTrailingNameEntriesLen = htonl(iNameLength);
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
char* pTrailingData = (char*)malloc(iLength);
@@ -874,21 +797,9 @@ bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset
{
pIDs[i] = htonl(pIDList[i]);
}
if (iNameCount > 0)
{
char *pNames = pTrailingData + iTextLen + iIDLength;
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
{
const char *szName = *it;
int iLen = strlen(szName);
strncpy(pNames, szName, iLen + 1);
pNames += iLen + 1;
}
}
bool OK = false;
if (!m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)))
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
{
perror("m_pConnection->Send");
}
@@ -912,28 +823,7 @@ bool RemoteClient::RequestServerShutdown()
SNZBShutdownRequest ShutdownRequest;
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest));
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestServerReload()
{
if (!InitConnection()) return false;
SNZBReloadRequest ReloadRequest;
InitMessageBase(&ReloadRequest.m_MessageBase, eRemoteRequestReload, sizeof(ReloadRequest));
bool OK = m_pConnection->Send((char*)(&ReloadRequest), sizeof(ReloadRequest));
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
@@ -954,7 +844,7 @@ bool RemoteClient::RequestServerVersion()
SNZBVersionRequest VersionRequest;
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest));
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
@@ -975,7 +865,7 @@ bool RemoteClient::RequestPostQueue()
SNZBPostQueueRequest PostQueueRequest;
InitMessageBase(&PostQueueRequest.m_MessageBase, eRemoteRequestPostQueue, sizeof(PostQueueRequest));
if (!m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)))
if (m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
@@ -985,12 +875,19 @@ bool RemoteClient::RequestPostQueue()
// Now listen for the returned list
SNZBPostQueueResponse PostQueueResponse;
bool bRead = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
if (!bRead ||
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))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-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;
}
@@ -998,7 +895,7 @@ bool RemoteClient::RequestPostQueue()
if (ntohl(PostQueueResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(PostQueueResponse.m_iTrailingDataLength));
if (!m_pConnection->Recv(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
if (!m_pConnection->RecvAll(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -1023,21 +920,22 @@ bool RemoteClient::RequestPostQueue()
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) != (int)PostInfo::ptExecutingScript)
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
{
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
}
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Unpacking", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
ntohl(pPostQueueAnswer->m_iInfoNameLen) + ntohl(pPostQueueAnswer->m_iDestDirLen) +
ntohl(pPostQueueAnswer->m_iProgressLabelLen);
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
}
free(pBuf);
@@ -1058,7 +956,7 @@ bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
int iLength = strlen(szText) + 1;
WriteLogRequest.m_iTrailingDataLength = htonl(iLength);
if (!m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)))
if (m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
@@ -1070,16 +968,14 @@ bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
return OK;
}
bool RemoteClient::RequestScan(bool bSyncMode)
bool RemoteClient::RequestScan()
{
if (!InitConnection()) return false;
SNZBScanRequest ScanRequest;
InitMessageBase(&ScanRequest.m_MessageBase, eRemoteRequestScan, sizeof(ScanRequest));
ScanRequest.m_bSyncMode = htonl(bSyncMode);
bool OK = m_pConnection->Send((char*)(&ScanRequest), sizeof(ScanRequest));
bool OK = m_pConnection->Send((char*)(&ScanRequest), sizeof(ScanRequest)) >= 0;
if (OK)
{
OK = ReceiveBoolResponse();
@@ -1100,7 +996,7 @@ bool RemoteClient::RequestHistory()
SNZBHistoryRequest HistoryRequest;
InitMessageBase(&HistoryRequest.m_MessageBase, eRemoteRequestHistory, sizeof(HistoryRequest));
if (!m_pConnection->Send((char*)(&HistoryRequest), sizeof(HistoryRequest)))
if (m_pConnection->Send((char*)(&HistoryRequest), sizeof(HistoryRequest)) < 0)
{
perror("m_pConnection->Send");
return false;
@@ -1110,12 +1006,19 @@ bool RemoteClient::RequestHistory()
// Now listen for the returned list
SNZBHistoryResponse HistoryResponse;
bool bRead = m_pConnection->Recv((char*) &HistoryResponse, sizeof(HistoryResponse));
if (!bRead ||
int iResponseLen = m_pConnection->Recv((char*) &HistoryResponse, sizeof(HistoryResponse));
if (iResponseLen != sizeof(HistoryResponse) ||
(int)ntohl(HistoryResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(HistoryResponse.m_MessageBase.m_iStructSize) != sizeof(HistoryResponse))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-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;
}
@@ -1123,7 +1026,7 @@ bool RemoteClient::RequestHistory()
if (ntohl(HistoryResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(HistoryResponse.m_iTrailingDataLength));
if (!m_pConnection->Recv(pBuf, ntohl(HistoryResponse.m_iTrailingDataLength)))
if (!m_pConnection->RecvAll(pBuf, ntohl(HistoryResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
@@ -1146,33 +1049,27 @@ bool RemoteClient::RequestHistory()
{
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) pBufPtr;
HistoryInfo::EKind eKind = (HistoryInfo::EKind)ntohl(pListAnswer->m_iKind);
const char* szNicename = pBufPtr + sizeof(SNZBHistoryResponseEntry);
const char* szFileName = pBufPtr + sizeof(SNZBHistoryResponseEntry);
if (eKind == HistoryInfo::hkNZBInfo)
{
long long lSize = Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo));
long long lSize = Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo));
char szSize[20];
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
char szNZBNiceName[1024];
NZBInfo::MakeNiceNZBName(szFileName, szNZBNiceName, 1024);
const char* szParStatusText[] = { "", "", ", Par failed", ", Par successful", ", Repair possible", ", Repair needed" };
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
char szSize[20];
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
printf("[%i] %s (%i files, %s%s%s)\n", ntohl(pListAnswer->m_iID), szNicename,
ntohl(pListAnswer->m_iFileCount), szSize,
szParStatusText[ntohl(pListAnswer->m_iParStatus)],
szScriptStatusText[ntohl(pListAnswer->m_iScriptStatus)]);
}
else if (eKind == HistoryInfo::hkUrlInfo)
{
const char* szUrlStatusText[] = { "", "", "Url download successful", "Url download failed", "" };
const char* szParStatusText[] = { "", ", Par failed", ", Par possible", ", Par successful" };
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
printf("[%i] %s (%s)\n", ntohl(pListAnswer->m_iID), szNicename,
szUrlStatusText[ntohl(pListAnswer->m_iUrlStatus)]);
}
printf("[%i] %s (%i files, %s%s%s)\n", ntohl(pListAnswer->m_iID), szNZBNiceName,
ntohl(pListAnswer->m_iFileCount), szSize,
szParStatusText[ntohl(pListAnswer->m_iParStatus)],
szScriptStatusText[ntohl(pListAnswer->m_iScriptStatus)]);
pBufPtr += sizeof(SNZBHistoryResponseEntry) + ntohl(pListAnswer->m_iNicenameLen);
pBufPtr += sizeof(SNZBHistoryResponseEntry) + ntohl(pListAnswer->m_iFilenameLen) +
ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen) +
ntohl(pListAnswer->m_iQueuedFilenameLen);
}
printf("-----------------------------------\n");
@@ -1183,117 +1080,3 @@ bool RemoteClient::RequestHistory()
return true;
}
bool RemoteClient::RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority)
{
if (!InitConnection()) return false;
SNZBDownloadUrlRequest DownloadUrlRequest;
InitMessageBase(&DownloadUrlRequest.m_MessageBase, eRemoteRequestDownloadUrl, sizeof(DownloadUrlRequest));
DownloadUrlRequest.m_bAddFirst = htonl(bAddFirst);
DownloadUrlRequest.m_bAddPaused = htonl(bAddPaused);
DownloadUrlRequest.m_iPriority = htonl(iPriority);
strncpy(DownloadUrlRequest.m_szURL, szURL, NZBREQUESTFILENAMESIZE - 1);
DownloadUrlRequest.m_szURL[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadUrlRequest.m_szCategory[0] = '\0';
if (szCategory)
{
strncpy(DownloadUrlRequest.m_szCategory, szCategory, NZBREQUESTFILENAMESIZE - 1);
}
DownloadUrlRequest.m_szCategory[NZBREQUESTFILENAMESIZE-1] = '\0';
DownloadUrlRequest.m_szNZBFilename[0] = '\0';
if (szNZBFilename)
{
strncpy(DownloadUrlRequest.m_szNZBFilename, szNZBFilename, NZBREQUESTFILENAMESIZE - 1);
}
DownloadUrlRequest.m_szNZBFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
bool OK = m_pConnection->Send((char*)(&DownloadUrlRequest), sizeof(DownloadUrlRequest));
if (OK)
{
OK = ReceiveBoolResponse();
}
else
{
perror("m_pConnection->Send");
}
m_pConnection->Disconnect();
return OK;
}
bool RemoteClient::RequestUrlQueue()
{
if (!InitConnection()) return false;
SNZBUrlQueueRequest UrlQueueRequest;
InitMessageBase(&UrlQueueRequest.m_MessageBase, eRemoteRequestUrlQueue, sizeof(UrlQueueRequest));
if (!m_pConnection->Send((char*)(&UrlQueueRequest), sizeof(UrlQueueRequest)))
{
perror("m_pConnection->Send");
return false;
}
printf("Request sent\n");
// Now listen for the returned list
SNZBUrlQueueResponse UrlQueueResponse;
bool bRead = m_pConnection->Recv((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
if (!bRead ||
(int)ntohl(UrlQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
ntohl(UrlQueueResponse.m_MessageBase.m_iStructSize) != sizeof(UrlQueueResponse))
{
printf("No response or invalid response (timeout, not nzbget-server or wrong nzbget-server version)\n");
return false;
}
char* pBuf = NULL;
if (ntohl(UrlQueueResponse.m_iTrailingDataLength) > 0)
{
pBuf = (char*)malloc(ntohl(UrlQueueResponse.m_iTrailingDataLength));
if (!m_pConnection->Recv(pBuf, ntohl(UrlQueueResponse.m_iTrailingDataLength)))
{
free(pBuf);
return false;
}
}
m_pConnection->Disconnect();
if (ntohl(UrlQueueResponse.m_iTrailingDataLength) == 0)
{
printf("Server has no urls queued for download\n");
}
else
{
printf("Url-Queue\n");
printf("-----------------------------------\n");
char* pBufPtr = (char*)pBuf;
for (unsigned int i = 0; i < ntohl(UrlQueueResponse.m_iNrTrailingEntries); i++)
{
SNZBUrlQueueResponseEntry* pUrlQueueAnswer = (SNZBUrlQueueResponseEntry*) pBufPtr;
const char* szURL = pBufPtr + sizeof(SNZBUrlQueueResponseEntry);
const char* szTitle = pBufPtr + sizeof(SNZBUrlQueueResponseEntry) + ntohl(pUrlQueueAnswer->m_iURLLen);
char szNiceName[1024];
UrlInfo::MakeNiceName(szURL, szTitle, szNiceName, 1024);
printf("[%i] %s\n", ntohl(pUrlQueueAnswer->m_iID), szNiceName);
pBufPtr += sizeof(SNZBUrlQueueResponseEntry) + ntohl(pUrlQueueAnswer->m_iURLLen) +
ntohl(pUrlQueueAnswer->m_iNZBFilenameLen);
}
free(pBuf);
printf("-----------------------------------\n");
}
return true;
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -35,19 +35,8 @@
class RemoteClient
{
private:
class MatchedNZBInfo: public NZBInfo
{
public:
bool m_bMatch;
};
class MatchedFileInfo: public FileInfo
{
public:
bool m_bMatch;
};
Connection* m_pConnection;
NetAddress* m_pNetAddress;
bool m_bVerbose;
bool InitConnection();
@@ -60,23 +49,19 @@ public:
RemoteClient();
~RemoteClient();
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst);
bool RequestServerList(bool bFiles, bool bGroups);
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
bool RequestServerSetDownloadRate(int iRate);
bool RequestServerSetDownloadRate(float fRate);
bool RequestServerDumpDebug();
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder);
bool RequestServerLog(int iLines);
bool RequestServerShutdown();
bool RequestServerReload();
bool RequestServerVersion();
bool RequestPostQueue();
bool RequestWriteLog(int iKind, const char* szText);
bool RequestScan(bool bSyncMode);
bool RequestScan();
bool RequestHistory();
bool RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
bool RequestUrlQueue();
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
};

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -44,21 +44,20 @@
#include "nzbget.h"
#include "RemoteServer.h"
#include "BinRpc.h"
#include "WebServer.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
//*****************************************************************
// RemoteServer
RemoteServer::RemoteServer(bool bTLS)
RemoteServer::RemoteServer()
{
debug("Creating RemoteServer");
m_bTLS = bTLS;
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
m_pConnection = NULL;
}
@@ -66,51 +65,36 @@ RemoteServer::~RemoteServer()
{
debug("Destroying RemoteServer");
delete m_pConnection;
if (m_pConnection)
{
delete m_pConnection;
}
delete m_pNetAddress;
}
void RemoteServer::Run()
{
debug("Entering RemoteServer-loop");
#ifndef DISABLE_TLS
if (m_bTLS)
{
if (strlen(g_pOptions->GetSecureCert()) == 0 || !Util::FileExists(g_pOptions->GetSecureCert()))
{
error("Could not initialize TLS, secure certificate is not configured or the cert-file was not found. Check option <SecureCert>");
return;
}
if (strlen(g_pOptions->GetSecureKey()) == 0 || !Util::FileExists(g_pOptions->GetSecureKey()))
{
error("Could not initialize TLS, secure key is not configured or the key-file was not found. Check option <SecureKey>");
return;
}
}
#endif
while (!IsStopped())
{
bool bBind = true;
if (!m_pConnection)
{
m_pConnection = new Connection(g_pOptions->GetControlIP(),
m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(),
m_bTLS);
m_pConnection = new Connection(m_pNetAddress);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind();
bBind = m_pConnection->Bind() == 0;
}
// Accept connections and store the new Connection
Connection* pAcceptedConnection = NULL;
// Accept connections and store the "new" socket value
SOCKET iSocket = INVALID_SOCKET;
if (bBind)
{
pAcceptedConnection = m_pConnection->Accept();
iSocket = m_pConnection->Accept();
}
if (!bBind || pAcceptedConnection == NULL)
if (!bBind || iSocket == INVALID_SOCKET)
{
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
if (IsStopped())
@@ -125,13 +109,9 @@ void RemoteServer::Run()
RequestProcessor* commandThread = new RequestProcessor();
commandThread->SetAutoDestroy(true);
commandThread->SetConnection(pAcceptedConnection);
#ifndef DISABLE_TLS
commandThread->SetTLS(m_bTLS);
#endif
commandThread->SetSocket(iSocket);
commandThread->Start();
}
if (m_pConnection)
{
m_pConnection->Disconnect();
@@ -156,32 +136,32 @@ void RemoteServer::Stop()
//*****************************************************************
// RequestProcessor
RequestProcessor::~RequestProcessor()
{
m_pConnection->Disconnect();
delete m_pConnection;
}
void RequestProcessor::Run()
{
// Read the first 4 bytes to determine request type
bool bOK = false;
m_pConnection->SetSuppressErrors(true);
#ifndef DISABLE_TLS
if (m_bTLS && !m_pConnection->StartTLS(false, g_pOptions->GetSecureCert(), g_pOptions->GetSecureKey()))
int iSignature = 0;
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
if (iBytesReceived < 0)
{
debug("Could not establish secure connection to web-client: Start TLS failed");
return;
}
#endif
// Read the first 4 bytes to determine request type
int iSignature = 0;
if (!m_pConnection->Recv((char*)&iSignature, 4))
// 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)
{
debug("Could not read request signature, request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
#ifdef WIN32
ip = inet_ntoa(PeerName.sin_addr);
#else
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
#endif
}
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
@@ -189,47 +169,62 @@ void RequestProcessor::Run()
// binary request received
bOK = true;
BinRpcProcessor processor;
processor.SetConnection(m_pConnection);
processor.SetSocket(m_iSocket);
processor.SetSignature(iSignature);
processor.SetClientIP(ip);
processor.Execute();
}
else if (!strncmp((char*)&iSignature, "POST", 4) ||
!strncmp((char*)&iSignature, "GET ", 4) ||
!strncmp((char*)&iSignature, "OPTI", 4))
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
{
// HTTP request received
// XML-RPC or JSON-RPC request received
Connection con(m_iSocket, false);
char szBuffer[1024];
if (m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
char* szUrl = szBuffer;
if (!strncmp((char*)&iSignature, "POST", 4))
{
eHttpMethod = WebProcessor::hmPost;
eHttpMethod = XmlRpcProcessor::hmPost;
szUrl++;
}
if (!strncmp((char*)&iSignature, "OPTI", 4) && strlen(szUrl) > 4)
{
eHttpMethod = WebProcessor::hmOptions;
szUrl += 4;
}
if (char* p = strchr(szUrl, ' '))
{
*p = '\0';
}
debug("url: %s", szUrl);
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;
}
else if (!strcmp(szUrl, "/jsonprpc") || !strncmp(szUrl, "/jsonprpc/", 10))
{
eProtocol = XmlRpcProcessor::rpJsonPRpc;
}
WebProcessor processor;
processor.SetConnection(m_pConnection);
processor.SetUrl(szUrl);
processor.SetHttpMethod(eHttpMethod);
processor.Execute();
bOK = true;
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", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
}
closesocket(m_iSocket);
}

View File

@@ -1,8 +1,8 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -28,16 +28,17 @@
#define REMOTESERVER_H
#include "Thread.h"
#include "NetAddress.h"
#include "Connection.h"
class RemoteServer : public Thread
{
private:
bool m_bTLS;
NetAddress* m_pNetAddress;
Connection* m_pConnection;
public:
RemoteServer(bool bTLS);
RemoteServer();
~RemoteServer();
virtual void Run();
virtual void Stop();
@@ -46,14 +47,11 @@ public:
class RequestProcessor : public Thread
{
private:
bool m_bTLS;
Connection* m_pConnection;
SOCKET m_iSocket;
public:
~RequestProcessor();
virtual void Run();
void SetTLS(bool bTLS) { m_bTLS = bTLS; }
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -33,13 +33,12 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "Scanner.h"
@@ -66,53 +65,14 @@ Scanner::FileData::~FileData()
free(m_szFilename);
}
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus)
{
m_szFilename = strdup(szFilename);
m_szNZBName = strdup(szNZBName);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iPriority = iPriority;
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
m_iDupeScore = iDupeScore;
m_eDupeMode = eDupeMode;
m_bAddTop = bAddTop;
m_bAddPaused = bAddPaused;
m_pAddStatus = pAddStatus;
if (pParameters)
{
m_Parameters.CopyFrom(pParameters);
}
}
Scanner::QueueData::~QueueData()
{
free(m_szFilename);
free(m_szNZBName);
free(m_szCategory);
free(m_szDupeKey);
}
void Scanner::QueueData::SetAddStatus(EAddStatus eAddStatus)
{
if (m_pAddStatus)
{
*m_pAddStatus = eAddStatus;
}
}
Scanner::Scanner()
{
debug("Creating Scanner");
m_bRequestedNZBDirScan = false;
m_bScanning = false;
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
m_iPass = 0;
m_iStepMSec = 0;
const char* szNZBScript = g_pOptions->GetNZBProcess();
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
@@ -127,38 +87,18 @@ Scanner::~Scanner()
delete *it;
}
m_FileList.clear();
ClearQueueList();
}
void Scanner::ClearQueueList()
{
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
delete *it;
}
m_QueueList.clear();
}
void Scanner::Check()
{
m_mutexScan.Lock();
if (m_bRequestedNZBDirScan ||
if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan ||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000))
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
{
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
bool bCheckStat = !m_bRequestedNZBDirScan;
m_bRequestedNZBDirScan = false;
m_bScanning = true;
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
if (!bCheckStat && m_bNZBScript)
{
// if immediate scan requested, we need second scan to process files extracted by NzbProcess-script
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
}
m_bScanning = false;
m_iNZBDirInterval = 0;
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
@@ -166,7 +106,7 @@ void Scanner::Check()
// - one additional scan is neccessary to check sizes of detected files;
// - another scan is required to check files which were extracted by NzbProcess-script;
// - third scan is needed to check sizes of extracted files.
if (g_pOptions->GetNzbDirInterval() > 0 && g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
if (g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
{
int iMaxPass = m_bNZBScript ? 3 : 1;
if (m_iPass < iMaxPass)
@@ -182,11 +122,8 @@ void Scanner::Check()
}
DropOldFiles();
ClearQueueList();
}
m_iNZBDirInterval += 200;
m_mutexScan.Unlock();
m_iNZBDirInterval += m_iStepMSec;
}
/**
@@ -323,8 +260,7 @@ void Scanner::DropOldFiles()
}
}
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
const char* szFullFilename, const char* szCategory)
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory)
{
const char* szExtension = strrchr(szBaseFilename, '.');
if (!szExtension)
@@ -332,47 +268,14 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
return;
}
char* szNZBName = strdup("");
char* szNZBCategory = strdup(szCategory);
NZBParameterList* pParameters = new NZBParameterList();
int iPriority = 0;
bool bAddTop = false;
bool bAddPaused = false;
const char* szDupeKey = NULL;
int iDupeScore = 0;
EDupeMode eDupeMode = dmScore;
EAddStatus eAddStatus = asSkipped;
bool bAdded = false;
QueueData* pQueueData = NULL;
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
QueueData* pQueueData1 = *it;
if (Util::SameFilename(pQueueData1->GetFilename(), szFullFilename))
{
pQueueData = pQueueData1;
free(szNZBName);
szNZBName = strdup(pQueueData->GetNZBName());
free(szNZBCategory);
szNZBCategory = strdup(pQueueData->GetCategory());
iPriority = pQueueData->GetPriority();
szDupeKey = pQueueData->GetDupeKey();
iDupeScore = pQueueData->GetDupeScore();
eDupeMode = pQueueData->GetDupeMode();
bAddTop = pQueueData->GetAddTop();
bAddPaused = pQueueData->GetAddPaused();
pParameters->CopyFrom(pQueueData->GetParameters());
}
}
InitPPParameters(szNZBCategory, pParameters);
NZBParameterList* pParameterList = new NZBParameterList;
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
{
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
&szNZBName, &szNZBCategory, &iPriority, pParameters, &bAddTop, &bAddPaused);
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory, &szNZBCategory, pParameterList);
bExists = Util::FileExists(szFullFilename);
if (bExists && strcasecmp(szExtension, ".nzb"))
{
@@ -380,8 +283,7 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
if (!bRenameOK)
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
}
}
}
@@ -392,260 +294,70 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
if (bRenameOK)
{
bAdded = AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority,
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szRenamedName, szNZBCategory, pParameterList);
}
else
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, szRenamedName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
eAddStatus = asFailed;
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
bAdded = AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority,
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szFullFilename, szNZBCategory, pParameterList);
}
delete pParameters;
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
delete *it;
}
pParameterList->clear();
delete pParameterList;
free(szNZBName);
free(szNZBCategory);
if (pQueueData)
{
pQueueData->SetAddStatus(eAddStatus == asFailed ? asFailed : bAdded ? asSuccess : asSkipped);
}
}
void Scanner::InitPPParameters(const char* szCategory, NZBParameterList* pParameters)
{
bool bUnpack = g_pOptions->GetUnpack();
const char* szDefScript = g_pOptions->GetDefScript();
if (szCategory && *szCategory)
{
Options::Category* pCategory = g_pOptions->FindCategory(szCategory, false);
if (pCategory)
{
bUnpack = pCategory->GetUnpack();
if (pCategory->GetDefScript() && *pCategory->GetDefScript())
{
szDefScript = pCategory->GetDefScript();
}
}
}
pParameters->SetParameter("*Unpack:", bUnpack ? "yes" : "no");
if (szDefScript && *szDefScript)
{
// split szDefScript into tokens and create pp-parameter for each token
char* szDefScript2 = strdup(szDefScript);
char* saveptr;
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
while (szScriptName)
{
szScriptName = Util::Trim(szScriptName);
if (szScriptName[0] != '\0')
{
char szParam[1024];
snprintf(szParam, 1024, "%s:", szScriptName);
szParam[1024-1] = '\0';
pParameters->SetParameter(szParam, "yes");
}
szScriptName = strtok_r(NULL, ",;", &saveptr);
}
free(szDefScript2);
}
}
bool Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, NZBParameterList* pParameterList)
{
const char* szBasename = Util::BaseFileName(szFilename);
info("Collection %s found", szBasename);
NZBFile* pNZBFile = NZBFile::Create(szFilename, szCategory);
bool bOK = pNZBFile != NULL;
if (!bOK)
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
if (!pNZBFile)
{
error("Could not add collection %s to queue", szBasename);
}
char bakname2[1024];
if (!Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024))
bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
if (!bRenameOK)
{
bOK = false;
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not rename file %s to %s! Errcode: %i", szFilename, bakname2, errno);
}
if (bOK)
if (pNZBFile && bRenameOK)
{
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
if (szNZBName && strlen(szNZBName) > 0)
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
#ifdef WIN32
char* szAnsiFilename = strdup(szNZBName);
WebUtil::Utf8ToAnsi(szAnsiFilename, strlen(szAnsiFilename) + 1);
pNZBFile->GetNZBInfo()->SetFilename(szAnsiFilename);
free(szAnsiFilename);
#else
pNZBFile->GetNZBInfo()->SetFilename(szNZBName);
#endif
pNZBFile->GetNZBInfo()->BuildDestDirName();
NZBParameter* pParameter = *it;
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
}
pNZBFile->GetNZBInfo()->SetDupeKey(szDupeKey);
pNZBFile->GetNZBInfo()->SetDupeScore(iDupeScore);
pNZBFile->GetNZBInfo()->SetDupeMode(eDupeMode);
if (pNZBFile->GetPassword())
{
pNZBFile->GetNZBInfo()->GetParameters()->SetParameter("*Unpack:Password", pNZBFile->GetPassword());
}
pNZBFile->GetNZBInfo()->GetParameters()->CopyFrom(pParameters);
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
pFileInfo->SetPaused(bAddPaused);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
info("Collection %s added to queue", szBasename);
}
delete pNZBFile;
return bOK;
if (pNZBFile)
{
delete pNZBFile;
}
}
void Scanner::ScanNZBDir(bool bSyncMode)
void Scanner::ScanNZBDir()
{
m_mutexScan.Lock();
m_bScanning = true;
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
// but it's not critical here.
m_bRequestedNZBDirScan = true;
m_mutexScan.Unlock();
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
{
usleep(100 * 1000);
}
}
Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize)
{
bool bNZB = false;
char szTempFileName[1024];
if (szFileName)
{
strncpy(szTempFileName, szFileName, 1024);
szTempFileName[1024-1] = '\0';
}
else
{
int iNum = 1;
while (iNum == 1 || Util::FileExists(szTempFileName))
{
snprintf(szTempFileName, 1024, "%snzb-%i.tmp", g_pOptions->GetTempDir(), iNum);
szTempFileName[1024-1] = '\0';
iNum++;
}
if (!Util::SaveBufferIntoFile(szTempFileName, szBuffer, iBufSize))
{
error("Could not create file %s", szTempFileName);
return asFailed;
}
char buf[1024];
strncpy(buf, szBuffer, 1024);
buf[1024-1] = '\0';
bNZB = !strncmp(buf, "<?xml", 5) && strstr(buf, "<nzb");
}
// move file into NzbDir, make sure the file name is unique
char szValidNZBName[1024];
strncpy(szValidNZBName, Util::BaseFileName(szNZBName), 1024);
szValidNZBName[1024-1] = '\0';
Util::MakeValidFilename(szValidNZBName, '_', false);
#ifdef WIN32
WebUtil::Utf8ToAnsi(szValidNZBName, 1024);
#endif
const char* szExtension = strrchr(szNZBName, '.');
if (bNZB && (!szExtension || strcasecmp(szExtension, ".nzb")))
{
strncat(szValidNZBName, ".nzb", 1024 - strlen(szValidNZBName) - 1);
}
char szScanFileName[1024];
snprintf(szScanFileName, 1024, "%s%s", g_pOptions->GetNzbDir(), szValidNZBName);
char *szExt = strrchr(szValidNZBName, '.');
if (szExt)
{
*szExt = '\0';
szExt++;
}
int iNum = 2;
while (Util::FileExists(szScanFileName))
{
if (szExt)
{
snprintf(szScanFileName, 1024, "%s%s_%i.%s", g_pOptions->GetNzbDir(), szValidNZBName, iNum, szExt);
}
else
{
snprintf(szScanFileName, 1024, "%s%s_%i", g_pOptions->GetNzbDir(), szValidNZBName, iNum);
}
szScanFileName[1024-1] = '\0';
iNum++;
}
m_mutexScan.Lock();
if (!Util::MoveFile(szTempFileName, szScanFileName))
{
char szSysErrStr[256];
error("Could not move file %s to %s: %s", szTempFileName, szScanFileName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
remove(szTempFileName);
m_mutexScan.Unlock(); // UNLOCK
return asFailed;
}
char* szUseCategory = strdup(szCategory ? szCategory : "");
Options::Category *pCategory = g_pOptions->FindCategory(szCategory, true);
if (pCategory && strcmp(szCategory, pCategory->GetName()))
{
free(szUseCategory);
szUseCategory = strdup(pCategory->GetName());
detail("Category %s matched to %s for %s", szCategory, szUseCategory, szNZBName);
}
EAddStatus eAddStatus = asSkipped;
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szUseCategory,
iPriority, szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused, &eAddStatus);
free(szUseCategory);
m_QueueList.push_back(pQueueData);
m_mutexScan.Unlock();
ScanNZBDir(true);
return eAddStatus;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -29,18 +29,9 @@
#include <deque>
#include <time.h>
#include "DownloadInfo.h"
#include "Thread.h"
class Scanner
{
public:
enum EAddStatus
{
asSkipped,
asSuccess,
asFailed
};
private:
class FileData
{
@@ -61,70 +52,25 @@ private:
typedef std::deque<FileData*> FileList;
class QueueData
{
private:
char* m_szFilename;
char* m_szNZBName;
char* m_szCategory;
int m_iPriority;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
NZBParameterList m_Parameters;
bool m_bAddTop;
bool m_bAddPaused;
EAddStatus* m_pAddStatus;
public:
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus);
~QueueData();
const char* GetFilename() { return m_szFilename; }
const char* GetNZBName() { return m_szNZBName; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
const char* GetDupeKey() { return m_szDupeKey; }
int GetDupeScore() { return m_iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
NZBParameterList* GetParameters() { return &m_Parameters; }
bool GetAddTop() { return m_bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
void SetAddStatus(EAddStatus eAddStatus);
};
typedef std::deque<QueueData*> QueueList;
bool m_bRequestedNZBDirScan;
int m_iNZBDirInterval;
bool m_bNZBScript;
int m_iPass;
int m_iStepMSec;
FileList m_FileList;
QueueList m_QueueList;
bool m_bScanning;
Mutex m_mutexScan;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
bool AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
const char* szFullFilename, const char* szCategory);
void AddFileToQueue(const char* szFilename, const char* szCategory, NZBParameterList* pParameterList);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
void InitPPParameters(const char* szCategory, NZBParameterList* pParameters);
void DropOldFiles();
void ClearQueueList();
public:
Scanner();
~Scanner();
void ScanNZBDir(bool bSyncMode);
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
void ScanNZBDir();
void Check();
EAddStatus AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -24,7 +24,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -35,35 +35,37 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "nzbget.h"
#include "Scheduler.h"
#include "ScriptController.h"
#include "Options.h"
#include "Log.h"
#include "NewsServer.h"
#include "ServerPool.h"
#include "FeedInfo.h"
#include "FeedCoordinator.h"
extern Options* g_pOptions;
extern ServerPool* g_pServerPool;
extern FeedCoordinator* g_pFeedCoordinator;
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, const char* szParam)
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess)
{
m_iHours = iHours;
m_iMinutes = iMinutes;
m_iWeekDaysBits = iWeekDaysBits;
m_eCommand = eCommand;
m_szParam = szParam ? strdup(szParam) : NULL;
m_iDownloadRate = iDownloadRate;
m_szProcess = NULL;
if (szProcess)
{
m_szProcess = strdup(szProcess);
}
m_tLastExecuted = 0;
}
Scheduler::Task::~Task()
{
free(m_szParam);
if (m_szProcess)
{
free(m_szProcess);
}
}
@@ -109,6 +111,9 @@ void Scheduler::FirstCheck()
m_tLastCheck = tCurrent - 60*60*24*7;
m_bDetectClockChanges = false;
m_bExecuteProcess = false;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
@@ -116,16 +121,21 @@ void Scheduler::IntervalCheck()
{
m_bDetectClockChanges = true;
m_bExecuteProcess = true;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
void Scheduler::CheckTasks()
{
PrepareLog();
m_mutexTaskList.Lock();
time_t tCurrent = time(NULL);
struct tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
struct tm tmLastCheck;
if (m_bDetectClockChanges)
{
@@ -145,12 +155,9 @@ void Scheduler::CheckTasks()
}
}
tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
tm tmLastCheck;
localtime_r(&m_tLastCheck, &tmLastCheck);
tm tmLoop;
struct tm tmLoop;
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
tmLoop.tm_hour = tmCurrent.tm_hour;
tmLoop.tm_min = tmCurrent.tm_min;
@@ -164,15 +171,13 @@ void Scheduler::CheckTasks()
Task* pTask = *it;
if (pTask->m_tLastExecuted != tLoop)
{
tm tmAppoint;
struct tm tmAppoint;
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
tmAppoint.tm_hour = pTask->m_iHours;
tmAppoint.tm_min = pTask->m_iMinutes;
tmAppoint.tm_sec = 0;
time_t tAppoint = mktime(&tmAppoint);
tAppoint -= g_pOptions->GetTimeCorrection();
int iWeekDay = tmAppoint.tm_wday;
if (iWeekDay == 0)
{
@@ -198,24 +203,25 @@ void Scheduler::CheckTasks()
m_tLastCheck = tCurrent;
m_mutexTaskList.Unlock();
PrintLog();
}
void Scheduler::ExecuteTask(Task* pTask)
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan",
"Enable Server", "Disable Server", "Fetch Feed" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
if (pTask->m_eCommand == scDownloadRate)
{
debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate);
}
else
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
}
switch (pTask->m_eCommand)
{
case scDownloadRate:
if (!Util::EmptyStr(pTask->m_szParam))
{
g_pOptions->SetDownloadRate(atoi(pTask->m_szParam) * 1024);
m_bDownloadRateChanged = true;
}
m_iDownloadRate = pTask->m_iDownloadRate;
m_bDownloadRateChanged = true;
break;
case scPauseDownload:
@@ -227,126 +233,14 @@ void Scheduler::ExecuteTask(Task* pTask)
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szParam);
SchedulerScriptController::StartScript(pTask->m_szProcess);
}
break;
case scPauseScan:
case scUnpauseScan:
g_pOptions->SetPauseScan(pTask->m_eCommand == scPauseScan);
m_bPauseScan = pTask->m_eCommand == scPauseScan;
m_bPauseScanChanged = true;
break;
case scActivateServer:
case scDeactivateServer:
EditServer(pTask->m_eCommand == scActivateServer, pTask->m_szParam);
break;
case scFetchFeed:
if (m_bExecuteProcess)
{
FetchFeed(pTask->m_szParam);
break;
}
}
}
void Scheduler::PrepareLog()
{
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
m_bServerChanged = false;
}
void Scheduler::PrintLog()
{
if (m_bDownloadRateChanged)
{
info("Scheduler: setting download rate to %i KB/s", g_pOptions->GetDownloadRate() / 1024);
}
if (m_bPauseScanChanged)
{
info("Scheduler: %s scan", g_pOptions->GetPauseScan() ? "pausing" : "unpausing");
}
if (m_bServerChanged)
{
int index = 0;
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++, index++)
{
NewsServer* pServer = *it;
if (pServer->GetActive() != m_ServerStatusList[index])
{
info("Scheduler: %s %s", pServer->GetActive() ? "activating" : "deactivating", pServer->GetName());
}
}
g_pServerPool->Changed();
}
}
void Scheduler::EditServer(bool bActive, const char* szServerList)
{
char* szServerList2 = strdup(szServerList);
char* saveptr;
char* szServer = strtok_r(szServerList2, ",;", &saveptr);
while (szServer)
{
szServer = Util::Trim(szServer);
if (!Util::EmptyStr(szServer))
{
int iID = atoi(szServer);
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pServer = *it;
if ((iID > 0 && pServer->GetID() == iID) ||
!strcasecmp(pServer->GetName(), szServer))
{
if (!m_bServerChanged)
{
// store old server status for logging
m_ServerStatusList.clear();
m_ServerStatusList.reserve(g_pServerPool->GetServers()->size());
for (Servers::iterator it2 = g_pServerPool->GetServers()->begin(); it2 != g_pServerPool->GetServers()->end(); it2++)
{
NewsServer* pServer2 = *it2;
m_ServerStatusList.push_back(pServer2->GetActive());
}
}
m_bServerChanged = true;
pServer->SetActive(bActive);
break;
}
}
}
szServer = strtok_r(NULL, ",;", &saveptr);
}
free(szServerList2);
}
void Scheduler::FetchFeed(const char* szFeedList)
{
char* szFeedList2 = strdup(szFeedList);
char* saveptr;
char* szFeed = strtok_r(szFeedList2, ",;", &saveptr);
while (szFeed)
{
szFeed = Util::Trim(szFeed);
if (!Util::EmptyStr(szFeed))
{
int iID = atoi(szFeed);
for (Feeds::iterator it = g_pFeedCoordinator->GetFeeds()->begin(); it != g_pFeedCoordinator->GetFeeds()->end(); it++)
{
FeedInfo* pFeed = *it;
if (pFeed->GetID() == iID ||
!strcasecmp(pFeed->GetName(), szFeed) ||
!strcasecmp("0", szFeed))
{
g_pFeedCoordinator->FetchFeed(pFeed->GetID());
break;
}
}
}
szFeed = strtok_r(NULL, ",;", &saveptr);
}
free(szFeedList2);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,7 +27,6 @@
#define SCHEDULER_H
#include <list>
#include <vector>
#include <time.h>
#include "Thread.h"
@@ -42,10 +41,7 @@ public:
scDownloadRate,
scProcess,
scPauseScan,
scUnpauseScan,
scActivateServer,
scDeactivateServer,
scFetchFeed
scUnpauseScan
};
class Task
@@ -55,12 +51,13 @@ public:
int m_iMinutes;
int m_iWeekDaysBits;
ECommand m_eCommand;
char* m_szParam;
int m_iDownloadRate;
char* m_szProcess;
time_t m_tLastExecuted;
public:
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
const char* szParam);
int iDownloadRate, const char* szProcess);
~Task();
friend class Scheduler;
};
@@ -68,7 +65,6 @@ public:
private:
typedef std::list<Task*> TaskList;
typedef std::vector<bool> ServerStatusList;
TaskList m_TaskList;
Mutex m_mutexTaskList;
@@ -76,18 +72,14 @@ private:
bool m_bDetectClockChanges;
bool m_bDownloadRateChanged;
bool m_bExecuteProcess;
int m_iDownloadRate;
bool m_bPauseDownloadChanged;
bool m_bPauseDownload;
bool m_bPauseScanChanged;
bool m_bServerChanged;
ServerStatusList m_ServerStatusList;
bool m_bPauseScan;
void ExecuteTask(Task* pTask);
void CheckTasks();
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
void PrepareLog();
void PrintLog();
void EditServer(bool bActive, const char* szServerList);
void FetchFeed(const char* szFeedList);
public:
Scheduler();
@@ -95,8 +87,12 @@ public:
void AddTask(Task* pTask);
void FirstCheck();
void IntervalCheck();
bool GetDownloadRateChanged() { return m_bDownloadRateChanged; }
int GetDownloadRate() { return m_iDownloadRate; }
bool GetPauseDownloadChanged() { return m_bPauseDownloadChanged; }
bool GetPauseDownload() { return m_bPauseDownload; }
bool GetPauseScanChanged() { return m_bPauseScanChanged; }
bool GetPauseScan() { return m_bPauseScan; }
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -43,7 +43,6 @@ private:
public:
EnvironmentStrings();
~EnvironmentStrings();
void Clear();
void InitFromCurrentProcess();
void Append(char* szString);
#ifdef WIN32
@@ -60,37 +59,28 @@ private:
const char* m_szWorkingDir;
const char** m_szArgs;
bool m_bFreeArgs;
const char* m_szStdArgs[2];
const char* m_szInfoName;
const char* m_szLogPrefix;
const char* m_szDefaultKindPrefix;
EnvironmentStrings m_environmentStrings;
Options::EScriptLogKind m_eDefaultLogKind;
bool m_bTerminated;
bool m_bDetached;
FILE* m_pReadpipe;
#ifdef WIN32
HANDLE m_hProcess;
char m_szCmdLine[2048];
#else
pid_t m_hProcess;
#endif
protected:
void ProcessOutput(char* szText);
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void AddMessage(Message::EKind eKind, const char* szText);
bool GetTerminated() { return m_bTerminated; }
void ResetEnv();
void PrepareEnvOptions(const char* szStripPrefix);
void PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix);
void PrepareArgs();
void PrepareEnvironmentStrings();
protected:
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
ScriptController();
virtual ~ScriptController();
int Execute();
void Terminate();
void Detach();
void SetScript(const char* szScript) { m_szScript = szScript; }
const char* GetScript() { return m_szScript; }
@@ -98,66 +88,42 @@ public:
void SetArgs(const char** szArgs, bool bFreeArgs) { m_szArgs = szArgs; m_bFreeArgs = bFreeArgs; }
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
const char* GetInfoName() { return m_szInfoName; }
void SetLogPrefix(const char* szLogPrefix) { m_szLogPrefix = szLogPrefix; }
void SetDefaultKindPrefix(const char* szDefaultKindPrefix) { m_szDefaultKindPrefix = szDefaultKindPrefix; }
void SetDefaultLogKind(Options::EScriptLogKind eDefaultLogKind) { m_eDefaultLogKind = eDefaultLogKind; }
void SetEnvVar(const char* szName, const char* szValue);
void SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue);
void SetIntEnvVar(const char* szName, int iValue);
};
class PostScriptController : public Thread, public ScriptController
class PostScriptController : public Thread, ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szNZBName[1024];
int m_iPrefixLen;
void ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation);
void PrepareParams(const char* szScriptName);
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
typedef std::deque<char*> FileList;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);
static void InitParamsForNewNZB(NZBInfo* pNZBInfo);
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
{
private:
char** m_pNZBName;
char** m_pCategory;
int* m_iPriority;
NZBParameterList* m_pParameters;
bool* m_bAddTop;
bool* m_bAddPaused;
int m_iPrefixLen;
NZBParameterList* m_pParameterList;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters,
bool* bAddTop, bool* bAddPaused);
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, NZBParameterList* pParameterList);
};
class NZBAddedScriptController : public Thread, public ScriptController
{
private:
char* m_szNZBName;
public:
virtual void Run();
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
};
class SchedulerScriptController : public Thread, public ScriptController
class SchedulerScriptController : public Thread, ScriptController
{
public:
virtual void Run();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -25,7 +25,7 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#include <config.h>
#endif
#ifdef WIN32
@@ -38,7 +38,6 @@
#include <unistd.h>
#include <errno.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ServerPool.h"
@@ -56,9 +55,8 @@ ServerPool::ServerPool()
{
debug("Creating ServerPool");
m_iMaxNormLevel = 0;
m_iMaxLevel = 0;
m_iTimeout = 60;
m_iGeneration = 0;
}
ServerPool::~ ServerPool()
@@ -72,7 +70,6 @@ ServerPool::~ ServerPool()
delete *it;
}
m_Servers.clear();
m_SortedServers.clear();
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
@@ -86,159 +83,69 @@ void ServerPool::AddServer(NewsServer* pNewsServer)
debug("Adding server to ServerPool");
m_Servers.push_back(pNewsServer);
m_SortedServers.push_back(pNewsServer);
}
/*
* Calculate normalized levels for all servers.
* Normalized Level means: starting from 0 with step 1.
* The servers of minimum Level must be always used even if they are not active;
* this is to prevent backup servers to act as main servers.
**/
void ServerPool::NormalizeLevels()
{
if (m_Servers.empty())
{
return;
}
std::sort(m_SortedServers.begin(), m_SortedServers.end(), CompareServers);
// find minimum level
int iMinLevel = m_SortedServers.front()->GetLevel();
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() < iMinLevel)
{
iMinLevel = pNewsServer->GetLevel();
}
}
m_iMaxNormLevel = 0;
int iLastLevel = iMinLevel;
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
{
NewsServer* pNewsServer = *it;
if ((pNewsServer->GetActive() && pNewsServer->GetMaxConnections() > 0) ||
(pNewsServer->GetLevel() == iMinLevel))
{
if (pNewsServer->GetLevel() != iLastLevel)
{
m_iMaxNormLevel++;
}
pNewsServer->SetNormLevel(m_iMaxNormLevel);
iLastLevel = pNewsServer->GetLevel();
}
else
{
pNewsServer->SetNormLevel(-1);
}
}
}
bool ServerPool::CompareServers(NewsServer* pServer1, NewsServer* pServer2)
{
return pServer1->GetLevel() < pServer2->GetLevel();
}
void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");
m_mutexConnections.Lock();
NormalizeLevels();
m_Levels.clear();
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
m_iMaxLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
int iNormLevel = pNewsServer->GetNormLevel();
if (pNewsServer->GetNormLevel() > -1)
if (m_iMaxLevel < pNewsServer->GetLevel())
{
if ((int)m_Levels.size() <= iNormLevel)
{
m_Levels.push_back(0);
}
if (pNewsServer->GetActive())
{
int iConnections = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (pConnection->GetNewsServer() == pNewsServer)
{
iConnections++;
}
}
for (int i = iConnections; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
iConnections++;
}
m_Levels[iNormLevel] += iConnections;
}
m_iMaxLevel = pNewsServer->GetLevel();
}
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
}
m_iGeneration++;
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
{
int iMaxConnectionsForLevel = 0;
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (iLevel == pNewsServer->GetLevel())
{
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
}
}
m_mutexConnections.Unlock();
m_Levels.push_back(iMaxConnectionsForLevel);
}
}
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
NNTPConnection* ServerPool::GetConnection(int iLevel)
{
PooledConnection* pConnection = NULL;
m_mutexConnections.Lock();
if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0)
if (m_Levels[iLevel] > 0)
{
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pCandidateConnection = *it;
NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer();
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetActive() &&
pCandidateServer->GetNormLevel() == iLevel &&
(!pWantServer || pCandidateServer == pWantServer ||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
PooledConnection* pConnection1 = *it;
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
{
// free connection found, check if it's not from the server which should be ignored
bool bUseConnection = true;
if (pIgnoreServers && !pWantServer)
{
for (Servers::iterator it = pIgnoreServers->begin(); it != pIgnoreServers->end(); it++)
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
{
bUseConnection = false;
break;
}
}
}
if (bUseConnection)
{
pConnection = pCandidateConnection;
pConnection->SetInUse(true);
break;
}
// free connection found, take it!
pConnection = pConnection1;
pConnection->SetInUse(true);
break;
}
}
if (pConnection)
m_Levels[iLevel]--;
if (!pConnection)
{
m_Levels[iLevel]--;
error("ServerPool: internal error, no free connection found, but there should be one");
}
}
@@ -261,11 +168,7 @@ void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
if (pConnection->GetNewsServer()->GetNormLevel() > -1 && pConnection->GetNewsServer()->GetActive())
{
m_Levels[pConnection->GetNewsServer()->GetNormLevel()]++;
}
m_Levels[pConnection->GetNewsServer()->GetLevel()]++;
m_mutexConnections.Unlock();
}
@@ -276,88 +179,36 @@ void ServerPool::CloseUnusedConnections()
time_t curtime = ::time(NULL);
int i = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); )
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
bool bDeleted = false;
if (!pConnection->GetInUse() &&
(pConnection->GetNewsServer()->GetNormLevel() == -1 ||
!pConnection->GetNewsServer()->GetActive()))
{
debug("Closing (and deleting) unused connection to server%i", pConnection->GetNewsServer()->GetID());
if (pConnection->GetStatus() == Connection::csConnected)
{
pConnection->Disconnect();
}
delete pConnection;
m_Connections.erase(it);
it = m_Connections.begin() + i;
bDeleted = true;
}
if (!bDeleted && !pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID());
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
pConnection->Disconnect();
}
}
if (!bDeleted)
{
it++;
i++;
}
}
m_mutexConnections.Unlock();
}
void ServerPool::Changed()
{
debug("Server config has been changed");
InitConnections();
CloseUnusedConnections();
}
void ServerPool::LogDebugInfo()
{
debug(" ServerPool");
debug(" ----------------");
debug(" Max-Level: %i", m_iMaxNormLevel);
debug(" Max-Level: %i", m_iMaxLevel);
m_mutexConnections.Lock();
debug(" Servers: %i", m_Servers.size());
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
debug(" %i) %s (%s): Level=%i, NormLevel=%i", pNewsServer->GetID(), pNewsServer->GetName(),
pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel());
}
debug(" Levels: %i", m_Levels.size());
int index = 0;
for (Levels::iterator it = m_Levels.begin(); it != m_Levels.end(); it++, index++)
{
int iSize = *it;
debug(" %i: Size=%i", index, iSize);
}
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
debug(" %i) %s (%s): Level=%i, NormLevel=%i, InUse:%i", pConnection->GetNewsServer()->GetID(),
pConnection->GetNewsServer()->GetName(), pConnection->GetNewsServer()->GetHost(),
pConnection->GetNewsServer()->GetLevel(), pConnection->GetNewsServer()->GetNormLevel(),
(int)pConnection->GetInUse());
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
m_mutexConnections.Unlock();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -50,20 +50,16 @@ private:
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
};
typedef std::vector<NewsServer*> Servers;
typedef std::vector<int> Levels;
typedef std::vector<PooledConnection*> Connections;
Servers m_Servers;
Servers m_SortedServers;
Connections m_Connections;
Levels m_Levels;
int m_iMaxNormLevel;
int m_iMaxLevel;
Mutex m_mutexConnections;
int m_iTimeout;
int m_iGeneration;
void NormalizeLevels();
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
public:
ServerPool();
@@ -71,13 +67,10 @@ public:
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
void AddServer(NewsServer* pNewsServer);
void InitConnections();
int GetMaxNormLevel() { return m_iMaxNormLevel; }
Servers* GetServers() { return &m_Servers; } // Only for read access (no lockings)
NNTPConnection* GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers);
int GetMaxLevel() { return m_iMaxLevel; }
NNTPConnection* GetConnection(int iLevel);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();
void Changed();
int GetGeneration() { return m_iGeneration; }
void LogDebugInfo();
};

1819
TLS.cpp
View File

File diff suppressed because it is too large Load Diff

206
TLS.h
View File

@@ -1,7 +1,13 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Based on "tls.h" from project "mpop" by Martin Lambers
* Original source code available on http://sourceforge.net/projects/mpop/
*
* Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
* Martin Lambers <marlam@marlam.de>
*
* Copyright (C) 2008-2009 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
@@ -15,7 +21,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,36 +33,180 @@
#ifndef DISABLE_TLS
class TLSSocket
#include <time.h>
#ifdef HAVE_LIBGNUTLS
# include <gnutls/gnutls.h>
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
# include <openssl/ssl.h>
#endif /* HAVE_OPENSSL */
/*
* If a function with an 'errstr' argument returns a value != TLS_EOK,
* '*errstr' either points to an allocates string containing an error
* description or is NULL.
* If such a function returns TLS_EOK, 'errstr' will not be changed.
*/
#define TLS_EOK 0 /* no error */
#define TLS_ELIBFAILED 1 /* The underlying library failed */
#define TLS_ESEED 2 /* Cannot seed pseudo random number generator */
#define TLS_ECERT 3 /* Certificate check or verification failed */
#define TLS_EIO 4 /* Input/output error */
#define TLS_EFILE 5 /* A file does not exist/cannot be read */
#define TLS_EHANDSHAKE 6 /* TLS handshake failed */
/*
* Always use tls_clear() before using a tls_t!
* Never call a tls_*() function with tls_t NULL!
*/
typedef struct
{
private:
bool m_bIsClient;
char* m_szCertFile;
char* m_szKeyFile;
char* m_szCipher;
SOCKET m_iSocket;
bool m_bSuppressErrors;
int m_iRetCode;
bool m_bInitialized;
bool m_bConnected;
int have_trust_file;
int is_active;
#ifdef HAVE_LIBGNUTLS
gnutls_session_t session;
gnutls_certificate_credentials_t cred;
#endif /* HAVE_LIBGNUTLS */
#ifdef HAVE_OPENSSL
SSL_CTX *ssl_ctx;
SSL *ssl;
#endif /* HAVE_OPENSSL */
} tls_t;
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TLS.h
void* m_pContext;
void* m_pSession;
/*
* Information about a X509 certificate.
* The 6 owner_info and issuer_info fields are:
* Common Name
* Organization
* Organizational unit
* Locality
* State/Province
* Country
* Each of these entries may be NULL if it was not provided.
*/
typedef struct
{
unsigned char sha1_fingerprint[20];
unsigned char md5_fingerprint[16];
time_t activation_time;
time_t expiration_time;
char *owner_info[6];
char *issuer_info[6];
} tls_cert_info_t;
void ReportError(const char* szErrMsg);
/*
* tls_lib_init()
*
* Initialize underlying TLS library. If this function returns TLS_ELIBFAILED,
* *errstr will always point to an error string.
* Used error codes: TLS_ELIBFAILED
*/
int tls_lib_init(char **errstr);
public:
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
~TLSSocket();
static void Init();
static void Final();
bool Start();
void Close();
int Send(const char* pBuffer, int iSize);
int Recv(char* pBuffer, int iSize);
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
};
/*
* tls_clear()
*
* Clears a tls_t type (marks it inactive).
*/
void tls_clear(tls_t *tls);
/*
* tls_init()
*
* Initializes a tls_t. If both 'key_file' and 'ca_file' are not NULL, they are
* set to be used when the peer request a certificate. If 'trust_file' is not
* NULL, it will be used to verify the peer certificate.
* All files must be in PEM format.
* If 'force_sslv3' is set, then only the SSLv3 protocol will be accepted. This
* option might be needed to talk to some obsolete broken servers. Only use this
* if you have to.
* Used error codes: TLS_ELIBFAILED, TLS_EFILE
*/
int tls_init(tls_t *tls,
const char *key_file, const char *ca_file, const char *trust_file,
int force_sslv3, char **errstr);
/*
* tls_start()
*
* Starts TLS encryption on a socket.
* 'tls' must be initialized using tls_init().
* If 'no_certcheck' is true, then no checks will be performed on the peer
* certificate. If it is false and no trust file was set with tls_init(),
* only sanity checks are performed on the peer certificate. If it is false
* and a trust file was set, real verification of the peer certificate is
* performed.
* 'hostname' is the host to start TLS with. It is needed for sanity checks/
* verification.
* 'tci' must be allocated with tls_cert_info_new(). Information about the
* peer's certificata will be stored in it. It can later be freed with
* tls_cert_info_free(). 'tci' is allowed to be NULL; no certificate
* information will be passed in this case.
* Used error codes: TLS_ELIBFAILED, TLS_ECERT, TLS_EHANDSHAKE
*/
int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck,
tls_cert_info_t *tci, char **errstr);
/*
* tls_is_active()
*
* Returns whether 'tls' is an active TLS connection.
*/
int tls_is_active(tls_t *tls);
/*
* tls_cert_info_new()
* Returns a new tls_cert_info_t
*/
tls_cert_info_t *tls_cert_info_new(void);
/*
* tls_cert_info_free()
* Frees a tls_cert_info_t
*/
void tls_cert_info_free(tls_cert_info_t *tci);
/*
* tls_cert_info_get()
*
* Extracts certificate information from the TLS connection 'tls' and stores
* it in 'tci'. See the description of tls_cert_info_t above.
* Used error codes: TLS_ECERT
*/
int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr);
/*
* tls_getbuf()
*
* Reads a buffer using TLS and stores it in 's'.
* Used error codes: TLS_EIO
*/
int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr);
/*
* tls_putbuf()
*
* Writes 'len' characters from the string 's' using TLS.
* Used error codes: TLS_EIO
*/
int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr);
/*
* tls_close()
*
* Close a TLS connection and mark it inactive
*/
void tls_close(tls_t *tls);
/*
* tls_lib_deinit()
*
* Deinit underlying TLS library.
*/
void tls_lib_deinit(void);
#endif
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -98,48 +98,6 @@ void Mutex::Unlock()
}
#ifdef HAVE_SPINLOCK
SpinLock::SpinLock()
{
#ifdef WIN32
m_pSpinLockObj = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)m_pSpinLockObj, 0x00FFFFFF);
#else
m_pSpinLockObj = (pthread_spinlock_t *)malloc(sizeof(pthread_spinlock_t));
pthread_spin_init((pthread_spinlock_t *)m_pSpinLockObj, PTHREAD_PROCESS_PRIVATE);
#endif
}
SpinLock::~SpinLock()
{
#ifdef WIN32
DeleteCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
pthread_spin_destroy((pthread_spinlock_t *)m_pSpinLockObj);
#endif
free((void*)m_pSpinLockObj);
}
void SpinLock::Lock()
{
#ifdef WIN32
EnterCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
pthread_spin_lock((pthread_spinlock_t *)m_pSpinLockObj);
#endif
}
void SpinLock::Unlock()
{
#ifdef WIN32
LeaveCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
#else
pthread_spin_unlock((pthread_spinlock_t *)m_pSpinLockObj);
#endif
}
#endif
void Thread::Init()
{
debug("Initializing global thread data");

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -16,7 +16,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -39,23 +39,6 @@ public:
void Unlock();
};
#ifdef HAVE_SPINLOCK
class SpinLock
{
private:
#ifdef WIN32
void* m_pSpinLockObj;
#else
volatile void* m_pSpinLockObj;
#endif
public:
SpinLock();
~SpinLock();
void Lock();
void Unlock();
};
#endif
class Thread
{

View File

@@ -1,860 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Unpack.h"
#include "Log.h"
#include "Util.h"
#include "ParCoordinator.h"
extern Options* g_pOptions;
extern DownloadQueueHolder* g_pDownloadQueueHolder;
void UnpackController::FileList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
free(*it);
}
clear();
}
bool UnpackController::FileList::Exists(const char* szFilename)
{
for (iterator it = begin(); it != end(); it++)
{
char* szFilename1 = *it;
if (!strcmp(szFilename1, szFilename))
{
return true;
}
}
return false;
}
void UnpackController::StartJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
pUnpackController->m_pPostInfo = pPostInfo;
pUnpackController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pUnpackController);
pUnpackController->Start();
}
void UnpackController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
strncpy(m_szName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
m_szName[1024-1] = '\0';
m_bCleanedUpDisk = false;
m_szPassword[0] = '\0';
m_szFinalDir[0] = '\0';
m_bFinalDirCreated = false;
NZBParameter* pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
bool bUnpack = !(pParameter && !strcasecmp(pParameter->GetValue(), "no"));
pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:Password", false);
if (pParameter)
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
}
g_pDownloadQueueHolder->UnlockQueue();
snprintf(m_szInfoName, 1024, "unpack for %s", m_szName);
m_szInfoName[1024-1] = '\0';
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
if (bUnpack)
{
bool bScanNonStdFiles = m_pPostInfo->GetNZBInfo()->GetRenameStatus() > NZBInfo::rsSkipped ||
m_pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
!m_bHasParFiles;
CheckArchiveFiles(bScanNonStdFiles);
}
if (bUnpack && (m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetWorkingDir(m_szDestDir);
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
CreateUnpackDir();
m_bUnpackOK = true;
m_bUnpackStartError = false;
m_bUnpackSpaceError = false;
m_bUnpackPasswordError = false;
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
{
ExecuteUnrar();
}
if (m_bHasSevenZipFiles && m_bUnpackOK)
{
ExecuteSevenZip(false);
}
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
{
ExecuteSevenZip(true);
}
Completed();
}
else
{
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped && m_bHasParFiles)
{
RequestParCheck();
}
else
#endif
{
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
m_pPostInfo->SetWorking(false);
}
void UnpackController::ExecuteUnrar()
{
// Format:
// unrar x -y -p- -o+ *.rar ./_unpack
char szPasswordParam[1024];
const char* szArgs[8];
szArgs[0] = g_pOptions->GetUnrarCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o+";
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
szArgs[6] = m_szUnpackDir;
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
SetLogPrefix("Unrar");
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
m_bUnpackSpaceError = iExitCode == 5;
m_bUnpackPasswordError = iExitCode == 11; // only for rar5-archives
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "Unrar error code: %i", iExitCode);
}
}
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
{
// Format:
// 7z x -y -p- -o./_unpack *.7z
// OR
// 7z x -y -p- -o./_unpack *.7z.001
char szPasswordParam[1024];
const char* szArgs[7];
szArgs[0] = g_pOptions->GetSevenZipCmd();
szArgs[1] = "x";
szArgs[2] = "-y";
szArgs[3] = "-p-";
if (strlen(m_szPassword) > 0)
{
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
szArgs[3] = szPasswordParam;
}
char szUnpackDirParam[1024];
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
szArgs[4] = szUnpackDirParam;
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
szArgs[6] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetSevenZipCmd());
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
PrintMessage(Message::mkInfo, "Executing 7-Zip");
SetLogPrefix("7-Zip");
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
if (!m_bUnpackOK && iExitCode > 0)
{
PrintMessage(Message::mkError, "7-Zip error code: %i", iExitCode);
}
}
void UnpackController::Completed()
{
bool bCleanupSuccess = Cleanup();
if (m_bUnpackOK && bCleanupSuccess)
{
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
if (g_pOptions->GetParRename())
{
//request par-rename check for extracted files
m_pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsNone);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
!m_bUnpackStartError && !m_bUnpackSpaceError && !m_bUnpackPasswordError &&
!GetTerminated() && m_bHasParFiles)
{
RequestParCheck();
}
else
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(
m_bUnpackSpaceError ? NZBInfo::usSpace :
m_bUnpackPasswordError ? NZBInfo::usPassword :
NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck()
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", m_szInfoNameUp);
m_pPostInfo->SetRequestParCheck(true);
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
void UnpackController::CreateUnpackDir()
{
m_bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (m_bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
m_bFinalDirCreated = !Util::DirectoryExists(m_szFinalDir);
}
else
{
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
}
m_szUnpackDir[1024-1] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szUnpackDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
}
}
void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
{
m_bHasRarFiles = false;
m_bHasNonStdRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
{
if (regExRar.Match(filename))
{
m_bHasRarFiles = true;
}
else if (regExSevenZip.Match(filename))
{
m_bHasSevenZipFiles = true;
}
else if (regExSevenZipMulti.Match(filename))
{
m_bHasSevenZipMultiFiles = true;
}
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles &&
!regExRarMultiSeq.Match(filename) && regExNumExt.Match(filename) &&
FileHasRarSignature(szFullFilename))
{
m_bHasNonStdRarFiles = true;
}
}
}
}
bool UnpackController::FileHasRarSignature(const char* szFilename)
{
char rar4Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 };
char rar5Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00 };
char fileSignature[8];
int cnt = 0;
FILE* infile;
infile = fopen(szFilename, "rb");
if (infile)
{
cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
fclose(infile);
}
bool bRar = cnt == sizeof(fileSignature) &&
(!strcmp(rar4Signature, fileSignature) || !strcmp(rar5Signature, fileSignature));
return bRar;
}
bool UnpackController::Cleanup()
{
// By success:
// - move unpacked files to destination dir;
// - remove _unpack-dir;
// - delete archive-files.
// By failure:
// - remove _unpack-dir.
bool bOK = true;
FileList extractedFiles;
if (m_bUnpackOK)
{
// moving files back
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
snprintf(szDstFile, 1024, "%s%c%s", m_szFinalDir[0] != '\0' ? m_szFinalDir : m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// silently overwrite existing files
remove(szDstFile);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s", szSrcFile, szDstFile);
bOK = false;
}
extractedFiles.push_back(strdup(filename));
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szUnpackDir))
{
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
}
if (!m_bUnpackOK && m_bFinalDirCreated)
{
Util::RemoveDirectory(m_szFinalDir);
}
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
{
PrintMessage(Message::mkInfo, "Deleting archive files");
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") &&
!Util::DirectoryExists(szFullFilename) &&
(m_bInterDir || !extractedFiles.Exists(filename)) &&
(regExRar.Match(filename) || regExSevenZip.Match(filename) ||
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(szFullFilename)) ||
(m_bHasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(szFullFilename))))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
m_bCleanedUpDisk = true;
}
extractedFiles.Clear();
return bOK;
}
/**
* Unrar prints progress information into the same line using backspace control character.
* In order to print progress continuously we analyze the output after every char
* and update post-job progress information.
*/
bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
bool bPrinted = false;
int i = 0;
for (; i < iBufSize - 1; i++)
{
int ch = fgetc(pStream);
szBuf[i] = ch;
szBuf[i+1] = '\0';
if (ch == EOF)
{
break;
}
if (ch == '\n')
{
i++;
break;
}
char* szBackspace = strrchr(szBuf, '\b');
if (szBackspace)
{
if (!bPrinted)
{
char tmp[1024];
strncpy(tmp, szBuf, 1024);
tmp[1024-1] = '\0';
char* szTmpPercent = strrchr(tmp, '\b');
if (szTmpPercent)
{
*szTmpPercent = '\0';
}
if (strncmp(szBuf, "...", 3))
{
ProcessOutput(tmp);
}
bPrinted = true;
}
if (strchr(szBackspace, '%'))
{
int iPercent = atoi(szBackspace + 1);
m_pPostInfo->SetStageProgress(iPercent * 10);
}
}
}
szBuf[i] = '\0';
if (bPrinted)
{
szBuf[0] = '\0';
}
return i > 0;
}
void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
{
char szMsgText[1024];
strncpy(szMsgText, szText, 1024);
szMsgText[1024-1] = '\0';
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting ", 19) &&
!strncmp(szText + 19, m_szUnpackDir, strlen(m_szUnpackDir)))
{
snprintf(szMsgText, 1024, "Unrar: Extracting %s", szText + 19 + strlen(m_szUnpackDir) + 1);
szMsgText[1024-1] = '\0';
}
ScriptController::AddMessage(eKind, szMsgText);
m_pPostInfo->AppendMessage(eKind, szMsgText);
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: UNRAR ", 6) &&
strstr(szMsgText, " Copyright ") && strstr(szMsgText, " Alexander Roshal"))
{
// reset start time for a case if user uses unpack-script to do some things
// (like sending Wake-On-Lan message) before executing unrar
m_pPostInfo->SetStageTime(time(NULL));
}
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: Extracting ", 18))
{
SetProgressLabel(szMsgText + 7);
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting from ", 23))
{
const char *szFilename = szText + 23;
debug("Filename: %s", szFilename);
SetProgressLabel(szText + 7);
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: All OK", 13)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "7-Zip: Everything is Ok", 23)))
{
m_bAllOKMessageReceived = true;
}
}
void UnpackController::Stop()
{
debug("Stopping unpack");
Thread::Stop();
Terminate();
}
void UnpackController::SetProgressLabel(const char* szProgressLabel)
{
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->SetProgressLabel(szProgressLabel);
g_pDownloadQueueHolder->UnlockQueue();
}
void MoveController::StartJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
pMoveController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pMoveController);
pMoveController->Start();
}
void MoveController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
m_szDestDir[1024-1] = '\0';
g_pDownloadQueueHolder->UnlockQueue();
info("Moving completed files for %s", szNZBName);
bool bOK = MoveFiles();
szInfoName[0] = 'M'; // uppercase
if (bOK)
{
info("%s successful", szInfoName);
// save new dest dir
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
g_pDownloadQueueHolder->UnlockQueue();
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool MoveController::MoveFiles()
{
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
bool bOK = true;
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
Util::MakeUniqueFilename(szDstFile, 1024, m_szDestDir, filename);
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
if (!Util::MoveFile(szSrcFile, szDstFile))
{
PrintMessage(Message::mkError, "Could not move file %s to %s! Errcode: %i", szSrcFile, szDstFile, errno);
bOK = false;
}
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir))
{
PrintMessage(Message::mkError, "Could not remove intermediate directory %s", m_szInterDir);
}
return bOK;
}
void CleanupController::StartJob(PostInfo* pPostInfo)
{
CleanupController* pCleanupController = new CleanupController();
pCleanupController->m_pPostInfo = pPostInfo;
pCleanupController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pCleanupController);
pCleanupController->Start();
}
void CleanupController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
}
else
{
m_szFinalDir[0] = '\0';
}
g_pDownloadQueueHolder->UnlockQueue();
info("Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
if (bOK && m_szFinalDir[0] != '\0')
{
bool bDeleted2 = false;
bOK = Cleanup(m_szFinalDir, &bDeleted2);
bDeleted = bDeleted || bDeleted2;
}
szInfoName[0] = 'C'; // uppercase
if (bOK && bDeleted)
{
info("%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
info("Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
{
*bDeleted = false;
bool bOK = true;
ExtList extList;
// split ExtCleanupDisk into tokens and create a list
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
char* saveptr;
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
while (szExt)
{
extList.push_back(szExt);
szExt = strtok_r(NULL, ",; ", &saveptr);
}
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
// check file extension
int iFilenameLen = strlen(filename);
bool bDeleteIt = false;
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
{
const char* szExt = *it;
int iExtLen = strlen(szExt);
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, filename + iFilenameLen - iExtLen))
{
bDeleteIt = true;
break;
}
}
if (bDeleteIt)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s! Errcode: %i", szFullFilename, errno);
bOK = false;
}
*bDeleted = true;
}
}
free(szExtCleanupDisk);
return bOK;
}

129
Unpack.h
View File

@@ -1,129 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef UNPACK_H
#define UNPACK_H
#include <deque>
#include "Log.h"
#include "Thread.h"
#include "DownloadInfo.h"
#include "ScriptController.h"
class UnpackController : public Thread, public ScriptController
{
private:
enum EUnpacker
{
upUnrar,
upSevenZip
};
typedef std::deque<char*> FileListBase;
class FileList : public FileListBase
{
public:
void Clear();
bool Exists(const char* szFilename);
};
private:
PostInfo* m_pPostInfo;
char m_szName[1024];
char m_szInfoName[1024];
char m_szInfoNameUp[1024];
char m_szDestDir[1024];
char m_szFinalDir[1024];
char m_szUnpackDir[1024];
char m_szPassword[1024];
bool m_bInterDir;
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasParFiles;
bool m_bHasRarFiles;
bool m_bHasNonStdRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bUnpackSpaceError;
bool m_bUnpackPasswordError;
bool m_bCleanedUpDisk;
EUnpacker m_eUnpacker;
bool m_bFinalDirCreated;
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
virtual void AddMessage(Message::EKind eKind, const char* szText);
void ExecuteUnrar();
void ExecuteSevenZip(bool bMultiVolumes);
void Completed();
void CreateUnpackDir();
bool Cleanup();
void CheckArchiveFiles(bool bScanNonStdFiles);
void SetProgressLabel(const char* szProgressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck();
#endif
bool FileHasRarSignature(const char* szFilename);
public:
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);
};
class MoveController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szInterDir[1024];
char m_szDestDir[1024];
bool MoveFiles();
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
class CleanupController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szDestDir[1024];
char m_szFinalDir[1024];
bool Cleanup(const char* szDestDir, bool *bDeleted);
typedef std::deque<char*> ExtList;
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
#endif

View File

@@ -1,454 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "UrlCoordinator.h"
#include "Options.h"
#include "WebDownloader.h"
#include "DiskState.h"
#include "Log.h"
#include "Util.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
extern QueueCoordinator* g_pQueueCoordinator;
extern Scanner* g_pScanner;
UrlDownloader::UrlDownloader() : WebDownloader()
{
m_szCategory = NULL;
}
UrlDownloader::~UrlDownloader()
{
free(m_szCategory);
}
void UrlDownloader::ProcessHeader(const char* szLine)
{
WebDownloader::ProcessHeader(szLine);
if (!strncmp(szLine, "X-DNZB-Category:", 16))
{
free(m_szCategory);
char* szCategory = strdup(szLine + 16);
m_szCategory = strdup(Util::Trim(szCategory));
free(szCategory);
debug("Category: %s", m_szCategory);
}
else if (!strncmp(szLine, "X-DNZB-", 7))
{
char* szModLine = strdup(szLine);
char* szValue = strchr(szModLine, ':');
if (szValue)
{
*szValue = NULL;
szValue++;
while (*szValue == ' ') szValue++;
Util::Trim(szValue);
debug("X-DNZB: %s", szModLine);
debug("Value: %s", szValue);
char szParamName[100];
snprintf(szParamName, 100, "*DNZB:%s", szModLine + 7);
szParamName[100-1] = '\0';
char* szVal = WebUtil::Latin1ToUtf8(szValue);
m_ppParameters.SetParameter(szParamName, szVal);
free(szVal);
}
free(szModLine);
}
}
UrlCoordinator::UrlCoordinator()
{
debug("Creating UrlCoordinator");
m_bHasMoreJobs = true;
m_bForce = false;
}
UrlCoordinator::~UrlCoordinator()
{
debug("Destroying UrlCoordinator");
// Cleanup
debug("Deleting UrlDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
delete *it;
}
m_ActiveDownloads.clear();
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
{
delete *it;
}
pDownloadQueue->GetUrlQueue()->clear();
g_pQueueCoordinator->UnlockQueue();
debug("UrlCoordinator destroyed");
}
void UrlCoordinator::Run()
{
debug("Entering UrlCoordinator-loop");
int iResetCounter = 0;
while (!IsStopped())
{
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
{
// start download for next URL
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
{
UrlInfo* pUrlInfo;
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
if (bHasMoreUrls && !IsStopped())
{
StartUrlDownload(pUrlInfo);
}
if (!bHasMoreUrls)
{
m_bForce = false;
}
}
g_pQueueCoordinator->UnlockQueue();
}
int iSleepInterval = 100;
usleep(iSleepInterval * 1000);
iResetCounter += iSleepInterval;
if (iResetCounter >= 1000)
{
// this code should not be called too often, once per second is OK
ResetHangingDownloads();
iResetCounter = 0;
}
}
// waiting for downloads
debug("UrlCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
g_pQueueCoordinator->LockQueue();
completed = m_ActiveDownloads.size() == 0;
g_pQueueCoordinator->UnlockQueue();
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("UrlCoordinator: Downloads are completed");
debug("Exiting UrlCoordinator-loop");
}
void UrlCoordinator::Stop()
{
Thread::Stop();
debug("Stopping UrlDownloads");
g_pQueueCoordinator->LockQueue();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
(*it)->Stop();
}
g_pQueueCoordinator->UnlockQueue();
debug("UrlDownloads are notified");
}
void UrlCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
{
return;
}
g_pQueueCoordinator->LockQueue();
time_t tm = ::time(NULL);
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
UrlDownloader* pUrlDownloader = *it;
if (tm - pUrlDownloader->GetLastUpdateTime() > TimeOut &&
pUrlDownloader->GetStatus() == UrlDownloader::adRunning)
{
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
debug("Terminating hanging download %s", pUrlDownloader->GetInfoName());
if (pUrlDownloader->Terminate())
{
error("Terminated hanging download %s", pUrlDownloader->GetInfoName());
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
}
else
{
error("Could not terminate hanging download %s", pUrlDownloader->GetInfoName());
}
m_ActiveDownloads.erase(it);
// it's not safe to destroy pUrlDownloader, because the state of object is unknown
delete pUrlDownloader;
it = m_ActiveDownloads.begin();
continue;
}
it++;
}
g_pQueueCoordinator->UnlockQueue();
}
void UrlCoordinator::LogDebugInfo()
{
debug(" UrlCoordinator");
debug(" ----------------");
g_pQueueCoordinator->LockQueue();
debug(" Active Downloads: %i", m_ActiveDownloads.size());
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
UrlDownloader* pUrlDownloader = *it;
pUrlDownloader->LogDebugInfo();
}
g_pQueueCoordinator->UnlockQueue();
}
void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
{
debug("Adding NZB-URL to queue");
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
if (pUrlInfo->GetForce())
{
m_bForce = true;
}
g_pQueueCoordinator->UnlockQueue();
}
/*
* Returns next URL for download.
*/
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
{
bool bPauseDownload = g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2();
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
{
pUrlInfo = *at;
if (pUrlInfo->GetStatus() == 0 && (!bPauseDownload || pUrlInfo->GetForce() || g_pOptions->GetUrlForce()))
{
return true;
break;
}
}
return false;
}
void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
{
debug("Starting new UrlDownloader");
UrlDownloader* pUrlDownloader = new UrlDownloader();
pUrlDownloader->SetAutoDestroy(true);
pUrlDownloader->Attach(this);
pUrlDownloader->SetUrlInfo(pUrlInfo);
pUrlDownloader->SetURL(pUrlInfo->GetURL());
pUrlDownloader->SetForce(pUrlInfo->GetForce() || g_pOptions->GetUrlForce());
char tmp[1024];
pUrlInfo->GetName(tmp, 1024);
pUrlDownloader->SetInfoName(tmp);
snprintf(tmp, 1024, "%surl-%i.tmp", g_pOptions->GetTempDir(), pUrlInfo->GetID());
tmp[1024-1] = '\0';
pUrlDownloader->SetOutputFilename(tmp);
pUrlInfo->SetStatus(UrlInfo::aiRunning);
m_ActiveDownloads.push_back(pUrlDownloader);
pUrlDownloader->Start();
}
void UrlCoordinator::Update(Subject* pCaller, void* pAspect)
{
debug("Notification from UrlDownloader received");
UrlDownloader* pUrlDownloader = (UrlDownloader*) pCaller;
if ((pUrlDownloader->GetStatus() == WebDownloader::adFinished) ||
(pUrlDownloader->GetStatus() == WebDownloader::adFailed) ||
(pUrlDownloader->GetStatus() == WebDownloader::adRetry))
{
UrlCompleted(pUrlDownloader);
}
}
void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
{
debug("URL downloaded");
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
if (pUrlDownloader->GetStatus() == WebDownloader::adFinished)
{
pUrlInfo->SetStatus(UrlInfo::aiFinished);
}
else if (pUrlDownloader->GetStatus() == WebDownloader::adFailed)
{
pUrlInfo->SetStatus(UrlInfo::aiFailed);
}
else if (pUrlDownloader->GetStatus() == WebDownloader::adRetry)
{
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
}
char filename[1024];
if (pUrlDownloader->GetOriginalFilename())
{
strncpy(filename, pUrlDownloader->GetOriginalFilename(), 1024);
filename[1024-1] = '\0';
}
else
{
strncpy(filename, Util::BaseFileName(pUrlInfo->GetURL()), 1024);
filename[1024-1] = '\0';
// TODO: decode URL escaping
}
Util::MakeValidFilename(filename, '_', false);
debug("Filename: [%s]", filename);
// delete Download from active jobs
g_pQueueCoordinator->LockQueue();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
UrlDownloader* pa = *it;
if (pa == pUrlDownloader)
{
m_ActiveDownloads.erase(it);
break;
}
}
g_pQueueCoordinator->UnlockQueue();
Aspect aspect = { eaUrlCompleted, pUrlInfo };
Notify(&aspect);
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
{
// add nzb-file to download queue
Scanner::EAddStatus eAddStatus = g_pScanner->AddExternalFile(
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : filename,
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : pUrlDownloader->GetCategory(),
pUrlInfo->GetPriority(), pUrlInfo->GetDupeKey(), pUrlInfo->GetDupeScore(), pUrlInfo->GetDupeMode(),
pUrlDownloader->GetParameters(), pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(),
pUrlDownloader->GetOutputFilename(), NULL, 0);
if (eAddStatus != Scanner::asSuccess)
{
pUrlInfo->SetStatus(eAddStatus == Scanner::asFailed ? UrlInfo::aiScanFailed : UrlInfo::aiScanSkipped);
}
}
// delete Download from Url Queue
if (pUrlInfo->GetStatus() != UrlInfo::aiRetry)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
{
UrlInfo* pa = *it;
if (pa == pUrlInfo)
{
pDownloadQueue->GetUrlQueue()->erase(it);
break;
}
}
bool bDeleteObj = true;
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() != UrlInfo::aiFinished)
{
HistoryInfo* pHistoryInfo = new HistoryInfo(pUrlInfo);
pHistoryInfo->SetTime(time(NULL));
pDownloadQueue->GetHistoryList()->push_front(pHistoryInfo);
bDeleteObj = false;
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
if (bDeleteObj)
{
delete pUrlInfo;
}
}
}

View File

@@ -1,98 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef URLCOORDINATOR_H
#define URLCOORDINATOR_H
#include <deque>
#include <list>
#include <time.h>
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "Observer.h"
class UrlDownloader;
class UrlCoordinator : public Thread, public Observer, public Subject
{
public:
typedef std::list<UrlDownloader*> ActiveDownloads;
enum EAspectAction
{
eaUrlAdded,
eaUrlCompleted
};
struct Aspect
{
EAspectAction eAction;
UrlInfo* pUrlInfo;
};
private:
ActiveDownloads m_ActiveDownloads;
bool m_bHasMoreJobs;
bool m_bForce;
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
void StartUrlDownload(UrlInfo* pUrlInfo);
void UrlCompleted(UrlDownloader* pUrlDownloader);
void ResetHangingDownloads();
public:
UrlCoordinator();
virtual ~UrlCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* pCaller, void* pAspect);
// Editing the queue
void AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst);
bool HasMoreJobs() { return m_bHasMoreJobs; }
void LogDebugInfo();
};
class UrlDownloader : public WebDownloader
{
private:
UrlInfo* m_pUrlInfo;
char* m_szCategory;
NZBParameterList m_ppParameters;
protected:
virtual void ProcessHeader(const char* szLine);
public:
UrlDownloader();
~UrlDownloader();
void SetUrlInfo(UrlInfo* pUrlInfo) { m_pUrlInfo = pUrlInfo; }
UrlInfo* GetUrlInfo() { return m_pUrlInfo; }
const char* GetCategory() { return m_szCategory; }
NZBParameterList* GetParameters() { return &m_ppParameters; }
};
#endif

1951
Util.cpp
View File

File diff suppressed because it is too large Load Diff

203
Util.h
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -57,19 +57,6 @@ public:
const char* Next();
};
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 Util
{
public:
@@ -77,33 +64,22 @@ public:
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen);
static bool CreateSparseFile(const char* szFilename, int iSize);
static bool TruncateFile(const char* szFilename, int iSize);
static bool SetFileSize(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
static bool MakeUniqueFilename(char* szDestBufFilename, int iDestBufSize, const char* szDestDir, const char* szBasename);
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
static bool FileExists(const char* szFilename);
static bool FileExists(const char* szPath, const char* szFilenameWithoutPath);
static bool DirectoryExists(const char* szDirFilename);
static bool CreateDirectory(const char* szDirFilename);
static bool RemoveDirectory(const char* szDirFilename);
static bool DeleteDirectoryWithContent(const char* szDirFilename);
static bool ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize);
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
static bool SetCurrentDirectory(const char* szDirFilename);
static bool ForceDirectories(const char* szPath);
static long long FileSize(const char* szFilename);
static long long FreeDiskSize(const char* szPath);
static bool DirEmpty(const char* szDirFilename);
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
#ifndef WIN32
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
static void FixExecPermission(const char* szFilename);
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
#endif
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
static bool SameFilename(const char* szFilename1, const char* szFilename2);
static char* GetLastErrorMessage(char* szBuffer, int iBufLen);
/*
* Split command line int arguments.
@@ -129,39 +105,6 @@ public:
*/
static float Int64ToFloat(long long Int64);
static void TrimRight(char* szStr);
static char* Trim(char* szStr);
static bool EmptyStr(const char* szStr) { return !szStr || !*szStr; }
/* replace all occurences of szFrom to szTo in string szStr with a limitation that szTo must be shorter than szFrom */
static char* ReduceStr(char* szStr, const char* szFrom, const char* szTo);
/* Calculate Hash using Bob Jenkins (1996) algorithm */
static unsigned int HashBJ96(const char* szBuffer, int iBufSize, unsigned int iInitValue);
#ifdef WIN32
static bool RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen);
#endif
/*
* Returns program version and revision number as string formatted like "0.7.0-r295".
* If revision number is not available only version is returned ("0.7.0").
*/
static const char* VersionRevision() { return VersionRevisionBuf; };
/*
* Initialize buffer for program version and revision number.
* This function must be called during program initialization before any
* call to "VersionRevision()".
*/
static void InitVersionRevision();
static char VersionRevisionBuf[40];
};
class WebUtil
{
public:
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
/*
@@ -212,137 +155,19 @@ public:
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
/*
* Unquote http quoted string.
* The string is decoded on the place overwriting the content of raw-data.
* Returns program version and revision number as string formatted like "0.7.0-r295".
* If revision number is not available only version is returned ("0.7.0").
*/
static void HttpUnquote(char* raw);
#ifdef WIN32
static bool Utf8ToAnsi(char* szBuffer, int iBufLen);
static bool AnsiToUtf8(char* szBuffer, int iBufLen);
#endif
/*
* Converts ISO-8859-1 (aka Latin-1) into UTF-8.
* Returns new string allocated with malloc, it needs to be freed by caller.
*/
static char* Latin1ToUtf8(const char* szStr);
static time_t ParseRfc822DateTime(const char* szDateTimeStr);
};
class URL
{
private:
char* m_szAddress;
char* m_szProtocol;
char* m_szUser;
char* m_szPassword;
char* m_szHost;
char* m_szResource;
int m_iPort;
bool m_bTLS;
bool m_bValid;
void ParseURL();
public:
URL(const char* szAddress);
~URL();
bool IsValid() { return m_bValid; }
const char* GetAddress() { return m_szAddress; }
const char* GetProtocol() { return m_szProtocol; }
const char* GetUser() { return m_szUser; }
const char* GetPassword() { return m_szPassword; }
const char* GetHost() { return m_szHost; }
const char* GetResource() { return m_szResource; }
int GetPort() { return m_iPort; }
bool GetTLS() { return m_bTLS; }
};
class RegEx
{
private:
void* m_pContext;
bool m_bValid;
void* m_pMatches;
int m_iMatchBufSize;
public:
RegEx(const char *szPattern, int iMatchBufSize = 100);
~RegEx();
bool IsValid() { return m_bValid; }
bool Match(const char *szStr);
int GetMatchCount();
int GetMatchStart(int index);
int GetMatchLen(int index);
};
class WildMask
{
private:
char* m_szPattern;
bool m_bWantsPositions;
int m_iWildCount;
int* m_WildStart;
int* m_WildLen;
int m_iArrLen;
void ExpandArray();
public:
WildMask(const char *szPattern, bool bWantsPositions = false);
~WildMask();
bool Match(const char *szStr);
int GetMatchCount() { return m_iWildCount; }
int GetMatchStart(int index) { return m_WildStart[index]; }
int GetMatchLen(int index) { return m_WildLen[index]; }
};
#ifndef DISABLE_GZIP
class ZLib
{
public:
/*
* calculates the size required for output buffer
*/
static unsigned int GZipLen(int iInputBufferLength);
static const char* VersionRevision() { return VersionRevisionBuf; };
/*
* returns the size of bytes written to szOutputBuffer or 0 if the buffer is too small or an error occured.
* Initialize buffer for program version and revision number.
* This function must be called during program initialization before any
* call to "VersionRevision()".
*/
static unsigned int GZip(const void* szInputBuffer, int iInputBufferLength, void* szOutputBuffer, int iOutputBufferLength);
static void InitVersionRevision();
static char VersionRevisionBuf[40];
};
class GUnzipStream
{
public:
enum EStatus
{
zlError,
zlFinished,
zlOK
};
private:
void* m_pZStream;
void* m_pOutputBuffer;
int m_iBufferSize;
public:
GUnzipStream(int BufferSize);
~GUnzipStream();
/*
* set next memory block for uncompression
*/
void Write(const void *pInputBuffer, int iInputBufferLength);
/*
* get next uncompressed memory block.
* iOutputBufferLength - the size of uncompressed block. if it is "0" the next compressed block must be provided via "Write".
*/
EStatus Read(const void **pOutputBuffer, int *iOutputBufferLength);
};
#endif
#endif

View File

@@ -1,722 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#include <sys/time.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "WebDownloader.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
WebDownloader::WebDownloader()
{
debug("Creating WebDownloader");
m_szURL = NULL;
m_szOutputFilename = NULL;
m_pConnection = NULL;
m_szInfoName = NULL;
m_bConfirmedLength = false;
m_eStatus = adUndefined;
m_szOriginalFilename = NULL;
m_bForce = false;
m_bRetry = true;
SetLastUpdateTimeNow();
}
WebDownloader::~WebDownloader()
{
debug("Destroying WebDownloader");
free(m_szURL);
free(m_szInfoName);
free(m_szOutputFilename);
free(m_szOriginalFilename);
}
void WebDownloader::SetOutputFilename(const char* v)
{
m_szOutputFilename = strdup(v);
}
void WebDownloader::SetInfoName(const char* v)
{
m_szInfoName = strdup(v);
}
void WebDownloader::SetURL(const char * szURL)
{
free(m_szURL);
m_szURL = strdup(szURL);
}
void WebDownloader::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void WebDownloader::Run()
{
debug("Entering WebDownloader-loop");
SetStatus(adRunning);
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedConnectRetries = iRemainedDownloadRetries > 10 ? iRemainedDownloadRetries : 10;
if (!m_bRetry)
{
iRemainedDownloadRetries = 1;
iRemainedConnectRetries = 1;
}
m_iRedirects = 0;
EStatus Status = adFailed;
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
{
SetLastUpdateTimeNow();
Status = Download();
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
&& !IsStopped() && !(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
!(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
{
usleep(100 * 1000);
msec += 100;
}
}
if (IsStopped() || (!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
{
Status = adRetry;
break;
}
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
{
break;
}
if (Status == adRedirect)
{
m_iRedirects++;
if (m_iRedirects > 5)
{
warn("Too many redirects for %s", m_szInfoName);
Status = adFailed;
break;
}
}
if (Status != adConnectError)
{
iRemainedDownloadRetries--;
}
else
{
iRemainedConnectRetries--;
}
}
if (Status != adFinished && Status != adRetry)
{
Status = adFailed;
}
if (Status == adFailed)
{
if (IsStopped())
{
detail("Download %s cancelled", m_szInfoName);
}
else
{
error("Download %s failed", m_szInfoName);
}
}
if (Status == adFinished)
{
detail("Download %s completed", m_szInfoName);
}
SetStatus(Status);
debug("Exiting WebDownloader-loop");
}
WebDownloader::EStatus WebDownloader::Download()
{
EStatus Status = adRunning;
URL url(m_szURL);
Status = CreateConnection(&url);
if (Status != adRunning)
{
return Status;
}
m_pConnection->SetSuppressErrors(false);
// connection
bool bConnected = m_pConnection->Connect();
if (!bConnected || IsStopped())
{
FreeConnection();
return adConnectError;
}
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s", m_szInfoName);
SendHeaders(&url);
Status = DownloadHeaders();
if (Status == adRunning)
{
Status = DownloadBody();
}
if (IsStopped())
{
Status = adFailed;
}
FreeConnection();
if (Status != adFinished)
{
// Download failed, delete broken output file
remove(m_szOutputFilename);
}
return Status;
}
WebDownloader::EStatus WebDownloader::CreateConnection(URL *pUrl)
{
if (!pUrl->IsValid())
{
error("URL is not valid: %s", pUrl->GetAddress());
return adFatalError;
}
int iPort = pUrl->GetPort();
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "http"))
{
iPort = 80;
}
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "https"))
{
iPort = 443;
}
if (strcasecmp(pUrl->GetProtocol(), "http") && strcasecmp(pUrl->GetProtocol(), "https"))
{
error("Unsupported protocol in URL: %s", pUrl->GetAddress());
return adFatalError;
}
#ifdef DISABLE_TLS
if (!strcasecmp(pUrl->GetProtocol(), "https"))
{
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", pUrl->GetAddress());
return adFatalError;
}
#endif
bool bTLS = !strcasecmp(pUrl->GetProtocol(), "https");
m_pConnection = new Connection(pUrl->GetHost(), iPort, bTLS);
return adRunning;
}
void WebDownloader::SendHeaders(URL *pUrl)
{
char tmp[1024];
// retrieve file
snprintf(tmp, 1024, "GET %s HTTP/1.0\r\n", pUrl->GetResource());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
snprintf(tmp, 1024, "User-Agent: nzbget/%s\r\n", Util::VersionRevision());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
snprintf(tmp, 1024, "Host: %s\r\n", pUrl->GetHost());
tmp[1024-1] = '\0';
m_pConnection->WriteLine(tmp);
m_pConnection->WriteLine("Accept: */*\r\n");
#ifndef DISABLE_GZIP
m_pConnection->WriteLine("Accept-Encoding: gzip\r\n");
#endif
m_pConnection->WriteLine("Connection: close\r\n");
m_pConnection->WriteLine("\r\n");
}
WebDownloader::EStatus WebDownloader::DownloadHeaders()
{
EStatus Status = adRunning;
m_bConfirmedLength = false;
const int LineBufSize = 1024*10;
char* szLineBuf = (char*)malloc(LineBufSize);
m_iContentLen = -1;
bool bFirstLine = true;
m_bGZip = false;
m_bRedirecting = false;
m_bRedirected = false;
// Headers
while (!IsStopped())
{
SetLastUpdateTimeNow();
int iLen = 0;
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
if (bFirstLine)
{
Status = CheckResponse(szLineBuf);
if (Status != adRunning)
{
break;
}
bFirstLine = false;
}
// Have we encountered a timeout?
if (!line)
{
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", m_szInfoName);
}
Status = adFailed;
break;
}
debug("Header: %s", line);
// detect body of response
if (*line == '\r' || *line == '\n')
{
if (m_iContentLen == -1 && !m_bGZip)
{
warn("URL %s: Content-Length is not submitted by server, cannot verify whether the file is complete", m_szInfoName);
}
break;
}
Util::TrimRight(line);
ProcessHeader(line);
if (m_bRedirected)
{
Status = adRedirect;
break;
}
}
free(szLineBuf);
return Status;
}
WebDownloader::EStatus WebDownloader::DownloadBody()
{
EStatus Status = adRunning;
m_pOutFile = NULL;
bool bEnd = false;
const int LineBufSize = 1024*10;
char* szLineBuf = (char*)malloc(LineBufSize);
int iWrittenLen = 0;
#ifndef DISABLE_GZIP
m_pGUnzipStream = NULL;
if (m_bGZip)
{
m_pGUnzipStream = new GUnzipStream(1024*10);
}
#endif
// Body
while (!IsStopped())
{
SetLastUpdateTimeNow();
char* szBuffer;
int iLen;
m_pConnection->ReadBuffer(&szBuffer, &iLen);
if (iLen == 0)
{
iLen = m_pConnection->TryRecv(szLineBuf, LineBufSize);
szBuffer = szLineBuf;
}
// Have we encountered a timeout?
if (iLen <= 0)
{
if (m_iContentLen == -1 && iWrittenLen > 0)
{
bEnd = true;
break;
}
if (!IsStopped())
{
warn("URL %s failed: Unexpected end of file", m_szInfoName);
}
Status = adFailed;
break;
}
// write to output file
if (!Write(szBuffer, iLen))
{
Status = adFatalError;
break;
}
iWrittenLen += iLen;
//detect end of file
if (iWrittenLen == m_iContentLen || (m_iContentLen == -1 && m_bGZip && m_bConfirmedLength))
{
bEnd = true;
break;
}
}
free(szLineBuf);
#ifndef DISABLE_GZIP
delete m_pGUnzipStream;
#endif
if (m_pOutFile)
{
fclose(m_pOutFile);
}
if (!bEnd && Status == adRunning && !IsStopped())
{
warn("URL %s failed: file incomplete", m_szInfoName);
Status = adFailed;
}
if (bEnd)
{
Status = adFinished;
}
return Status;
}
WebDownloader::EStatus WebDownloader::CheckResponse(const char* szResponse)
{
if (!szResponse)
{
if (!IsStopped())
{
warn("URL %s: Connection closed by remote host", m_szInfoName);
}
return adConnectError;
}
const char* szHTTPResponse = strchr(szResponse, ' ');
if (strncmp(szResponse, "HTTP", 4) || !szHTTPResponse)
{
warn("URL %s failed: %s", m_szInfoName, szResponse);
return adFailed;
}
szHTTPResponse++;
if (!strncmp(szHTTPResponse, "400", 3) || !strncmp(szHTTPResponse, "499", 3))
{
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
return adConnectError;
}
else if (!strncmp(szHTTPResponse, "404", 3))
{
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
return adNotFound;
}
else if (!strncmp(szHTTPResponse, "301", 3) || !strncmp(szHTTPResponse, "302", 3))
{
m_bRedirecting = true;
return adRunning;
}
else if (!strncmp(szHTTPResponse, "200", 3))
{
// OK
return adRunning;
}
else
{
// unknown error, no special handling
warn("URL %s failed: %s", m_szInfoName, szResponse);
return adFailed;
}
}
void WebDownloader::ProcessHeader(const char* szLine)
{
if (!strncasecmp(szLine, "Content-Length: ", 16))
{
m_iContentLen = atoi(szLine + 16);
m_bConfirmedLength = true;
}
else if (!strncasecmp(szLine, "Content-Encoding: gzip", 22))
{
m_bGZip = true;
}
else if (!strncasecmp(szLine, "Content-Disposition: ", 21))
{
ParseFilename(szLine);
}
else if (m_bRedirecting && !strncasecmp(szLine, "Location: ", 10))
{
ParseRedirect(szLine + 10);
m_bRedirected = true;
}
}
void WebDownloader::ParseFilename(const char* szContentDisposition)
{
// Examples:
// Content-Disposition: attachment; filename="fname.ext"
// Content-Disposition: attachement;filename=fname.ext
// Content-Disposition: attachement;filename=fname.ext;
const char *p = strstr(szContentDisposition, "filename");
if (!p)
{
return;
}
p = strchr(p, '=');
if (!p)
{
return;
}
p++;
while (*p == ' ') p++;
char fname[1024];
strncpy(fname, p, 1024);
fname[1024-1] = '\0';
char *pe = fname + strlen(fname) - 1;
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
*pe = '\0';
pe--;
}
WebUtil::HttpUnquote(fname);
free(m_szOriginalFilename);
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
debug("OriginalFilename: %s", m_szOriginalFilename);
}
void WebDownloader::ParseRedirect(const char* szLocation)
{
const char* szNewURL = szLocation;
char szUrlBuf[1024];
URL newUrl(szNewURL);
if (!newUrl.IsValid())
{
// relative address
URL oldUrl(m_szURL);
if (oldUrl.GetPort() > 0)
{
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szNewURL);
}
else
{
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szNewURL);
}
szUrlBuf[1024-1] = '\0';
szNewURL = szUrlBuf;
}
detail("URL %s redirected to %s", m_szURL, szNewURL);
SetURL(szNewURL);
}
bool WebDownloader::Write(void* pBuffer, int iLen)
{
if (!m_pOutFile && !PrepareFile())
{
return false;
}
#ifndef DISABLE_GZIP
if (m_bGZip)
{
m_pGUnzipStream->Write(pBuffer, iLen);
const void *pOutBuf;
int iOutLen = 1;
while (iOutLen > 0)
{
GUnzipStream::EStatus eGZStatus = m_pGUnzipStream->Read(&pOutBuf, &iOutLen);
if (eGZStatus == GUnzipStream::zlError)
{
error("URL %s: GUnzip failed", m_szInfoName);
return false;
}
if (iOutLen > 0 && fwrite(pOutBuf, 1, iOutLen, m_pOutFile) <= 0)
{
return false;
}
if (eGZStatus == GUnzipStream::zlFinished)
{
m_bConfirmedLength = true;
return true;
}
}
return true;
}
else
#endif
return fwrite(pBuffer, 1, iLen, m_pOutFile) > 0;
}
bool WebDownloader::PrepareFile()
{
// prepare file for writing
const char* szFilename = m_szOutputFilename;
m_pOutFile = fopen(szFilename, "wb");
if (!m_pOutFile)
{
error("Could not %s file %s", "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() > 0)
{
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
}
return true;
}
void WebDownloader::LogDebugInfo()
{
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&m_tLastUpdateTime, szTime, 50);
#else
ctime_r(&m_tLastUpdateTime, szTime);
#endif
debug(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(m_szOutputFilename));
}
void WebDownloader::Stop()
{
debug("Trying to stop WebDownloader");
Thread::Stop();
m_mutexConnection.Lock();
if (m_pConnection)
{
m_pConnection->SetSuppressErrors(true);
m_pConnection->Cancel();
}
m_mutexConnection.Unlock();
debug("WebDownloader stopped successfully");
}
bool WebDownloader::Terminate()
{
Connection* pConnection = m_pConnection;
bool terminated = Kill();
if (terminated && pConnection)
{
debug("Terminating connection");
pConnection->SetSuppressErrors(true);
pConnection->Cancel();
pConnection->Disconnect();
delete pConnection;
}
return terminated;
}
void WebDownloader::FreeConnection()
{
if (m_pConnection)
{
debug("Releasing connection");
m_mutexConnection.Lock();
if (m_pConnection->GetStatus() == Connection::csCancelled)
{
m_pConnection->Disconnect();
}
delete m_pConnection;
m_pConnection = NULL;
m_mutexConnection.Unlock();
}
}

View File

@@ -1,112 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef WEBDOWNLOADER_H
#define WEBDOWNLOADER_H
#include <time.h>
#include "Observer.h"
#include "Thread.h"
#include "Connection.h"
#include "Util.h"
class WebDownloader : public Thread, public Subject
{
public:
enum EStatus
{
adUndefined,
adRunning,
adFinished,
adFailed,
adRetry,
adNotFound,
adRedirect,
adConnectError,
adFatalError
};
private:
char* m_szURL;
char* m_szOutputFilename;
Connection* m_pConnection;
Mutex m_mutexConnection;
EStatus m_eStatus;
time_t m_tLastUpdateTime;
char* m_szInfoName;
FILE* m_pOutFile;
int m_iContentLen;
bool m_bConfirmedLength;
char* m_szOriginalFilename;
bool m_bForce;
bool m_bRedirecting;
bool m_bRedirected;
int m_iRedirects;
bool m_bGZip;
bool m_bRetry;
#ifndef DISABLE_GZIP
GUnzipStream* m_pGUnzipStream;
#endif
void SetStatus(EStatus eStatus);
bool Write(void* pBuffer, int iLen);
bool PrepareFile();
void FreeConnection();
EStatus CheckResponse(const char* szResponse);
EStatus CreateConnection(URL *pUrl);
void ParseFilename(const char* szContentDisposition);
void SendHeaders(URL *pUrl);
EStatus DownloadHeaders();
EStatus DownloadBody();
void ParseRedirect(const char* szLocation);
protected:
virtual void ProcessHeader(const char* szLine);
public:
WebDownloader();
~WebDownloader();
EStatus GetStatus() { return m_eStatus; }
virtual void Run();
virtual void Stop();
EStatus Download();
bool Terminate();
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
void SetURL(const char* szURL);
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* v);
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
bool GetConfirmedLength() { return m_bConfirmedLength; }
const char* GetOriginalFilename() { return m_szOriginalFilename; }
void SetForce(bool bForce) { m_bForce = bForce; }
void SetRetry(bool bRetry) { m_bRetry = bRetry; }
void LogDebugInfo();
};
#endif

View File

@@ -1,498 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include "nzbget.h"
#include "WebServer.h"
#include "XmlRpc.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
static const char* ERR_HTTP_BAD_REQUEST = "400 Bad Request";
static const char* ERR_HTTP_NOT_FOUND = "404 Not Found";
static const char* ERR_HTTP_SERVICE_UNAVAILABLE = "503 Service Unavailable";
static const int MAX_UNCOMPRESSED_SIZE = 500;
//*****************************************************************
// WebProcessor
WebProcessor::WebProcessor()
{
m_pConnection = NULL;
m_szRequest = NULL;
m_szUrl = NULL;
m_szOrigin = NULL;
}
WebProcessor::~WebProcessor()
{
free(m_szRequest);
free(m_szUrl);
free(m_szOrigin);
}
void WebProcessor::SetUrl(const char* szUrl)
{
m_szUrl = strdup(szUrl);
}
void WebProcessor::Execute()
{
m_bGZip =false;
char szAuthInfo[1024];
szAuthInfo[0] = '\0';
// reading http header
char szBuffer[1024];
int iContentLen = 0;
while (char* p = m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
{
if (char* pe = strrchr(p, '\r')) *pe = '\0';
debug("header=%s", p);
if (!strncasecmp(p, "Content-Length: ", 16))
{
iContentLen = atoi(p + 16);
}
if (!strncasecmp(p, "Authorization: Basic ", 21))
{
char* szAuthInfo64 = p + 21;
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
{
error("Invalid-request: auth-info too big");
return;
}
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
}
if (!strncasecmp(p, "Accept-Encoding: ", 17))
{
m_bGZip = strstr(p, "gzip");
}
if (!strncasecmp(p, "Origin: ", 8))
{
m_szOrigin = strdup(p + 8);
}
if (*p == '\0')
{
break;
}
}
debug("URL=%s", m_szUrl);
debug("Authorization=%s", szAuthInfo);
if (m_eHttpMethod == hmPost && iContentLen <= 0)
{
error("Invalid-request: content length is 0");
return;
}
if (m_eHttpMethod == hmOptions)
{
SendOptionsResponse();
return;
}
// remove subfolder "nzbget" from the path (if exists)
// http://localhost:6789/nzbget/username:password/jsonrpc -> http://localhost:6789/username:password/jsonrpc
if (!strncmp(m_szUrl, "/nzbget/", 8))
{
char* sz_OldUrl = m_szUrl;
m_szUrl = strdup(m_szUrl + 7);
free(sz_OldUrl);
}
// http://localhost:6789/nzbget -> http://localhost:6789
if (!strcmp(m_szUrl, "/nzbget"))
{
char szRedirectURL[1024];
snprintf(szRedirectURL, 1024, "%s/", m_szUrl);
szRedirectURL[1024-1] = '\0';
SendRedirectResponse(szRedirectURL);
return;
}
// authorization via URL in format:
// http://localhost:6789/username:password/jsonrpc
char* pauth1 = strchr(m_szUrl + 1, ':');
char* pauth2 = strchr(m_szUrl + 1, '/');
if (pauth1 && pauth1 < pauth2)
{
char* pstart = m_szUrl + 1;
int iLen = 0;
char* pend = strchr(pstart + 1, '/');
if (pend)
{
iLen = (int)(pend - pstart < (int)sizeof(szAuthInfo) - 1 ? pend - pstart : (int)sizeof(szAuthInfo) - 1);
}
else
{
iLen = strlen(pstart);
}
strncpy(szAuthInfo, pstart, iLen);
szAuthInfo[iLen] = '\0';
char* sz_OldUrl = m_szUrl;
m_szUrl = strdup(pend);
free(sz_OldUrl);
}
debug("Final URL=%s", m_szUrl);
if (strlen(g_pOptions->GetControlPassword()) > 0 &&
!(strlen(g_pOptions->GetAuthorizedIP()) > 0 && IsAuthorizedIP(m_pConnection->GetRemoteAddr())))
{
if (strlen(szAuthInfo) == 0)
{
SendAuthResponse();
return;
}
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(szAuthInfo, g_pOptions->GetControlUsername())) ||
strcmp(pw, g_pOptions->GetControlPassword()))
{
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr(), szAuthInfo, pw);
SendAuthResponse();
return;
}
}
if (m_eHttpMethod == hmPost)
{
// reading http body (request content)
m_szRequest = (char*)malloc(iContentLen + 1);
m_szRequest[iContentLen] = '\0';
if (!m_pConnection->Recv(m_szRequest, iContentLen))
{
error("Invalid-request: could not read data");
return;
}
debug("Request=%s", m_szRequest);
}
debug("request received from %s", m_pConnection->GetRemoteAddr());
Dispatch();
}
bool WebProcessor::IsAuthorizedIP(const char* szRemoteAddr)
{
const char* szRemoteIP = m_pConnection->GetRemoteAddr();
// split option AuthorizedIP into tokens and check each token
bool bAuthorized = false;
char* szAuthorizedIP = strdup(g_pOptions->GetAuthorizedIP());
char* saveptr;
char* szIP = strtok_r(szAuthorizedIP, ",;", &saveptr);
while (szIP)
{
szIP = Util::Trim(szIP);
if (szIP[0] != '\0' && !strcmp(szIP, szRemoteIP))
{
bAuthorized = true;
break;
}
szIP = strtok_r(NULL, ",;", &saveptr);
}
free(szAuthorizedIP);
return bAuthorized;
}
void WebProcessor::Dispatch()
{
if (*m_szUrl != '/')
{
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
return;
}
if (XmlRpcProcessor::IsRpcRequest(m_szUrl))
{
XmlRpcProcessor processor;
processor.SetRequest(m_szRequest);
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
processor.SetUrl(m_szUrl);
processor.Execute();
SendBodyResponse(processor.GetResponse(), strlen(processor.GetResponse()), processor.GetContentType());
return;
}
if (!g_pOptions->GetWebDir() || strlen(g_pOptions->GetWebDir()) == 0)
{
SendErrorResponse(ERR_HTTP_SERVICE_UNAVAILABLE);
return;
}
if (m_eHttpMethod != hmGet)
{
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
return;
}
// for security reasons we allow only characters "0..9 A..Z a..z . - _ /" in the URLs
// we also don't allow ".." in the URLs
for (char *p = m_szUrl; *p; p++)
{
if (!((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
*p == '.' || *p == '-' || *p == '_' || *p == '/') || (*p == '.' && p[1] == '.'))
{
SendErrorResponse(ERR_HTTP_NOT_FOUND);
return;
}
}
const char *szDefRes = "";
if (m_szUrl[strlen(m_szUrl)-1] == '/')
{
// default file in directory (if not specified) is "index.html"
szDefRes = "index.html";
}
char disk_filename[1024];
snprintf(disk_filename, sizeof(disk_filename), "%s%s%s", g_pOptions->GetWebDir(), m_szUrl + 1, szDefRes);
disk_filename[sizeof(disk_filename)-1] = '\0';
SendFileResponse(disk_filename);
}
void WebProcessor::SendAuthResponse()
{
const char* AUTH_RESPONSE_HEADER =
"HTTP/1.0 401 Unauthorized\r\n"
"WWW-Authenticate: Basic realm=\"NZBGet\"\r\n"
"Connection: close\r\n"
"Content-Type: text/plain\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, AUTH_RESPONSE_HEADER, Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendOptionsResponse()
{
const char* OPTIONS_RESPONSE_HEADER =
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
//"Content-Type: plain/text\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Origin: %s\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Max-Age: 86400\r\n"
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, OPTIONS_RESPONSE_HEADER,
m_szOrigin ? m_szOrigin : "",
Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendErrorResponse(const char* szErrCode)
{
const char* RESPONSE_HEADER =
"HTTP/1.0 %s\r\n"
"Connection: close\r\n"
"Content-Length: %i\r\n"
"Content-Type: text/html\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
warn("Web-Server: %s, Resource: %s", szErrCode, m_szUrl);
char szResponseBody[1024];
snprintf(szResponseBody, 1024, "<html><head><title>%s</title></head><body>Error: %s</body></html>", szErrCode, szErrCode);
int iPageContentLen = strlen(szResponseBody);
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, RESPONSE_HEADER, szErrCode, iPageContentLen, Util::VersionRevision());
// Send the response answer
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
m_pConnection->Send(szResponseBody, iPageContentLen);
}
void WebProcessor::SendRedirectResponse(const char* szURL)
{
const char* REDIRECT_RESPONSE_HEADER =
"HTTP/1.0 301 Moved Permanently\r\n"
"Location: %s\r\n"
"Connection: close\r\n"
"Server: nzbget-%s\r\n"
"\r\n";
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, REDIRECT_RESPONSE_HEADER, szURL, Util::VersionRevision());
// Send the response answer
debug("ResponseHeader=%s", szResponseHeader);
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
}
void WebProcessor::SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType)
{
const char* RESPONSE_HEADER =
"HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
"Access-Control-Allow-Origin: %s\r\n"
"Access-Control-Allow-Credentials: true\r\n"
"Access-Control-Max-Age: 86400\r\n"
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
"Content-Length: %i\r\n"
"%s" // Content-Type: xxx
"%s" // Content-Encoding: gzip
"Server: nzbget-%s\r\n"
"\r\n";
#ifndef DISABLE_GZIP
char *szGBuf = NULL;
bool bGZip = m_bGZip && iBodyLen > MAX_UNCOMPRESSED_SIZE;
if (bGZip)
{
unsigned int iOutLen = ZLib::GZipLen(iBodyLen);
szGBuf = (char*)malloc(iOutLen);
int iGZippedLen = ZLib::GZip(szBody, iBodyLen, szGBuf, iOutLen);
if (iGZippedLen > 0 && iGZippedLen < iBodyLen)
{
szBody = szGBuf;
iBodyLen = iGZippedLen;
}
else
{
free(szGBuf);
szGBuf = NULL;
bGZip = false;
}
}
#else
bool bGZip = false;
#endif
char szContentTypeHeader[1024];
if (szContentType)
{
snprintf(szContentTypeHeader, 1024, "Content-Type: %s\r\n", szContentType);
}
else
{
szContentTypeHeader[0] = '\0';
}
char szResponseHeader[1024];
snprintf(szResponseHeader, 1024, RESPONSE_HEADER,
m_szOrigin ? m_szOrigin : "",
iBodyLen, szContentTypeHeader,
bGZip ? "Content-Encoding: gzip\r\n" : "",
Util::VersionRevision());
// Send the request answer
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
m_pConnection->Send(szBody, iBodyLen);
#ifndef DISABLE_GZIP
free(szGBuf);
#endif
}
void WebProcessor::SendFileResponse(const char* szFilename)
{
debug("serving file: %s", szFilename);
char *szBody;
int iBodyLen;
if (!Util::LoadFileIntoBuffer(szFilename, &szBody, &iBodyLen))
{
SendErrorResponse(ERR_HTTP_NOT_FOUND);
return;
}
// "LoadFileIntoBuffer" adds a trailing NULL, which we don't need here
iBodyLen--;
SendBodyResponse(szBody, iBodyLen, DetectContentType(szFilename));
free(szBody);
}
const char* WebProcessor::DetectContentType(const char* szFilename)
{
if (const char *szExt = strrchr(szFilename, '.'))
{
if (!strcasecmp(szExt, ".css"))
{
return "text/css";
}
else if (!strcasecmp(szExt, ".html"))
{
return "text/html";
}
else if (!strcasecmp(szExt, ".js"))
{
return "application/javascript";
}
else if (!strcasecmp(szExt, ".png"))
{
return "image/png";
}
else if (!strcasecmp(szExt, ".jpeg"))
{
return "image/jpeg";
}
else if (!strcasecmp(szExt, ".gif"))
{
return "image/gif";
}
}
return NULL;
}

View File

@@ -1,68 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef WEBSERVER_H
#define WEBSERVER_H
#include "Connection.h"
class WebProcessor
{
public:
enum EHttpMethod
{
hmPost,
hmGet,
hmOptions
};
private:
Connection* m_pConnection;
char* m_szRequest;
char* m_szUrl;
EHttpMethod m_eHttpMethod;
bool m_bGZip;
char* m_szOrigin;
void Dispatch();
void SendAuthResponse();
void SendOptionsResponse();
void SendErrorResponse(const char* szErrCode);
void SendFileResponse(const char* szFilename);
void SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType);
void SendRedirectResponse(const char* szURL);
const char* DetectContentType(const char* szFilename);
bool IsAuthorizedIP(const char* szRemoteAddr);
public:
WebProcessor();
~WebProcessor();
void Execute();
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
void SetUrl(const char* szUrl);
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
};
#endif

2336
XmlRpc.cpp
View File

File diff suppressed because it is too large Load Diff

150
XmlRpc.h
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -15,7 +15,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Revision$
* $Date$
@@ -27,7 +27,19 @@
#define XMLRPC_H
#include "Connection.h"
#include "Util.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;
@@ -49,28 +61,28 @@ public:
};
private:
Connection* m_pConnection;
const char* m_szClientIP;
char* m_szRequest;
const char* m_szContentType;
ERpcProtocol m_eProtocol;
EHttpMethod m_eHttpMethod;
char* m_szUrl;
StringBuilder m_cResponse;
void Dispatch();
void SendAuthResponse();
void SendResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
XmlCommand* CreateCommand(const char* szMethodName);
void MutliCall();
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
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 SetRequest(char* szRequest) { m_szRequest = szRequest; }
const char* GetResponse() { return m_cResponse.GetBuffer(); }
const char* GetContentType() { return m_szContentType; }
static bool IsRpcRequest(const char* szUrl);
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
};
class XmlCommand
@@ -84,7 +96,7 @@ protected:
XmlRpcProcessor::ERpcProtocol m_eProtocol;
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
void BuildErrorResponse(int iErrCode, const char* szErrText, ...);
void BuildErrorResponse(int iErrCode, const char* szErrText);
void BuildBoolResponse(bool bOK);
void AppendResponse(const char* szPart);
bool IsJson();
@@ -94,7 +106,6 @@ protected:
bool NextParamAsStr(char** szValueBuf);
const char* BoolToStr(bool bValue);
char* EncodeStr(const char* szStr);
void DecodeStr(char* szStr);
public:
XmlCommand();
@@ -109,4 +120,119 @@ public:
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 PauseUnpauseXmlCommand: public XmlCommand
{
public:
enum EPauseAction
{
paDownload,
paDownload2,
paPostProcess,
paScan
};
private:
bool m_bPause;
EPauseAction m_eEPauseAction;
public:
PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction);
virtual void Execute();
};
class ShutdownXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class VersionXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DumpDebugXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SetDownloadRateXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class StatusXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListFilesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListGroupsXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class EditQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class PostQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class WriteLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ScanXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class HistoryXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
#endif

View File

@@ -6,9 +6,6 @@
/* Define to 1 to not use curses */
#undef DISABLE_CURSES
/* Define to 1 to disable gzip-support */
#undef DISABLE_GZIP
/* Define to 1 to disable smart par-verification and restoration */
#undef DISABLE_PARCHECK
@@ -67,17 +64,11 @@
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
#undef HAVE_OPENSSL
/* Define to 1 if libpar2 has recent bugfixes-patch (version 2) */
#undef HAVE_PAR2_BUGFIXES_V2
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
#undef HAVE_PAR2_CANCEL
/* Define to 1 if you have the <regex.h> header file. */
#undef HAVE_REGEX_H
/* Define to 1 if spinlocks are supported */
#undef HAVE_SPINLOCK
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -124,9 +115,6 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to 1 to install an empty signal handler for SIGCHLD */
#undef SIGCHLD_HANDLER
/* Determine what socket length (socklen_t) data type is */
#undef SOCKLEN_T
@@ -135,9 +123,3 @@
/* Version number of package */
#undef VERSION
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES

5252
configure vendored
View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,18 +2,34 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 12.0, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 12.0)
AC_INIT(nzbget, 0.7.0, hugbug@users.sourceforge.net)
AM_INIT_AUTOMAKE(nzbget, 0.7.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
dnl
dnl Architecture check.
dnl
AC_CANONICAL_HOST
dnl
dnl Set default library path, if not specified in environment variable "LIBPREF".
dnl
if test "$LIBPREF" = ""; then
LIBPREF="/usr"
case "$host" in
*-linux*)
LIBPREF="/usr"
;;
*-freebsd*)
LIBPREF="/usr/local"
;;
*-solaris*)
LIBPREF="/usr"
;;
esac
fi
@@ -36,7 +52,6 @@ dnl
dnl Checks for header files.
dnl
AC_CHECK_HEADERS(sys/prctl.h)
AC_CHECK_HEADERS(regex.h)
dnl
@@ -56,9 +71,10 @@ AC_CHECK_FUNC(getopt_long,
dnl
dnl use 64-Bits for file sizes
dnl stat64
dnl
AC_SYS_LARGEFILE
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],)
dnl
@@ -142,14 +158,6 @@ if test "$FOUND" = "no"; then
fi
dnl
dnl cCheck if spinlocks are available
dnl
AC_CHECK_FUNC(pthread_spin_init,
[AC_DEFINE([HAVE_SPINLOCK], 1, [Define to 1 if spinlocks are supported])]
AC_SEARCH_LIBS([pthread_spin_init], [pthread]),)
dnl
dnl Determine what socket length (socklen_t) data type is
dnl
@@ -186,6 +194,7 @@ dnl
AC_ARG_WITH(libxml2_includes,
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[CFLAGS="${CFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
AC_ARG_WITH(libxml2_libraries,
@@ -195,8 +204,9 @@ AC_ARG_WITH(libxml2_libraries,
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libxml2, libxml-2.0,
[LIBS="${LIBS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"])
[LDFLAGS="${LDFLAGS} $libxml2_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"]
[CFLAGS="${CFLAGS} $libxml2_CFLAGS"])
fi
AC_CHECK_HEADER(libxml/tree.h,,
AC_MSG_ERROR("libxml2 header files not found"))
@@ -220,6 +230,7 @@ if test "$USECURSES" = "yes"; then
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(libcurses_libraries,
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
[LIBVAL="$withval"])
@@ -279,7 +290,7 @@ if test "$ENABLEPARCHECK" = "yes"; then
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
[LIBS="${LIBS} $libsigc_LIBS"]
[LDFLAGS="${LDFLAGS} $libsigc_LIBS"]
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
fi
@@ -334,32 +345,6 @@ if test "$ENABLEPARCHECK" = "yes"; then
AC_MSG_RESULT([[yes]])
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
AC_MSG_RESULT([[no]]))
dnl
dnl check if libpar2 has recent bugfixes-patch
dnl
AC_MSG_CHECKING(whether libpar2 has recent bugfixes-patch (version 2))
AC_TRY_COMPILE(
[#include <libpar2/par2cmdline.h>]
[#include <libpar2/par2repairer.h>]
[ class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } }; ],
[],
AC_MSG_RESULT([[yes]])
PAR2PATCHV2=yes
AC_DEFINE([HAVE_PAR2_BUGFIXES_V2], 1, [Define to 1 if libpar2 has recent bugfixes-patch (version 2)]),
AC_MSG_RESULT([[no]])
PAR2PATCHV2=no)
if test "$PAR2PATCHV2" = "no" ; then
AC_ARG_ENABLE(libpar2-bugfixes-check,
[AS_HELP_STRING([--disable-libpar2-bugfixes-check], [do not check if libpar2 has recent bugfixes-patch applied])],
[ PAR2PATCHCHECK=$enableval ],
[ PAR2PATCHCHECK=yes] )
if test "$PAR2PATCHCHECK" = "yes"; then
AC_ERROR([Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!])
fi
fi
else
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
fi
@@ -389,11 +374,12 @@ if test "$USETLS" = "yes"; then
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(libgnutls_libraries,
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(gnutls/gnutls.h,
FOUND=yes
TLSHEADERS=yes,
@@ -418,22 +404,18 @@ if test "$USETLS" = "yes"; then
fi
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(openssl_includes,
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
[INCVAL="yes"],
[INCVAL="no"])
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
CFLAGS="${CFLAGS} -I${INCVAL}"
AC_ARG_WITH(openssl_libraries,
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
[LDFLAGS="${LDFLAGS} -L${withval}"]
[LIBVAL="yes"],
[LIBVAL="no"])
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
PKG_CHECK_MODULES(openssl, openssl,
[LIBS="${LIBS} $openssl_LIBS"]
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
fi
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(openssl/ssl.h,
FOUND=yes
TLSHEADERS=yes,
@@ -442,10 +424,8 @@ if test "$USETLS" = "yes"; then
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
fi
if test "$FOUND" = "yes"; then
AC_SEARCH_LIBS([CRYPTO_set_locking_callback], [crypto],
AC_SEARCH_LIBS([SSL_library_init], [ssl],
FOUND=yes,
FOUND=no),
AC_SEARCH_LIBS([SSL_library_init], [ssl],
FOUND=yes,
FOUND=no)
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
AC_MSG_ERROR([Couldn't find OpenSSL library])
@@ -469,53 +449,6 @@ else
fi
dnl
dnl checks for zlib includes and libraries.
dnl
AC_MSG_CHECKING(whether to use gzip)
AC_ARG_ENABLE(gzip,
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
[USEZLIB=$enableval],
[USEZLIB=yes] )
AC_MSG_RESULT($USEZLIB)
if test "$USEZLIB" = "yes"; then
INCVAL="${LIBPREF}/include"
LIBVAL="${LIBPREF}/lib"
AC_ARG_WITH(zlib_includes,
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
[INCVAL="$withval"])
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
AC_ARG_WITH(zlib_libraries,
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
[LIBVAL="$withval"])
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
AC_CHECK_HEADER(zlib.h,,
AC_MSG_ERROR("zlib header files not found"))
AC_SEARCH_LIBS([deflateBound], [z], ,
AC_MSG_ERROR("zlib library not found"))
else
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
fi
dnl
dnl Some Linux systems require an empty signal handler for SIGCHLD
dnl in order for exit codes to be correctly delivered to parent process.
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to install the handler.
dnl
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
AC_ARG_ENABLE(sigchld-handler,
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
[SIGCHLDHANDLER=$enableval],
[SIGCHLDHANDLER=yes])
AC_MSG_RESULT($SIGCHLDHANDLER)
if test "$SIGCHLDHANDLER" = "yes"; then
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
fi
dnl
dnl Debugging. Default: no
dnl
@@ -612,5 +545,15 @@ dnl
fi
dnl Substitute flags.
AC_SUBST(CFLAGS)
AC_SUBST(CPPFLAGS)
AC_SUBST(LDFLAGS)
AC_SUBST(CXXFLAGS)
AC_SUBST(TAR)
AC_SUBST(AR)
AC_SUBST(ADDSRCS)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@@ -1,9 +1,7 @@
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2012-11-30 14:23:31.000000000 +0100
@@ -76,10 +76,11 @@
++sf;
}
+++ ../libpar2-0.2/par2repairer.cpp 2008-02-06 12:02:53.226050300 +0100
@@ -78,6 +78,7 @@
delete mainpacket;
delete creatorpacket;
@@ -11,11 +9,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
Result Par2Repairer::PreProcess(const CommandLine &commandline)
{
@@ -1259,11 +1260,11 @@
string path;
string name;
@@ -1261,7 +1262,7 @@
DiskFile::SplitFilename(filename, path, name);
cout << "Target: \"" << name << "\" - missing." << endl;
@@ -24,37 +18,12 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
}
++sf;
}
@@ -1802,11 +1803,11 @@
<< "\" - no data found."
<< endl;
@@ -1804,7 +1805,7 @@
}
}
}
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
+ sig_done.emit(name,count, count>0 && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
+ sig_done.emit(name,count, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
sig_progress.emit(1000.0);
return true;
}
// Find out how much data we have found
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2006-01-20 00:38:27.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2012-11-30 14:24:46.000000000 +0100
@@ -34,10 +34,15 @@
sigc::signal<void, std::string> sig_filename;
sigc::signal<void, double> sig_progress;
sigc::signal<void, ParHeaders*> sig_headers;
sigc::signal<void, std::string, int, int> sig_done;
+ // This method allows to determine whether libpar2 includes the patches
+ // ("libpar2-0.2-bugfixes.patch") submitted to libpar2 project.
+ // Use the method in configure scripts for detection.
+ void BugfixesPatchVersion2() { }
+
protected:
// Steps in verifying and repairing files:
// Load packets from the specified file
bool LoadPacketsFromFile(string filename);

View File

@@ -1,9 +1,7 @@
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2012-12-03 10:48:13.000000000 +0100
@@ -50,10 +50,12 @@
outputbuffer = 0;
diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
--- ../libpar2-0.2-original/par2repairer.cpp 2008-10-26 19:54:33.000000000 +0100
+++ ../libpar2-0.2/par2repairer.cpp 2008-10-29 10:24:48.000000000 +0100
@@ -52,6 +52,8 @@
noiselevel = CommandLine::nlNormal;
headers = new ParHeaders;
alreadyloaded = false;
@@ -12,11 +10,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
Par2Repairer::~Par2Repairer(void)
{
delete [] (u8*)inputbuffer;
@@ -404,10 +406,14 @@
{
cout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
@@ -406,6 +408,10 @@
progress = offset;
sig_progress.emit(newfraction);
@@ -27,11 +21,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
}
// Attempt to read the next packet header
PACKET_HEADER header;
@@ -582,10 +588,15 @@
if (noiselevel > CommandLine::nlQuiet)
cout << "No new packets found" << endl;
@@ -584,6 +590,11 @@
delete diskfile;
}
@@ -43,11 +33,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
return true;
}
// Finish loading a recovery packet
bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header)
@@ -831,26 +842,42 @@
// Load packets from each file that was found
@@ -833,9 +844,17 @@
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
@@ -65,10 +51,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
{
string wildcard = name.empty() ? "*.PAR2" : name + ".*.PAR2";
list<string> *files = DiskFile::FindFiles(path, wildcard);
// Load packets from each file that was found
@@ -846,9 +865,17 @@
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
{
LoadPacketsFromFile(*s);
@@ -86,11 +69,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
return true;
}
@@ -864,13 +891,22 @@
// If the filename contains ".par2" anywhere
if (string::npos != filename.find(".par2") ||
@@ -866,9 +893,18 @@
string::npos != filename.find(".PAR2"))
{
LoadPacketsFromFile(filename);
@@ -109,11 +88,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
return true;
}
// Check that the packets are consistent and discard any that are not
bool Par2Repairer::CheckPacketConsistency(void)
@@ -1208,10 +1244,15 @@
// Start verifying the files
@@ -1210,6 +1246,11 @@
sf = sortedfiles.begin();
while (sf != sortedfiles.end())
{
@@ -125,11 +100,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
// Do we have a source file
Par2RepairerSourceFile *sourcefile = *sf;
// What filename does the file use
string filename = sourcefile->TargetFileName();
@@ -1560,10 +1601,14 @@
if (oldfraction != newfraction)
{
@@ -1562,6 +1603,10 @@
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -140,11 +111,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
}
}
// If we fail to find a match, it might be because it was a duplicate of a block
// that we have already found.
@@ -1649,10 +1694,15 @@
return false;
}
@@ -1651,6 +1696,11 @@
}
}
@@ -156,11 +123,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
// Get the Full and 16k hash values of the file
filechecksummer.GetFileHashes(hashfull, hash16k);
// Did we make any matches at all
if (count > 0)
@@ -2289,14 +2339,23 @@
if (oldfraction != newfraction)
{
@@ -2291,10 +2341,19 @@
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -180,11 +143,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
++inputblock;
++inputindex;
}
}
else
@@ -2346,13 +2405,22 @@
if (oldfraction != newfraction)
{
@@ -2348,9 +2407,18 @@
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
sig_progress.emit(newfraction);
@@ -203,11 +162,7 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
++copyblock;
++inputblock;
}
}
@@ -2360,10 +2428,15 @@
if (lastopenfile != NULL)
{
@@ -2362,6 +2430,11 @@
lastopenfile->Close();
}
@@ -219,14 +174,10 @@ diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repai
if (noiselevel > CommandLine::nlQuiet)
cout << "Writing recovered data\r";
// For each output block that has been recomputed
vector<DataBlock*>::iterator outputblock = outputblocks.begin();
diff -aud -U 5 ../libpar2-0.2-with-bugfixes-patch/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2012-12-03 10:47:04.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2012-12-03 10:48:13.000000000 +0100
@@ -186,8 +186,9 @@
u64 progress; // How much data has been processed.
diff -aud ../libpar2-0.2-original/par2repairer.h ../libpar2-0.2/par2repairer.h
--- ../libpar2-0.2-original/par2repairer.h 2006-01-20 00:38:27.000000000 +0100
+++ ../libpar2-0.2/par2repairer.h 2008-10-26 19:01:08.000000000 +0100
@@ -183,6 +183,7 @@
u64 totaldata; // Total amount of data to be processed.
u64 totalsize; // Total data size

View File

@@ -4,7 +4,7 @@ rem
rem Batch file to start nzbget shell
rem
rem Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
rem Copyright (C) 2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
rem Copyright (C) 2009 Andrei Prygounkov <hugbug@users.sourceforge.net>
rem
rem This program is free software; you can redistribute it and/or modify
rem it under the terms of the GNU General Public License as published by
@@ -18,7 +18,7 @@ rem GNU General Public License for more details.
rem
rem You should have received a copy of the GNU General Public License
rem along with this program; if not, write to the Free Software
rem Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
rem Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
rem
rem ####################### Usage instructions #######################

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