Compare commits
112 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
896a7c1988 | ||
|
|
f56c1226b6 | ||
|
|
bc3e4742f0 | ||
|
|
891b16ac76 | ||
|
|
11a32c3537 | ||
|
|
8afc96e1ad | ||
|
|
55d2c9e49c | ||
|
|
9baabee3fd | ||
|
|
ad20cb6644 | ||
|
|
04d2d92524 | ||
|
|
6630a8c2a5 | ||
|
|
67ee86eaeb | ||
|
|
2fcfbc2e1a | ||
|
|
2bb1162adf | ||
|
|
f5e0b67305 | ||
|
|
0b25fb5771 | ||
|
|
27ff29329f | ||
|
|
b52cfbb602 | ||
|
|
c5a1c64a35 | ||
|
|
cbedd9bec5 | ||
|
|
7a90844970 | ||
|
|
45dcb72178 | ||
|
|
d23b5bb58b | ||
|
|
bf7de99182 | ||
|
|
8ddfab4b47 | ||
|
|
57c2dc2d65 | ||
|
|
62236a38f3 | ||
|
|
25ab7bba02 | ||
|
|
fe14f3ee0e | ||
|
|
425120de94 | ||
|
|
2520c8d173 | ||
|
|
7b4ee1c44b | ||
|
|
58a1dcd141 | ||
|
|
3b4f44f276 | ||
|
|
ebcc06686c | ||
|
|
45b3a7dbcd | ||
|
|
16f04f2255 | ||
|
|
a23fcbd095 | ||
|
|
7491c0f7c4 | ||
|
|
83da75a5e5 | ||
|
|
2474c32f60 | ||
|
|
6910f1f0b7 | ||
|
|
c426aeac6a | ||
|
|
0e2716ba31 | ||
|
|
011239d45c | ||
|
|
5bb3d1a9e1 | ||
|
|
43766c7ab9 | ||
|
|
e12eeed65d | ||
|
|
711ecb4025 | ||
|
|
88957699c5 | ||
|
|
fcd6c51d55 | ||
|
|
adda02dd0d | ||
|
|
ab1cff2a7d | ||
|
|
0716a743d8 | ||
|
|
d0e17fde77 | ||
|
|
d6c0aa8a80 | ||
|
|
815bf9b390 | ||
|
|
0aa6e0a8b2 | ||
|
|
8b1aff33fe | ||
|
|
fdc9464576 | ||
|
|
dc6c1a0fe1 | ||
|
|
78a73ac15f | ||
|
|
48891ed7c7 | ||
|
|
d3fd5ba9ac | ||
|
|
2b6f575802 | ||
|
|
f604460d56 | ||
|
|
7472893e8e | ||
|
|
eff074faae | ||
|
|
07c04b40b1 | ||
|
|
754adb545e | ||
|
|
5fc04277c1 | ||
|
|
78f5fd3f71 | ||
|
|
0277c6b9bd | ||
|
|
e34b4b8ae7 | ||
|
|
c0de18f3aa | ||
|
|
4b78918347 | ||
|
|
6606a883c5 | ||
|
|
30c1a64d31 | ||
|
|
91dbcc40aa | ||
|
|
b0f5119ec0 | ||
|
|
ed9aba18b8 | ||
|
|
d507325378 | ||
|
|
0a0546168b | ||
|
|
5abbbe80d1 | ||
|
|
4a6413f654 | ||
|
|
6c60244b26 | ||
|
|
a384f0e6e9 | ||
|
|
2a56410543 | ||
|
|
7ef22fc1e0 | ||
|
|
5051d698c0 | ||
|
|
1a87c08bc2 | ||
|
|
ed0c5908ce | ||
|
|
f571ced9c5 | ||
|
|
3110181a9f | ||
|
|
fcb7966f70 | ||
|
|
be2945a16f | ||
|
|
6b3326ad42 | ||
|
|
3e81a03087 | ||
|
|
3778430ead | ||
|
|
31bd251f37 | ||
|
|
f49f01ec85 | ||
|
|
1bd6721af9 | ||
|
|
4a069266d8 | ||
|
|
d10d7f3f02 | ||
|
|
05adbb1325 | ||
|
|
ca8719b42b | ||
|
|
ec80850e76 | ||
|
|
ab75a8b3e5 | ||
|
|
d00c8119fa | ||
|
|
2b7d188677 | ||
|
|
12c09693bd | ||
|
|
87793b3dc3 |
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -189,7 +189,7 @@ void ArticleDownloader::Run()
|
||||
if (bConnected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
detail("Downloading %s @ %s", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
detail("Downloading %s @ %s", m_szInfoName, m_pConnection->GetHost());
|
||||
Status = Download();
|
||||
}
|
||||
|
||||
@@ -407,7 +407,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetHost());
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
@@ -441,7 +441,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
|
||||
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
@@ -469,7 +469,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
|
||||
if (!bEnd && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetHost());
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
@@ -496,18 +496,18 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment);
|
||||
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetHost(), szComment);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2) || !strncmp(szResponse, "43", 2))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(szResponse, "2", 1))
|
||||
@@ -518,7 +518,7 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
@@ -609,7 +609,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
long iArticleFilesize = atol(pb);
|
||||
if (!Util::SetFileSize(m_szOutputFilename, iArticleFilesize))
|
||||
if (!Util::CreateSparseFile(m_szOutputFilename, iArticleFilesize))
|
||||
{
|
||||
error("Could not create file %s!", m_szOutputFilename);
|
||||
return false;
|
||||
@@ -825,17 +825,18 @@ void ArticleDownloader::CompleteFileParts()
|
||||
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
char szNZBName[1024];
|
||||
char szNZBDestDir[1024];
|
||||
// the locking is needed for accessing the memebers of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
m_pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
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 InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
if (!g_pOptions->GetDecode())
|
||||
|
||||
350
BinRpc.cpp
@@ -48,19 +48,23 @@
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Util.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern UrlCoordinator* g_pUrlCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
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", "Version", "Post-queue", "Write log", "Scan",
|
||||
"Pause/Unpause postprocessor", "History" };
|
||||
"Edit queue", "Log", "Quit", "Reload", "Version", "Post-queue", "Write log", "Scan",
|
||||
"Pause/Unpause postprocessor", "History", "Download URL", "URL-queue" };
|
||||
|
||||
const unsigned int g_iMessageRequestSizes[] =
|
||||
{ 0,
|
||||
@@ -72,11 +76,14 @@ const unsigned int g_iMessageRequestSizes[] =
|
||||
sizeof(SNZBEditQueueRequest),
|
||||
sizeof(SNZBLogRequest),
|
||||
sizeof(SNZBShutdownRequest),
|
||||
sizeof(SNZBReloadRequest),
|
||||
sizeof(SNZBVersionRequest),
|
||||
sizeof(SNZBPostQueueRequest),
|
||||
sizeof(SNZBWriteLogRequest),
|
||||
sizeof(SNZBScanRequest),
|
||||
sizeof(SNZBHistoryRequest)
|
||||
sizeof(SNZBHistoryRequest),
|
||||
sizeof(SNZBDownloadUrlRequest),
|
||||
sizeof(SNZBUrlQueueRequest)
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
@@ -94,13 +101,13 @@ void BinRpcProcessor::Execute()
|
||||
// Make sure this is a nzbget request from a client
|
||||
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -156,6 +163,10 @@ void BinRpcProcessor::Dispatch()
|
||||
command = new ShutdownBinCommand();
|
||||
break;
|
||||
|
||||
case eRemoteRequestReload:
|
||||
command = new ReloadBinCommand();
|
||||
break;
|
||||
|
||||
case eRemoteRequestVersion:
|
||||
command = new VersionBinCommand();
|
||||
break;
|
||||
@@ -176,6 +187,14 @@ 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;
|
||||
@@ -276,6 +295,7 @@ void DumpDebugBinCommand::Execute()
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
g_pUrlCoordinator->LogDebugInfo();
|
||||
SendBoolResponse(true, "Debug-Command completed successfully");
|
||||
}
|
||||
|
||||
@@ -291,6 +311,18 @@ 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;
|
||||
@@ -331,11 +363,22 @@ void DownloadBinCommand::Execute()
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
int iPriority = ntohl(DownloadRequest.m_iPriority);
|
||||
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
|
||||
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
|
||||
|
||||
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, ntohl(DownloadRequest.m_bAddFirst));
|
||||
delete pNZBFile;
|
||||
|
||||
@@ -369,12 +412,24 @@ 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();
|
||||
|
||||
@@ -386,6 +441,7 @@ 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;
|
||||
@@ -429,13 +485,17 @@ 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());
|
||||
@@ -484,7 +544,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++)
|
||||
@@ -497,6 +557,13 @@ 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);
|
||||
@@ -525,6 +592,11 @@ void ListBinCommand::Execute()
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pRegEx)
|
||||
{
|
||||
delete pRegEx;
|
||||
}
|
||||
|
||||
ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries);
|
||||
ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries);
|
||||
ListResponse.m_iNrTrailingFileEntries = htonl(iNrFileEntries);
|
||||
@@ -671,28 +743,23 @@ void EditQueueBinCommand::Execute()
|
||||
return;
|
||||
}
|
||||
|
||||
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
|
||||
int iNrIDEntries = ntohl(EditQueueRequest.m_iNrTrailingIDEntries);
|
||||
int iNrNameEntries = ntohl(EditQueueRequest.m_iNrTrailingNameEntries);
|
||||
int iNameEntriesLen = ntohl(EditQueueRequest.m_iTrailingNameEntriesLen);
|
||||
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 (iNrEntries * sizeof(int32_t) + iTextLen != iBufLength)
|
||||
if (iNrIDEntries * sizeof(int32_t) + iTextLen + iNameEntriesLen != iBufLength)
|
||||
{
|
||||
error("Invalid struct size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iNrEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
|
||||
return;
|
||||
}
|
||||
|
||||
char* pBuf = (char*)malloc(iBufLength);
|
||||
char* szText = NULL;
|
||||
int32_t* pIDs = NULL;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
char* pBufPtr = pBuf;
|
||||
@@ -712,26 +779,51 @@ void EditQueueBinCommand::Execute()
|
||||
}
|
||||
bool bOK = NeedBytes == 0;
|
||||
|
||||
if (iNrIDEntries <= 0 && iNrNameEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs/Names specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
szText = iTextLen > 0 ? pBuf : NULL;
|
||||
pIDs = (int32_t*)(pBuf + iTextLen);
|
||||
}
|
||||
char* szText = iTextLen > 0 ? pBuf : NULL;
|
||||
int32_t* pIDs = (int32_t*)(pBuf + iTextLen);
|
||||
char* pNames = (pBuf + iTextLen + iNrIDEntries * sizeof(int32_t));
|
||||
|
||||
IDList cIDList;
|
||||
cIDList.reserve(iNrEntries);
|
||||
for (int i = 0; i < iNrEntries; i++)
|
||||
{
|
||||
cIDList.push_back(ntohl(pIDs[i]));
|
||||
}
|
||||
IDList cIDList;
|
||||
NameList cNameList;
|
||||
|
||||
if (iAction < eRemoteEditActionPostMoveOffset)
|
||||
{
|
||||
bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset, szText);
|
||||
}
|
||||
else
|
||||
{
|
||||
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
|
||||
if (iNrIDEntries > 0)
|
||||
{
|
||||
cIDList.reserve(iNrIDEntries);
|
||||
for (int i = 0; i < iNrIDEntries; 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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
|
||||
}
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
@@ -742,6 +834,13 @@ 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");
|
||||
}
|
||||
}
|
||||
@@ -903,8 +1002,10 @@ void ScanBinCommand::Execute()
|
||||
return;
|
||||
}
|
||||
|
||||
g_pPrePostProcessor->ScanNZBDir();
|
||||
SendBoolResponse(true, "Scan-Command scheduled successfully");
|
||||
bool bSyncMode = ntohl(ScanRequest.m_bSyncMode);
|
||||
|
||||
g_pPrePostProcessor->ScanNZBDir(bSyncMode);
|
||||
SendBoolResponse(true, bSyncMode ? "Scan-Command completed" : "Scan-Command scheduled successfully");
|
||||
}
|
||||
|
||||
void HistoryBinCommand::Execute()
|
||||
@@ -928,15 +1029,14 @@ void HistoryBinCommand::Execute()
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
// calculate required buffer size for nzbs
|
||||
int iNrNZBEntries = pDownloadQueue->GetHistoryList()->size();
|
||||
bufsize += iNrNZBEntries * sizeof(SNZBHistoryResponseEntry);
|
||||
int iNrEntries = pDownloadQueue->GetHistoryList()->size();
|
||||
bufsize += iNrEntries * sizeof(SNZBHistoryResponseEntry);
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
bufsize += strlen(pNZBInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pNZBInfo->GetDestDir()) + 1;
|
||||
bufsize += strlen(pNZBInfo->GetCategory()) + 1;
|
||||
bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1;
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
char szNicename[1024];
|
||||
pHistoryInfo->GetName(szNicename, sizeof(szNicename));
|
||||
bufsize += strlen(szNicename) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
@@ -945,36 +1045,42 @@ void HistoryBinCommand::Execute()
|
||||
char* bufptr = buf;
|
||||
|
||||
// write nzb entries
|
||||
for (NZBInfoList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
unsigned long iSizeHi, iSizeLo;
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr;
|
||||
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);
|
||||
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->GetScriptStatus());
|
||||
}
|
||||
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
|
||||
{
|
||||
UrlInfo* pUrlInfo = pHistoryInfo->GetUrlInfo();
|
||||
pListAnswer->m_iUrlStatus = htonl(pUrlInfo->GetStatus());
|
||||
}
|
||||
|
||||
bufptr += sizeof(SNZBHistoryResponseEntry);
|
||||
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);
|
||||
strcpy(bufptr, szNicename);
|
||||
bufptr += ntohl(pListAnswer->m_iNicenameLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4);
|
||||
pListAnswer->m_iNicenameLen = htonl(ntohl(pListAnswer->m_iNicenameLen) + 4 - (size_t)bufptr % 4);
|
||||
memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data"
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
@@ -982,7 +1088,7 @@ void HistoryBinCommand::Execute()
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
HistoryResponse.m_iNrTrailingEntries = htonl(iNrNZBEntries);
|
||||
HistoryResponse.m_iNrTrailingEntries = htonl(iNrEntries);
|
||||
HistoryResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
@@ -996,3 +1102,113 @@ void HistoryBinCommand::Execute()
|
||||
|
||||
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
|
||||
send(m_iSocket, (char*) &UrlQueueResponse, sizeof(UrlQueueResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
18
BinRpc.h
@@ -110,6 +110,12 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ReloadBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
@@ -140,4 +146,16 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadUrlBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UrlQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
139
ChangeLog
@@ -1,3 +1,142 @@
|
||||
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>
|
||||
|
||||
100
Connection.cpp
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -66,7 +66,7 @@ Mutex* Connection::m_pMutexGetHostByName = NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void Connection::Init(bool bTLS)
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Initializing global connection data");
|
||||
|
||||
@@ -87,19 +87,16 @@ void Connection::Init(bool bTLS)
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (bTLS)
|
||||
debug("Initializing TLS library");
|
||||
char* szErrStr;
|
||||
int iRes = tls_lib_init(&szErrStr);
|
||||
bTLSLibInitialized = iRes == TLS_EOK;
|
||||
if (!bTLSLibInitialized)
|
||||
{
|
||||
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)
|
||||
{
|
||||
error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error");
|
||||
if (szErrStr)
|
||||
{
|
||||
free(szErrStr);
|
||||
}
|
||||
free(szErrStr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -134,11 +131,13 @@ void Connection::Final()
|
||||
#endif
|
||||
}
|
||||
|
||||
Connection::Connection(NetAddress* pNetAddress)
|
||||
Connection::Connection(const char* szHost, int iPort, bool bTLS)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = pNetAddress;
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
m_bTLS = bTLS;
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
@@ -150,13 +149,20 @@ Connection::Connection(NetAddress* pNetAddress)
|
||||
m_pTLS = NULL;
|
||||
m_bTLSError = false;
|
||||
#endif
|
||||
|
||||
if (szHost)
|
||||
{
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
}
|
||||
|
||||
Connection::Connection(SOCKET iSocket, bool bAutoClose)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = NULL;
|
||||
m_szHost = NULL;
|
||||
m_iPort = 0;
|
||||
m_bTLS = false;
|
||||
m_eStatus = csConnected;
|
||||
m_iSocket = iSocket;
|
||||
m_iBufAvail = 0;
|
||||
@@ -173,7 +179,11 @@ Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected && m_bAutoClose)
|
||||
if (m_szHost)
|
||||
{
|
||||
free(m_szHost);
|
||||
}
|
||||
if (m_bAutoClose)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
@@ -191,14 +201,20 @@ bool Connection::Connect()
|
||||
debug("Connecting");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bRes = DoConnect();
|
||||
|
||||
if (bRes)
|
||||
{
|
||||
m_eStatus = csConnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
Connection::DoDisconnect();
|
||||
}
|
||||
|
||||
return bRes;
|
||||
}
|
||||
@@ -208,7 +224,9 @@ bool Connection::Disconnect()
|
||||
debug("Disconnecting");
|
||||
|
||||
if (m_eStatus == csDisconnected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bRes = DoDisconnect();
|
||||
|
||||
@@ -357,12 +375,12 @@ bool Connection::DoConnect()
|
||||
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
addr_hints.ai_socktype = SOCK_STREAM,
|
||||
|
||||
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
|
||||
sprintf(iPortStr, "%d", m_iPort);
|
||||
|
||||
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
|
||||
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
|
||||
if (res != 0)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Could not resolve hostname %s", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -390,8 +408,8 @@ bool Connection::DoConnect()
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
sSocketAddress.sin_port = htons(m_iPort);
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return false;
|
||||
@@ -400,7 +418,7 @@ bool Connection::DoConnect()
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Socket creation failed for %s", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -415,7 +433,7 @@ bool Connection::DoConnect()
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Connection to %s failed", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Connection to %s failed", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -433,6 +451,13 @@ bool Connection::DoConnect()
|
||||
ReportError("setsockopt failed", NULL, true, 0);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_bTLS && !StartTLS())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -533,6 +558,15 @@ char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
|
||||
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
|
||||
{
|
||||
*iBufLen = m_iBufAvail;
|
||||
*pBuffer = m_szBufPtr;
|
||||
m_iBufAvail = 0;
|
||||
};
|
||||
|
||||
|
||||
int Connection::DoBind()
|
||||
{
|
||||
debug("Do binding");
|
||||
@@ -546,12 +580,12 @@ int Connection::DoBind()
|
||||
addr_hints.ai_socktype = SOCK_STREAM,
|
||||
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
|
||||
|
||||
sprintf(iPortStr, "%d", m_pNetAddress->GetPort());
|
||||
sprintf(iPortStr, "%d", m_iPort);
|
||||
|
||||
int res = getaddrinfo(m_pNetAddress->GetHost(), iPortStr, &addr_hints, &addr_list);
|
||||
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
|
||||
if (res != 0)
|
||||
{
|
||||
error( "Could not resolve hostname %s", m_pNetAddress->GetHost() );
|
||||
error("Could not resolve hostname %s", m_szHost);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -582,24 +616,24 @@ int Connection::DoBind()
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
|
||||
if (!m_szHost || strlen(m_szHost) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
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_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Socket creation failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -617,13 +651,13 @@ int Connection::DoBind()
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Binding socket failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(m_iSocket, 10) < 0)
|
||||
{
|
||||
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
14
Connection.h
@@ -27,7 +27,6 @@
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
@@ -46,8 +45,10 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
SOCKET m_iSocket;
|
||||
bool m_bTLS;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
@@ -84,10 +85,10 @@ protected:
|
||||
#endif
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
Connection(const char* szHost, int iPort, bool bTLS);
|
||||
Connection(SOCKET iSocket, bool bAutoClose);
|
||||
virtual ~Connection();
|
||||
static void Init(bool bTLS);
|
||||
static void Init();
|
||||
static void Final();
|
||||
bool Connect();
|
||||
bool Disconnect();
|
||||
@@ -96,10 +97,13 @@ public:
|
||||
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);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
172
DiskState.cpp
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -82,7 +82,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 14);
|
||||
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 16);
|
||||
|
||||
// save nzb-infos
|
||||
SaveNZBList(pDownloadQueue, outfile);
|
||||
@@ -93,6 +93,9 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
// save post-queue
|
||||
SavePostQueue(pDownloadQueue, outfile);
|
||||
|
||||
// save url-queue
|
||||
SaveUrlQueue(pDownloadQueue, outfile);
|
||||
|
||||
// save history
|
||||
SaveHistory(pDownloadQueue, outfile);
|
||||
|
||||
@@ -102,6 +105,7 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
fclose(outfile);
|
||||
|
||||
if (pDownloadQueue->GetFileQueue()->empty() &&
|
||||
pDownloadQueue->GetUrlQueue()->empty() &&
|
||||
pDownloadQueue->GetPostQueue()->empty() &&
|
||||
pDownloadQueue->GetHistoryList()->empty())
|
||||
{
|
||||
@@ -132,7 +136,7 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
int iFormatVersion = ParseFormatVersion(FileSignatur);
|
||||
if (iFormatVersion < 3 || iFormatVersion > 14)
|
||||
if (iFormatVersion < 3 || iFormatVersion > 16)
|
||||
{
|
||||
error("Could not load diskstate due to file version mismatch");
|
||||
fclose(infile);
|
||||
@@ -156,10 +160,16 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
LoadOldPostQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
if (iFormatVersion >= 15)
|
||||
{
|
||||
// load url-queue
|
||||
if (!LoadUrlQueue(pDownloadQueue, infile, iFormatVersion)) goto error;
|
||||
}
|
||||
|
||||
if (iFormatVersion >= 9)
|
||||
{
|
||||
// load history
|
||||
if (!LoadHistory(pDownloadQueue, infile)) goto error;
|
||||
if (!LoadHistory(pDownloadQueue, infile, iFormatVersion)) goto error;
|
||||
|
||||
// load parked file-infos
|
||||
if (!LoadFileQueue(pDownloadQueue, pDownloadQueue->GetParkedFiles(), infile, iFormatVersion)) goto error;
|
||||
@@ -191,7 +201,7 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetQueuedFilename());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetUserNZBName());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetName());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
|
||||
fprintf(outfile, "%i\n", pNZBInfo->GetPostProcess() ? 1 : 0);
|
||||
fprintf(outfile, "%i\n", (int)pNZBInfo->GetParStatus());
|
||||
@@ -274,7 +284,10 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pNZBInfo->SetUserNZBName(buf);
|
||||
if (strlen(buf) > 0)
|
||||
{
|
||||
pNZBInfo->SetName(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (iFormatVersion >= 4)
|
||||
@@ -425,7 +438,7 @@ bool DiskState::LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQue
|
||||
{
|
||||
if (fscanf(infile, "%i,%i,%i\n", &id, &iNZBIndex, &paused) != 3) goto error;
|
||||
}
|
||||
if (iNZBIndex < 0 || iNZBIndex > pDownloadQueue->GetNZBInfoList()->size()) goto error;
|
||||
if (iNZBIndex > pDownloadQueue->GetNZBInfoList()->size()) goto error;
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
@@ -807,6 +820,94 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiskState::SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile)
|
||||
{
|
||||
debug("Saving url-queue to disk");
|
||||
|
||||
fprintf(outfile, "%i\n", pDownloadQueue->GetUrlQueue()->size());
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
UrlInfo* pUrlInfo = *it;
|
||||
SaveUrlInfo(pUrlInfo, outfile);
|
||||
}
|
||||
}
|
||||
|
||||
bool DiskState::LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion)
|
||||
{
|
||||
debug("Loading url-queue from disk");
|
||||
|
||||
bool bSkipUrlQueue = !g_pOptions->GetReloadUrlQueue();
|
||||
int size;
|
||||
|
||||
// load url-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
UrlInfo* pUrlInfo = NULL;
|
||||
if (!bSkipUrlQueue)
|
||||
{
|
||||
pUrlInfo = new UrlInfo();
|
||||
}
|
||||
|
||||
if (!LoadUrlInfo(pUrlInfo, infile, iFormatVersion)) goto error;
|
||||
|
||||
if (!bSkipUrlQueue)
|
||||
{
|
||||
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
error("Error reading diskstate for url-queue");
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiskState::SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile)
|
||||
{
|
||||
fprintf(outfile, "%i,%i\n", (int)pUrlInfo->GetStatus(), pUrlInfo->GetPriority());
|
||||
fprintf(outfile, "%i,%i\n", (int)pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused());
|
||||
fprintf(outfile, "%s\n", pUrlInfo->GetURL());
|
||||
fprintf(outfile, "%s\n", pUrlInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pUrlInfo->GetCategory());
|
||||
}
|
||||
|
||||
bool DiskState::LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion)
|
||||
{
|
||||
char buf[10240];
|
||||
|
||||
int iStatus, iPriority;
|
||||
if (fscanf(infile, "%i,%i\n", &iStatus, &iPriority) != 2) goto error;
|
||||
if (pUrlInfo) pUrlInfo->SetStatus((UrlInfo::EStatus)iStatus);
|
||||
if (pUrlInfo) pUrlInfo->SetPriority(iPriority);
|
||||
|
||||
if (iFormatVersion >= 16)
|
||||
{
|
||||
int iAddTop, iAddPaused;
|
||||
if (fscanf(infile, "%i,%i\n", &iAddTop, &iAddPaused) != 2) goto error;
|
||||
if (pUrlInfo) pUrlInfo->SetAddTop(iAddTop);
|
||||
if (pUrlInfo) pUrlInfo->SetAddPaused(iAddPaused);
|
||||
}
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (pUrlInfo) pUrlInfo->SetURL(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (pUrlInfo) pUrlInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (pUrlInfo) pUrlInfo->SetCategory(buf);
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
return false;
|
||||
}
|
||||
|
||||
void DiskState::SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile)
|
||||
{
|
||||
debug("Saving history to disk");
|
||||
@@ -814,14 +915,25 @@ void DiskState::SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile)
|
||||
fprintf(outfile, "%i\n", pDownloadQueue->GetHistoryList()->size());
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
int iNZBIndex = FindNZBInfoIndex(pDownloadQueue, pNZBInfo);
|
||||
fprintf(outfile, "%i\n", iNZBIndex);
|
||||
fprintf(outfile, "%i\n", (int)pNZBInfo->GetHistoryTime());
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
|
||||
fprintf(outfile, "%i\n", (int)pHistoryInfo->GetKind());
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
int iNZBIndex = FindNZBInfoIndex(pDownloadQueue, pHistoryInfo->GetNZBInfo());
|
||||
fprintf(outfile, "%i\n", iNZBIndex);
|
||||
}
|
||||
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
|
||||
{
|
||||
SaveUrlInfo(pHistoryInfo->GetUrlInfo(), outfile);
|
||||
}
|
||||
|
||||
fprintf(outfile, "%i\n", (int)pHistoryInfo->GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile)
|
||||
bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion)
|
||||
{
|
||||
debug("Loading history from disk");
|
||||
|
||||
@@ -829,23 +941,41 @@ bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile)
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
unsigned int iNZBIndex;
|
||||
if (fscanf(infile, "%i\n", &iNZBIndex) != 1) goto error;
|
||||
HistoryInfo* pHistoryInfo = NULL;
|
||||
HistoryInfo::EKind eKind = HistoryInfo::hkNZBInfo;
|
||||
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetNZBInfoList()->at(iNZBIndex - 1);
|
||||
if (iFormatVersion >= 15)
|
||||
{
|
||||
int iKind = 0;
|
||||
if (fscanf(infile, "%i\n", &iKind) != 1) goto error;
|
||||
eKind = (HistoryInfo::EKind)iKind;
|
||||
}
|
||||
|
||||
if (eKind == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
unsigned int iNZBIndex;
|
||||
if (fscanf(infile, "%i\n", &iNZBIndex) != 1) goto error;
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetNZBInfoList()->at(iNZBIndex - 1);
|
||||
pHistoryInfo = new HistoryInfo(pNZBInfo);
|
||||
}
|
||||
else if (eKind == HistoryInfo::hkUrlInfo)
|
||||
{
|
||||
UrlInfo* pUrlInfo = new UrlInfo();
|
||||
if (!LoadUrlInfo(pUrlInfo, infile, iFormatVersion)) goto error;
|
||||
pHistoryInfo = new HistoryInfo(pUrlInfo);
|
||||
}
|
||||
|
||||
int iTime;
|
||||
if (fscanf(infile, "%i\n", &iTime) != 1) goto error;
|
||||
pNZBInfo->SetHistoryTime((time_t)iTime);
|
||||
pHistoryInfo->SetTime((time_t)iTime);
|
||||
|
||||
pNZBInfo->AddReference();
|
||||
pDownloadQueue->GetHistoryList()->push_back(pNZBInfo);
|
||||
pDownloadQueue->GetHistoryList()->push_back(pHistoryInfo);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
error:
|
||||
error("Error reading diskstate for post-processor queue");
|
||||
error("Error reading diskstate for history");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -888,7 +1018,7 @@ bool DiskState::DiscardDownloadQueue()
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
int iFormatVersion = ParseFormatVersion(FileSignatur);
|
||||
if (3 <= iFormatVersion && iFormatVersion <= 14)
|
||||
if (3 <= iFormatVersion && iFormatVersion <= 16)
|
||||
{
|
||||
// skip nzb-infos
|
||||
int size = 0;
|
||||
@@ -904,7 +1034,7 @@ bool DiskState::DiscardDownloadQueue()
|
||||
}
|
||||
if (iFormatVersion >= 13)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) break; // UserNZBname
|
||||
if (!fgets(buf, sizeof(buf), infile)) break; // name
|
||||
}
|
||||
if (iFormatVersion >= 4)
|
||||
{
|
||||
|
||||
@@ -41,8 +41,12 @@ private:
|
||||
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
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 SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile);
|
||||
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
|
||||
public:
|
||||
|
||||
252
DownloadInfo.cpp
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
@@ -49,6 +50,8 @@ extern Options* g_pOptions;
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
int NZBInfo::m_iIDGen = 0;
|
||||
int PostInfo::m_iIDGen = 0;
|
||||
int UrlInfo::m_iIDGen = 0;
|
||||
int HistoryInfo::m_iIDGen = 0;
|
||||
|
||||
NZBParameter::NZBParameter(const char* szName)
|
||||
{
|
||||
@@ -121,7 +124,7 @@ NZBInfo::NZBInfo()
|
||||
m_szFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szCategory = strdup("");
|
||||
m_szUserNZBName = strdup("");
|
||||
m_szName = NULL;
|
||||
m_iFileCount = 0;
|
||||
m_iParkedFileCount = 0;
|
||||
m_lSize = 0;
|
||||
@@ -133,7 +136,6 @@ NZBInfo::NZBInfo()
|
||||
m_bParCleanup = false;
|
||||
m_bCleanupDisk = false;
|
||||
m_szQueuedFilename = strdup("");
|
||||
m_tHistoryTime = 0;
|
||||
m_Owner = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDMessageGen = 0;
|
||||
@@ -157,9 +159,9 @@ NZBInfo::~NZBInfo()
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
if (m_szUserNZBName)
|
||||
if (m_szName)
|
||||
{
|
||||
free(m_szUserNZBName);
|
||||
free(m_szName);
|
||||
}
|
||||
if (m_szQueuedFilename)
|
||||
{
|
||||
@@ -225,6 +227,23 @@ void NZBInfo::SetFilename(const char * szFilename)
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
|
||||
if (!m_szName)
|
||||
{
|
||||
char szNZBNicename[1024];
|
||||
MakeNiceNZBName(m_szFilename, szNZBNicename, sizeof(szNZBNicename), true);
|
||||
szNZBNicename[1024-1] = '\0';
|
||||
SetName(szNZBNicename);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::SetName(const char* szName)
|
||||
{
|
||||
if (m_szName)
|
||||
{
|
||||
free(m_szName);
|
||||
}
|
||||
m_szName = szName ? strdup(szName) : NULL;
|
||||
}
|
||||
|
||||
void NZBInfo::SetCategory(const char* szCategory)
|
||||
@@ -236,15 +255,6 @@ void NZBInfo::SetCategory(const char* szCategory)
|
||||
m_szCategory = strdup(szCategory);
|
||||
}
|
||||
|
||||
void NZBInfo::SetUserNZBName(const char* szUserNZBName)
|
||||
{
|
||||
if (m_szUserNZBName)
|
||||
{
|
||||
free(m_szUserNZBName);
|
||||
}
|
||||
m_szUserNZBName = strdup(szUserNZBName);
|
||||
}
|
||||
|
||||
void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
|
||||
{
|
||||
if (m_szQueuedFilename)
|
||||
@@ -254,52 +264,23 @@ void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
|
||||
m_szQueuedFilename = strdup(szQueuedFilename);
|
||||
}
|
||||
|
||||
void NZBInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(strlen(m_szUserNZBName) > 0 ? m_szUserNZBName : m_szFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize, bool bRemoveExt)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = Util::BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// OK, using stripped name
|
||||
}
|
||||
else
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
if (bRemoveExt)
|
||||
{
|
||||
// wipe out ".nzb"
|
||||
char* p = strrchr(postname, '.');
|
||||
if (p && !strcasecmp(p, ".nzb")) *p = '\0';
|
||||
}
|
||||
|
||||
Util::MakeValidFilename(postname, '_', false);
|
||||
|
||||
// if the resulting name is empty, use basename without cleaning up "msgid_"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
Util::MakeValidFilename(postname, '_', false);
|
||||
|
||||
// if the resulting name is STILL empty, use "noname"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
strncpy(postname, "noname", 1024);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
@@ -318,15 +299,13 @@ void NZBInfo::BuildDestDirName()
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
GetNiceNZBName(szNiceNZBName, 1024);
|
||||
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
|
||||
{
|
||||
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, szNiceNZBName);
|
||||
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), GetName());
|
||||
}
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
@@ -716,19 +695,12 @@ void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
|
||||
void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
|
||||
{
|
||||
std::map<int, GroupInfo*> groupMap;
|
||||
|
||||
for (FileQueue::iterator it = GetFileQueue()->begin(); it != GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
GroupInfo* pGroupInfo = NULL;
|
||||
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
|
||||
{
|
||||
GroupInfo* pGroupInfo1 = *itg;
|
||||
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
GroupInfo *&pGroupInfo = groupMap[pFileInfo->GetNZBInfo()->GetID()];
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupInfo = new GroupInfo();
|
||||
@@ -789,3 +761,151 @@ void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UrlInfo::UrlInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
m_szURL = NULL;
|
||||
m_szNZBFilename = strdup("");
|
||||
m_szCategory = strdup("");
|
||||
m_iPriority = 0;
|
||||
m_bAddTop = false;
|
||||
m_bAddPaused = false;
|
||||
m_eStatus = aiUndefined;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
UrlInfo::~ UrlInfo()
|
||||
{
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlInfo::SetURL(const char* szURL)
|
||||
{
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
m_szURL = strdup(szURL);
|
||||
}
|
||||
|
||||
void UrlInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void UrlInfo::SetNZBFilename(const char* szNZBFilename)
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void UrlInfo::SetCategory(const char* szCategory)
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
m_szCategory = strdup(szCategory);
|
||||
}
|
||||
|
||||
void UrlInfo::GetName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceName(m_szURL, m_szNZBFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void UrlInfo::MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize)
|
||||
{
|
||||
URL url(szURL);
|
||||
|
||||
if (strlen(szNZBFilename) > 0)
|
||||
{
|
||||
char szNZBNicename[1024];
|
||||
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNicename, sizeof(szNZBNicename), true);
|
||||
snprintf(szBuffer, iSize, "%s @ %s", szNZBNicename, url.GetHost());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, iSize, "%s%s", url.GetHost(), url.GetResource());
|
||||
}
|
||||
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
|
||||
HistoryInfo::HistoryInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
m_eKind = hkNZBInfo;
|
||||
m_pInfo = pNZBInfo;
|
||||
pNZBInfo->AddReference();
|
||||
m_tTime = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
HistoryInfo::HistoryInfo(UrlInfo* pUrlInfo)
|
||||
{
|
||||
m_eKind = hkUrlInfo;
|
||||
m_pInfo = pUrlInfo;
|
||||
m_tTime = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
HistoryInfo::~HistoryInfo()
|
||||
{
|
||||
if (m_eKind == hkNZBInfo && m_pInfo)
|
||||
{
|
||||
((NZBInfo*)m_pInfo)->Release();
|
||||
}
|
||||
else if (m_eKind == hkUrlInfo && m_pInfo)
|
||||
{
|
||||
delete (UrlInfo*)m_pInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInfo::GetName(char* szBuffer, int iSize)
|
||||
{
|
||||
if (m_eKind == hkNZBInfo)
|
||||
{
|
||||
strncpy(szBuffer, GetNZBInfo()->GetName(), iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
else if (m_eKind == hkUrlInfo)
|
||||
{
|
||||
GetUrlInfo()->GetName(szBuffer, iSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, "<unknown>", iSize);
|
||||
}
|
||||
}
|
||||
|
||||
100
DownloadInfo.h
@@ -231,7 +231,7 @@ private:
|
||||
int m_iID;
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szUserNZBName;
|
||||
char* m_szName;
|
||||
char* m_szDestDir;
|
||||
char* m_szCategory;
|
||||
int m_iFileCount;
|
||||
@@ -245,7 +245,6 @@ private:
|
||||
bool m_bDeleted;
|
||||
bool m_bParCleanup;
|
||||
bool m_bCleanupDisk;
|
||||
time_t m_tHistoryTime;
|
||||
NZBInfoList* m_Owner;
|
||||
NZBParameterList m_ppParameters;
|
||||
Mutex m_mutexLog;
|
||||
@@ -264,14 +263,13 @@ public:
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize); // needs locking (for shared objects)
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
|
||||
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
|
||||
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
|
||||
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
|
||||
void SetCategory(const char* szCategory); // needs locking (for shared objects)
|
||||
const char* GetUserNZBName() { return m_szUserNZBName; } // needs locking (for shared objects)
|
||||
void SetUserNZBName(const char* szUserNZBName); // 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; }
|
||||
@@ -295,8 +293,6 @@ public:
|
||||
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
|
||||
bool GetCleanupDisk() { return m_bCleanupDisk; }
|
||||
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
|
||||
time_t GetHistoryTime() { return m_tHistoryTime; }
|
||||
void SetHistoryTime(time_t tHistoryTime) { m_tHistoryTime = tHistoryTime; }
|
||||
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
|
||||
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
|
||||
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
|
||||
@@ -423,7 +419,91 @@ typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
typedef std::deque<NZBInfo*> HistoryList;
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
class UrlInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed,
|
||||
aiRetry
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szURL;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
bool m_bAddTop;
|
||||
bool m_bAddPaused;
|
||||
EStatus m_eStatus;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
UrlInfo();
|
||||
~UrlInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
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; }
|
||||
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);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
};
|
||||
|
||||
typedef std::deque<UrlInfo*> UrlQueue;
|
||||
|
||||
class HistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
hkUnknown,
|
||||
hkNZBInfo,
|
||||
hkUrlInfo
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
EKind m_eKind;
|
||||
void* m_pInfo;
|
||||
time_t m_tTime;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
HistoryInfo(NZBInfo* pNZBInfo);
|
||||
HistoryInfo(UrlInfo* pUrlInfo);
|
||||
~HistoryInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
EKind GetKind() { return m_eKind; }
|
||||
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
|
||||
UrlInfo* GetUrlInfo() { return (UrlInfo*)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;
|
||||
|
||||
class DownloadQueue
|
||||
{
|
||||
@@ -433,6 +513,7 @@ protected:
|
||||
PostQueue m_PostQueue;
|
||||
HistoryList m_HistoryList;
|
||||
FileQueue m_ParkedFiles;
|
||||
UrlQueue m_UrlQueue;
|
||||
|
||||
public:
|
||||
NZBInfoList* GetNZBInfoList() { return &m_NZBInfoList; }
|
||||
@@ -440,6 +521,7 @@ 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);
|
||||
};
|
||||
|
||||
|
||||
12
Frontend.cpp
@@ -87,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->GetServerIP(), g_pOptions->GetServerPort());
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
@@ -235,14 +235,13 @@ 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_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
@@ -313,8 +312,7 @@ bool Frontend::RequestMessages()
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
@@ -410,5 +408,5 @@ bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID, false);
|
||||
}
|
||||
|
||||
23
Log.cpp
@@ -59,11 +59,7 @@ Log::Log()
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
Clear();
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
free(m_szLogFilename);
|
||||
@@ -315,6 +311,17 @@ Message::~ Message()
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Clear()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
@@ -351,9 +358,10 @@ 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 two things:
|
||||
* intializing stage and does three 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()
|
||||
{
|
||||
@@ -364,6 +372,8 @@ void Log::InitOptions()
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
m_iIDGen = 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_Messages.size(); )
|
||||
{
|
||||
Message* pMessage = m_Messages.at(i);
|
||||
@@ -399,6 +409,7 @@ void Log::InitOptions()
|
||||
}
|
||||
else
|
||||
{
|
||||
pMessage->m_iID = ++m_iIDGen;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
7
Log.h
@@ -64,9 +64,11 @@ 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; }
|
||||
@@ -108,6 +110,7 @@ public:
|
||||
~Log();
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
void Clear();
|
||||
void ResetLog();
|
||||
void InitOptions();
|
||||
};
|
||||
|
||||
129
Makefile.am
@@ -1,23 +1,123 @@
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-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.
|
||||
#
|
||||
#
|
||||
|
||||
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 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 \
|
||||
NZBFile.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 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 WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
|
||||
|
||||
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd nzbget-postprocess.sh \
|
||||
$(patches_FILES) $(windows_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
|
||||
nzbgetd nzbget-shell.bat
|
||||
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
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf nzbget-postprocess.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/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
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
bin_SCRIPTS = nzbget-postprocess.sh
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuiconfdir = $(datadir)/nzbget/webui
|
||||
dist_webuiconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_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 files
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(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" ; \
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
fi
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.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)
|
||||
@@ -56,4 +156,15 @@ 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 {} \;
|
||||
|
||||
|
||||
321
Makefile.in
@@ -14,6 +14,29 @@
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-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.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
@@ -38,7 +61,9 @@ build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
bin_PROGRAMS = nzbget$(EXEEXT)
|
||||
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
|
||||
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
|
||||
$(dist_exampleconf_DATA) $(dist_webuiconf_DATA) \
|
||||
$(nobase_dist_webui_DATA) $(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
|
||||
@@ -52,7 +77,10 @@ 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)"
|
||||
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
|
||||
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
|
||||
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" \
|
||||
"$(DESTDIR)$(webuidir)"
|
||||
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
|
||||
PROGRAMS = $(bin_PROGRAMS)
|
||||
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
@@ -60,16 +88,20 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
|
||||
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
|
||||
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
|
||||
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
|
||||
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
|
||||
NZBFile.$(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) nzbget.$(OBJEXT)
|
||||
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
|
||||
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) nzbget.$(OBJEXT)
|
||||
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
|
||||
nzbget_LDADD = $(LDADD)
|
||||
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
|
||||
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
|
||||
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
@@ -84,6 +116,18 @@ CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(nzbget_SOURCES)
|
||||
DIST_SOURCES = $(nzbget_SOURCES)
|
||||
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|^.*/||'`;
|
||||
dist_docDATA_INSTALL = $(INSTALL_DATA)
|
||||
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
|
||||
dist_webuiconfDATA_INSTALL = $(INSTALL_DATA)
|
||||
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
|
||||
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
|
||||
$(dist_webuiconf_DATA) $(nobase_dist_webui_DATA)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
@@ -96,7 +140,6 @@ 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@
|
||||
@@ -197,25 +240,65 @@ 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 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 \
|
||||
NZBFile.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 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 WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
|
||||
|
||||
EXTRA_DIST = nzbget.conf.example postprocess-example.sh postprocess-example.conf \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd nzbget-postprocess.sh \
|
||||
$(patches_FILES) $(windows_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj \
|
||||
nzbgetd nzbget-shell.bat
|
||||
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
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf nzbget-postprocess.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/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
|
||||
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
bin_SCRIPTS = nzbget-postprocess.sh
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuiconfdir = $(datadir)/nzbget/webui
|
||||
dist_webuiconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
|
||||
# Ignore "svn_version.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
all: config.h
|
||||
$(MAKE) $(AM_MAKEFLAGS) all-am
|
||||
@@ -298,6 +381,44 @@ clean-binPROGRAMS:
|
||||
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
|
||||
@rm -f nzbget$(EXEEXT)
|
||||
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
install-binSCRIPTS: $(bin_SCRIPTS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
|
||||
@list='$(bin_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 " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
|
||||
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
uninstall-binSCRIPTS:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(bin_SCRIPTS)'; for p in $$list; do \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(bindir)/$$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)
|
||||
@@ -318,7 +439,6 @@ distclean-compile:
|
||||
@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@
|
||||
@@ -334,7 +454,10 @@ 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)/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@
|
||||
@@ -353,6 +476,76 @@ 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-dist_webuiconfDATA: $(dist_webuiconf_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(webuiconfdir)" || $(mkdir_p) "$(DESTDIR)$(webuiconfdir)"
|
||||
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_webuiconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuiconfdir)/$$f'"; \
|
||||
$(dist_webuiconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuiconfdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_webuiconfDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(webuiconfdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(webuiconfdir)/$$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)'; \
|
||||
@@ -405,6 +598,7 @@ distclean-tags:
|
||||
distdir: $(DISTFILES)
|
||||
$(am__remove_distdir)
|
||||
mkdir $(distdir)
|
||||
$(mkdir_p) $(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 \
|
||||
@@ -431,6 +625,9 @@ 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 \
|
||||
@@ -530,9 +727,9 @@ distcleancheck: distclean
|
||||
exit 1; } >&2
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(PROGRAMS) config.h
|
||||
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(bindir)"; do \
|
||||
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" "$(DESTDIR)$(webuidir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
@@ -580,9 +777,15 @@ info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
|
||||
install-dist_webuiconfDATA install-nobase_dist_webuiDATA
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
|
||||
|
||||
install-exec-am: install-binPROGRAMS
|
||||
install-exec-am: install-binPROGRAMS install-binSCRIPTS \
|
||||
install-sbinSCRIPTS
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
@@ -609,23 +812,76 @@ ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-info-am
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
|
||||
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
|
||||
uninstall-dist_webuiconfDATA uninstall-info-am \
|
||||
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
|
||||
|
||||
.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-shar dist-tarZ dist-zip distcheck distclean \
|
||||
distclean-compile distclean-generic distclean-hdr \
|
||||
dist-gzip dist-hook 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-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-info-am
|
||||
install-binPROGRAMS install-binSCRIPTS install-data \
|
||||
install-data-am install-data-hook install-dist_docDATA \
|
||||
install-dist_exampleconfDATA install-dist_webuiconfDATA \
|
||||
install-exec install-exec-am install-exec-hook install-info \
|
||||
install-info-am install-man install-nobase_dist_webuiDATA \
|
||||
install-sbinSCRIPTS 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-binSCRIPTS uninstall-dist_docDATA \
|
||||
uninstall-dist_exampleconfDATA uninstall-dist_webuiconfDATA \
|
||||
uninstall-info-am uninstall-nobase_dist_webuiDATA \
|
||||
uninstall-sbinSCRIPTS
|
||||
|
||||
|
||||
# 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 files
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(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" ; \
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
fi
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.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.
|
||||
@@ -664,6 +920,11 @@ 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:
|
||||
|
||||
130
MessageBase.h
@@ -27,7 +27,7 @@
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A620A; // = "nzbA" (protocol version)
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6212; // = "nzb-XX" (protocol version)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
@@ -56,11 +56,14 @@ enum eRemoteRequest
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestReload,
|
||||
eRemoteRequestVersion,
|
||||
eRemoteRequestPostQueue,
|
||||
eRemoteRequestWriteLog,
|
||||
eRemoteRequestScan,
|
||||
eRemoteRequestHistory
|
||||
eRemoteRequestHistory,
|
||||
eRemoteRequestDownloadUrl,
|
||||
eRemoteRequestUrlQueue
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
@@ -77,6 +80,7 @@ enum eRemoteEditAction
|
||||
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)
|
||||
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
|
||||
@@ -108,6 +112,14 @@ 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
|
||||
{
|
||||
@@ -129,8 +141,10 @@ struct SNZBDownloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, be empty
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can 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
|
||||
};
|
||||
@@ -150,6 +164,9 @@ 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
|
||||
@@ -172,6 +189,7 @@ 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
|
||||
@@ -186,11 +204,14 @@ 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
|
||||
@@ -219,6 +240,7 @@ struct SNZBListResponseFileEntry
|
||||
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
|
||||
@@ -286,7 +308,7 @@ struct SNZBSetDownloadRateResponse
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// An edit queue request
|
||||
// edit queue request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
@@ -294,11 +316,15 @@ 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_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
|
||||
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
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
@@ -340,6 +366,21 @@ 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
|
||||
{
|
||||
@@ -414,6 +455,7 @@ 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
|
||||
@@ -431,34 +473,80 @@ struct SNZBHistoryRequest
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// A history response
|
||||
// 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_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
|
||||
// SNZBHistoryResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list response nzb entry
|
||||
// history entry
|
||||
struct SNZBHistoryResponseEntry
|
||||
{
|
||||
int32_t m_iID; // NZBID
|
||||
int32_t m_iID; // History-ID
|
||||
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
|
||||
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
|
||||
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
|
||||
// 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
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -842,7 +842,7 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
|
||||
char szNZBNiceName[1024];
|
||||
if (m_bShowNZBname)
|
||||
{
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
|
||||
strncpy(szNZBNiceName, pFileInfo->GetNZBInfo()->GetName(), 1023);
|
||||
int len = strlen(szNZBNiceName);
|
||||
szNZBNiceName[len] = PATH_SEPARATOR;
|
||||
szNZBNiceName[len + 1] = '\0';
|
||||
@@ -926,7 +926,7 @@ void NCursesFrontend::PrintGroupQueue()
|
||||
{
|
||||
int iLineNr = m_iQueueWinTop;
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
LockQueue();
|
||||
GroupQueue* pGroupQueue = &m_groupQueue;
|
||||
if (pGroupQueue->empty())
|
||||
{
|
||||
@@ -1017,9 +1017,6 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
|
||||
char szRemaining[20];
|
||||
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
|
||||
|
||||
char szPriority[100];
|
||||
szPriority[0] = '\0';
|
||||
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
|
||||
@@ -1063,7 +1060,7 @@ void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSel
|
||||
|
||||
char szNameWithIds[1024];
|
||||
snprintf(szNameWithIds, 1024, "%c%i-%i%c%s%s %s", chBrace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), chBrace2,
|
||||
szPriority, szDownloading, szNZBNiceName);
|
||||
szPriority, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
||||
szNameWithIds[iNameLen] = '\0';
|
||||
|
||||
char szTime[100];
|
||||
@@ -1109,7 +1106,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, szNZBNiceName);
|
||||
pGroupInfo->GetLastID(), chBrace2, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
||||
}
|
||||
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -44,8 +44,9 @@
|
||||
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer->GetHost(), pNewsServer->GetPort(), pNewsServer->GetTLS())
|
||||
{
|
||||
m_pNewsServer = pNewsServer;
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
|
||||
m_bAuthError = false;
|
||||
@@ -81,7 +82,7 @@ const char* NNTPConnection::Request(const char* req)
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
debug("%s requested authorization", GetHost());
|
||||
|
||||
//authentication required!
|
||||
if (!Authenticate())
|
||||
@@ -101,8 +102,8 @@ const char* NNTPConnection::Request(const char* req)
|
||||
|
||||
bool NNTPConnection::Authenticate()
|
||||
{
|
||||
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
|
||||
!((NewsServer*)m_pNetAddress)->GetPassword())
|
||||
if (!(m_pNewsServer)->GetUser() ||
|
||||
!(m_pNewsServer)->GetPassword())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -118,7 +119,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", m_pNewsServer->GetUser());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
@@ -126,13 +127,13 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("Authorization for %s successful", m_pNetAddress->GetHost());
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
@@ -161,7 +162,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", m_pNewsServer->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
@@ -169,12 +170,12 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("Authorization for %s successful", m_pNetAddress->GetHost());
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
@@ -212,7 +213,7 @@ const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
debug("Changed group to %s on %s", grp, GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
@@ -222,8 +223,7 @@ const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Error changing group on %s to %s: %s.",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
debug("Error changing group on %s to %s: %s.", GetHost(), grp, answer);
|
||||
}
|
||||
|
||||
return answer;
|
||||
@@ -231,28 +231,18 @@ const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
|
||||
bool NNTPConnection::DoConnect()
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
debug("Opening connection to %s", GetHost());
|
||||
bool res = Connection::DoConnect();
|
||||
if (!res)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (GetNewsServer()->GetTLS())
|
||||
{
|
||||
if (!StartTLS())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), true, 0);
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -262,7 +252,7 @@ bool NNTPConnection::DoConnect()
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetServer()->GetHost());
|
||||
debug("Connection to %s established", GetHost());
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -284,7 +274,7 @@ bool NNTPConnection::DoDisconnect()
|
||||
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
|
||||
{
|
||||
char szErrStr[1024];
|
||||
snprintf(szErrStr, 1024, szMsgPrefix, m_pNetAddress->GetHost(), szAnswer);
|
||||
snprintf(szErrStr, 1024, szMsgPrefix, GetHost(), szAnswer);
|
||||
szErrStr[1024-1] = '\0';
|
||||
|
||||
ReportError(szErrStr, NULL, false, 0);
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
NewsServer* m_pNewsServer;
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
@@ -43,9 +44,9 @@ private:
|
||||
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
NNTPConnection(NewsServer* pNewsServer);
|
||||
virtual ~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
NewsServer* GetNewsServer() { return m_pNewsServer; }
|
||||
const char* Request(const char* req);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur = 0);
|
||||
@@ -54,6 +55,5 @@ public:
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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 "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;
|
||||
}
|
||||
44
NetAddress.h
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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 NETADDRESS_H
|
||||
#define NETADDRESS_H
|
||||
|
||||
class NetAddress
|
||||
{
|
||||
private:
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
|
||||
public:
|
||||
NetAddress(const char* szHost, int iPort);
|
||||
virtual ~NetAddress();
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -37,10 +37,11 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NewsServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel) : NetAddress(szHost, iPort)
|
||||
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel)
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = iLevel;
|
||||
@@ -48,6 +49,10 @@ NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const
|
||||
m_bJoinGroup = bJoinGroup;
|
||||
m_bTLS = bTLS;
|
||||
|
||||
if (szHost)
|
||||
{
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
if (szUser)
|
||||
{
|
||||
m_szUser = strdup(szUser);
|
||||
@@ -60,6 +65,10 @@ NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
if (m_szHost)
|
||||
{
|
||||
free(m_szHost);
|
||||
}
|
||||
if (m_szUser)
|
||||
{
|
||||
free(m_szUser);
|
||||
|
||||
10
NewsServer.h
@@ -27,11 +27,11 @@
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
class NewsServer
|
||||
{
|
||||
private:
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
@@ -41,7 +41,9 @@ private:
|
||||
|
||||
public:
|
||||
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
|
||||
virtual ~NewsServer();
|
||||
~NewsServer();
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
1160
Options.cpp
92
Options.h
@@ -45,10 +45,12 @@ public:
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestReload,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog,
|
||||
opClientRequestScan,
|
||||
opClientRequestScanSync,
|
||||
opClientRequestScanAsync,
|
||||
opClientRequestDownloadPause,
|
||||
opClientRequestDownloadUnpause,
|
||||
opClientRequestDownload2Pause,
|
||||
@@ -57,7 +59,9 @@ public:
|
||||
opClientRequestPostUnpause,
|
||||
opClientRequestScanPause,
|
||||
opClientRequestScanUnpause,
|
||||
opClientRequestHistory
|
||||
opClientRequestHistory,
|
||||
opClientRequestDownloadUrl,
|
||||
opClientRequestUrlQueue
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
@@ -88,25 +92,53 @@ public:
|
||||
slDebug
|
||||
};
|
||||
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
enum EDomain
|
||||
{
|
||||
dmServer = 1,
|
||||
dmPostProcess
|
||||
};
|
||||
|
||||
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*> OptEntries;
|
||||
typedef std::vector<OptEntry*> OptEntriesBase;
|
||||
|
||||
class OptEntries: public OptEntriesBase
|
||||
{
|
||||
public:
|
||||
~OptEntries();
|
||||
OptEntry* FindOption(const char* szName);
|
||||
};
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
private:
|
||||
OptEntries m_OptEntries;
|
||||
@@ -114,11 +146,14 @@ private:
|
||||
Mutex m_mutexOptEntries;
|
||||
|
||||
// Options
|
||||
bool m_bConfigErrors;
|
||||
int m_iConfigLine;
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
char* m_szWebDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
@@ -137,14 +172,16 @@ private:
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szServerIP;
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szControlIP;
|
||||
char* m_szControlPassword;
|
||||
int m_szControlPort;
|
||||
char* m_szLockFile;
|
||||
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;
|
||||
@@ -152,7 +189,9 @@ private:
|
||||
bool m_bParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
char* m_szPostConfigFilename;
|
||||
char* m_szNZBProcess;
|
||||
char* m_szNZBAddedProcess;
|
||||
bool m_bStrictParName;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
@@ -190,9 +229,14 @@ private:
|
||||
int m_iEditQueueOffset;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
NameList m_EditQueueNameList;
|
||||
EMatchMode m_EMatchMode;
|
||||
char* m_szEditQueueText;
|
||||
char* m_szArgFilename;
|
||||
char* m_szCategory;
|
||||
char* m_szAddCategory;
|
||||
int m_iAddPriority;
|
||||
bool m_bAddPaused;
|
||||
char* m_szAddNZBFilename;
|
||||
char* m_szLastArg;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
@@ -213,35 +257,46 @@ private:
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitPostConfig();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void InitScheduler();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseIntValue(const char* OptName, int iBase);
|
||||
float ParseFloatValue(const char* OptName);
|
||||
OptEntry* FindOption(const char* optname);
|
||||
const char* GetOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty);
|
||||
void LoadConfigFile();
|
||||
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
void ParseFileNameList(int argc, char* argv[], int optind);
|
||||
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
|
||||
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
|
||||
void ConfigError(const char* msg, ...);
|
||||
void ConvertOldOptionName(char *szOption, int iBufLen);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
bool LoadConfig(EDomain eDomain, OptEntries* pOptEntries);
|
||||
bool SaveConfig(EDomain eDomain, OptEntries* pOptEntries);
|
||||
|
||||
// Options
|
||||
OptEntries* LockOptEntries();
|
||||
void UnlockOptEntries();
|
||||
const char* GetConfigFilename() { return m_szConfigFilename; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
const char* GetWebDir() { return m_szWebDir; }
|
||||
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
|
||||
bool GetResetLog() const { return m_bResetLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
@@ -260,14 +315,16 @@ public:
|
||||
int GetRetryInterval() { return m_iRetryInterval; }
|
||||
bool GetSaveQueue() { return m_bSaveQueue; }
|
||||
bool GetDupeCheck() { return m_bDupeCheck; }
|
||||
const char* GetServerIP() { return m_szServerIP; }
|
||||
const char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
const char* GetControlIP() { return m_szControlIP; }
|
||||
const char* GetControlPassword() { return m_szControlPassword; }
|
||||
int GetControlPort() { return m_szControlPort; }
|
||||
const char* GetLockFile() { return m_szLockFile; }
|
||||
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; }
|
||||
@@ -275,7 +332,9 @@ public:
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
const char* GetPostConfigFilename() { return m_szPostConfigFilename; }
|
||||
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; }
|
||||
@@ -313,10 +372,15 @@ 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* GetCategory() { return m_szCategory; }
|
||||
const char* GetAddCategory() { return m_szAddCategory; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
const char* GetLastArg() { return m_szLastArg; }
|
||||
int GetAddPriority() { return m_iAddPriority; }
|
||||
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -112,7 +112,7 @@ void PrePostProcessor::Cleanup()
|
||||
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
(*it)->Release();
|
||||
delete *it;
|
||||
}
|
||||
pDownloadQueue->GetHistoryList()->clear();
|
||||
|
||||
@@ -240,21 +240,19 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
|
||||
#ifndef DISABLE_PARCHECK
|
||||
!AddPar(pAspect->pFileInfo, pAspect->eAction == QueueCoordinator::eaFileDeleted) &&
|
||||
#endif
|
||||
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true, false, false) &&
|
||||
(!pAspect->pFileInfo->GetPaused() || IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, false, false)))
|
||||
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, true, false, false) &&
|
||||
(!pAspect->pFileInfo->GetPaused() || IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, false)))
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pAspect->pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
if (pAspect->eAction == QueueCoordinator::eaFileCompleted)
|
||||
{
|
||||
info("Collection %s completely downloaded", szNZBNiceName);
|
||||
info("Collection %s completely downloaded", pAspect->pNZBInfo->GetName());
|
||||
NZBDownloaded(pAspect->pDownloadQueue, pAspect->pNZBInfo);
|
||||
}
|
||||
else if (pAspect->pNZBInfo->GetDeleted() &&
|
||||
!pAspect->pNZBInfo->GetParCleanup() &&
|
||||
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, false, true))
|
||||
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, true))
|
||||
{
|
||||
info("Collection %s deleted from queue", szNZBNiceName);
|
||||
info("Collection %s deleted from queue", pAspect->pNZBInfo->GetName());
|
||||
NZBDeleted(pAspect->pDownloadQueue, pAspect->pNZBInfo);
|
||||
}
|
||||
}
|
||||
@@ -272,6 +270,11 @@ void PrePostProcessor::NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo
|
||||
{
|
||||
PausePars(pDownloadQueue, pNZBInfo);
|
||||
}
|
||||
|
||||
if (strlen(g_pOptions->GetNZBAddedProcess()) > 0)
|
||||
{
|
||||
NZBAddedScriptController::StartScript(pDownloadQueue, pNZBInfo, g_pOptions->GetNZBAddedProcess());
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
@@ -331,11 +334,21 @@ void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZB
|
||||
{
|
||||
if (g_pOptions->GetKeepHistory() > 0)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
pNZBInfo->AddReference();
|
||||
pNZBInfo->SetHistoryTime(time(NULL));
|
||||
pDownloadQueue->GetHistoryList()->push_front(pNZBInfo);
|
||||
//remove old item for the same NZB
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
if (pHistoryInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
delete pHistoryInfo;
|
||||
pDownloadQueue->GetHistoryList()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HistoryInfo* pHistoryInfo = new HistoryInfo(pNZBInfo);
|
||||
pHistoryInfo->SetTime(time(NULL));
|
||||
pDownloadQueue->GetHistoryList()->push_front(pHistoryInfo);
|
||||
|
||||
// park files
|
||||
int iParkedFiles = 0;
|
||||
@@ -365,7 +378,7 @@ void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZB
|
||||
SaveQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
info("Collection %s added to history", szNZBNiceName);
|
||||
info("Collection %s added to history", pNZBInfo->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,14 +397,14 @@ void PrePostProcessor::CheckHistory()
|
||||
// (just to produce the log-messages in a more logical order)
|
||||
for (HistoryList::reverse_iterator it = pDownloadQueue->GetHistoryList()->rbegin(); it != pDownloadQueue->GetHistoryList()->rend(); )
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
if (pNZBInfo->GetHistoryTime() < tMinTime)
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
if (pHistoryInfo->GetTime() < tMinTime)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
char szNiceName[1024];
|
||||
pHistoryInfo->GetName(szNiceName, 1024);
|
||||
pDownloadQueue->GetHistoryList()->erase(pDownloadQueue->GetHistoryList()->end() - 1 - index);
|
||||
pNZBInfo->Release();
|
||||
info("Collection %s removed from history", szNZBNiceName);
|
||||
delete pHistoryInfo;
|
||||
info("Collection %s removed from history", szNiceName);
|
||||
it = pDownloadQueue->GetHistoryList()->rbegin() + index;
|
||||
bChanged = true;
|
||||
}
|
||||
@@ -448,14 +461,14 @@ NZBInfo* PrePostProcessor::MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* p
|
||||
}
|
||||
}
|
||||
|
||||
// merge(2): check if queue has other nzb-files with the same filename
|
||||
// merge(2): check if queue has other nzb-files with the same name
|
||||
if (iAddedGroupID > 0)
|
||||
{
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() != pNZBInfo &&
|
||||
!strcmp(pFileInfo->GetNZBInfo()->GetFilename(), pNZBInfo->GetFilename()))
|
||||
!strcmp(pFileInfo->GetNZBInfo()->GetName(), pNZBInfo->GetName()))
|
||||
{
|
||||
// file found, do merging
|
||||
|
||||
@@ -473,9 +486,9 @@ NZBInfo* PrePostProcessor::MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* p
|
||||
return pNZBInfo;
|
||||
}
|
||||
|
||||
void PrePostProcessor::ScanNZBDir()
|
||||
void PrePostProcessor::ScanNZBDir(bool bSyncMode)
|
||||
{
|
||||
m_Scanner.ScanNZBDir();
|
||||
m_Scanner.ScanNZBDir(bSyncMode);
|
||||
}
|
||||
|
||||
void PrePostProcessor::CheckDiskSpace()
|
||||
@@ -502,9 +515,7 @@ void PrePostProcessor::CheckPostQueue()
|
||||
{
|
||||
if (!CreatePostJobs(pDownloadQueue, pPostInfo->GetNZBInfo(), true, false, true))
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pPostInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, sizeof(szNZBNiceName));
|
||||
error("Could not par-check %s: there are no par-files", szNZBNiceName);
|
||||
error("Could not par-check %s: there are no par-files", pPostInfo->GetNZBInfo()->GetName());
|
||||
}
|
||||
}
|
||||
else if (pPostInfo->GetRequestParCheck() == PostInfo::rpCurrent && !pPostInfo->GetParCheck())
|
||||
@@ -602,7 +613,7 @@ void PrePostProcessor::StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* p
|
||||
}
|
||||
pPostInfo->SetStageTime(time(NULL));
|
||||
|
||||
bool bNZBFileCompleted = IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, true, false);
|
||||
bool bNZBFileCompleted = IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false);
|
||||
bool bHasFailedParJobs = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prFailure ||
|
||||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prRepairPossible;
|
||||
|
||||
@@ -645,7 +656,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
|
||||
pPostInfo->GetNZBInfo()->SetScriptStatus(NZBInfo::srSuccess);
|
||||
}
|
||||
|
||||
if (IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, true, false))
|
||||
if (IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false))
|
||||
{
|
||||
// Cleaning up queue if all par-checks were successful or all scripts were successful
|
||||
bool bCanCleanupQueue = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::prSuccess ||
|
||||
@@ -658,9 +669,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
|
||||
FileInfo* pFileInfo = GetQueueGroup(pDownloadQueue, pPostInfo->GetNZBInfo());
|
||||
if (pFileInfo)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pPostInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, sizeof(szNZBNiceName));
|
||||
info("Cleaning up download queue for %s", szNZBNiceName);
|
||||
info("Cleaning up download queue for %s", pPostInfo->GetNZBInfo()->GetName());
|
||||
pFileInfo->GetNZBInfo()->ClearCompletedFiles();
|
||||
pFileInfo->GetNZBInfo()->SetParCleanup(true);
|
||||
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupDelete, 0, NULL);
|
||||
@@ -692,7 +701,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
|
||||
}
|
||||
|
||||
bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
|
||||
bool bIgnoreFirstInPostQueue, bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted)
|
||||
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted)
|
||||
{
|
||||
bool bNZBFileCompleted = true;
|
||||
int iDeleted = 0;
|
||||
@@ -706,8 +715,11 @@ bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo
|
||||
{
|
||||
iDeleted++;
|
||||
}
|
||||
// Special case if option "AllowReProcess" is active:
|
||||
// paused non-par-files are treated the same way as paused par-files,
|
||||
// meaning: the NZB considered completed even if there are paused non-par-files.
|
||||
if (((!pFileInfo->GetPaused() || !bIgnorePausedPars ||
|
||||
!ParseParFilename(pFileInfo->GetFilename(), NULL, NULL)) &&
|
||||
!(ParseParFilename(pFileInfo->GetFilename(), NULL, NULL) || g_pOptions->GetAllowReProcess())) &&
|
||||
!pFileInfo->GetDeleted()) ||
|
||||
(bAllowOnlyOneDeleted && iDeleted > 1))
|
||||
{
|
||||
@@ -719,7 +731,7 @@ bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo
|
||||
|
||||
if (bNZBFileCompleted && bCheckPostQueue)
|
||||
{
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin() + int(bIgnoreFirstInPostQueue); it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin() + 1; it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
if (pPostInfo->GetNZBInfo() == pNZBInfo)
|
||||
@@ -738,9 +750,6 @@ bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pN
|
||||
{
|
||||
debug("Queueing post-process-jobs");
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
PostQueue cPostQueue;
|
||||
bool bJobsAdded = false;
|
||||
|
||||
@@ -766,10 +775,10 @@ bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pN
|
||||
szInfoName[maxlen] = '\0';
|
||||
|
||||
char szParInfoName[1024];
|
||||
snprintf(szParInfoName, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
snprintf(szParInfoName, 1024, "%s%c%s", pNZBInfo->GetName(), (int)PATH_SEPARATOR, szInfoName);
|
||||
szParInfoName[1024-1] = '\0';
|
||||
|
||||
info("Queueing %s%c%s for par-check", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
info("Queueing %s%c%s for par-check", pNZBInfo->GetName(), (int)PATH_SEPARATOR, szInfoName);
|
||||
PostInfo* pPostInfo = new PostInfo();
|
||||
pPostInfo->SetNZBInfo(pNZBInfo);
|
||||
pPostInfo->SetParFilename(szFullParFilename);
|
||||
@@ -791,11 +800,11 @@ bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pN
|
||||
|
||||
if (cPostQueue.empty() && bPostScript && m_bPostScript)
|
||||
{
|
||||
info("Queueing %s for post-process-script", szNZBNiceName);
|
||||
info("Queueing %s for post-process-script", pNZBInfo->GetName());
|
||||
PostInfo* pPostInfo = new PostInfo();
|
||||
pPostInfo->SetNZBInfo(pNZBInfo);
|
||||
pPostInfo->SetParFilename("");
|
||||
pPostInfo->SetInfoName(szNZBNiceName);
|
||||
pPostInfo->SetInfoName(pNZBInfo->GetName());
|
||||
pPostInfo->SetParCheck(false);
|
||||
cPostQueue.push_back(pPostInfo);
|
||||
bJobsAdded = true;
|
||||
@@ -1137,9 +1146,6 @@ bool PrePostProcessor::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilen
|
||||
|
||||
if (iBlockFound >= iBlockNeeded)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
|
||||
// starting from the file with max block count.
|
||||
// if par-collection was built exponentially and all par-files present,
|
||||
@@ -1160,7 +1166,7 @@ bool PrePostProcessor::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilen
|
||||
{
|
||||
if (pBestBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBestBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
|
||||
@@ -1183,7 +1189,7 @@ bool PrePostProcessor::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilen
|
||||
BlockInfo* pBlockInfo = blocks.front();
|
||||
if (pBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBlockInfo->m_iBlockCount;
|
||||
@@ -1607,37 +1613,42 @@ bool PrePostProcessor::HistoryDelete(IDList* pIDList)
|
||||
int iID = *itID;
|
||||
for (HistoryList::iterator itHistory = pDownloadQueue->GetHistoryList()->begin(); itHistory != pDownloadQueue->GetHistoryList()->end(); itHistory++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *itHistory;
|
||||
if (pNZBInfo->GetID() == iID)
|
||||
HistoryInfo* pHistoryInfo = *itHistory;
|
||||
if (pHistoryInfo->GetID() == iID)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
info("Deleting %s from history", szNZBNiceName);
|
||||
char szNiceName[1024];
|
||||
pHistoryInfo->GetName(szNiceName, 1024);
|
||||
info("Deleting %s from history", szNiceName);
|
||||
|
||||
// delete parked files
|
||||
int index = 0;
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetParkedFiles()->begin(); it != pDownloadQueue->GetParkedFiles()->end(); )
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
|
||||
|
||||
// delete parked files
|
||||
int index = 0;
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetParkedFiles()->begin(); it != pDownloadQueue->GetParkedFiles()->end(); )
|
||||
{
|
||||
pDownloadQueue->GetParkedFiles()->erase(it);
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
g_pDiskState->DiscardFile(pFileInfo);
|
||||
pDownloadQueue->GetParkedFiles()->erase(it);
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(pFileInfo);
|
||||
}
|
||||
delete pFileInfo;
|
||||
it = pDownloadQueue->GetParkedFiles()->begin() + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
index++;
|
||||
}
|
||||
delete pFileInfo;
|
||||
it = pDownloadQueue->GetParkedFiles()->begin() + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
pDownloadQueue->GetHistoryList()->erase(itHistory);
|
||||
pNZBInfo->Release();
|
||||
delete pHistoryInfo;
|
||||
bOK = true;
|
||||
break;
|
||||
}
|
||||
@@ -1665,64 +1676,83 @@ bool PrePostProcessor::HistoryReturn(IDList* pIDList, bool bReprocess)
|
||||
int iID = *itID;
|
||||
for (HistoryList::iterator itHistory = pDownloadQueue->GetHistoryList()->begin(); itHistory != pDownloadQueue->GetHistoryList()->end(); itHistory++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *itHistory;
|
||||
if (pNZBInfo->GetID() == iID)
|
||||
HistoryInfo* pHistoryInfo = *itHistory;
|
||||
if (pHistoryInfo->GetID() == iID)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
debug("Returning %s from history back to download queue", szNZBNiceName);
|
||||
char szNiceName[1024];
|
||||
pHistoryInfo->GetName(szNiceName, 1024);
|
||||
debug("Returning %s from history back to download queue", szNiceName);
|
||||
bool bUnparked = false;
|
||||
|
||||
// unpark files
|
||||
int index = 0;
|
||||
for (FileQueue::reverse_iterator it = pDownloadQueue->GetParkedFiles()->rbegin(); it != pDownloadQueue->GetParkedFiles()->rend(); )
|
||||
if (bReprocess && pHistoryInfo->GetKind() != HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
detail("Unpark file %s", pFileInfo->GetFilename());
|
||||
pDownloadQueue->GetParkedFiles()->erase(pDownloadQueue->GetParkedFiles()->end() - 1 - index);
|
||||
pDownloadQueue->GetFileQueue()->push_front(pFileInfo);
|
||||
bUnparked = true;
|
||||
it = pDownloadQueue->GetParkedFiles()->rbegin() + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
index++;
|
||||
}
|
||||
error("Could not restart postprocessing for %s: history item has wrong type", szNiceName);
|
||||
break;
|
||||
}
|
||||
|
||||
// reset postprocessing status variables
|
||||
pNZBInfo->SetPostProcess(false);
|
||||
pNZBInfo->SetParStatus(NZBInfo::prNone);
|
||||
pNZBInfo->SetParCleanup(false);
|
||||
pNZBInfo->SetScriptStatus(NZBInfo::srNone);
|
||||
pNZBInfo->SetHistoryTime(0);
|
||||
pNZBInfo->SetParkedFileCount(0);
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
|
||||
|
||||
// unpark files
|
||||
int index = 0;
|
||||
for (FileQueue::reverse_iterator it = pDownloadQueue->GetParkedFiles()->rbegin(); it != pDownloadQueue->GetParkedFiles()->rend(); )
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
detail("Unpark file %s", pFileInfo->GetFilename());
|
||||
pDownloadQueue->GetParkedFiles()->erase(pDownloadQueue->GetParkedFiles()->end() - 1 - index);
|
||||
pDownloadQueue->GetFileQueue()->push_front(pFileInfo);
|
||||
bUnparked = true;
|
||||
it = pDownloadQueue->GetParkedFiles()->rbegin() + index;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
// reset postprocessing status variables
|
||||
pNZBInfo->SetPostProcess(false);
|
||||
pNZBInfo->SetParStatus(NZBInfo::prNone);
|
||||
pNZBInfo->SetParCleanup(false);
|
||||
pNZBInfo->SetScriptStatus(NZBInfo::srNone);
|
||||
pNZBInfo->SetParkedFileCount(0);
|
||||
}
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
|
||||
{
|
||||
UrlInfo* pUrlInfo = pHistoryInfo->GetUrlInfo();
|
||||
pHistoryInfo->DiscardUrlInfo();
|
||||
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
|
||||
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
|
||||
bUnparked = true;
|
||||
}
|
||||
|
||||
if (bUnparked || bReprocess)
|
||||
{
|
||||
pDownloadQueue->GetHistoryList()->erase(itHistory);
|
||||
// the object "pNZBInfo" is released fe lines later, after the call to "NZBDownloaded"
|
||||
info("%s returned from history back to download queue", szNZBNiceName);
|
||||
// the object "pHistoryInfo" is released few lines later, after the call to "NZBDownloaded"
|
||||
info("%s returned from history back to download queue", szNiceName);
|
||||
bOK = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not return %s back from history to download queue: history item does not have any files left for download", szNZBNiceName);
|
||||
warn("Could not return %s back from history to download queue: history item does not have any files left for download", szNiceName);
|
||||
}
|
||||
|
||||
if (bReprocess)
|
||||
{
|
||||
// start postprocessing
|
||||
debug("Restarting postprocessing for %s", szNZBNiceName);
|
||||
NZBDownloaded(pDownloadQueue, pNZBInfo);
|
||||
debug("Restarting postprocessing for %s", szNiceName);
|
||||
NZBDownloaded(pDownloadQueue, pHistoryInfo->GetNZBInfo());
|
||||
}
|
||||
|
||||
if (bUnparked || bReprocess)
|
||||
{
|
||||
pNZBInfo->Release();
|
||||
delete pHistoryInfo;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -103,7 +103,7 @@ private:
|
||||
Scanner m_Scanner;
|
||||
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
|
||||
bool bIgnoreFirstInPostQueue, bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
|
||||
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
|
||||
void CheckPostQueue();
|
||||
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
@@ -152,7 +152,7 @@ public:
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
void ScanNZBDir();
|
||||
void ScanNZBDir(bool bSyncMode);
|
||||
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -168,7 +168,10 @@ void QueueCoordinator::Run()
|
||||
int iSleepInterval = bStandBy ? 100 : 5;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
AddSpeedReading(0);
|
||||
if (!bStandBy)
|
||||
{
|
||||
AddSpeedReading(0);
|
||||
}
|
||||
|
||||
iResetCounter += iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
@@ -323,7 +326,7 @@ void QueueCoordinator::AddSpeedReading(int iBytes)
|
||||
#endif
|
||||
}
|
||||
|
||||
if (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
|
||||
while (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
|
||||
{
|
||||
//record bytes in next slot
|
||||
m_iSpeedBytesIndex++;
|
||||
@@ -546,10 +549,7 @@ void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloade
|
||||
tmpname[1024-1] = '\0';
|
||||
pArticleDownloader->SetTempFilename(tmpname);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetInfoName(name);
|
||||
|
||||
@@ -769,9 +769,25 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
void QueueCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug("--------------------------------------------");
|
||||
debug("Dumping debug info to log");
|
||||
debug("Dumping debug debug to log");
|
||||
debug("--------------------------------------------");
|
||||
|
||||
debug(" SpeedMeter");
|
||||
debug(" ----------");
|
||||
float fSpeed = CalcCurrentDownloadSpeed();
|
||||
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
|
||||
debug(" Speed: %f", fSpeed);
|
||||
debug(" SpeedStartTime: %i", m_iSpeedStartTime);
|
||||
debug(" SpeedTotalBytes: %i", m_iSpeedTotalBytes);
|
||||
debug(" SpeedBytesIndex: %i", m_iSpeedBytesIndex);
|
||||
debug(" AllBytes: %i", m_iAllBytes);
|
||||
debug(" Time: %i", (int)time(NULL));
|
||||
debug(" TimeDiff: %i", iTimeDiff);
|
||||
for (int i=0; i < SPEEDMETER_SLOTS; i++)
|
||||
{
|
||||
debug(" Bytes[%i]: %i, Time[%i]: %i", i, m_iSpeedBytes[i], i, m_iSpeedTime[i]);
|
||||
}
|
||||
|
||||
debug(" QueueCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
@@ -900,9 +916,7 @@ bool QueueCoordinator::SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* s
|
||||
{
|
||||
if (pNZBInfo->GetPostProcess())
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
error("Could not change category for %s. File in post-process-stage", szNZBNiceName);
|
||||
error("Could not change category for %s. File in post-process-stage", pNZBInfo->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -923,9 +937,13 @@ bool QueueCoordinator::SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szNam
|
||||
{
|
||||
if (pNZBInfo->GetPostProcess())
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pNZBInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
error("Could not rename %s. File in post-process-stage", szNZBNiceName);
|
||||
error("Could not rename %s. File in post-process-stage", pNZBInfo->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strlen(szName) == 0)
|
||||
{
|
||||
error("Could not rename %s. The new name cannot be empty", pNZBInfo->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -933,7 +951,10 @@ bool QueueCoordinator::SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szNam
|
||||
strncpy(szOldDestDir, pNZBInfo->GetDestDir(), 1024);
|
||||
szOldDestDir[1024-1] = '\0';
|
||||
|
||||
pNZBInfo->SetUserNZBName(szName);
|
||||
char szNZBNicename[1024];
|
||||
NZBInfo::MakeNiceNZBName(szName, szNZBNicename, sizeof(szNZBNicename), false);
|
||||
pNZBInfo->SetName(szNZBNicename);
|
||||
|
||||
pNZBInfo->BuildDestDirName();
|
||||
|
||||
bool bDirUnchanged = !strcmp(pNZBInfo->GetDestDir(), szOldDestDir);
|
||||
@@ -949,11 +970,7 @@ bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZB
|
||||
{
|
||||
if (pDestNZBInfo->GetPostProcess() || pSrcNZBInfo->GetPostProcess())
|
||||
{
|
||||
char szDestNZBNiceName[1024];
|
||||
pDestNZBInfo->GetNiceNZBName(szDestNZBNiceName, 1024);
|
||||
char szSrcNZBNiceName[1024];
|
||||
pSrcNZBInfo->GetNiceNZBName(szSrcNZBNiceName, 1024);
|
||||
error("Could not merge %s and %s. File in post-process-stage", szDestNZBNiceName, szSrcNZBNiceName);
|
||||
error("Could not merge %s and %s. File in post-process-stage", pDestNZBInfo->GetName(), pSrcNZBInfo->GetName());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
167
QueueEditor.cpp
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <set>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
@@ -164,7 +165,7 @@ bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int i
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
return EditList(&cIDList, NULL, mmID, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
@@ -175,11 +176,20 @@ bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bS
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder,
|
||||
EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
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);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
@@ -188,6 +198,11 @@ bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eActio
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pNameList)
|
||||
{
|
||||
delete pIDList;
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
@@ -214,6 +229,10 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
|
||||
{
|
||||
MergeGroups(pDownloadQueue, &cItemList);
|
||||
}
|
||||
else if (eAction == eaFileReorder)
|
||||
{
|
||||
ReorderFiles(pDownloadQueue, &cItemList);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
@@ -270,6 +289,7 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
case eaGroupMerge:
|
||||
case eaFileReorder:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
}
|
||||
@@ -385,6 +405,78 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pRegEx)
|
||||
{
|
||||
delete pRegEx;
|
||||
}
|
||||
|
||||
if (!bFound && (eMatchMode == mmName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
@@ -452,7 +544,7 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom,
|
||||
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
|
||||
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
|
||||
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
|
||||
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
|
||||
@@ -556,11 +648,11 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
|
||||
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
AlignGroup(pDownloadQueue, pFileInfo->GetNZBInfo());
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
@@ -568,7 +660,7 @@ void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFile
|
||||
while (iNum < pDownloadQueue->GetFileQueue()->size())
|
||||
{
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
|
||||
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
@@ -762,20 +854,57 @@ void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList
|
||||
delete pItem;
|
||||
}
|
||||
|
||||
// align group ("AlignGroup" needs the first file item as parameter)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pDestItem->m_pFileInfo->GetNZBInfo())
|
||||
{
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// align group
|
||||
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
|
||||
|
||||
delete pDestItem;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
|
||||
{
|
||||
debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename()));
|
||||
@@ -791,7 +920,7 @@ void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not set nzb parameter for %s: invalid argument: %s", Util::BaseFileName(pNZBInfo->GetFilename()), szParamString);
|
||||
error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString);
|
||||
}
|
||||
|
||||
free(szStr);
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaFileSetPriority,
|
||||
eaFileReorder,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
@@ -59,6 +60,13 @@ public:
|
||||
eaGroupSetName
|
||||
};
|
||||
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
private:
|
||||
class EditItem
|
||||
{
|
||||
@@ -77,17 +85,19 @@ 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);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
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);
|
||||
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
@@ -100,7 +110,7 @@ public:
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, 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);
|
||||
|
||||
138
README
@@ -2,6 +2,10 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.sourceforge.net
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. About NZBGet
|
||||
@@ -29,6 +33,9 @@ In server/client mode NZBGet runs as server in background.
|
||||
Then you use client to send requests to server. The sample requests
|
||||
are: download nzb-file, list files in queue, etc.
|
||||
|
||||
There is also a built-in web-interface. The server has RPC-support
|
||||
and can be controlled from third party applications too.
|
||||
|
||||
Standalone-tool, server and client are all contained in only one
|
||||
executable file "nzbget". The mode in which the program works
|
||||
depends on command-line parameters passed to the program.
|
||||
@@ -41,7 +48,7 @@ NZBGet is written in C++ and was initialy developed on Linux.
|
||||
It was ported to Windows later and tested for compatibility with
|
||||
several POSIX-OS'es.
|
||||
|
||||
The current version (0.8.0) should run at least on:
|
||||
It should run at least on:
|
||||
- Linux Debian 5.0 on x86;
|
||||
- Linux with uClibc on MIPSEL and ARM;
|
||||
- OpenBSD 5.0 on x86;
|
||||
@@ -88,6 +95,9 @@ 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
|
||||
@@ -98,24 +108,41 @@ download the libraries at the given URLs and compile them (see hints below).
|
||||
4. Installation on POSIX
|
||||
=====================================
|
||||
|
||||
Well, the usual stuff:
|
||||
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
|
||||
|
||||
- untar the nzbget-source via
|
||||
tar -zxf nzbget-VERSION.tar.gz
|
||||
|
||||
- change into nzbget-directory via
|
||||
cd nzbget-VERSION
|
||||
|
||||
- configure it via
|
||||
./configure
|
||||
|
||||
(maybe you have to tell configure, where to find some libraries.
|
||||
./configure --help is your friend!)
|
||||
also see "Configure-options" later.)
|
||||
./configure --help is your friend!
|
||||
also see "Configure-options" later)
|
||||
|
||||
- in a case you don't have root access or want to install the program
|
||||
in your home directory use the configure parameter --prefix, e. g.:
|
||||
|
||||
./configure --prefix ~/usr
|
||||
|
||||
- compile it via
|
||||
make
|
||||
- become root via
|
||||
|
||||
- to install system wide become root via:
|
||||
su
|
||||
- install it via
|
||||
|
||||
- install it via:
|
||||
make install
|
||||
|
||||
- install configuration files into <prefix>/etc via:
|
||||
make install-conf
|
||||
|
||||
(you can skip this step if you intend to store configuration
|
||||
files in a non-standard location)
|
||||
|
||||
Configure-options
|
||||
-----------------
|
||||
You may run configure with additional arguments:
|
||||
@@ -131,6 +158,9 @@ 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.
|
||||
@@ -244,17 +274,27 @@ in MS Visual C++ 2005 you should be able to compile NZBGet.
|
||||
6. Configuration
|
||||
=====================================
|
||||
|
||||
NZBGet needs a configuration-file to work properly.
|
||||
NZBGet needs a configuration file.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one newsserver in
|
||||
configuration file. Have a look at the example in nzbget.conf.example,
|
||||
it has comments on how to use each option.
|
||||
An example configuration file is provided in "nzbget.conf", which
|
||||
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
|
||||
system configuration and configure options - typically "/usr/local",
|
||||
"/usr" or "/opt"). The installer adjusts the file according to your
|
||||
system paths. If you have performed the installation step
|
||||
"make install-conf" this file is already copied to "<prefix>/etc" and
|
||||
NZBGet finds it automatically. If you install the program manually
|
||||
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
|
||||
to one of the locations listed below.
|
||||
|
||||
Open the file in a text editor and modify it accodring to your needs.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one news server in
|
||||
configuration file. The file has comments on how to use each option.
|
||||
|
||||
The program looks for configuration file in following standard
|
||||
locations (in this order):
|
||||
|
||||
On POSIX systems:
|
||||
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -382,7 +422,7 @@ Running client & server on seperate machines:
|
||||
Since nzbget communicates via TCP/IP it's possible to have a server running on
|
||||
one computer and adding downloads via a client on another computer.
|
||||
|
||||
Do this by setting the "serverip" option in the nzbget.conf file to point to the
|
||||
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
|
||||
IP of the server (default is localhost which means client and server runs on
|
||||
same computer)
|
||||
|
||||
@@ -401,28 +441,72 @@ nzbget-client-commands in this terminal.
|
||||
Post processing scripts
|
||||
-----------------------
|
||||
|
||||
After the download of nzb-file is completed nzbget can call post-process-script,
|
||||
defined in configuration file. See example configuration file for the
|
||||
description of parameters passed to the script.
|
||||
After the download of nzb-file is completed nzbget can call post-processing
|
||||
script, defined in configuration file. See example configuration file for
|
||||
the description of parameters passed to the script (option "PostProcess").
|
||||
|
||||
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).
|
||||
"nzbget-postprocess.sh" installed into "<prefix>/bin". The script requires
|
||||
configuration file "nzbget-postprocess.conf". If you have installed the
|
||||
program with "make install" this file is copied to "<prefix>/etc",
|
||||
where the post-processing script finds it automatically. If you install
|
||||
the program manually from a binary archive you have to copy the file
|
||||
from "<prefix>/share/nzbget" to the directory where you have put the
|
||||
nzbget configuration file ("nzbget.conf").
|
||||
|
||||
Set the option "PostProcess" in "nzbget.conf" to point to the post-
|
||||
processing script.
|
||||
|
||||
Additional usage instructions are included in "nzbget-postprocess.sh",
|
||||
please open the file in a text editor to read them.
|
||||
|
||||
NOTE: The post-processing script "nzbget-postprocess.sh" is for
|
||||
POSIX systems and will not work on Windows.
|
||||
|
||||
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.
|
||||
|
||||
=====================================
|
||||
8. Authors
|
||||
=====================================
|
||||
|
||||
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
|
||||
|
||||
Up to version 0.2.3 it was developed and maintained by Bo Cordes Petersen
|
||||
(placebodk@users.sourceforge.net).
|
||||
|
||||
Beginning at version 0.3.0 the program is being developed by Andrey Prygunkov
|
||||
NZBGet is developed and maintained by Andrey Prygunkov
|
||||
(hugbug@users.sourceforge.net).
|
||||
|
||||
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers (marlam@marlam.de).
|
||||
The original project was initially created by Sven Henkel
|
||||
(sidddy@users.sourceforge.net) in 2004 and later developed by
|
||||
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
|
||||
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
|
||||
Since then the program has been completely rewritten.
|
||||
|
||||
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers
|
||||
(marlam@marlam.de).
|
||||
|
||||
=====================================
|
||||
9. Copyright
|
||||
@@ -449,9 +533,9 @@ libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
|
||||
10. Contact
|
||||
=====================================
|
||||
|
||||
If you encounter any problem, feel free to use forums on
|
||||
If you encounter any problem, feel free to use the forum
|
||||
|
||||
sourceforge.net/projects/nzbget
|
||||
nzbget.sourceforge.net/forum
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
336
RemoteClient.cpp
@@ -55,7 +55,6 @@ extern Options* g_pOptions;
|
||||
RemoteClient::RemoteClient()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_pNetAddress = NULL;
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
@@ -80,11 +79,6 @@ RemoteClient::~RemoteClient()
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
if (m_pNetAddress)
|
||||
{
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::printf(const char * msg,...)
|
||||
@@ -109,13 +103,19 @@ void RemoteClient::perror(const char * msg)
|
||||
bool RemoteClient::InitConnection()
|
||||
{
|
||||
// Create a connection to the server
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
|
||||
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);
|
||||
|
||||
bool OK = m_pConnection->Connect();
|
||||
if (!OK)
|
||||
{
|
||||
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
printf("Unable to send request to nzbserver at %s (port %i)\n", szControlIP, g_pOptions->GetControlPort());
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@@ -125,7 +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_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
@@ -166,6 +166,7 @@ bool RemoteClient::ReceiveBoolResponse()
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
free(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -177,7 +178,7 @@ bool RemoteClient::ReceiveBoolResponse()
|
||||
/*
|
||||
* Sends a message to the running nzbget process.
|
||||
*/
|
||||
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst)
|
||||
bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority)
|
||||
{
|
||||
// Read the file into the buffer
|
||||
char* szBuffer = NULL;
|
||||
@@ -194,6 +195,8 @@ 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);
|
||||
|
||||
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
|
||||
@@ -239,23 +242,29 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
|
||||
SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) pBufPtr;
|
||||
|
||||
const char* szFileName = pBufPtr + sizeof(SNZBListResponseNZBEntry);
|
||||
const char* szDestDir = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen);
|
||||
const char* szCategory = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
const char* m_szQueuedFilename = pBufPtr + sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen);
|
||||
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);
|
||||
|
||||
NZBInfo* pNZBInfo = new NZBInfo();
|
||||
MatchedNZBInfo* pNZBInfo = new MatchedNZBInfo();
|
||||
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->AddReference();
|
||||
pDownloadQueue->GetNZBInfoList()->Add(pNZBInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
|
||||
ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen) +
|
||||
ntohl(pListAnswer->m_iQueuedFilenameLen);
|
||||
ntohl(pListAnswer->m_iNameLen) + ntohl(pListAnswer->m_iDestDirLen) +
|
||||
ntohl(pListAnswer->m_iCategoryLen) + ntohl(pListAnswer->m_iQueuedFilenameLen);
|
||||
}
|
||||
|
||||
//read ppp entries
|
||||
@@ -281,7 +290,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);
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
MatchedFileInfo* pFileInfo = new MatchedFileInfo();
|
||||
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)));
|
||||
@@ -291,6 +300,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
|
||||
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);
|
||||
|
||||
@@ -306,7 +316,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
|
||||
pDownloadQueue->GetNZBInfoList()->ReleaseAll();
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPattern)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
@@ -314,6 +324,13 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
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)) < 0)
|
||||
{
|
||||
@@ -354,6 +371,13 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
|
||||
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)
|
||||
@@ -370,6 +394,7 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
int iMatches = 0;
|
||||
|
||||
for (FileQueue::iterator it = cRemoteQueue.GetFileQueue()->begin(); it != cRemoteQueue.GetFileQueue()->end(); it++)
|
||||
{
|
||||
@@ -408,19 +433,29 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
lRemaining += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
printf("[%i] %s%s%c%s (%.2f MB%s%s)%s\n", pFileInfo->GetID(), szPriority, szNZBNiceName,
|
||||
(int)PATH_SEPARATOR, pFileInfo->GetFilename(),
|
||||
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
|
||||
szCompleted, szThreads, szStatus);
|
||||
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++;
|
||||
}
|
||||
|
||||
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),
|
||||
@@ -452,6 +487,7 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
int iMatches = 0;
|
||||
|
||||
for (GroupQueue::iterator it = cGroupQueue.begin(); it != cGroupQueue.end(); it++)
|
||||
{
|
||||
@@ -487,9 +523,6 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
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)
|
||||
@@ -526,9 +559,14 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
strncat(szParameters, ")", 1024);
|
||||
}
|
||||
|
||||
printf("[%i-%i] %s%s (%i file%s, %s%s%s)%s%s\n", pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), szPriority, szNZBNiceName,
|
||||
pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining,
|
||||
szPaused, szThreads, szCategory, szParameters);
|
||||
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++;
|
||||
}
|
||||
|
||||
delete pGroupInfo;
|
||||
}
|
||||
@@ -538,8 +576,17 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups)
|
||||
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)
|
||||
{
|
||||
@@ -800,9 +847,10 @@ bool RemoteClient::RequestServerDumpDebug()
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder)
|
||||
bool RemoteClient::RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
|
||||
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder)
|
||||
{
|
||||
if (iIDCount <= 0 || pIDList == NULL)
|
||||
if ((iIDCount <= 0 || pIDList == NULL) && (pNameList == NULL || pNameList->size() == 0))
|
||||
{
|
||||
printf("File(s) not specified\n");
|
||||
return false;
|
||||
@@ -810,18 +858,38 @@ 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 = sizeof(int32_t) * iIDCount + iTextLen;
|
||||
|
||||
int iLength = iTextLen + iIDLength + iNameLength;
|
||||
|
||||
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_iNrTrailingEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iNrTrailingIDEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iNrTrailingNameEntries = htonl(iNameCount);
|
||||
EditQueueRequest.m_iTrailingNameEntriesLen = htonl(iNameLength);
|
||||
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
char* pTrailingData = (char*)malloc(iLength);
|
||||
@@ -837,7 +905,19 @@ 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)) < 0)
|
||||
{
|
||||
@@ -877,6 +957,27 @@ bool RemoteClient::RequestServerShutdown()
|
||||
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)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerVersion()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
@@ -1008,13 +1109,15 @@ bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestScan()
|
||||
bool RemoteClient::RequestScan(bool bSyncMode)
|
||||
{
|
||||
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)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
@@ -1089,27 +1192,33 @@ bool RemoteClient::RequestHistory()
|
||||
{
|
||||
SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) pBufPtr;
|
||||
|
||||
const char* szFileName = pBufPtr + sizeof(SNZBHistoryResponseEntry);
|
||||
HistoryInfo::EKind eKind = (HistoryInfo::EKind)ntohl(pListAnswer->m_iKind);
|
||||
const char* szNicename = pBufPtr + sizeof(SNZBHistoryResponseEntry);
|
||||
|
||||
long long lSize = Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo));
|
||||
if (eKind == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
long long lSize = Util::JoinInt64(ntohl(pListAnswer->m_iSizeHi), ntohl(pListAnswer->m_iSizeLo));
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
NZBInfo::MakeNiceNZBName(szFileName, szNZBNiceName, 1024);
|
||||
char szSize[20];
|
||||
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
|
||||
|
||||
char szSize[20];
|
||||
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
|
||||
const char* szParStatusText[] = { "", ", Par failed", ", Par possible", ", Par successful" };
|
||||
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
|
||||
|
||||
const char* szParStatusText[] = { "", ", Par failed", ", Par possible", ", Par successful" };
|
||||
const char* szScriptStatusText[] = { "", ", Script status unknown", ", Script failed", ", Script successful" };
|
||||
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", "" };
|
||||
|
||||
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)]);
|
||||
printf("[%i] %s (%s)\n", ntohl(pListAnswer->m_iID), szNicename,
|
||||
szUrlStatusText[ntohl(pListAnswer->m_iUrlStatus)]);
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBHistoryResponseEntry) + ntohl(pListAnswer->m_iFilenameLen) +
|
||||
ntohl(pListAnswer->m_iDestDirLen) + ntohl(pListAnswer->m_iCategoryLen) +
|
||||
ntohl(pListAnswer->m_iQueuedFilenameLen);
|
||||
pBufPtr += sizeof(SNZBHistoryResponseEntry) + ntohl(pListAnswer->m_iNicenameLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
@@ -1120,3 +1229,124 @@ 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)) >= 0;
|
||||
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)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBUrlQueueResponse UrlQueueResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &UrlQueueResponse, sizeof(UrlQueueResponse));
|
||||
if (iResponseLen != sizeof(UrlQueueResponse) ||
|
||||
(int)ntohl(UrlQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(UrlQueueResponse.m_MessageBase.m_iStructSize) != sizeof(UrlQueueResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(UrlQueueResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(UrlQueueResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(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;
|
||||
}
|
||||
|
||||
@@ -35,8 +35,19 @@
|
||||
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();
|
||||
@@ -49,19 +60,23 @@ public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
|
||||
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst);
|
||||
bool RequestServerList(bool bFiles, bool bGroups);
|
||||
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
|
||||
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
|
||||
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
|
||||
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerReload();
|
||||
bool RequestServerVersion();
|
||||
bool RequestPostQueue();
|
||||
bool RequestWriteLog(int iKind, const char* szText);
|
||||
bool RequestScan();
|
||||
bool RequestScan(bool bSyncMode);
|
||||
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);
|
||||
};
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "BinRpc.h"
|
||||
#include "XmlRpc.h"
|
||||
#include "WebServer.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
|
||||
@@ -57,7 +57,6 @@ RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
@@ -69,7 +68,6 @@ RemoteServer::~RemoteServer()
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
@@ -82,7 +80,7 @@ void RemoteServer::Run()
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection = new Connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
@@ -174,73 +172,52 @@ void RequestProcessor::Run()
|
||||
processor.SetClientIP(ip);
|
||||
processor.Execute();
|
||||
}
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) ||
|
||||
!strncmp((char*)&iSignature, "GET ", 4) ||
|
||||
!strncmp((char*)&iSignature, "OPTI", 4))
|
||||
{
|
||||
// XML-RPC or JSON-RPC request received
|
||||
// HTTP request received
|
||||
Connection con(m_iSocket, false);
|
||||
char szBuffer[1024];
|
||||
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
|
||||
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
|
||||
char* szUrl = szBuffer;
|
||||
if (!strncmp((char*)&iSignature, "POST", 4))
|
||||
{
|
||||
eHttpMethod = XmlRpcProcessor::hmPost;
|
||||
eHttpMethod = WebProcessor::hmPost;
|
||||
szUrl++;
|
||||
}
|
||||
if (!strncmp((char*)&iSignature, "OPTI", 4) && strlen(szUrl) > 4)
|
||||
{
|
||||
eHttpMethod = WebProcessor::hmOptions;
|
||||
szUrl += 4;
|
||||
}
|
||||
if (char* p = strchr(szUrl, ' '))
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
|
||||
XmlRpcProcessor::EAuthMode eAuthMode = XmlRpcProcessor::amHeader;
|
||||
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpXmlRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/xmlrpc-auth") || !strncmp(szUrl, "/xmlrpc-auth/", 13))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpXmlRpc;
|
||||
eAuthMode = XmlRpcProcessor::amURL;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonrpc-auth") || !strncmp(szUrl, "/jsonrpc-auth/", 14))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonRpc;
|
||||
eAuthMode = XmlRpcProcessor::amURL;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonprpc") || !strncmp(szUrl, "/jsonprpc/", 10))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonPRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonprpc-auth") || !strncmp(szUrl, "/jsonprpc-auth/", 15))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonPRpc;
|
||||
eAuthMode = XmlRpcProcessor::amURL;
|
||||
}
|
||||
debug("url: %s", szUrl);
|
||||
|
||||
if (eProtocol != XmlRpcProcessor::rpUndefined)
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetProtocol(eProtocol);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.SetAuthMode(eAuthMode);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
WebProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOK)
|
||||
if (!bOK && iBytesReceived > 0)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), ip);
|
||||
}
|
||||
|
||||
if (!bOK && iBytesReceived == 0)
|
||||
{
|
||||
debug("empty request received on port %i from %s", g_pOptions->GetControlPort(), ip);
|
||||
}
|
||||
|
||||
closesocket(m_iSocket);
|
||||
|
||||
@@ -28,13 +28,11 @@
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
|
||||
20
Scanner.cpp
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -36,6 +36,8 @@
|
||||
#include <fstream>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
@@ -70,6 +72,7 @@ Scanner::Scanner()
|
||||
debug("Creating Scanner");
|
||||
|
||||
m_bRequestedNZBDirScan = false;
|
||||
m_bScanning = false;
|
||||
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
|
||||
m_iPass = 0;
|
||||
m_iStepMSec = 0;
|
||||
@@ -98,7 +101,14 @@ void Scanner::Check()
|
||||
// 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 requesten, 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
|
||||
@@ -363,9 +373,15 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::ScanNZBDir()
|
||||
void Scanner::ScanNZBDir(bool bSyncMode)
|
||||
{
|
||||
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
|
||||
// but it's not critical here.
|
||||
m_bScanning = true;
|
||||
m_bRequestedNZBDirScan = true;
|
||||
|
||||
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ private:
|
||||
int m_iPass;
|
||||
int m_iStepMSec;
|
||||
FileList m_FileList;
|
||||
bool m_bScanning;
|
||||
|
||||
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
|
||||
void AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList);
|
||||
@@ -69,7 +70,7 @@ public:
|
||||
Scanner();
|
||||
~Scanner();
|
||||
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
|
||||
void ScanNZBDir();
|
||||
void ScanNZBDir(bool bSyncMode);
|
||||
void Check();
|
||||
};
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -50,7 +50,7 @@
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern char* (*szEnvironmentVariables)[];
|
||||
extern char* (*g_szEnvironmentVariables)[];
|
||||
extern DownloadQueueHolder* g_pDownloadQueueHolder;
|
||||
|
||||
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
|
||||
@@ -120,9 +120,9 @@ EnvironmentStrings::~EnvironmentStrings()
|
||||
|
||||
void EnvironmentStrings::InitFromCurrentProcess()
|
||||
{
|
||||
for (int i = 0; (*szEnvironmentVariables)[i]; i++)
|
||||
for (int i = 0; (*g_szEnvironmentVariables)[i]; i++)
|
||||
{
|
||||
char* szVar = (*szEnvironmentVariables)[i];
|
||||
char* szVar = (*g_szEnvironmentVariables)[i];
|
||||
Append(strdup(szVar));
|
||||
}
|
||||
}
|
||||
@@ -612,9 +612,13 @@ void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScr
|
||||
|
||||
void PostScriptController::Run()
|
||||
{
|
||||
// the locking is needed for accessing the memebers of NZBInfo
|
||||
// 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 szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
|
||||
szParStatus[10-1] = '\0';
|
||||
@@ -663,6 +667,7 @@ void PostScriptController::Run()
|
||||
szArgs[8] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
SetEnvVar("NZBPP_NZBNAME", szNZBName);
|
||||
SetEnvVar("NZBPP_DIRECTORY", szDestDir);
|
||||
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
|
||||
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
|
||||
@@ -889,6 +894,75 @@ void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Op
|
||||
}
|
||||
}
|
||||
|
||||
void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript)
|
||||
{
|
||||
NZBAddedScriptController* pScriptController = new NZBAddedScriptController();
|
||||
pScriptController->SetScript(szScript);
|
||||
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
|
||||
pScriptController->SetEnvVar("NZBNA_NAME", pNZBInfo->GetName());
|
||||
pScriptController->SetEnvVar("NZBNA_FILENAME", pNZBInfo->GetFilename());
|
||||
pScriptController->SetEnvVar("NZBNA_CATEGORY", pNZBInfo->GetCategory());
|
||||
|
||||
int iLastID = 0;
|
||||
int iMaxPriority = 0;
|
||||
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo && ( pFileInfo->GetPriority() > iMaxPriority || iLastID == 0))
|
||||
{
|
||||
iMaxPriority = pFileInfo->GetPriority();
|
||||
}
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo && pFileInfo->GetID() > iLastID)
|
||||
{
|
||||
iLastID = pFileInfo->GetID();
|
||||
}
|
||||
}
|
||||
|
||||
char buf[100];
|
||||
|
||||
snprintf(buf, 100, "%i", iLastID);
|
||||
pScriptController->SetEnvVar("NZBNA_LASTID", buf);
|
||||
|
||||
snprintf(buf, 100, "%i", iMaxPriority);
|
||||
pScriptController->SetEnvVar("NZBNA_PRIORITY", buf);
|
||||
|
||||
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
char szVarname[1024];
|
||||
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
|
||||
szVarname[1024-1] = '\0';
|
||||
pScriptController->SetEnvVar(szVarname, pParameter->GetValue());
|
||||
}
|
||||
|
||||
pScriptController->SetAutoDestroy(true);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void NZBAddedScriptController::Run()
|
||||
{
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "nzb-added process-script for %s", m_szNZBName);
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
info("Executing %s", szInfoName);
|
||||
|
||||
SetDefaultKindPrefix("NZB-Added Process: ");
|
||||
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
|
||||
|
||||
const char* szArgs[2];
|
||||
szArgs[0] = GetScript();
|
||||
szArgs[1] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
Execute();
|
||||
|
||||
free(m_szNZBName);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::StartScript(const char* szCommandLine)
|
||||
{
|
||||
char** argv = NULL;
|
||||
|
||||
@@ -124,6 +124,16 @@ public:
|
||||
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
|
||||
};
|
||||
|
||||
class NZBAddedScriptController : public Thread, ScriptController
|
||||
{
|
||||
private:
|
||||
char* m_szNZBName;
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
|
||||
};
|
||||
|
||||
class SchedulerScriptController : public Thread, ScriptController
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -187,7 +187,7 @@ void ServerPool::CloseUnusedConnections()
|
||||
int tdiff = (int)(curtime - pConnection->GetFreeTime());
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
|
||||
debug("Closing unused connection to %s", pConnection->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
8
TLS.cpp
@@ -908,7 +908,7 @@ int tls_check_cert(tls_t *tls, const char *hostname, int verify, char **errstr)
|
||||
char *buf;
|
||||
int bufsize;
|
||||
/* needed to get the DNS subjectAltNames: */
|
||||
STACK *subj_alt_names;
|
||||
STACK_OF(GENERAL_NAME) *subj_alt_names;
|
||||
int subj_alt_names_count;
|
||||
GENERAL_NAME *subj_alt_name;
|
||||
/* did we find a name matching hostname? */
|
||||
@@ -960,7 +960,7 @@ int tls_check_cert(tls_t *tls, const char *hostname, int verify, char **errstr)
|
||||
/* Try the DNS subjectAltNames. */
|
||||
match_found = 0;
|
||||
if ((subj_alt_names =
|
||||
(STACK*)X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL)))
|
||||
(STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(x509cert, NID_subject_alt_name, NULL, NULL)))
|
||||
{
|
||||
subj_alt_names_count = sk_GENERAL_NAME_num(subj_alt_names);
|
||||
for (i = 0; i < subj_alt_names_count; i++)
|
||||
@@ -1105,7 +1105,11 @@ int tls_init(tls_t *tls, const char *key_file, const char *cert_file,
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
|
||||
const SSL_METHOD *ssl_method = NULL;
|
||||
#else
|
||||
SSL_METHOD *ssl_method = NULL;
|
||||
#endif
|
||||
|
||||
ssl_method = force_sslv3 ? SSLv3_client_method() : SSLv23_client_method();
|
||||
if (!ssl_method)
|
||||
|
||||
455
UrlCoordinator.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "NZBFile.h"
|
||||
#include "QueueCoordinator.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
|
||||
UrlDownloader::UrlDownloader() : WebDownloader()
|
||||
{
|
||||
m_szCategory = NULL;
|
||||
}
|
||||
|
||||
UrlDownloader::~UrlDownloader()
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlDownloader::ProcessHeader(const char* szLine)
|
||||
{
|
||||
WebDownloader::ProcessHeader(szLine);
|
||||
|
||||
if (!strncmp(szLine, "X-DNZB-Category: ", 17))
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
|
||||
const char *szCat = szLine + 17;
|
||||
|
||||
int iCatLen = strlen(szCat);
|
||||
|
||||
// trim trailing CR/LF/spaces
|
||||
while (iCatLen > 0 && (szCat[iCatLen-1] == '\n' || szCat[iCatLen-1] == '\r' || szCat[iCatLen-1] == ' ')) iCatLen--;
|
||||
|
||||
m_szCategory = (char*)malloc(iCatLen + 1);
|
||||
strncpy(m_szCategory, szCat, iCatLen);
|
||||
m_szCategory[iCatLen] = '\0';
|
||||
|
||||
debug("Category: %s", m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
UrlCoordinator::UrlCoordinator()
|
||||
{
|
||||
debug("Creating UrlCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
}
|
||||
|
||||
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()))
|
||||
{
|
||||
// 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() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartUrlDownload(pUrlInfo);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns next URL for download.
|
||||
*/
|
||||
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
|
||||
{
|
||||
bool bOK = false;
|
||||
|
||||
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
|
||||
{
|
||||
pUrlInfo = *at;
|
||||
if (pUrlInfo->GetStatus() == 0)
|
||||
{
|
||||
bOK = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
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* Caller, void* Aspect)
|
||||
{
|
||||
debug("Notification from UrlDownloader received");
|
||||
|
||||
UrlDownloader* pUrlDownloader = (UrlDownloader*) Caller;
|
||||
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);
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
// delete Download from Queue
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
UrlDownloader* pa = *it;
|
||||
if (pa == pUrlDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool bDeleteObj = false;
|
||||
|
||||
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished || pUrlInfo->GetStatus() == UrlInfo::aiFailed)
|
||||
{
|
||||
// delete UrlInfo from Queue
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
UrlInfo* pa = *it;
|
||||
if (pa == pUrlInfo)
|
||||
{
|
||||
pDownloadQueue->GetUrlQueue()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bDeleteObj = true;
|
||||
|
||||
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() == UrlInfo::aiFailed)
|
||||
{
|
||||
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 (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
|
||||
{
|
||||
// add nzb-file to download queue
|
||||
AddToNZBQueue(pUrlInfo, pUrlDownloader->GetOutputFilename(), filename, pUrlDownloader->GetCategory());
|
||||
}
|
||||
|
||||
if (bDeleteObj)
|
||||
{
|
||||
delete pUrlInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
|
||||
{
|
||||
info("Queue downloaded collection %s", szOriginalFilename);
|
||||
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szTempFilename, pUrlInfo->GetCategory());
|
||||
if (pNZBFile)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetName(NULL);
|
||||
pNZBFile->GetNZBInfo()->SetFilename(pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename);
|
||||
|
||||
if (strlen(pUrlInfo->GetCategory()) > 0)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetCategory(pUrlInfo->GetCategory());
|
||||
}
|
||||
else if (szOriginalCategory)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetCategory(szOriginalCategory);
|
||||
}
|
||||
|
||||
pNZBFile->GetNZBInfo()->BuildDestDirName();
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPriority(pUrlInfo->GetPriority());
|
||||
pFileInfo->SetPaused(pUrlInfo->GetAddPaused());
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pUrlInfo->GetAddTop());
|
||||
delete pNZBFile;
|
||||
info("Collection %s added to queue", szOriginalFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not add downloaded collection %s to queue", szOriginalFilename);
|
||||
}
|
||||
}
|
||||
86
UrlCoordinator.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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 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;
|
||||
|
||||
private:
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
|
||||
void StartUrlDownload(UrlInfo* pUrlInfo);
|
||||
void UrlCompleted(UrlDownloader* pUrlDownloader);
|
||||
void ResetHangingDownloads();
|
||||
void AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory);
|
||||
|
||||
public:
|
||||
UrlCoordinator();
|
||||
virtual ~UrlCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// 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;
|
||||
|
||||
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; }
|
||||
};
|
||||
|
||||
#endif
|
||||
131
Util.h
@@ -64,21 +64,24 @@ public:
|
||||
static char* BaseFileName(const char* filename);
|
||||
static void NormalizePathSeparators(char* szPath);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
static bool SetFileSize(const char* szFilename, int iSize);
|
||||
static bool CreateSparseFile(const char* szFilename, int iSize);
|
||||
static bool TruncateFile(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
|
||||
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
|
||||
static bool FileExists(const char* szFilename);
|
||||
static bool DirectoryExists(const char* szDirFilename);
|
||||
static bool CreateDirectory(const char* szDirFilename);
|
||||
static bool ForceDirectories(const char* szPath);
|
||||
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
|
||||
static bool SetCurrentDirectory(const char* szDirFilename);
|
||||
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 ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
#endif
|
||||
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
|
||||
/*
|
||||
@@ -105,6 +108,27 @@ public:
|
||||
*/
|
||||
static float Int64ToFloat(long long Int64);
|
||||
|
||||
static void TrimRight(char* szStr);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
@@ -155,19 +179,98 @@ public:
|
||||
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
|
||||
|
||||
/*
|
||||
* 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").
|
||||
* Unquote http quoted string.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
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];
|
||||
static void HttpUnquote(char* raw);
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
public:
|
||||
RegEx(const char *szPattern);
|
||||
~RegEx();
|
||||
bool IsValid() { return m_bValid; }
|
||||
bool Match(const char *szStr);
|
||||
};
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
class ZLib
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* calculates the size required for output buffer
|
||||
*/
|
||||
static unsigned int GZipLen(int iInputBufferLength);
|
||||
|
||||
/*
|
||||
* returns the size of bytes written to szOutputBuffer or 0 if the buffer is too small or an error occured.
|
||||
*/
|
||||
static unsigned int GZip(const void* szInputBuffer, int iInputBufferLength, void* szOutputBuffer, int iOutputBufferLength);
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
679
WebDownloader.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#ifdef WIN32
|
||||
#include <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;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
WebDownloader::~WebDownloader()
|
||||
{
|
||||
debug("Destroying WebDownloader");
|
||||
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
if (m_szOriginalFilename)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = Download();
|
||||
|
||||
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(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) &&
|
||||
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
|
||||
{
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
ProcessHeader(line);
|
||||
}
|
||||
|
||||
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->Recv(szLineBuf, LineBufSize);
|
||||
szBuffer = szLineBuf;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (iLen <= 0)
|
||||
{
|
||||
if (m_iContentLen == -1)
|
||||
{
|
||||
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
|
||||
if (m_pGUnzipStream)
|
||||
{
|
||||
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, "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 (!strncmp(szLine, "Content-Length: ", 16))
|
||||
{
|
||||
m_iContentLen = atoi(szLine + 16);
|
||||
m_bConfirmedLength = true;
|
||||
}
|
||||
|
||||
if (!strncmp(szLine, "Content-Encoding: gzip", 22))
|
||||
{
|
||||
m_bGZip = true;
|
||||
}
|
||||
|
||||
if (!strncmp(szLine, "Content-Disposition: ", 21))
|
||||
{
|
||||
ParseFilename(szLine);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (m_szOriginalFilename)
|
||||
{
|
||||
free(m_szOriginalFilename);
|
||||
}
|
||||
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
|
||||
|
||||
debug("OriginalFilename: %s", m_szOriginalFilename);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
103
WebDownloader.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 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,
|
||||
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_bGZip;
|
||||
#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 Download();
|
||||
EStatus CreateConnection(URL *pUrl);
|
||||
void ParseFilename(const char* szContentDisposition);
|
||||
void SendHeaders(URL *pUrl);
|
||||
EStatus DownloadHeaders();
|
||||
EStatus DownloadBody();
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* szLine);
|
||||
|
||||
public:
|
||||
WebDownloader();
|
||||
~WebDownloader();
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
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 LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
485
WebServer.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#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_szClientIP = NULL;
|
||||
m_szRequest = NULL;
|
||||
m_szUrl = NULL;
|
||||
m_szOrigin = NULL;
|
||||
}
|
||||
|
||||
WebProcessor::~WebProcessor()
|
||||
{
|
||||
if (m_szRequest)
|
||||
{
|
||||
free(m_szRequest);
|
||||
}
|
||||
if (m_szUrl)
|
||||
{
|
||||
free(m_szUrl);
|
||||
}
|
||||
if (m_szOrigin)
|
||||
{
|
||||
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];
|
||||
bool bBody = false;
|
||||
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')
|
||||
{
|
||||
bBody = true;
|
||||
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(szAuthInfo) == 0)
|
||||
{
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Authorization
|
||||
char* pw = strchr(szAuthInfo, ':');
|
||||
if (pw) *pw++ = '\0';
|
||||
if (strcmp(szAuthInfo, "nzbget") || strcmp(pw, g_pOptions->GetControlPassword()))
|
||||
{
|
||||
warn("request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_szClientIP);
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod == hmPost)
|
||||
{
|
||||
// reading http body (request content)
|
||||
m_szRequest = (char*)malloc(iContentLen + 1);
|
||||
m_szRequest[iContentLen] = '\0';
|
||||
|
||||
if (!m_pConnection->RecvAll(m_szRequest, iContentLen))
|
||||
{
|
||||
free(m_szRequest);
|
||||
error("invalid-request: could not read data");
|
||||
return;
|
||||
}
|
||||
debug("Request=%s", m_szRequest);
|
||||
}
|
||||
|
||||
debug("request received from %s", m_szClientIP);
|
||||
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void WebProcessor::Dispatch()
|
||||
{
|
||||
if (*m_szUrl != '/')
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (XmlRpcProcessor::IsRpcRequest(m_szUrl))
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetRequest(m_szRequest);
|
||||
processor.SetClientIP(m_szClientIP);
|
||||
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
|
||||
if (szGBuf)
|
||||
{
|
||||
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;
|
||||
}
|
||||
69
WebServer.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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;
|
||||
const char* m_szClientIP;
|
||||
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);
|
||||
|
||||
public:
|
||||
WebProcessor();
|
||||
~WebProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
#endif
|
||||
929
XmlRpc.cpp
66
XmlRpc.h
@@ -60,37 +60,31 @@ public:
|
||||
hmGet
|
||||
};
|
||||
|
||||
enum EAuthMode
|
||||
{
|
||||
amHeader,
|
||||
amURL
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
const char* m_szContentType;
|
||||
ERpcProtocol m_eProtocol;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
EAuthMode m_eAuthMode;
|
||||
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 SetAuthMode(EAuthMode eAuthMode) { m_eAuthMode = eAuthMode; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
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);
|
||||
};
|
||||
|
||||
class XmlCommand
|
||||
@@ -104,7 +98,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();
|
||||
@@ -114,6 +108,7 @@ protected:
|
||||
bool NextParamAsStr(char** szValueBuf);
|
||||
const char* BoolToStr(bool bValue);
|
||||
char* EncodeStr(const char* szStr);
|
||||
void DecodeStr(char* szStr);
|
||||
|
||||
public:
|
||||
XmlCommand();
|
||||
@@ -165,6 +160,12 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ReloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
@@ -231,6 +232,12 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ClearLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ScanXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
@@ -243,4 +250,35 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadUrlXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UrlQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LoadConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SaveConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
void Save(const char *szFilename);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
/* 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,6 +70,9 @@
|
||||
/* 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
|
||||
|
||||
|
||||
436
configure
vendored
@@ -1,6 +1,6 @@
|
||||
#! /bin/sh
|
||||
# Guess values for system-dependent variables and create Makefiles.
|
||||
# Generated by GNU Autoconf 2.61 for nzbget 0.8.0.
|
||||
# Generated by GNU Autoconf 2.61 for nzbget 9.0.
|
||||
#
|
||||
# Report bugs to <hugbug@users.sourceforge.net>.
|
||||
#
|
||||
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
|
||||
# Identity of this package.
|
||||
PACKAGE_NAME='nzbget'
|
||||
PACKAGE_TARNAME='nzbget'
|
||||
PACKAGE_VERSION='0.8.0'
|
||||
PACKAGE_STRING='nzbget 0.8.0'
|
||||
PACKAGE_VERSION='9.0'
|
||||
PACKAGE_STRING='nzbget 9.0'
|
||||
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
|
||||
|
||||
ac_unique_file="nzbget.cpp"
|
||||
@@ -1234,7 +1234,7 @@ if test "$ac_init_help" = "long"; then
|
||||
# Omit some internal or obsolete options to make the list less imposing.
|
||||
# This message is too long to be a string in the A/UX 3.1 sh.
|
||||
cat <<_ACEOF
|
||||
\`configure' configures nzbget 0.8.0 to adapt to many kinds of systems.
|
||||
\`configure' configures nzbget 9.0 to adapt to many kinds of systems.
|
||||
|
||||
Usage: $0 [OPTION]... [VAR=VALUE]...
|
||||
|
||||
@@ -1305,7 +1305,7 @@ fi
|
||||
|
||||
if test -n "$ac_init_help"; then
|
||||
case $ac_init_help in
|
||||
short | recursive ) echo "Configuration of nzbget 0.8.0:";;
|
||||
short | recursive ) echo "Configuration of nzbget 9.0:";;
|
||||
esac
|
||||
cat <<\_ACEOF
|
||||
|
||||
@@ -1320,6 +1320,8 @@ Optional Features:
|
||||
dependency from libpar2- and libsigc-libraries)
|
||||
--disable-tls do not use TLS/SSL (removes dependency from
|
||||
TLS/SSL-libraries)
|
||||
--disable-gzip disable gzip-compression/decompression (removes
|
||||
dependency from zlib-library)
|
||||
--disable-sigchld-handler
|
||||
do not use sigchld-handler (the disabling is
|
||||
recommended for BSD)
|
||||
@@ -1354,6 +1356,10 @@ Optional Packages:
|
||||
OpenSSL include directory
|
||||
--with-openssl-libraries=DIR
|
||||
OpenSSL library directory
|
||||
--with-zlib-includes=DIR
|
||||
zlib include directory
|
||||
--with-zlib-libraries=DIR
|
||||
zlib library directory
|
||||
|
||||
Some influential environment variables:
|
||||
CXX C++ compiler command
|
||||
@@ -1438,7 +1444,7 @@ fi
|
||||
test -n "$ac_init_help" && exit $ac_status
|
||||
if $ac_init_version; then
|
||||
cat <<\_ACEOF
|
||||
nzbget configure 0.8.0
|
||||
nzbget configure 9.0
|
||||
generated by GNU Autoconf 2.61
|
||||
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
|
||||
@@ -1452,7 +1458,7 @@ cat >config.log <<_ACEOF
|
||||
This file contains any messages produced by compilers while
|
||||
running configure, to aid debugging if configure makes a mistake.
|
||||
|
||||
It was created by nzbget $as_me 0.8.0, which was
|
||||
It was created by nzbget $as_me 9.0, which was
|
||||
generated by GNU Autoconf 2.61. Invocation command line was
|
||||
|
||||
$ $0 $@
|
||||
@@ -2248,7 +2254,7 @@ fi
|
||||
|
||||
# Define the identity of the package.
|
||||
PACKAGE=nzbget
|
||||
VERSION=0.8.0
|
||||
VERSION=9.0
|
||||
|
||||
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
@@ -4136,6 +4142,151 @@ fi
|
||||
done
|
||||
|
||||
|
||||
for ac_header in regex.h
|
||||
do
|
||||
as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
|
||||
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
|
||||
{ echo "$as_me:$LINENO: checking for $ac_header" >&5
|
||||
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
|
||||
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
fi
|
||||
ac_res=`eval echo '${'$as_ac_Header'}'`
|
||||
{ echo "$as_me:$LINENO: result: $ac_res" >&5
|
||||
echo "${ECHO_T}$ac_res" >&6; }
|
||||
else
|
||||
# Is the header compilable?
|
||||
{ echo "$as_me:$LINENO: checking $ac_header usability" >&5
|
||||
echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
$ac_includes_default
|
||||
#include <$ac_header>
|
||||
_ACEOF
|
||||
rm -f conftest.$ac_objext
|
||||
if { (ac_try="$ac_compile"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||
(eval "$ac_compile") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } && {
|
||||
test -z "$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
} && test -s conftest.$ac_objext; then
|
||||
ac_header_compiler=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_header_compiler=no
|
||||
fi
|
||||
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
|
||||
echo "${ECHO_T}$ac_header_compiler" >&6; }
|
||||
|
||||
# Is the header present?
|
||||
{ echo "$as_me:$LINENO: checking $ac_header presence" >&5
|
||||
echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
#include <$ac_header>
|
||||
_ACEOF
|
||||
if { (ac_try="$ac_cpp conftest.$ac_ext"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } >/dev/null && {
|
||||
test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
}; then
|
||||
ac_header_preproc=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_header_preproc=no
|
||||
fi
|
||||
|
||||
rm -f conftest.err conftest.$ac_ext
|
||||
{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
|
||||
echo "${ECHO_T}$ac_header_preproc" >&6; }
|
||||
|
||||
# So? What about this header?
|
||||
case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
|
||||
yes:no: )
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
|
||||
echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
|
||||
echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
|
||||
ac_header_preproc=yes
|
||||
;;
|
||||
no:yes:* )
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
|
||||
echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5
|
||||
echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
|
||||
echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5
|
||||
echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
|
||||
echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
|
||||
echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
|
||||
( cat <<\_ASBOX
|
||||
## ------------------------------------------- ##
|
||||
## Report this to hugbug@users.sourceforge.net ##
|
||||
## ------------------------------------------- ##
|
||||
_ASBOX
|
||||
) | sed "s/^/$as_me: WARNING: /" >&2
|
||||
;;
|
||||
esac
|
||||
{ echo "$as_me:$LINENO: checking for $ac_header" >&5
|
||||
echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }
|
||||
if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
else
|
||||
eval "$as_ac_Header=\$ac_header_preproc"
|
||||
fi
|
||||
ac_res=`eval echo '${'$as_ac_Header'}'`
|
||||
{ echo "$as_me:$LINENO: result: $ac_res" >&5
|
||||
echo "${ECHO_T}$ac_res" >&6; }
|
||||
|
||||
fi
|
||||
if test `eval echo '${'$as_ac_Header'}'` = yes; then
|
||||
cat >>confdefs.h <<_ACEOF
|
||||
#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
|
||||
|
||||
{ echo "$as_me:$LINENO: checking for library containing pthread_create" >&5
|
||||
echo $ECHO_N "checking for library containing pthread_create... $ECHO_C" >&6; }
|
||||
@@ -7871,6 +8022,271 @@ _ACEOF
|
||||
fi
|
||||
|
||||
|
||||
{ echo "$as_me:$LINENO: checking whether to use gzip" >&5
|
||||
echo $ECHO_N "checking whether to use gzip... $ECHO_C" >&6; }
|
||||
# Check whether --enable-gzip was given.
|
||||
if test "${enable_gzip+set}" = set; then
|
||||
enableval=$enable_gzip; USEZLIB=$enableval
|
||||
else
|
||||
USEZLIB=yes
|
||||
fi
|
||||
|
||||
{ echo "$as_me:$LINENO: result: $USEZLIB" >&5
|
||||
echo "${ECHO_T}$USEZLIB" >&6; }
|
||||
if test "$USEZLIB" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
|
||||
# Check whether --with-zlib_includes was given.
|
||||
if test "${with_zlib_includes+set}" = set; then
|
||||
withval=$with_zlib_includes; INCVAL="$withval"
|
||||
fi
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
|
||||
# Check whether --with-zlib_libraries was given.
|
||||
if test "${with_zlib_libraries+set}" = set; then
|
||||
withval=$with_zlib_libraries; LIBVAL="$withval"
|
||||
fi
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
if test "${ac_cv_header_zlib_h+set}" = set; then
|
||||
{ echo "$as_me:$LINENO: checking for zlib.h" >&5
|
||||
echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6; }
|
||||
if test "${ac_cv_header_zlib_h+set}" = set; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
fi
|
||||
{ echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
|
||||
echo "${ECHO_T}$ac_cv_header_zlib_h" >&6; }
|
||||
else
|
||||
# Is the header compilable?
|
||||
{ echo "$as_me:$LINENO: checking zlib.h usability" >&5
|
||||
echo $ECHO_N "checking zlib.h usability... $ECHO_C" >&6; }
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
$ac_includes_default
|
||||
#include <zlib.h>
|
||||
_ACEOF
|
||||
rm -f conftest.$ac_objext
|
||||
if { (ac_try="$ac_compile"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||
(eval "$ac_compile") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } && {
|
||||
test -z "$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
} && test -s conftest.$ac_objext; then
|
||||
ac_header_compiler=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_header_compiler=no
|
||||
fi
|
||||
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
|
||||
echo "${ECHO_T}$ac_header_compiler" >&6; }
|
||||
|
||||
# Is the header present?
|
||||
{ echo "$as_me:$LINENO: checking zlib.h presence" >&5
|
||||
echo $ECHO_N "checking zlib.h presence... $ECHO_C" >&6; }
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
#include <zlib.h>
|
||||
_ACEOF
|
||||
if { (ac_try="$ac_cpp conftest.$ac_ext"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } >/dev/null && {
|
||||
test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
}; then
|
||||
ac_header_preproc=yes
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
ac_header_preproc=no
|
||||
fi
|
||||
|
||||
rm -f conftest.err conftest.$ac_ext
|
||||
{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
|
||||
echo "${ECHO_T}$ac_header_preproc" >&6; }
|
||||
|
||||
# So? What about this header?
|
||||
case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in
|
||||
yes:no: )
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&5
|
||||
echo "$as_me: WARNING: zlib.h: accepted by the compiler, rejected by the preprocessor!" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the compiler's result" >&5
|
||||
echo "$as_me: WARNING: zlib.h: proceeding with the compiler's result" >&2;}
|
||||
ac_header_preproc=yes
|
||||
;;
|
||||
no:yes:* )
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: present but cannot be compiled" >&5
|
||||
echo "$as_me: WARNING: zlib.h: present but cannot be compiled" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: check for missing prerequisite headers?" >&5
|
||||
echo "$as_me: WARNING: zlib.h: check for missing prerequisite headers?" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: see the Autoconf documentation" >&5
|
||||
echo "$as_me: WARNING: zlib.h: see the Autoconf documentation" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&5
|
||||
echo "$as_me: WARNING: zlib.h: section \"Present But Cannot Be Compiled\"" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: proceeding with the preprocessor's result" >&5
|
||||
echo "$as_me: WARNING: zlib.h: proceeding with the preprocessor's result" >&2;}
|
||||
{ echo "$as_me:$LINENO: WARNING: zlib.h: in the future, the compiler will take precedence" >&5
|
||||
echo "$as_me: WARNING: zlib.h: in the future, the compiler will take precedence" >&2;}
|
||||
( cat <<\_ASBOX
|
||||
## ------------------------------------------- ##
|
||||
## Report this to hugbug@users.sourceforge.net ##
|
||||
## ------------------------------------------- ##
|
||||
_ASBOX
|
||||
) | sed "s/^/$as_me: WARNING: /" >&2
|
||||
;;
|
||||
esac
|
||||
{ echo "$as_me:$LINENO: checking for zlib.h" >&5
|
||||
echo $ECHO_N "checking for zlib.h... $ECHO_C" >&6; }
|
||||
if test "${ac_cv_header_zlib_h+set}" = set; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
else
|
||||
ac_cv_header_zlib_h=$ac_header_preproc
|
||||
fi
|
||||
{ echo "$as_me:$LINENO: result: $ac_cv_header_zlib_h" >&5
|
||||
echo "${ECHO_T}$ac_cv_header_zlib_h" >&6; }
|
||||
|
||||
fi
|
||||
if test $ac_cv_header_zlib_h = yes; then
|
||||
:
|
||||
else
|
||||
{ { echo "$as_me:$LINENO: error: \"zlib header files not found\"" >&5
|
||||
echo "$as_me: error: \"zlib header files not found\"" >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
|
||||
|
||||
{ echo "$as_me:$LINENO: checking for library containing deflateBound" >&5
|
||||
echo $ECHO_N "checking for library containing deflateBound... $ECHO_C" >&6; }
|
||||
if test "${ac_cv_search_deflateBound+set}" = set; then
|
||||
echo $ECHO_N "(cached) $ECHO_C" >&6
|
||||
else
|
||||
ac_func_search_save_LIBS=$LIBS
|
||||
cat >conftest.$ac_ext <<_ACEOF
|
||||
/* confdefs.h. */
|
||||
_ACEOF
|
||||
cat confdefs.h >>conftest.$ac_ext
|
||||
cat >>conftest.$ac_ext <<_ACEOF
|
||||
/* end confdefs.h. */
|
||||
|
||||
/* Override any GCC internal prototype to avoid an error.
|
||||
Use char because int might match the return type of a GCC
|
||||
builtin and then its argument prototype would still apply. */
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
char deflateBound ();
|
||||
int
|
||||
main ()
|
||||
{
|
||||
return deflateBound ();
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
for ac_lib in '' z; do
|
||||
if test -z "$ac_lib"; then
|
||||
ac_res="none required"
|
||||
else
|
||||
ac_res=-l$ac_lib
|
||||
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
|
||||
fi
|
||||
rm -f conftest.$ac_objext conftest$ac_exeext
|
||||
if { (ac_try="$ac_link"
|
||||
case "(($ac_try" in
|
||||
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
|
||||
*) ac_try_echo=$ac_try;;
|
||||
esac
|
||||
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
|
||||
(eval "$ac_link") 2>conftest.er1
|
||||
ac_status=$?
|
||||
grep -v '^ *+' conftest.er1 >conftest.err
|
||||
rm -f conftest.er1
|
||||
cat conftest.err >&5
|
||||
echo "$as_me:$LINENO: \$? = $ac_status" >&5
|
||||
(exit $ac_status); } && {
|
||||
test -z "$ac_cxx_werror_flag" ||
|
||||
test ! -s conftest.err
|
||||
} && test -s conftest$ac_exeext &&
|
||||
$as_test_x conftest$ac_exeext; then
|
||||
ac_cv_search_deflateBound=$ac_res
|
||||
else
|
||||
echo "$as_me: failed program was:" >&5
|
||||
sed 's/^/| /' conftest.$ac_ext >&5
|
||||
|
||||
|
||||
fi
|
||||
|
||||
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
|
||||
conftest$ac_exeext
|
||||
if test "${ac_cv_search_deflateBound+set}" = set; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
if test "${ac_cv_search_deflateBound+set}" = set; then
|
||||
:
|
||||
else
|
||||
ac_cv_search_deflateBound=no
|
||||
fi
|
||||
rm conftest.$ac_ext
|
||||
LIBS=$ac_func_search_save_LIBS
|
||||
fi
|
||||
{ echo "$as_me:$LINENO: result: $ac_cv_search_deflateBound" >&5
|
||||
echo "${ECHO_T}$ac_cv_search_deflateBound" >&6; }
|
||||
ac_res=$ac_cv_search_deflateBound
|
||||
if test "$ac_res" != no; then
|
||||
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
|
||||
|
||||
else
|
||||
{ { echo "$as_me:$LINENO: error: \"zlib library not found\"" >&5
|
||||
echo "$as_me: error: \"zlib library not found\"" >&2;}
|
||||
{ (exit 1); exit 1; }; }
|
||||
fi
|
||||
|
||||
else
|
||||
|
||||
cat >>confdefs.h <<\_ACEOF
|
||||
#define DISABLE_GZIP 1
|
||||
_ACEOF
|
||||
|
||||
fi
|
||||
|
||||
|
||||
{ echo "$as_me:$LINENO: checking whether to use an empty SIGCHLD handler" >&5
|
||||
echo $ECHO_N "checking whether to use an empty SIGCHLD handler... $ECHO_C" >&6; }
|
||||
# Check whether --enable-sigchld-handler was given.
|
||||
@@ -8603,7 +9019,7 @@ exec 6>&1
|
||||
# report actual input values of CONFIG_FILES etc. instead of their
|
||||
# values after options handling.
|
||||
ac_log="
|
||||
This file was extended by nzbget $as_me 0.8.0, which was
|
||||
This file was extended by nzbget $as_me 9.0, which was
|
||||
generated by GNU Autoconf 2.61. Invocation command line was
|
||||
|
||||
CONFIG_FILES = $CONFIG_FILES
|
||||
@@ -8656,7 +9072,7 @@ Report bugs to <bug-autoconf@gnu.org>."
|
||||
_ACEOF
|
||||
cat >>$CONFIG_STATUS <<_ACEOF
|
||||
ac_cs_version="\\
|
||||
nzbget config.status 0.8.0
|
||||
nzbget config.status 9.0
|
||||
configured by $0, generated by GNU Autoconf 2.61,
|
||||
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
|
||||
|
||||
|
||||
36
configure.ac
@@ -2,9 +2,9 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.8.0, hugbug@users.sourceforge.net)
|
||||
AC_INIT(nzbget, 9.0, hugbug@users.sourceforge.net)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.8.0)
|
||||
AM_INIT_AUTOMAKE(nzbget, 9.0)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
@@ -36,6 +36,7 @@ dnl
|
||||
dnl Checks for header files.
|
||||
dnl
|
||||
AC_CHECK_HEADERS(sys/prctl.h)
|
||||
AC_CHECK_HEADERS(regex.h)
|
||||
|
||||
|
||||
dnl
|
||||
@@ -441,6 +442,37 @@ 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}"
|
||||
CFLAGS="${CFLAGS} -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.
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# Template configuration file for postprocessing script "postprocess-example.sh".
|
||||
# Please refer to "postprocess-example.sh" for usage instructions.
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Template configuration file for postprocessing script "nzbget-postprocess.sh".
|
||||
# Please refer to "nzbget-postprocess.sh" for usage instructions.
|
||||
#
|
||||
# Copyright (C) 2008-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.
|
||||
#
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
### PATHS ###
|
||||
@@ -1,10 +1,12 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Example postprocessing script for NZBGet
|
||||
#
|
||||
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
|
||||
# Copyright (C) 2008 Otmar Werner
|
||||
# Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
# Copyright (C) 2008-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
|
||||
@@ -31,20 +33,13 @@
|
||||
# PostProcess=/home/user/nzbget/nzbget-postprocess.sh
|
||||
#
|
||||
# o The script needs a configuration file. An example configuration file
|
||||
# is provided in file "postprocess-example.conf". Put the configuration file
|
||||
# into the directory where nzbget's configuration file (nzbget.conf) or where
|
||||
# this script itself is located. Then edit the configuration file in any
|
||||
# text editor to adjust the settings.
|
||||
# is provided in file "nzbget-postprocess.conf". Put the configuration file
|
||||
# into the directory where nzbget's configuration file (nzbget.conf) is located.
|
||||
# Then edit the configuration file in any text editor to adjust the settings.
|
||||
#
|
||||
# o You can also edit the script's configuration via web-interface (requires
|
||||
# NZBGetWeb 1.4 or later). Set the options "PostProcessConfigFile" and
|
||||
# "PostProcessConfigTemplate" to point to "postprocess-example.conf"
|
||||
# (including full path). The both options are under the section
|
||||
# "CONFIGURATION OF POSTPROCESSING-SCRIPT" in NZBGetWeb.
|
||||
# o You can also edit the script's configuration via web-interface.
|
||||
#
|
||||
# o There are few options, which can be ajdusted for each nzb-file
|
||||
# individually. To view/edit them in web-interface click on a spanner icon
|
||||
# near the name of nzb-file.
|
||||
# o There are few options, which can be ajdusted for each nzb-file individually.
|
||||
#
|
||||
# o The script supports the feature called "delayed par-check".
|
||||
# That means it can try to unpack downloaded files without par-checking
|
||||
@@ -84,7 +79,7 @@
|
||||
|
||||
|
||||
# Name of script's configuration file
|
||||
SCRIPT_CONFIG_FILE="postprocess-example.conf"
|
||||
SCRIPT_CONFIG_FILE="nzbget-postprocess.conf"
|
||||
|
||||
# Exit codes
|
||||
POSTPROCESS_PARCHECK_CURRENT=91
|
||||
@@ -145,7 +140,7 @@ if [ "$NZBOP_PARREPAIR" = "no" ]; then
|
||||
fi
|
||||
|
||||
if [ "$BadConfig" -eq 1 ]; then
|
||||
echo "[ERROR] Post-Process: Existing because of not compatible nzbget configuration"
|
||||
echo "[ERROR] Post-Process: Exiting because of not compatible nzbget configuration"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
@@ -214,7 +209,7 @@ if (ls *.rar >/dev/null 2>&1); then
|
||||
fi
|
||||
|
||||
$UnrarCmd x -y -p- "$rarpasswordparam" -o+ "*.rar" ./extracted/
|
||||
if [ "$?" -eq 3 ]; then
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo "[ERROR] Post-Process: Unrar failed"
|
||||
if [ "$ExtractedDirExists" -eq 0 ]; then
|
||||
rm -R extracted
|
||||
@@ -243,7 +238,7 @@ if (ls *.rar >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Unraring (second pass)"
|
||||
$UnrarCmd x -y -p- -o+ "*.rar"
|
||||
|
||||
if [ "$?" -eq 3 ]; then
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo "[INFO] Post-Process: Unrar (second pass) failed"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
@@ -1,4 +1,4 @@
|
||||
# Sample configuration file for nzbget
|
||||
# Sample configuration file for NZBGet
|
||||
#
|
||||
# On POSIX put this file to one of the following locations:
|
||||
# ~/.nzbget
|
||||
@@ -13,79 +13,87 @@
|
||||
# using switch "-c", e.g:
|
||||
# nzbget -c /home/user/myconfig.txt
|
||||
|
||||
# For quick start change the option MAINDIR and configure one news-server
|
||||
# For quick start change the option MainDir and configure one news-server
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PATHS ###
|
||||
|
||||
# Root directory for all related tasks.
|
||||
# Root directory for all tasks.
|
||||
#
|
||||
# MAINDIR is a variable and therefore starts with "$".
|
||||
# On POSIX you can use "~" as alias for home directory (e.g. "~/download").
|
||||
# On Windows use absolute paths (e.g. "C:\Download").
|
||||
$MAINDIR=~/download
|
||||
MainDir=~/download
|
||||
|
||||
# Destination-directory to store the downloaded files.
|
||||
DestDir=${MAINDIR}/dst
|
||||
# Destination-directory to store downloaded files.
|
||||
DestDir=${MainDir}/dst
|
||||
|
||||
# Directory to monitor for incoming nzb-jobs.
|
||||
#
|
||||
# Can have subdirectories.
|
||||
# A nzb-file queued from a subdirectory will be automatically assigned to
|
||||
# Can have subdirectories.
|
||||
# A nzb-file queued from a subdirectory will be automatically assigned to
|
||||
# category with the directory-name.
|
||||
NzbDir=${MAINDIR}/nzb
|
||||
NzbDir=${MainDir}/nzb
|
||||
|
||||
# Directory to store download queue.
|
||||
QueueDir=${MAINDIR}/queue
|
||||
QueueDir=${MainDir}/queue
|
||||
|
||||
# Directory to store temporary files.
|
||||
#
|
||||
# NOTE: when option <DirectWrite> is enabled the temporary directory (option
|
||||
# NOTE: When option <DirectWrite> is enabled the temporary directory (option
|
||||
# <TempDir>) must be located on the same partition with destination directory
|
||||
# (option DestDir>) for better performance. If option <DirectWrite> is disabled
|
||||
# (option <DestDir>) for better performance. If option <DirectWrite> is disabled
|
||||
# it's better to use different drives for temporary and destination directories.
|
||||
TempDir=${MAINDIR}/tmp
|
||||
TempDir=${MainDir}/tmp
|
||||
|
||||
# Lock-file for daemon-mode, POSIX only.
|
||||
#
|
||||
# If the option is not empty, nzbget creates the file and writes process-id
|
||||
# If the option is not empty, NZBGet creates the file and writes process-id
|
||||
# (PID) into it. That info can be used in shell scripts.
|
||||
LockFile=/tmp/nzbget.lock
|
||||
LockFile=${MainDir}/nzbget.lock
|
||||
|
||||
# Where to store log file, if it needs to be created.
|
||||
#
|
||||
# NOTE: See also option <CreateLog>.
|
||||
LogFile=${DestDir}/nzbget.log
|
||||
|
||||
# Directory with web-interface files.
|
||||
#
|
||||
# Example: "WebDir=/usr/local/share/nzbget/webui".
|
||||
#
|
||||
# NOTE: To disable web-interface set the option to an empty value.
|
||||
# This however doesn't disable the built-in web-server completely because
|
||||
# it is also used to serve JSON-/XML-RPC requests.
|
||||
WebDir=
|
||||
|
||||
|
||||
##############################################################################
|
||||
### NEWS-SERVERS ###
|
||||
|
||||
# This section defines which servers nzbget should connect to.
|
||||
# This section defines which servers NZBGet should connect to.
|
||||
#
|
||||
# The servers should be numbered subsequently without holes.
|
||||
# For example if you configure three servers you should name them as Server1,
|
||||
# Server2 and Server3. If you need to delete Server2 later you should also
|
||||
# For example if you configure three servers you should name them as Server1,
|
||||
# Server2 and Server3. If you need to delete Server2 later you should also
|
||||
# change the name of Server3 to Server2. Otherwise it will not be properly
|
||||
# read from the config file. Server number doesn't affect its priority (level).
|
||||
|
||||
# Level of newsserver (0-99).
|
||||
#
|
||||
# The servers will be ordered by their level, i.e. nzbget will at
|
||||
# The servers will be ordered by their level, i.e. NZBGet will at
|
||||
# first try to download an article from the level-0-server.
|
||||
# If that server fails, nzbget proceeds with the level-1-server, etc.
|
||||
# If that server fails, NZBGet proceeds with the level-1-server, etc.
|
||||
# A good idea is surely to put your major download-server at level 0
|
||||
# and your fill-servers at levels 1,2,...
|
||||
#
|
||||
# NOTE: Do not leave out a level in your server-list and start with level 0.
|
||||
#
|
||||
# NOTE: Several servers with the same level may be used, they will have
|
||||
# NOTE: Several servers with the same level may be used, they will have
|
||||
# the same priority.
|
||||
Server1.Level=0
|
||||
|
||||
# Host name of newsserver.
|
||||
Server1.Host=my1.newsserver.com
|
||||
Server1.Host=my.newsserver.com
|
||||
|
||||
# Port to connect to (1-65535).
|
||||
Server1.Port=119
|
||||
@@ -100,6 +108,9 @@ Server1.Password=pass
|
||||
Server1.JoinGroup=yes
|
||||
|
||||
# Encrypted server connection (TLS/SSL) (yes, no).
|
||||
#
|
||||
# NOTE: By changing this option you should also change the option <ServerX.Port>
|
||||
# accordingly because unsecure and encrypted connections use different ports.
|
||||
Server1.Encryption=no
|
||||
|
||||
# Maximal number of simultaneous connections to this server (0-999).
|
||||
@@ -126,27 +137,58 @@ Server1.Connections=4
|
||||
#Server3.Connections=1
|
||||
|
||||
|
||||
##############################################################################
|
||||
### REMOTE CONTROL ###
|
||||
|
||||
# IP on which NZBGet server listen and which clients use to contact NZBGet.
|
||||
#
|
||||
# It could be a dns-hostname (e. g. "mypc") or an ip-address (e. g. "192.168.1.2" or
|
||||
# "127.0.0.1"). An IP-address is more effective because does not require dns-lookup.
|
||||
#
|
||||
# Your computer may have multiple network interfaces and therefore multiple IP
|
||||
# addresses. If you want NZBGet to listen to all interfaces and be available from
|
||||
# all IP-addresses use value "0.0.0.0".
|
||||
#
|
||||
# NOTE: When you start NZBGet as client (to send remote commands to NZBGet server) and
|
||||
# the option <ControlIP> is set to "0.0.0.0" the client will use IP "127.0.0.1".
|
||||
#
|
||||
# NOTE: If you set the option to "127.0.0.1" you will be able to connect to NZBGet
|
||||
# only from the computer running NZBGet. This restriction applies to web-interface too.
|
||||
ControlIP=0.0.0.0
|
||||
|
||||
# Port which NZBGet server and remote client use (1-65535).
|
||||
ControlPort=6789
|
||||
|
||||
# Password which NZBGet server and remote client use.
|
||||
#
|
||||
# For authorization to web-interface use predefined username "nzbget" (not configurable)
|
||||
# and the password defined here.
|
||||
ControlPassword=tegbzn6789
|
||||
|
||||
# See also option <LogBufferSize> in section "LOGGING"
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PERMISSIONS ###
|
||||
|
||||
# User name for daemon-mode, POSIX only.
|
||||
#
|
||||
# Set the user that the daemon normally runs at (POSIX in daemon-mode only).
|
||||
# Set $MAINDIR with an absolute path to be sure where it will write.
|
||||
# This allows nzbget daemon to be launched in rc.local (at boot), and
|
||||
# Set MainDir with an absolute path to be sure where it will write.
|
||||
# This allows NZBGet daemon to be launched in rc.local (at boot), and
|
||||
# download items as a specific user id.
|
||||
#
|
||||
# NOTE: This option has effect only if the program was started from
|
||||
# root-account, otherwise it is ignored and the daemon runs under
|
||||
# NOTE: This option has effect only if the program was started from
|
||||
# root-account, otherwise it is ignored and the daemon runs under
|
||||
# current user id.
|
||||
DaemonUserName=root
|
||||
|
||||
# Specify default umask (affects file permissions) for newly created
|
||||
# Specify default umask (affects file permissions) for newly created
|
||||
# files, POSIX only (000-1000).
|
||||
#
|
||||
# The value should be written in octal form (the same as for "umask" shell
|
||||
# The value should be written in octal form (the same as for "umask" shell
|
||||
# command).
|
||||
# Empty value or value "1000" disable the setting of umask-mode; current
|
||||
# Empty value or value "1000" disable the setting of umask-mode; current
|
||||
# umask-mode (set via shell) is used in this case.
|
||||
UMask=1000
|
||||
|
||||
@@ -160,7 +202,7 @@ AppendCategoryDir=yes
|
||||
# Create subdirectory with nzb-filename in destination-directory (yes, no).
|
||||
AppendNzbDir=yes
|
||||
|
||||
# How often incoming-directory (option <NzbDir>) must be checked for new
|
||||
# How often incoming-directory (option <NzbDir>) must be checked for new
|
||||
# nzb-files (seconds).
|
||||
#
|
||||
# Value "0" disables the check.
|
||||
@@ -168,24 +210,24 @@ NzbDirInterval=5
|
||||
|
||||
# How old nzb-file should at least be for it to be loaded to queue (seconds).
|
||||
#
|
||||
# Nzbget checks if nzb-file was not modified in last few seconds, defined by
|
||||
# this option. That safety interval prevents the loading of files, which
|
||||
# NZBGet checks if nzb-file was not modified in last few seconds, defined by
|
||||
# this option. That safety interval prevents the loading of files, which
|
||||
# were not yet completely saved to disk, for example if they are still being
|
||||
# downloaded in web-browser.
|
||||
NzbDirFileAge=60
|
||||
|
||||
# Automatic merging of nzb-files with the same filename (yes, no).
|
||||
#
|
||||
# A typical scenario: you put nzb-file into incoming directory, nzbget adds
|
||||
# A typical scenario: you put nzb-file into incoming directory, NZBGet adds
|
||||
# file to queue. You find out, that the file doesn't have par-files. You
|
||||
# find required par-files, put nzb-file with the par-files into incoming
|
||||
# directory, nzbget adds it to queue as a separate group. You want the second
|
||||
# file to be merged with the first for parchecking to work properly. With
|
||||
# option "MergeNzb" nzbget can merge files automatically. You only need to
|
||||
# find required par-files, put nzb-file with the par-files into incoming
|
||||
# directory, NZBGet adds it to queue as a separate group. You want the second
|
||||
# file to be merged with the first for parchecking to work properly. With
|
||||
# option "MergeNzb" NZBGet can merge files automatically. You only need to
|
||||
# save the second file under the same filename as the first one.
|
||||
MergeNzb=no
|
||||
|
||||
# Set path to program, that must be executed before any file in incoming
|
||||
# Set path to program, that must be executed before any file in incoming
|
||||
# directory (option <NzbDir>) is processed.
|
||||
#
|
||||
# Example: "NzbProcess=~/nzbprocess.sh".
|
||||
@@ -194,17 +236,18 @@ MergeNzb=no
|
||||
# filename cleanup, assign category and post-processing parameters to nzb-file
|
||||
# or do something else.
|
||||
#
|
||||
# INFO FOR DEVELOPERS:
|
||||
# NZBGet passes following arguments to nzbprocess-program as environment
|
||||
# variables:
|
||||
# NZBNP_DIRECTORY - path to directory, where file is located. It is a directory
|
||||
# specified by the option <NzbDir> or a subdirectory;
|
||||
# NZBNP_FILENAME - name of file to be processed;
|
||||
#
|
||||
# In addition to these arguments nzbget passes all
|
||||
# nzbget.conf-options to postprocess-program as environment variables. These
|
||||
# In addition to these arguments NZBGet passes all
|
||||
# nzbget.conf-options to nzbprocess-program as environment variables. These
|
||||
# variables have prefix "NZBOP_" and are written in UPPER CASE. For Example
|
||||
# option "ParRepair" is passed as environment variable "NZBOP_PARREPAIR".
|
||||
# The dots in option names are replaced with underscores, for example
|
||||
# The dots in option names are replaced with underscores, for example
|
||||
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
|
||||
# the values are passed always in lower case.
|
||||
#
|
||||
@@ -216,7 +259,7 @@ MergeNzb=no
|
||||
# echo "[NZB] CATEGORY=my category";
|
||||
#
|
||||
# To assign priority:
|
||||
# echo "[NZB] PRIORITY=<signed integer value>";
|
||||
# echo "[NZB] PRIORITY=signed_integer_value";
|
||||
#
|
||||
# for example: to set priority higher than normal:
|
||||
# echo "[NZB] PRIORITY=50";
|
||||
@@ -232,33 +275,78 @@ MergeNzb=no
|
||||
# with nzb-file.
|
||||
#
|
||||
# The nzbprocess-script can delete processed file, rename it or move somewhere.
|
||||
# After the calling of the script the file will be either added to queue
|
||||
# After the calling of the script the file will be either added to queue
|
||||
# (if it was an nzb-file) or renamed by adding the extension ".processed".
|
||||
#
|
||||
# NOTE: Files with extensions ".processed", ".queued" and ".error" are skipped
|
||||
# during the directory scanning.
|
||||
#
|
||||
# NOTE: Files with extension ".nzb_processed" are not passed to
|
||||
# NzbProcess-script before adding to queue. This feature allows
|
||||
# NzbProcess-script to prevent the scanning of nzb-files extracted from
|
||||
# NOTE: Files with extension ".nzb_processed" are not passed to
|
||||
# NzbProcess-script before adding to queue. This feature allows
|
||||
# NzbProcess-script to prevent the scanning of nzb-files extracted from
|
||||
# archives, if they were already processed by the script.
|
||||
NzbProcess=
|
||||
|
||||
# Set path to program, that must be executed after a nzb-file is added
|
||||
# to queue.
|
||||
#
|
||||
# This program is called each time a new nzb-file is added to queue: from
|
||||
# nzb incoming directory, via command line call "nzbget -A filename.nzb",
|
||||
# via RPC-method "append" or from web-interface.
|
||||
#
|
||||
# Example: "NzbAddedProcess=~/nzbaddedprocess.sh".
|
||||
#
|
||||
# That program can modify the files in download queue (for example
|
||||
# delete or pause all nfo, sfv, sample files) or do something else.
|
||||
#
|
||||
# INFO FOR DEVELOPERS:
|
||||
# NZBGet passes following arguments to nzbaddedprocess-program as environment
|
||||
# variables:
|
||||
# NZBNA_NZBNAME - name of nzb-group. This name can be used in calls
|
||||
# to nzbget edit-command using subswitch "-GN name";
|
||||
# NZBNA_FILENAME - filename of the nzb-file. If the file was added
|
||||
# from nzb-directory this is the fullname with path.
|
||||
# If the file was added via web-interface it contains
|
||||
# only filename without path;
|
||||
# NZBNA_CATEGORY - category of nzb-file (if assigned);
|
||||
# NZBNA_LASTID - the id of the last file in the nzb-file. This ID can
|
||||
# be used with calls to nzbget edit-command;
|
||||
# NZBNA_PRIORITY - priority (default is 0).
|
||||
#
|
||||
# In addition to these arguments NZBGet passes all
|
||||
# nzbget.conf-options to nzbaddedprocess-program as environment variables. These
|
||||
# variables have prefix "NZBOP_" and are written in UPPER CASE. For Example
|
||||
# option "ParRepair" is passed as environment variable "NZBOP_PARREPAIR".
|
||||
# The dots in option names are replaced with underscores, for example
|
||||
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
|
||||
# the values are passed always in lower case.
|
||||
#
|
||||
# Examples:
|
||||
# 1) pausing nzb-file using file-id:
|
||||
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E G P $NZBNA_LASTID
|
||||
#
|
||||
# 2) setting category using nzb-name:
|
||||
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E GN K "my cat" "$NZBNA_NZBNAME"
|
||||
#
|
||||
# 3) pausing files with extension "nzb":
|
||||
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E FR P "$NZBNA_NAME/.*\.nzb"
|
||||
NzbAddedProcess=
|
||||
|
||||
# Check for duplicate files (yes, no).
|
||||
#
|
||||
# If this option is enabled the program checks by adding of a new nzb-file:
|
||||
# 1) if nzb-file contains duplicate entries. This check aims on detecting
|
||||
# of reposted files (if first file was not fully uploaded);
|
||||
# If the program find two files with identical names, only the
|
||||
# of reposted files (if first file was not fully uploaded);
|
||||
# If the program find two files with identical names, only the
|
||||
# biggest of these files will be added to queue;
|
||||
# 2) if download queue already contains file with the same name;
|
||||
# 3) if destination file on disk already exists.
|
||||
# In last two cases: if the file exists it will not be added to queue;
|
||||
#
|
||||
# If this option is disabled, all files are downloaded and duplicate files
|
||||
# If this option is disabled, all files are downloaded and duplicate files
|
||||
# are renamed to "filename_duplicate1".
|
||||
# Existing files are never deleted or overwritten.
|
||||
DupeCheck=no
|
||||
DupeCheck=yes
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -272,6 +360,12 @@ SaveQueue=yes
|
||||
# Reload download queue on start, if it exists (yes, no).
|
||||
ReloadQueue=yes
|
||||
|
||||
# Reload url-queue on start, if it exists (yes, no).
|
||||
#
|
||||
# For this option to work the options <SaveQueue> and <ReloadQueue> must
|
||||
# be also enabled.
|
||||
ReloadUrlQueue=yes
|
||||
|
||||
# Reload Post-processor-queue on start, if it exists (yes, no).
|
||||
#
|
||||
# For this option to work the options <SaveQueue> and <ReloadQueue> must
|
||||
@@ -280,7 +374,7 @@ ReloadPostQueue=yes
|
||||
|
||||
# Reuse articles saved in temp-directory from previous program start (yes, no).
|
||||
#
|
||||
# This allows to continue download of file, if program was exited before
|
||||
# This allows to continue download of file, if program was exited before
|
||||
# the file was completed.
|
||||
ContinuePartial=yes
|
||||
|
||||
@@ -292,7 +386,7 @@ RenameBroken=no
|
||||
# Decode articles (yes, no).
|
||||
#
|
||||
# yes - decode articles using internal decoder (supports yEnc and UU formats);
|
||||
# no - the articles will not be decoded and joined. External programs
|
||||
# no - the articles will not be decoded and joined. External programs
|
||||
# (like "uudeview") can be used to decode and join downloaded articles.
|
||||
# Also useful for debugging to look at article's source text.
|
||||
Decode=yes
|
||||
@@ -305,9 +399,9 @@ Decode=yes
|
||||
# When option <DirectWrite> is disabled, the program downloads all articles
|
||||
# into temporary directory and then combine them into destination file.
|
||||
#
|
||||
# With this option enabled the program at first creates the output
|
||||
# destination file with required size (total size of all articles),
|
||||
# then writes on the fly decoded articles directly to the file
|
||||
# With this option enabled the program at first creates the output
|
||||
# destination file with required size (total size of all articles),
|
||||
# then writes on the fly decoded articles directly to the file
|
||||
# without creating of any temporary files.
|
||||
#
|
||||
# This may improve performance but depends on OS and file system ability to
|
||||
@@ -324,41 +418,41 @@ Decode=yes
|
||||
# activating of this option results in up to 20% better performance during
|
||||
# downloading.
|
||||
#
|
||||
# NOTE: for test try to download few big nzb-collections (each 4GB or more)
|
||||
# NOTE: For test try to download few big nzb-collections (each 4GB or more)
|
||||
# and measure the time used for downloading and post-processing (use timestamps
|
||||
# in a log-file to determine when the post-processing was ended).
|
||||
#
|
||||
# NOTE: when option <DirectWrite> is enabled the temporary directory (option
|
||||
# NOTE: When option <DirectWrite> is enabled the temporary directory (option
|
||||
# <TempDir>) must be located on the same partition with destination directory
|
||||
# (option DestDir>) for better performance. If option <DirectWrite> is disabled
|
||||
# (option DestDir>) for better performance. If option <DirectWrite> is disabled
|
||||
# it's better to use different drives for temporary and destination directories.
|
||||
#
|
||||
# NOTE: if both options <DirectWrite> and <ContinuePartial> are enabled,
|
||||
# the program still creates empty article-files in temp-directory. They are used
|
||||
# NOTE: If both options <DirectWrite> and <ContinuePartial> are enabled,
|
||||
# the program still creates empty article-files in temp-directory. They are used
|
||||
# by the option <ContinuePartial> to check if a certain article was downloaded.
|
||||
# To minimize disk-io it is recommended to disable option <ContinuePartial>,
|
||||
# if <DirectWrite> is enabled. Especially on a fast connections (where you
|
||||
# if <DirectWrite> is enabled. Especially on a fast connections (where you
|
||||
# would want to activate <DirectWrite>) it should not be a problem to redownload
|
||||
# an interrupted file.
|
||||
DirectWrite=no
|
||||
DirectWrite=yes
|
||||
|
||||
# Check CRC of downloaded and decoded articles (yes, no).
|
||||
#
|
||||
# Normally this option should be enabled for better detecting of download
|
||||
# errors. However checking of CRC needs about the same CPU time as
|
||||
# Normally this option should be enabled for better detecting of download
|
||||
# errors. However checking of CRC needs about the same CPU time as
|
||||
# decoding of articles. On a fast connections with slow CPUs disabling of
|
||||
# CPU-check may slightly improve performance (if CPU is a limiting factor).
|
||||
CrcCheck=yes
|
||||
|
||||
# How much retries should be attempted if a download error occurs (0-99).
|
||||
Retries=4
|
||||
Retries=3
|
||||
|
||||
# Set the interval between retries (seconds).
|
||||
RetryInterval=10
|
||||
|
||||
# Redownload article if CRC-check fails (yes, no).
|
||||
#
|
||||
# Helps to minimize number of broken files, but may be effective
|
||||
# Helps to minimize number of broken files, but may be effective
|
||||
# only if you have multiple download servers (even from the same provider
|
||||
# but from different locations (e.g. europe, usa)).
|
||||
# In any case the option increases your traffic.
|
||||
@@ -371,30 +465,30 @@ ConnectionTimeout=60
|
||||
|
||||
# Timeout until a download-thread should be killed (seconds).
|
||||
#
|
||||
# This can help on hanging downloads, but is dangerous.
|
||||
# This can help on hanging downloads, but is dangerous.
|
||||
# Do not use small values!
|
||||
TerminateTimeout=600
|
||||
|
||||
# Set the (approximate) maximum number of allowed threads (0-999).
|
||||
#
|
||||
# Sometimes under certain circumstances the program may create way to many
|
||||
# Sometimes under certain circumstances the program may create way to many
|
||||
# download threads. Most of them are in wait-state. That is not bad,
|
||||
# but threads are usually a limited resource. If a program creates to many
|
||||
# of them, operating system may kill it. The option <ThreadLimit> prevents that.
|
||||
#
|
||||
# NOTE: the number of threads is not the same as the number of connections
|
||||
# NOTE: The number of threads is not the same as the number of connections
|
||||
# opened to NNTP-servers. Do not use the option <ThreadLimit> to limit the
|
||||
# number of connections. Use the appropriate options <ServerX.Connections>
|
||||
# instead.
|
||||
#
|
||||
# NOTE: the actual number of created threads can be slightly larger as
|
||||
# NOTE: The actual number of created threads can be slightly larger as
|
||||
# defined by the option. Important threads may be created even if the
|
||||
# number of threads is exceeded. The option prevents only the creation of
|
||||
# additional download threads.
|
||||
#
|
||||
# NOTE: in most cases you should leave the default value "100" unchanged.
|
||||
# However you may increase that value if you need more than 90 connections
|
||||
# (that's very unlikely) or decrease the value if the OS does not allow so
|
||||
# NOTE: In most cases you should leave the default value "100" unchanged.
|
||||
# However you may increase that value if you need more than 90 connections
|
||||
# (that's very unlikely) or decrease the value if the OS does not allow so
|
||||
# many threads. But the most OSes should not have problems with 100 threads.
|
||||
ThreadLimit=100
|
||||
|
||||
@@ -416,16 +510,13 @@ DownloadRate=0
|
||||
# provide accurate speed calculations.
|
||||
#
|
||||
# NOTE: The program uses spinlocks if the operating system supports them.
|
||||
# Otherwise it uses mutexes, which are much less effective. Anyway thread
|
||||
# synchronisation increases CPU load. Be aware that spinlocks are effective
|
||||
# only on multicore systems. If you enabled the option on a single-core
|
||||
# system (even if the underlying OS supports spinlocks), that would
|
||||
# probably significantly increase CPU load.
|
||||
# Otherwise it uses mutexes, which are less effective. In any case the thread
|
||||
# synchronisation increases CPU load.
|
||||
#
|
||||
# NOTE: It is recommended to run tests to determine how the option affects
|
||||
# the CPU usage and the download speed on a particular system.
|
||||
#
|
||||
# NOTE: the average (session) download speed is always accurate. It uses
|
||||
# NOTE: The average (session) download speed is always accurate. It uses
|
||||
# other data for speed calculation and is not affected by this option.
|
||||
AccurateRate=no
|
||||
|
||||
@@ -433,22 +524,22 @@ AccurateRate=no
|
||||
#
|
||||
# Bigger values decrease disk-io, but increase memory usage.
|
||||
# Value "0" causes an OS-dependent default value to be used.
|
||||
# With value "-1" (which means "max/auto") the program sets the size of
|
||||
# With value "-1" (which means "max/auto") the program sets the size of
|
||||
# buffer according to the size of current article (typically less than 500K).
|
||||
#
|
||||
# NOTE: the value must be written in bytes, do not use postfixes "K" or "M".
|
||||
# NOTE: The value must be written in bytes, do not use postfixes "K" or "M".
|
||||
#
|
||||
# NOTE: to calculate the memory usage multiply WriteBufferSize by max number
|
||||
# NOTE: To calculate the memory usage multiply WriteBufferSize by max number
|
||||
# of connections, configured in section "NEWS-SERVERS".
|
||||
#
|
||||
# NOTE: typical article's size not exceed 500000 bytes, so using bigger values
|
||||
# NOTE: Typical article's size not exceed 500000 bytes, so using bigger values
|
||||
# (like several megabytes) will just waste memory.
|
||||
#
|
||||
# NOTE: for desktop computers with large amount of memory value "-1" (max/auto)
|
||||
# NOTE: For desktop computers with large amount of memory value "-1" (max/auto)
|
||||
# is recommended, but for computers with very low memory (routers, NAS)
|
||||
# value "0" (default OS-dependent size) could be better alternative.
|
||||
#
|
||||
# NOTE: write-buffer is managed by OS (system libraries) and therefore
|
||||
# NOTE: Write-buffer is managed by OS (system libraries) and therefore
|
||||
# the effect of the option is highly OS-dependent.
|
||||
WriteBufferSize=0
|
||||
|
||||
@@ -459,13 +550,13 @@ WriteBufferSize=0
|
||||
# The drive with <TempDir> is not checked.
|
||||
DiskSpace=250
|
||||
|
||||
# Delete already downloaded files from disk, if the download of nzb-file was
|
||||
# Delete already downloaded files from disk, if the download of nzb-file was
|
||||
# cancelled (nzb-file was deleted from queue) (yes, no).
|
||||
#
|
||||
# NOTE: nzbget does not delete files in a case if all remaining files in
|
||||
# NOTE: NZBGet does not delete files in a case if all remaining files in
|
||||
# queue are par-files. That prevents the accidental deletion if the option
|
||||
# <ParCleanupQueue> is disabled or if the program was interrupted during
|
||||
# parcheck and later restarted without reloading of post queue (option
|
||||
# <ParCleanupQueue> is disabled or if the program was interrupted during
|
||||
# parcheck and later restarted without reloading of post queue (option
|
||||
# <ReloadPostQueue> disabled).
|
||||
DeleteCleanupDisk=no
|
||||
|
||||
@@ -473,7 +564,7 @@ DeleteCleanupDisk=no
|
||||
#
|
||||
# Value "0" disables the history.
|
||||
#
|
||||
# NOTE: when a collection having paused files is added to history all remaining
|
||||
# NOTE: When a collection having paused files is added to history all remaining
|
||||
# files are moved from download queue to a list of parked files. It holds files
|
||||
# which could be required later if the collection will be moved back to
|
||||
# download queue for downloading of remaining files. The parked files still
|
||||
@@ -481,7 +572,34 @@ DeleteCleanupDisk=no
|
||||
# and successfully par-checked or postprocessed it is recommended to discard the
|
||||
# unneeded parked files before adding the collection to history. For par2-files
|
||||
# that can be achieved with the option <ParCleanupQueue>.
|
||||
KeepHistory=1
|
||||
KeepHistory=7
|
||||
|
||||
# Maximal number of simultaneous connections for nzb URL downloads (0-999).
|
||||
#
|
||||
# When NZB-files are added to queue via URL, the program downloads them
|
||||
# from the specified URL. The option limits the maximal number of connections
|
||||
# used for this purpose, when multiple URLs were added at the same time.
|
||||
UrlConnections=4
|
||||
|
||||
|
||||
##############################################################################
|
||||
### CATEGORIES ###
|
||||
|
||||
# This section defines categories available in web-interface.
|
||||
|
||||
# Category name.
|
||||
#
|
||||
# Each nzb-file can be assigned to a category. If the option <AppendCategoryDir>
|
||||
# is active, the program creates a subdirectory with category name within
|
||||
# destination directory.
|
||||
# Category name is passed to post-processing script and can be used by it
|
||||
# to perform category specific processing.
|
||||
Category1.Name=Movies
|
||||
|
||||
Category2.Name=Series
|
||||
Category3.Name=Music
|
||||
Category4.Name=Software
|
||||
|
||||
|
||||
##############################################################################
|
||||
### LOGGING ###
|
||||
@@ -506,13 +624,13 @@ DetailTarget=both
|
||||
|
||||
# How debug messages must be printed (screen, log, both, none).
|
||||
#
|
||||
# Debug-messages can be printed only if the program was compiled in
|
||||
# Debug-messages can be printed only if the program was compiled in
|
||||
# debug-mode: "./configure --enable-debug".
|
||||
DebugTarget=both
|
||||
|
||||
# Set the default message-kind for output received from process-scripts
|
||||
# (PostProcess, NzbProcess, TaskX.Process) (none, detail, info, warning,
|
||||
# error, debug).
|
||||
# (PostProcess, NzbProcess, TaskX.Process) (detail, info, warning,
|
||||
# error, debug, none).
|
||||
#
|
||||
# NZBGet checks if the line written by the script to stdout or stderr starts
|
||||
# with special character-sequence, determining the message-kind, e.g.:
|
||||
@@ -526,7 +644,7 @@ DebugTarget=both
|
||||
# Otherwise the message becomes the default kind, specified in this option.
|
||||
ProcessLogKind=detail
|
||||
|
||||
# Number of messages stored in buffer and available for remote
|
||||
# Number of messages stored in buffer and available for remote
|
||||
# clients (messages).
|
||||
LogBufferSize=1000
|
||||
|
||||
@@ -540,7 +658,7 @@ CreateBrokenLog=yes
|
||||
#
|
||||
# Core-files are very helpful for debugging.
|
||||
#
|
||||
# NOTE: core-files may contain sensible data, like your login/password to
|
||||
# NOTE: Core-files may contain sensible data, like your login/password to
|
||||
# newsserver etc.
|
||||
DumpCore=no
|
||||
|
||||
@@ -555,7 +673,7 @@ DumpCore=no
|
||||
# loggable - only messages will be printed to standard output;
|
||||
# colored - prints messages (with simple coloring for messages categories)
|
||||
# and download progress info; uses escape-sequences to move cursor;
|
||||
# curses - advanced interactive interface with the ability to edit
|
||||
# curses - advanced interactive interface with the ability to edit
|
||||
# download queue and various output option.
|
||||
OutputMode=curses
|
||||
|
||||
@@ -577,7 +695,7 @@ CursesGroup=no
|
||||
# it can be switched on/off in run-time with T-key.
|
||||
CursesTime=no
|
||||
|
||||
# Update interval for Frontend-output in console mode or remote client
|
||||
# Update interval for Frontend-output in console mode or remote client
|
||||
# mode (milliseconds).
|
||||
#
|
||||
# Min value 25. Bigger values reduce CPU usage (especially in curses-outputmode)
|
||||
@@ -585,39 +703,20 @@ CursesTime=no
|
||||
UpdateInterval=200
|
||||
|
||||
|
||||
##############################################################################
|
||||
### CLIENT/SERVER COMMUNICATION ###
|
||||
|
||||
# IP on which the server listen and which client uses to contact the server.
|
||||
#
|
||||
# It could be dns-hostname or ip-address (more effective since does not
|
||||
# require dns-lookup).
|
||||
# If you want the server to listen to all interfaces, use "0.0.0.0".
|
||||
ServerIp=127.0.0.1
|
||||
|
||||
# Port which the server & client use (1-65535).
|
||||
ServerPort=6789
|
||||
|
||||
# Password which the server & client use.
|
||||
ServerPassword=tegbzn6789
|
||||
|
||||
# See also option <LogBufferSize> in section "LOGGING"
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PAR CHECK/REPAIR ###
|
||||
|
||||
# How many par2-files to load (none, all, one).
|
||||
# How many par2-files to load (one, all, none).
|
||||
#
|
||||
# none - all par2-files must be automatically paused;
|
||||
# one - only one main par2-file must be downloaded and other must be paused;
|
||||
# all - all par2-files must be downloaded;
|
||||
# one - only one main par2-file must be downloaded and other must be paused.
|
||||
# none - all par2-files must be automatically paused.
|
||||
# Paused files remain in queue and can be unpaused by parchecker when needed.
|
||||
LoadPars=one
|
||||
|
||||
# Automatic par-verification (yes, no).
|
||||
#
|
||||
# To download only needed par2-files (smart par-files loading) set also
|
||||
# To download only needed par2-files (smart par-files loading) set also
|
||||
# the option <LoadPars> to "one". If option <LoadPars> is set to "all",
|
||||
# all par2-files will be downloaded before verification and repair starts.
|
||||
# The option <RenameBroken> must be set to "no", otherwise the par-checker
|
||||
@@ -631,23 +730,22 @@ ParCheck=no
|
||||
# not start repair-process. This is useful if the server does not have
|
||||
# enough CPU power, since repairing of large files may take too much
|
||||
# resources and time on a slow computers.
|
||||
# This option has effect only if the option <ParCheck> is enabled.
|
||||
ParRepair=yes
|
||||
|
||||
# Use only par2-files with matching names (yes, no).
|
||||
#
|
||||
# If par-check needs extra par-blocks it searches for par2-files
|
||||
# in download queue, which can be unpaused and used for restore.
|
||||
# These par2-files should have the same base name as the main par2-file,
|
||||
# currently loaded in par-checker. Sometimes extra par files (especially if
|
||||
# they were uploaded by a different poster) have not matching names.
|
||||
# Normally par-checker does not use these files, but you can allow it
|
||||
# in download queue, which can be unpaused and used for restore.
|
||||
# These par2-files should have the same base name as the main par2-file,
|
||||
# currently loaded in par-checker. Sometimes extra par files (especially if
|
||||
# they were uploaded by a different poster) have not matching names.
|
||||
# Normally par-checker does not use these files, but you can allow it
|
||||
# to use these files by setting <StrictParName> to "no".
|
||||
# This has however a side effect: if NZB-file contains more than one collection
|
||||
# of files (with different par-sets), par-checker may download par-files from
|
||||
# a wrong collection. This increases you traffic (but not harm par-check).
|
||||
#
|
||||
# NOTE: par-checker always uses only par-files added from the same NZB-file
|
||||
# NOTE: Par-checker always uses only par-files added from the same NZB-file
|
||||
# and the option <StrictParName> does not change this behavior.
|
||||
StrictParName=yes
|
||||
|
||||
@@ -655,25 +753,25 @@ StrictParName=yes
|
||||
#
|
||||
# Value "0" means unlimited.
|
||||
#
|
||||
# If you use nzbget on a very slow computer like NAS-device, it may be good to
|
||||
# limit the time allowed for par-repair. Nzbget calculates the estimated time
|
||||
# If you use NZBGet on a very slow computer like NAS-device, it may be good to
|
||||
# limit the time allowed for par-repair. NZBGet calculates the estimated time
|
||||
# required for par-repair. If the estimated value exceeds the limit defined
|
||||
# here, nzbget cancels the repair.
|
||||
# here, NZBGet cancels the repair.
|
||||
#
|
||||
# To avoid a false cancellation nzbget compares the estimated time with
|
||||
# To avoid a false cancellation NZBGet compares the estimated time with
|
||||
# <ParTimeLimit> after the first 5 minutes of repairing, when the calculated
|
||||
# estimated time is more or less accurate. But in a case if <ParTimeLimit> is
|
||||
# set to a value smaller than 5 minutes, the comparison is made after the first
|
||||
# set to a value smaller than 5 minutes, the comparison is made after the first
|
||||
# whole minute.
|
||||
#
|
||||
# NOTE: the option limits only the time required for repairing. It doesn't
|
||||
# affect the first stage of parcheck - verification of files. However the
|
||||
# NOTE: The option limits only the time required for repairing. It doesn't
|
||||
# affect the first stage of parcheck - verification of files. However the
|
||||
# verification speed is constant, it doesn't depend on files integrity and
|
||||
# therefore it is not necessary to limit the time needed for the first stage.
|
||||
#
|
||||
# NOTE: this option requires an extended version of libpar2 (the original
|
||||
# version doesn't support the cancelling of repairing). Please refer to
|
||||
# nzbget's README for info on how to apply a patch to libpar2.
|
||||
# NOTE: This option requires an extended version of libpar2 (the original
|
||||
# version doesn't support the cancelling of repairing). Please refer to
|
||||
# NZBGet's README for info on how to apply the patch to libpar2.
|
||||
ParTimeLimit=0
|
||||
|
||||
# Pause download queue during check/repair (yes, no).
|
||||
@@ -681,7 +779,7 @@ ParTimeLimit=0
|
||||
# Enable the option to give CPU more time for par-check/repair. That helps
|
||||
# to speed up check/repair on slow CPUs with fast connection (e.g. NAS-devices).
|
||||
#
|
||||
# NOTE: if parchecker needs additional par-files it temporarily unpauses
|
||||
# NOTE: If parchecker needs additional par-files it temporarily unpauses
|
||||
# the queue.
|
||||
#
|
||||
# NOTE: See also option <PostPauseQueue>.
|
||||
@@ -689,32 +787,34 @@ ParPauseQueue=no
|
||||
|
||||
# Cleanup download queue after successful check/repair (yes, no).
|
||||
#
|
||||
# Enable this option for automatic deletion of unneeded (paused) par-files
|
||||
# Enable this option for automatic deletion of unneeded (paused) par-files
|
||||
# from download queue after successful check/repair.
|
||||
ParCleanupQueue=yes
|
||||
|
||||
# Delete source nzb-file after successful check/repair (yes, no).
|
||||
#
|
||||
# Enable this option for automatic deletion of nzb-file from incoming directory
|
||||
# Enable this option for automatic deletion of nzb-file from incoming directory
|
||||
# after successful check/repair.
|
||||
NzbCleanupDisk=no
|
||||
|
||||
|
||||
##############################################################################
|
||||
### POSTPROCESSING ###
|
||||
### POST-PROCESSING ###
|
||||
|
||||
# Set path to program, that must be executed after the download of nzb-file
|
||||
# or one collection in nzb-file (if par-check enabled and nzb-file contains
|
||||
# multiple collections; see note below for the definition of "collection")
|
||||
# is completed and possibly par-checked/repaired.
|
||||
# Set path to program, that must be executed after the download of nzb-file
|
||||
# or one collection in nzb-file is completed and possibly par-checked/repaired.
|
||||
#
|
||||
# Example: "PostProcess=~/postprocess-example.sh".
|
||||
# Example: "PostProcess=~/nzbget-postprocess.sh".
|
||||
#
|
||||
# NOTE: An example script for unrarring is provided within distribution
|
||||
# in file "nzbget-postprocess.sh".
|
||||
#
|
||||
# INFO FOR DEVELOPERS:
|
||||
# NZBGet passes following arguments to postprocess-program as environment
|
||||
# variables:
|
||||
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
|
||||
# NZBPP_NZBFILENAME - name of processed nzb-file;
|
||||
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
|
||||
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
|
||||
# found);
|
||||
# NZBPP_PARSTATUS - result of par-check:
|
||||
# 0 = not checked: par-check disabled or nzb-file does
|
||||
@@ -732,24 +832,24 @@ NzbCleanupDisk=no
|
||||
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string).
|
||||
#
|
||||
# If nzb-file has associated postprocess-parameters (which can be set using
|
||||
# subcommand <O> of command <-E>, for example: nzbget -E G O "myvar=hello !" 10)
|
||||
# or using XML-/JSON-RPC (for example via web-interface), they are also passed
|
||||
# subcommand <O> of command <-E>, for example: NZBGet -E G O "myvar=hello !" 10)
|
||||
# or using XML-/JSON-RPC (for example via web-interface), they are also passed
|
||||
# as environment variables. These variables have prefix "NZBPR_" in their names.
|
||||
# For example, pp-parameter "myvar" will be passed as environment
|
||||
# For example, pp-parameter "myvar" will be passed as environment
|
||||
# variable "NZBPR_myvar".
|
||||
#
|
||||
# In addition to arguments and postprocess-parameters nzbget passes all
|
||||
# In addition to arguments and postprocess-parameters NZBGet passes all
|
||||
# nzbget.conf-options to postprocess-program as environment variables. These
|
||||
# variables have prefix "NZBOP_" and are written in UPPER CASE. For Example
|
||||
# option "ParRepair" is passed as environment variable "NZBOP_PARREPAIR".
|
||||
# The dots in option names are replaced with underscores, for example
|
||||
# The dots in option names are replaced with underscores, for example
|
||||
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
|
||||
# the values are passed always in lower case.
|
||||
#
|
||||
# Return value: nzbget processes the exit code returned by the script:
|
||||
# 91 - request nzbget to do par-check/repair for current collection in the
|
||||
# Return value: NZBGet processes the exit code returned by the script:
|
||||
# 91 - request NZBGet to do par-check/repair for current collection in the
|
||||
# current nzb-file;
|
||||
# 92 - request nzbget to do par-check/repair for all collections in the
|
||||
# 92 - request NZBGet to do par-check/repair for all collections in the
|
||||
# current nzb-file;
|
||||
# 93 - post-process successful (status = SUCCESS);
|
||||
# 94 - post-process failed (status = FAILURE);
|
||||
@@ -762,64 +862,62 @@ NzbCleanupDisk=no
|
||||
# to standard output. For example:
|
||||
# echo "[ERROR] [HISTORY] Unpack failed, not enough disk space";
|
||||
#
|
||||
# NOTE: The parameter NZBPP_NZBCOMPLETED is very important and MUST be checked
|
||||
# NOTE: The parameter NZBPP_NZBCOMPLETED is very important and MUST be checked
|
||||
# even in the simplest scripts.
|
||||
# If par-check is enabled and nzb-file contains more than one collection
|
||||
# of files the postprocess-program is called after each collection is completed
|
||||
# and par-checked. If you want to unpack files or clean up the directory
|
||||
# and par-checked. If you want to unpack files or clean up the directory
|
||||
# (delete par-files, etc.) there are two possibilities, when you can do this:
|
||||
# 1) you parse NZBPP_PARFILENAME to find out the base name of collection and
|
||||
# 1) you parse NZBPP_PARFILENAME to find out the base name of collection and
|
||||
# clean up only files from this collection (not reliable, because par-files
|
||||
# sometimes have different names than rar-files);
|
||||
# 2) or you just check the parameters NZBPP_NZBCOMPLETED and NZBPP_PARFAILED
|
||||
# and do the processing, only if NZBPP_NZBCOMPLETED is set to "1" (which
|
||||
# means, that this was the last collection in nzb-file and all files
|
||||
# and do the processing, only if NZBPP_NZBCOMPLETED is set to "1" (which
|
||||
# means, that this was the last collection in nzb-file and all files
|
||||
# are now completed) and NZBPP_PARFAILED is set to "0" (no failed par-jobs);
|
||||
#
|
||||
# NOTE: the term "collection" in the above description actually means
|
||||
# "par-set". To determine what "collections" are present in nzb-file nzbget
|
||||
# looks for par-sets. If any collection of files within nzb-file does
|
||||
# NOTE: The term "collection" in the above description actually means
|
||||
# "par-set". To determine what "collections" are present in nzb-file NZBGet
|
||||
# looks for par-sets. If any collection of files within nzb-file does
|
||||
# not have any par-files, this collection will not be detected.
|
||||
# For example, for nzb-file containing three collections but only two par-sets,
|
||||
# For example, for nzb-file containing three collections but only two par-sets,
|
||||
# the postprocess will be called two times - after processing of each par-set.
|
||||
#
|
||||
# NOTE: if nzbget doesn't find any collections it calls PostProcess once
|
||||
# NOTE: If NZBGet doesn't find any collections it calls PostProcess once
|
||||
# with empty string for parameter NZBPP_PARFILENAME;
|
||||
#
|
||||
# NOTE: the using of special return values (91 and 92) for requesting of
|
||||
# NOTE: The using of special return values (91 and 92) for requesting of
|
||||
# par-check/repair allows to organize the delayed parcheck. To do that:
|
||||
# 1) set options: LoadPars=one, ParCheck=no, ParRepair=yes;
|
||||
# 2) in post-process-script check the parameter NZBPP_PARSTATUS. If it is "0",
|
||||
# that means, the script is called for the first time. Try to unpack files.
|
||||
# If unpack fails, exit the script with exit code for par-check/repair;
|
||||
# 3) nzbget will start par-check/repair. After that it calls the script again;
|
||||
# 4) on second pass the parameter NZBPP_PARSTATUS will have value
|
||||
# 3) NZBGet will start par-check/repair. After that it calls the script again;
|
||||
# 4) on second pass the parameter NZBPP_PARSTATUS will have value
|
||||
# greater than "0". If it is "2" ("checked and successfully repaired")
|
||||
# you can try unpack again.
|
||||
#
|
||||
# NOTE: an example script for unrarring is provided within distribution
|
||||
# in file "postprocess-example.sh".
|
||||
# you can try unpack again.
|
||||
PostProcess=
|
||||
|
||||
# Allow multiple post-processing for the same nzb-file (yes, no).
|
||||
#
|
||||
# After the post-processing (par-check and call of a postprocess-script) is
|
||||
# completed, nzbget adds the nzb-file to a list of completed-jobs. The nzb-file
|
||||
# stays in the list until the last file from that nzb-file is deleted from
|
||||
# the download queue (it occurs straight away if the par-check was successful
|
||||
# and the option <ParCleanupQueue> is enabled).
|
||||
# That means, if a paused file from a nzb-collection becomes unpaused
|
||||
# (manually or from a post-process-script) after the collection was already
|
||||
# postprocessed nzbget will not post-process nzb-file again.
|
||||
# This prevents the unwanted multiple post-processings of the same nzb-file.
|
||||
# But it might be needed if the par-check/-repair are performed not directly
|
||||
# by nzbget but from a post-process-script.
|
||||
#
|
||||
# NOTE: enable this option only if you were advised to do that by the author
|
||||
# NOTE: Enable this option only if you were advised to do that by the author
|
||||
# of the post-process-script.
|
||||
#
|
||||
# NOTE: by enabling <AllowReProcess> you should disable the option <ParCheck>
|
||||
# NOTE: By enabling <AllowReProcess> you should disable the option <ParCheck>
|
||||
# to prevent multiple par-checking.
|
||||
#
|
||||
# INFO FOR DEVELOPERS:
|
||||
# After the post-processing (par-check and call of a postprocess-script) is
|
||||
# completed, NZBGet adds the nzb-file to a list of completed-jobs. The nzb-file
|
||||
# stays in the list until the last file from that nzb-file is deleted from
|
||||
# the download queue (it occurs straight away if the par-check was successful
|
||||
# and the option <ParCleanupQueue> is enabled).
|
||||
# That means, if a paused file from a nzb-collection becomes unpaused
|
||||
# (manually or from a post-process-script) after the collection was already
|
||||
# postprocessed NZBGet will not post-process nzb-file again.
|
||||
# This prevents the unwanted multiple post-processings of the same nzb-file.
|
||||
# But it might be needed if the par-check/-repair are performed not directly
|
||||
# by NZBGet but from a post-process-script.
|
||||
AllowReProcess=no
|
||||
|
||||
# Pause download queue during executing of postprocess-script (yes, no).
|
||||
@@ -837,7 +935,7 @@ PostPauseQueue=no
|
||||
# This section defines scheduler commands.
|
||||
# For each command create a set of options <TaskX.Time>, <TaskX.Command>,
|
||||
# <TaskX.WeekDays> and <TaskX.DownloadRate>.
|
||||
# The following example shows how to throttle downloads in the daytime
|
||||
# The following example shows how to throttle downloads in the daytime
|
||||
# by 100 KB/s and download at full speed overnights:
|
||||
|
||||
# Time to execute the command (HH:MM).
|
||||
@@ -850,22 +948,22 @@ PostPauseQueue=no
|
||||
|
||||
# Week days to execute the command (1-7).
|
||||
#
|
||||
# Comma separated list of week days numbers.
|
||||
# Comma separated list of week days numbers.
|
||||
# 1 is Monday.
|
||||
# Character '-' may be used to define ranges.
|
||||
#
|
||||
# Examples: "1-7", "1-5", "5,6", "1-5, 7".
|
||||
#Task1.WeekDays=1-7
|
||||
|
||||
# Command to be executed (PauseDownload, UnpauseDownload, PauseScan,
|
||||
# UnpauseScan, DownloadRate, Process).
|
||||
# Command to be executed (DownloadRate, PauseDownload, UnpauseDownload, PauseScan,
|
||||
# UnpauseScan, Process).
|
||||
#
|
||||
# Possible commands:
|
||||
# DownloadRate - sets download rate in KB/s;
|
||||
# PauseDownload - pauses download;
|
||||
# UnpauseDownload - resumes download;
|
||||
# PauseScan - pauses scan of incoming nzb-directory;
|
||||
# UnpauseScan - resumes scan of incoming nzb-directory;
|
||||
# DownloadRate - sets download rate in KB/s;
|
||||
# Process - executes external program.
|
||||
#Task1.Command=DownloadRate
|
||||
|
||||
@@ -884,15 +982,15 @@ PostPauseQueue=no
|
||||
# If the option <TaskX.Command> is not set to "Process" this option
|
||||
# is ignored and can be omitted.
|
||||
#
|
||||
# NOTE: it's allowed to add parameters to command line. If filename or
|
||||
# NOTE: It's allowed to add parameters to command line. If filename or
|
||||
# any parameter contains spaces it must be surrounded with single quotation
|
||||
# marks. If filename/parameter contains single quotation marks, each of them
|
||||
# marks. If filename/parameter contains single quotation marks, each of them
|
||||
# must be replaced with two single quotation marks and the resulting filename/
|
||||
# parameter must be surrounded with single quotation marks.
|
||||
# Example: '/home/user/download/my scripts/task process.sh' 'world''s fun'.
|
||||
# In this example one parameter (world's fun) is passed to the script
|
||||
# In this example one parameter (world's fun) is passed to the script
|
||||
# (task process.sh).
|
||||
#Task1.Process=
|
||||
#Task1.Process=/home/user/script.sh
|
||||
|
||||
#Task2.Time=20:00
|
||||
#Task2.WeekDays=1-7
|
||||
@@ -903,23 +1001,24 @@ PostPauseQueue=no
|
||||
##############################################################################
|
||||
## PERFORMANCE ##
|
||||
|
||||
# On a very fast connection and slow CPU and/or drive the following
|
||||
# On a very fast connection and slow CPU and/or drive the following
|
||||
# settings may improve performance:
|
||||
# 1) Disable par-checking and -repairing ("ParCheck=no"). VERY important,
|
||||
# because par-checking/repairing needs a lot of CPU-power and
|
||||
# because par-checking/repairing needs a lot of CPU-power and
|
||||
# significantly increases disk usage;
|
||||
# 2) Try to activate option <DirectWrite> ("DirectWrite=yes"), especially
|
||||
# if you use EXT3-partitions (Linux) or NTFS (Windows);
|
||||
# 3) Disable option <CrcCheck> ("CrcCheck=no");
|
||||
# 4) Disable option <ContinuePartial> ("ContinuePartial=no");
|
||||
# 5) Do not limit download rate ("DownloadRate=0"), because the bandwidth
|
||||
# 5) Do not limit download rate ("DownloadRate=0"), because the bandwidth
|
||||
# throttling eats some CPU time. Disable accurate speed rate
|
||||
# meter ("AccurateRate=no");
|
||||
# 6) Disable logging for detail- and debug-messages ("DetailTarget=none",
|
||||
# 6) Disable logging for detail- and debug-messages ("DetailTarget=none",
|
||||
# "DebugTarget=none");
|
||||
# 7) Run the program in daemon (POSIX) or service (Windows) mode and use
|
||||
# remote client for short periods of time needed for controlling of
|
||||
# download process on server. Daemon/Service mode eats less CPU
|
||||
# remote client for short periods of time needed for controlling of
|
||||
# download process on server. Daemon/Service mode eats less CPU
|
||||
# resources than console server mode due to not updating the screen.
|
||||
# 8) Increase the value of option <WriteBufferSize> or better set it to
|
||||
# 8) Increase the value of option <WriteBufferSize> or better set it to
|
||||
# "-1" (max/auto) if you have spare 5-20 MB of memory.
|
||||
|
||||
197
nzbget.cpp
@@ -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-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
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -66,6 +66,7 @@
|
||||
#include "ColoredFrontend.h"
|
||||
#include "NCursesFrontend.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "MessageBase.h"
|
||||
@@ -79,7 +80,9 @@
|
||||
#endif
|
||||
|
||||
// Prototypes
|
||||
void Run();
|
||||
void RunMain();
|
||||
void Run(bool bReload);
|
||||
void Reload();
|
||||
void Cleanup();
|
||||
void ProcessClientRequest();
|
||||
#ifndef WIN32
|
||||
@@ -101,6 +104,7 @@ Thread* g_pFrontend = NULL;
|
||||
Options* g_pOptions = NULL;
|
||||
ServerPool* g_pServerPool = NULL;
|
||||
QueueCoordinator* g_pQueueCoordinator = NULL;
|
||||
UrlCoordinator* g_pUrlCoordinator = NULL;
|
||||
RemoteServer* g_pRemoteServer = NULL;
|
||||
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
|
||||
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
|
||||
@@ -108,7 +112,10 @@ Log* g_pLog = NULL;
|
||||
PrePostProcessor* g_pPrePostProcessor = NULL;
|
||||
DiskState* g_pDiskState = NULL;
|
||||
Scheduler* g_pScheduler = NULL;
|
||||
char* (*szEnvironmentVariables)[] = NULL;
|
||||
int g_iArgumentCount;
|
||||
char* (*g_szEnvironmentVariables)[] = NULL;
|
||||
char* (*g_szArguments)[] = NULL;
|
||||
bool g_bReloading = true;
|
||||
|
||||
/*
|
||||
* Main loop
|
||||
@@ -137,17 +144,67 @@ int main(int argc, char *argv[], char *argp[])
|
||||
DisableCout();
|
||||
#endif
|
||||
|
||||
g_iArgumentCount = argc;
|
||||
g_szArguments = (char*(*)[])argv;
|
||||
g_szEnvironmentVariables = (char*(*)[])argp;
|
||||
|
||||
#ifdef WIN32
|
||||
for (int i=0; i < argc; i++)
|
||||
{
|
||||
if (!strcmp(argv[i], "-D"))
|
||||
{
|
||||
StartService(RunMain);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RunMain();
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RunMain()
|
||||
{
|
||||
// we need to save and later restore current directory each time
|
||||
// the program is reloaded (RPC-Method "reload") in order for
|
||||
// config to properly load in a case relative paths are used
|
||||
// in command line
|
||||
char szCurDir[MAX_PATH + 1];
|
||||
Util::GetCurrentDirectory(szCurDir, sizeof(szCurDir));
|
||||
|
||||
bool bReload = false;
|
||||
while (g_bReloading)
|
||||
{
|
||||
g_bReloading = false;
|
||||
Util::SetCurrentDirectory(szCurDir);
|
||||
Run(bReload);
|
||||
bReload = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Run(bool bReload)
|
||||
{
|
||||
g_pLog = new Log();
|
||||
|
||||
debug("nzbget %s", Util::VersionRevision());
|
||||
|
||||
if (!bReload)
|
||||
{
|
||||
Thread::Init();
|
||||
}
|
||||
|
||||
g_pServerPool = new ServerPool();
|
||||
g_pScheduler = new Scheduler();
|
||||
Thread::Init();
|
||||
|
||||
debug("Reading options");
|
||||
g_pOptions = new Options(argc, argv);
|
||||
szEnvironmentVariables = (char*(*)[])argp;
|
||||
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
|
||||
|
||||
#ifndef WIN32
|
||||
if (g_pOptions->GetUMask() < 01000)
|
||||
@@ -165,7 +222,7 @@ int main(int argc, char *argv[], char *argp[])
|
||||
|
||||
g_pLog->InitOptions();
|
||||
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
if (g_pOptions->GetDaemonMode() && !bReload)
|
||||
{
|
||||
#ifdef WIN32
|
||||
info("nzbget %s service-mode", Util::VersionRevision());
|
||||
@@ -174,7 +231,7 @@ int main(int argc, char *argv[], char *argp[])
|
||||
info("nzbget %s daemon-mode", Util::VersionRevision());
|
||||
#endif
|
||||
}
|
||||
else if (g_pOptions->GetServerMode())
|
||||
else if (g_pOptions->GetServerMode() && !bReload)
|
||||
{
|
||||
info("nzbget %s server-mode", Util::VersionRevision());
|
||||
}
|
||||
@@ -183,6 +240,11 @@ int main(int argc, char *argv[], char *argp[])
|
||||
info("nzbget %s remote-mode", Util::VersionRevision());
|
||||
}
|
||||
|
||||
if (!bReload)
|
||||
{
|
||||
Connection::Init();
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
g_pServerPool->InitConnections();
|
||||
@@ -191,13 +253,7 @@ int main(int argc, char *argv[], char *argp[])
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
{
|
||||
StartService(Run);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#ifndef WIN32
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
if (g_pOptions->GetDumpCore())
|
||||
{
|
||||
@@ -206,19 +262,6 @@ int main(int argc, char *argv[], char *argp[])
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Run();
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Run()
|
||||
{
|
||||
#ifndef WIN32
|
||||
InstallSignalHandlers();
|
||||
#ifdef DEBUG
|
||||
@@ -228,9 +271,6 @@ void Run()
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Connection::Init(g_pOptions->GetTLS() && !g_pOptions->GetRemoteClientMode() &&
|
||||
(g_pOptions->GetClientOperation() == Options::opClientNoOperation));
|
||||
|
||||
// client request
|
||||
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
|
||||
@@ -246,6 +286,8 @@ void Run()
|
||||
g_pQueueCoordinator = new QueueCoordinator();
|
||||
g_pDownloadSpeedMeter = g_pQueueCoordinator;
|
||||
g_pDownloadQueueHolder = g_pQueueCoordinator;
|
||||
|
||||
g_pUrlCoordinator = new UrlCoordinator();
|
||||
}
|
||||
|
||||
// Setup the network-server
|
||||
@@ -292,7 +334,7 @@ void Run()
|
||||
// Standalone-mode
|
||||
if (!g_pOptions->GetServerMode())
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetCategory() ? g_pOptions->GetCategory() : "");
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
|
||||
if (!pNZBFile)
|
||||
{
|
||||
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
|
||||
@@ -308,18 +350,28 @@ void Run()
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->Start();
|
||||
g_pUrlCoordinator->Start();
|
||||
g_pPrePostProcessor->Start();
|
||||
|
||||
// enter main program-loop
|
||||
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
|
||||
while (g_pQueueCoordinator->IsRunning() ||
|
||||
g_pUrlCoordinator->IsRunning() ||
|
||||
g_pPrePostProcessor->IsRunning())
|
||||
{
|
||||
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->HasMoreJobs() && !g_pPrePostProcessor->HasMoreJobs())
|
||||
if (!g_pOptions->GetServerMode() &&
|
||||
!g_pQueueCoordinator->HasMoreJobs() &&
|
||||
!g_pUrlCoordinator->HasMoreJobs() &&
|
||||
!g_pPrePostProcessor->HasMoreJobs())
|
||||
{
|
||||
// Standalone-mode: download completed
|
||||
if (!g_pQueueCoordinator->IsStopped())
|
||||
{
|
||||
g_pQueueCoordinator->Stop();
|
||||
}
|
||||
if (!g_pUrlCoordinator->IsStopped())
|
||||
{
|
||||
g_pUrlCoordinator->Stop();
|
||||
}
|
||||
if (!g_pPrePostProcessor->IsStopped())
|
||||
{
|
||||
g_pPrePostProcessor->Stop();
|
||||
@@ -330,6 +382,7 @@ void Run()
|
||||
|
||||
// main program-loop is terminated
|
||||
debug("QueueCoordinator stopped");
|
||||
debug("UrlCoordinator stopped");
|
||||
debug("PrePostProcessor stopped");
|
||||
}
|
||||
|
||||
@@ -351,7 +404,7 @@ void Run()
|
||||
}
|
||||
debug("RemoteServer stopped");
|
||||
}
|
||||
|
||||
|
||||
// Stop Frontend
|
||||
if (g_pFrontend)
|
||||
{
|
||||
@@ -377,15 +430,15 @@ void ProcessClientRequest()
|
||||
switch (g_pOptions->GetClientOperation())
|
||||
{
|
||||
case Options::opClientRequestListFiles:
|
||||
Client->RequestServerList(true, false);
|
||||
Client->RequestServerList(true, false, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestListGroups:
|
||||
Client->RequestServerList(false, true);
|
||||
Client->RequestServerList(false, true, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestListStatus:
|
||||
Client->RequestServerList(false, false);
|
||||
Client->RequestServerList(false, false, NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownloadPause:
|
||||
@@ -414,7 +467,8 @@ void ProcessClientRequest()
|
||||
|
||||
case Options::opClientRequestEditQueue:
|
||||
Client->RequestServerEditQueue((eRemoteEditAction)g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
|
||||
g_pOptions->GetEditQueueText(), g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(), true);
|
||||
g_pOptions->GetEditQueueText(), g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(),
|
||||
g_pOptions->GetEditQueueNameList(), (eRemoteMatchMode)g_pOptions->GetMatchMode(), true);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestLog:
|
||||
@@ -425,8 +479,12 @@ void ProcessClientRequest()
|
||||
Client->RequestServerShutdown();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestReload:
|
||||
Client->RequestServerReload();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownload:
|
||||
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetCategory(), g_pOptions->GetAddTop());
|
||||
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestVersion:
|
||||
@@ -441,8 +499,12 @@ void ProcessClientRequest()
|
||||
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScan:
|
||||
Client->RequestScan();
|
||||
case Options::opClientRequestScanAsync:
|
||||
Client->RequestScan(false);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScanSync:
|
||||
Client->RequestScan(true);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestPostPause:
|
||||
@@ -465,6 +527,14 @@ void ProcessClientRequest()
|
||||
Client->RequestHistory();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownloadUrl:
|
||||
Client->RequestServerDownloadUrl(g_pOptions->GetLastArg(), g_pOptions->GetAddNZBFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestUrlQueue:
|
||||
Client->RequestUrlQueue();
|
||||
break;
|
||||
|
||||
case Options::opClientNoOperation:
|
||||
break;
|
||||
}
|
||||
@@ -474,7 +544,10 @@ void ProcessClientRequest()
|
||||
|
||||
void ExitProc()
|
||||
{
|
||||
info("Stopping, please wait...");
|
||||
if (!g_bReloading)
|
||||
{
|
||||
info("Stopping, please wait...");
|
||||
}
|
||||
if (g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
if (g_pFrontend)
|
||||
@@ -489,11 +562,19 @@ void ExitProc()
|
||||
{
|
||||
debug("Stopping QueueCoordinator");
|
||||
g_pQueueCoordinator->Stop();
|
||||
g_pUrlCoordinator->Stop();
|
||||
g_pPrePostProcessor->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reload()
|
||||
{
|
||||
g_bReloading = true;
|
||||
info("Reloading...");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef DEBUG
|
||||
typedef void(*sighandler)(int);
|
||||
@@ -606,13 +687,13 @@ void Cleanup()
|
||||
{
|
||||
debug("Cleaning up global objects");
|
||||
|
||||
debug("Deleting QueueCoordinator");
|
||||
if (g_pQueueCoordinator)
|
||||
debug("Deleting UrlCoordinator");
|
||||
if (g_pUrlCoordinator)
|
||||
{
|
||||
delete g_pQueueCoordinator;
|
||||
g_pQueueCoordinator = NULL;
|
||||
delete g_pUrlCoordinator;
|
||||
g_pUrlCoordinator = NULL;
|
||||
}
|
||||
debug("QueueCoordinator deleted");
|
||||
debug("UrlCoordinator deleted");
|
||||
|
||||
debug("Deleting RemoteServer");
|
||||
if (g_pRemoteServer)
|
||||
@@ -638,6 +719,14 @@ void Cleanup()
|
||||
}
|
||||
debug("Frontend deleted");
|
||||
|
||||
debug("Deleting QueueCoordinator");
|
||||
if (g_pQueueCoordinator)
|
||||
{
|
||||
delete g_pQueueCoordinator;
|
||||
g_pQueueCoordinator = NULL;
|
||||
}
|
||||
debug("QueueCoordinator deleted");
|
||||
|
||||
debug("Deleting DiskState");
|
||||
if (g_pDiskState)
|
||||
{
|
||||
@@ -649,7 +738,7 @@ void Cleanup()
|
||||
debug("Deleting Options");
|
||||
if (g_pOptions)
|
||||
{
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
if (g_pOptions->GetDaemonMode() && !g_bReloading)
|
||||
{
|
||||
info("Deleting lock file");
|
||||
remove(g_pOptions->GetLockFile());
|
||||
@@ -675,9 +764,11 @@ void Cleanup()
|
||||
}
|
||||
debug("Scheduler deleted");
|
||||
|
||||
Connection::Final();
|
||||
|
||||
Thread::Final();
|
||||
if (!g_bReloading)
|
||||
{
|
||||
Connection::Final();
|
||||
Thread::Final();
|
||||
}
|
||||
|
||||
debug("Global objects cleaned up");
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib libgnutls-26.lib libgcrypt-11.lib $(NOINHERIT)"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
@@ -131,7 +131,7 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib libgnutls-26.lib libgcrypt-11.lib $(NOINHERIT)"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="0"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
@@ -206,7 +206,7 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib $(NOINHERIT)"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="0"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
@@ -335,14 +335,6 @@
|
||||
RelativePath=".\NCursesFrontend.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NetAddress.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NetAddress.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NewsServer.cpp"
|
||||
>
|
||||
@@ -495,6 +487,14 @@
|
||||
RelativePath=".\TLS.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\UrlCoordinator.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\UrlCoordinator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Util.cpp"
|
||||
>
|
||||
@@ -503,6 +503,22 @@
|
||||
RelativePath=".\Util.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebDownloader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebDownloader.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebServer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebServer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\win32.h"
|
||||
>
|
||||
|
||||
56
nzbgetd
@@ -3,7 +3,7 @@
|
||||
# Script used to start and stop the nzbget usenet service
|
||||
#
|
||||
# Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
|
||||
# Copyright (C) 2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
# Copyright (C) 2009-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
|
||||
@@ -21,44 +21,43 @@
|
||||
#
|
||||
#
|
||||
|
||||
# --- CONFIGURATION -----------------------------------------------
|
||||
# Location of the nzbget executable
|
||||
export NZBGET_BINARY="/usr/local/bin/nzbget"
|
||||
NZBGET_BINARY="/usr/local/bin/nzbget"
|
||||
|
||||
# Additional options, e. g. config file location:
|
||||
# NZBGET_OPTS="-c /mnt/hdd/tools/nzbget/conf/nzbget.conf"
|
||||
NZBGET_OPTS=""
|
||||
# -----------------------------------------------------------------
|
||||
# start/stop section
|
||||
|
||||
execCommand() {
|
||||
"$NZBGET_BINARY" $@
|
||||
sleep 1 # allows prompt to return
|
||||
}
|
||||
|
||||
start() {
|
||||
execCommand "--daemon"
|
||||
}
|
||||
|
||||
stop() {
|
||||
execCommand "--quit"
|
||||
}
|
||||
|
||||
status() {
|
||||
execCommand "--log 5"
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
if [ -z "$1" ] ; then
|
||||
case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
|
||||
S??*) rc="start" ;;
|
||||
K??*) rc="stop" ;;
|
||||
*) rc="usage" ;;
|
||||
esac
|
||||
else
|
||||
rc="$1"
|
||||
fi
|
||||
|
||||
case "$rc" in
|
||||
start)
|
||||
start
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -D
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -Q
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -Q
|
||||
sleep 10 # since stop is backgrounded
|
||||
start
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -D
|
||||
;;
|
||||
status)
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -L S
|
||||
;;
|
||||
pstatus)
|
||||
retval=$(pgrep -l -f "$NZBGET_BINARY --daemon" > /dev/null ; echo $?)
|
||||
retval=$(pgrep -l -f nzbget > /dev/null ; echo $?)
|
||||
if [ "$retval" = "0" ] ; then
|
||||
echo " ------- nzbget *is* running -------"
|
||||
ps -Ho user,pid,cmd:32,pcpu -C nzbget
|
||||
@@ -68,11 +67,8 @@ case "$1" in
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
istatus)
|
||||
status
|
||||
;;
|
||||
*)
|
||||
echo "Usage $0 {start|stop|restart|pstatus|istatus}"
|
||||
echo "Usage: $0 {start|stop|restart|status|pstatus|usage}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
|
||||
1421
webui/config.js
Normal file
683
webui/downloads.js
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Download tab;
|
||||
* 2) Functions for html generation for downloads, also used from other modules (edit and add dialogs).
|
||||
*/
|
||||
|
||||
/*** DOWNLOADS TAB ***********************************************************/
|
||||
|
||||
var Downloads = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $DownloadsTable;
|
||||
var $DownloadsTabBadge;
|
||||
var $DownloadsTabBadgeEmpty;
|
||||
var $DownloadQueueEmpty;
|
||||
var $DownloadsRecordsPerPage;
|
||||
|
||||
// State
|
||||
var notification = null;
|
||||
var updateTabInfo;
|
||||
var groups;
|
||||
var urls;
|
||||
|
||||
this.init = function(options)
|
||||
{
|
||||
updateTabInfo = options.updateTabInfo;
|
||||
|
||||
$DownloadsTable = $('#DownloadsTable');
|
||||
$DownloadsTabBadge = $('#DownloadsTabBadge');
|
||||
$DownloadsTabBadgeEmpty = $('#DownloadsTabBadgeEmpty');
|
||||
$DownloadQueueEmpty = $('#DownloadQueueEmpty');
|
||||
$DownloadsRecordsPerPage = $('#DownloadsRecordsPerPage');
|
||||
|
||||
var recordsPerPage = UISettings.read('$DownloadsRecordsPerPage', 10);
|
||||
$DownloadsRecordsPerPage.val(recordsPerPage);
|
||||
|
||||
$DownloadsTable.fasttable(
|
||||
{
|
||||
filterInput: $('#DownloadsTable_filter'),
|
||||
filterClearButton: $("#DownloadsTable_clearfilter"),
|
||||
pagerContainer: $('#DownloadsTable_pager'),
|
||||
infoContainer: $('#DownloadsTable_info'),
|
||||
headerCheck: $('#DownloadsTable > thead > tr:first-child'),
|
||||
filterCaseSensitive: false,
|
||||
infoEmpty: ' ', // this is to disable default message "No records"
|
||||
pageSize: recordsPerPage,
|
||||
maxPages: UISettings.miniTheme ? 1 : 5,
|
||||
pageDots: !UISettings.miniTheme,
|
||||
fillFieldsCallback: fillFieldsCallback,
|
||||
renderCellCallback: renderCellCallback,
|
||||
updateInfoCallback: updateInfo
|
||||
});
|
||||
|
||||
$DownloadsTable.on('click', 'a', itemClick);
|
||||
$DownloadsTable.on('click', 'tbody div.check',
|
||||
function(event) { $DownloadsTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
|
||||
$DownloadsTable.on('click', 'thead div.check',
|
||||
function() { $DownloadsTable.fasttable('titleCheckClick') });
|
||||
$DownloadsTable.on('mousedown', Util.disableShiftMouseDown);
|
||||
}
|
||||
|
||||
this.applyTheme = function()
|
||||
{
|
||||
$DownloadsTable.fasttable('setPageSize', UISettings.read('$DownloadsRecordsPerPage', 10),
|
||||
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
RPC.call('listgroups', [], groups_loaded);
|
||||
}
|
||||
|
||||
function groups_loaded(_groups)
|
||||
{
|
||||
groups = _groups;
|
||||
RPC.call('postqueue', [100], posts_loaded);
|
||||
}
|
||||
|
||||
function posts_loaded(posts)
|
||||
{
|
||||
mergequeues(posts);
|
||||
prepare();
|
||||
RPC.call('urlqueue', [], urls_loaded);
|
||||
}
|
||||
|
||||
function urls_loaded(_urls)
|
||||
{
|
||||
urls = _urls;
|
||||
RPC.next();
|
||||
}
|
||||
|
||||
function mergequeues(posts)
|
||||
{
|
||||
var lastPPItemIndex = -1;
|
||||
for (var i=0, il=posts.length; i < il; i++)
|
||||
{
|
||||
var post = posts[i];
|
||||
var found = false;
|
||||
for (var j=0, jl=groups.length; j < jl; j++)
|
||||
{
|
||||
var group = groups[j];
|
||||
if (group.NZBID === post.NZBID)
|
||||
{
|
||||
found = true;
|
||||
if (!group.post)
|
||||
{
|
||||
group.post = post;
|
||||
}
|
||||
lastPPItemIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
// create a virtual group-item
|
||||
var group = {post: post};
|
||||
group.NZBID = post.NZBID;
|
||||
group.NZBName = post.NZBName;
|
||||
group.MaxPriority = 0;
|
||||
group.Category = '';
|
||||
group.LastID = 0;
|
||||
group.MinPostTime = 0;
|
||||
group.FileSizeMB = 0;
|
||||
group.FileSizeLo = 0;
|
||||
group.RemainingSizeMB = 0;
|
||||
group.RemainingSizeLo = 0;
|
||||
group.PausedSizeMB = 0;
|
||||
group.PausedSizeLo = 0;
|
||||
group.FileCount = 0;
|
||||
group.RemainingFileCount = 0;
|
||||
group.RemainingParCount = 0;
|
||||
|
||||
// insert it after the last pp-item
|
||||
if (lastPPItemIndex > -1)
|
||||
{
|
||||
groups.splice(lastPPItemIndex + 1, 0, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
groups.unshift(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prepare()
|
||||
{
|
||||
for (var j=0, jl=groups.length; j < jl; j++)
|
||||
{
|
||||
detectStatus(groups[j]);
|
||||
}
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
redraw_table();
|
||||
|
||||
Util.show($DownloadsTabBadge, groups.length > 0);
|
||||
Util.show($DownloadsTabBadgeEmpty, groups.length === 0 && UISettings.miniTheme);
|
||||
Util.show($DownloadQueueEmpty, groups.length === 0);
|
||||
}
|
||||
|
||||
/*** TABLE *************************************************************************/
|
||||
|
||||
function redraw_table()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
|
||||
var nametext = group.NZBName;
|
||||
var priority = DownloadsUI.buildPriorityText(group.MaxPriority);
|
||||
var estimated = DownloadsUI.buildEstimated(group);
|
||||
var age = Util.formatAge(group.MinPostTime + UISettings.timeZoneCorrection*60*60);
|
||||
var size = Util.formatSizeMB(group.FileSizeMB, group.FileSizeLo);
|
||||
var remaining = Util.formatSizeMB(group.RemainingSizeMB-group.PausedSizeMB, group.RemainingSizeLo-group.PausedSizeLo);
|
||||
|
||||
var item =
|
||||
{
|
||||
id: group.NZBID,
|
||||
group: group,
|
||||
data: { age: age, estimated: estimated, size: size, remaining: remaining },
|
||||
search: group.status + ' ' + nametext + ' ' + priority + ' ' + group.Category + ' ' + age + ' ' + size + ' ' + remaining + ' ' + estimated
|
||||
};
|
||||
|
||||
data.push(item);
|
||||
}
|
||||
|
||||
$DownloadsTable.fasttable('update', data);
|
||||
}
|
||||
|
||||
function fillFieldsCallback(item)
|
||||
{
|
||||
var group = item.group;
|
||||
|
||||
var status = DownloadsUI.buildStatus(group);
|
||||
var priority = DownloadsUI.buildPriority(group.MaxPriority);
|
||||
var progresslabel = DownloadsUI.buildProgressLabel(group);
|
||||
var progress = DownloadsUI.buildProgress(group, item.data.size, item.data.remaining, item.data.estimated);
|
||||
|
||||
var name = '<a href="#" nzbid="' + group.NZBID + '">' + Util.textToHtml(Util.formatNZBName(group.NZBName)) + '</a>';
|
||||
var category = Util.textToHtml(group.Category);
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
var info = name + ' ' + priority + progresslabel;
|
||||
item.fields = ['<div class="check img-check"></div>', status, info, category, item.data.age, progress, item.data.estimated];
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' +
|
||||
' ' + (group.status === 'queued' ? '' : status) + ' ' + priority;
|
||||
if (category)
|
||||
{
|
||||
info += ' <span class="label label-status">' + category + '</span>';
|
||||
}
|
||||
|
||||
if (progresslabel)
|
||||
{
|
||||
progress = '<div class="downloads-progresslabel">' + progresslabel + '</div>' + progress;
|
||||
}
|
||||
item.fields = [info, progress];
|
||||
}
|
||||
}
|
||||
|
||||
function renderCellCallback(cell, index, item)
|
||||
{
|
||||
if (4 <= index && index <= 7)
|
||||
{
|
||||
cell.className = 'text-right';
|
||||
}
|
||||
}
|
||||
|
||||
function detectStatus(group)
|
||||
{
|
||||
group.paused = (group.PausedSizeLo != 0) && (group.RemainingSizeLo == group.PausedSizeLo);
|
||||
group.postprocess = group.post !== undefined;
|
||||
if (group.postprocess)
|
||||
{
|
||||
switch (group.post.Stage)
|
||||
{
|
||||
case 'QUEUED': group.status = 'pp-queued'; break;
|
||||
case 'LOADING_PARS': group.status = 'checking'; break;
|
||||
case 'VERIFYING_SOURCES': group.status = 'checking'; break;
|
||||
case 'REPAIRING': group.status = 'repairing'; break;
|
||||
case 'VERIFYING_REPAIRED': group.status = 'verifying'; break;
|
||||
case 'EXECUTING_SCRIPT': group.status = 'unpacking'; break;
|
||||
case 'FINISHED': group.status = 'finished'; break;
|
||||
default: group.status = 'error: ' + group.post.Stage; break;
|
||||
}
|
||||
}
|
||||
else if (group.ActiveDownloads > 0)
|
||||
{
|
||||
group.status = 'downloading';
|
||||
}
|
||||
else if (group.paused)
|
||||
{
|
||||
group.status = 'paused';
|
||||
}
|
||||
else
|
||||
{
|
||||
group.status = 'queued';
|
||||
}
|
||||
}
|
||||
|
||||
this.recordsPerPageChange = function()
|
||||
{
|
||||
var val = $DownloadsRecordsPerPage.val();
|
||||
UISettings.write('$DownloadsRecordsPerPage', val);
|
||||
$DownloadsTable.fasttable('setPageSize', val);
|
||||
}
|
||||
|
||||
function updateInfo(stat)
|
||||
{
|
||||
updateTabInfo($DownloadsTabBadge, stat);
|
||||
}
|
||||
|
||||
/*** EDIT ******************************************************/
|
||||
|
||||
function itemClick()
|
||||
{
|
||||
var nzbid = $(this).attr('nzbid');
|
||||
$(this).blur();
|
||||
DownloadsEditDialog.showModal(nzbid, groups);
|
||||
}
|
||||
|
||||
function editCompleted()
|
||||
{
|
||||
Refresher.update();
|
||||
if (notification)
|
||||
{
|
||||
Notification.show(notification);
|
||||
notification = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*** CHECKMARKS ******************************************************/
|
||||
|
||||
function checkBuildEditIDList(UseLastID)
|
||||
{
|
||||
var checkedRows = $DownloadsTable.fasttable('checkedRows');
|
||||
|
||||
var hasIDs = false;
|
||||
var checkedEditIDs = [];
|
||||
for (var i = 0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
if (checkedRows.indexOf(group.NZBID) > -1)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_CheckPostProcess');
|
||||
return null;
|
||||
}
|
||||
|
||||
checkedEditIDs.push(UseLastID ? group.LastID : group.NZBID);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length === 0)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_Select');
|
||||
return null;
|
||||
}
|
||||
|
||||
return checkedEditIDs;
|
||||
}
|
||||
|
||||
/*** TOOLBAR: SELECTED ITEMS ******************************************************/
|
||||
|
||||
this.editClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(false);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length == 1)
|
||||
{
|
||||
DownloadsEditDialog.showModal(checkedEditIDs[0], groups);
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadsMultiDialog.showModal(checkedEditIDs, groups);
|
||||
}
|
||||
}
|
||||
|
||||
this.mergeClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(false);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length < 2)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_SelectMulti');
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadsMergeDialog.showModal(checkedEditIDs, groups);
|
||||
}
|
||||
|
||||
this.pauseClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
notification = '#Notif_Downloads_Paused';
|
||||
RPC.call('editqueue', ['GroupPause', 0, '', checkedEditIDs], editCompleted);
|
||||
}
|
||||
|
||||
this.resumeClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
notification = '#Notif_Downloads_Resumed';
|
||||
RPC.call('editqueue', ['GroupResume', 0, '', checkedEditIDs], function()
|
||||
{
|
||||
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
|
||||
});
|
||||
}
|
||||
|
||||
this.deleteClick = function()
|
||||
{
|
||||
var checkedRows = $DownloadsTable.fasttable('checkedRows');
|
||||
var downloadIDs = [];
|
||||
var postprocessIDs = [];
|
||||
for (var i = 0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
if (checkedRows.indexOf(group.NZBID) > -1)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
postprocessIDs.push(group.post.ID);
|
||||
}
|
||||
if (group.LastID > 0)
|
||||
{
|
||||
downloadIDs.push(group.LastID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadIDs.length === 0 && postprocessIDs.length === 0)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_Select');
|
||||
return;
|
||||
}
|
||||
|
||||
notification = '#Notif_Downloads_Deleted';
|
||||
|
||||
var deletePosts = function()
|
||||
{
|
||||
if (postprocessIDs.length > 0)
|
||||
{
|
||||
RPC.call('editqueue', ['PostDelete', 0, '', postprocessIDs], editCompleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
editCompleted();
|
||||
}
|
||||
};
|
||||
|
||||
var deleteGroups = function()
|
||||
{
|
||||
if (downloadIDs.length > 0)
|
||||
{
|
||||
RPC.call('editqueue', ['GroupDelete', 0, '', downloadIDs], deletePosts);
|
||||
}
|
||||
else
|
||||
{
|
||||
deletePosts();
|
||||
}
|
||||
};
|
||||
|
||||
ConfirmDialog.showModal('DownloadsDeleteConfirmDialog', deleteGroups);
|
||||
}
|
||||
|
||||
this.moveClick = function(action)
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var EditAction = '';
|
||||
var EditOffset = 0;
|
||||
switch (action)
|
||||
{
|
||||
case 'top':
|
||||
EditAction = 'GroupMoveTop';
|
||||
checkedEditIDs.reverse();
|
||||
break;
|
||||
case 'bottom':
|
||||
EditAction = 'GroupMoveBottom';
|
||||
break;
|
||||
case 'up':
|
||||
EditAction = 'GroupMoveOffset';
|
||||
EditOffset = -1;
|
||||
break;
|
||||
case 'down':
|
||||
EditAction = 'GroupMoveOffset';
|
||||
EditOffset = 1;
|
||||
checkedEditIDs.reverse();
|
||||
break;
|
||||
}
|
||||
|
||||
notification = '';
|
||||
RPC.call('editqueue', [EditAction, EditOffset, '', checkedEditIDs], editCompleted);
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** FUNCTIONS FOR HTML GENERATION (also used from other modules) *****************************/
|
||||
|
||||
var DownloadsUI = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
this.fillPriorityCombo = function(combo)
|
||||
{
|
||||
combo.empty();
|
||||
combo.append('<option value="-100">very low</option>');
|
||||
combo.append('<option value="-50">low</option>');
|
||||
combo.append('<option value="0">normal</option>');
|
||||
combo.append('<option value="50">high</option>');
|
||||
combo.append('<option value="100">very high</option>');
|
||||
}
|
||||
|
||||
this.fillCategoryCombo = function(combo)
|
||||
{
|
||||
combo.empty();
|
||||
combo.append('<option></option>');
|
||||
|
||||
for (var i=0; i < Options.categories.length; i++)
|
||||
{
|
||||
combo.append($('<option></option>').text(Options.categories[i]));
|
||||
}
|
||||
}
|
||||
|
||||
this.buildStatus = function(group)
|
||||
{
|
||||
if (group.postprocess && group.status !== 'pp-queued')
|
||||
{
|
||||
if (Status.status.PostPaused)
|
||||
{
|
||||
return '<span class="label label-status label-warning">' + group.status + '</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span class="label label-status label-success">' + group.status + '</span>';
|
||||
}
|
||||
}
|
||||
switch (group.status)
|
||||
{
|
||||
case 'pp-queued': return '<span class="label label-status">pp-queued</span>';
|
||||
case 'downloading': return '<span class="label label-status label-success">downloading</span>';
|
||||
case 'paused': return '<span class="label label-status label-warning">paused</span>';
|
||||
case 'queued': return '<span class="label label-status">queued</span>';
|
||||
default: return '<span class="label label-status label-important">internal error(' + group.status + ')</span>';
|
||||
}
|
||||
}
|
||||
|
||||
this.buildProgress = function(group, totalsize, remaining, estimated)
|
||||
{
|
||||
if (group.status === 'downloading' || (group.postprocess && !Status.status.PostPaused))
|
||||
{
|
||||
var kind = 'progress-success';
|
||||
}
|
||||
else if (group.status === 'paused' || (group.postprocess && Status.status.PostPaused))
|
||||
{
|
||||
var kind = 'progress-warning';
|
||||
}
|
||||
else
|
||||
{
|
||||
var kind = 'progress-none';
|
||||
}
|
||||
|
||||
var totalMB = group.FileSizeMB-group.PausedSizeMB;
|
||||
var remainingMB = group.RemainingSizeMB-group.PausedSizeMB;
|
||||
var percent = Math.round((totalMB - remainingMB) / totalMB * 100);
|
||||
var progress = '';
|
||||
|
||||
if (group.postprocess)
|
||||
{
|
||||
totalsize = '';
|
||||
remaining = '';
|
||||
percent = Math.round(group.post.StageProgress / 10);
|
||||
}
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
progress =
|
||||
'<div class="progress-block">'+
|
||||
'<div class="progress progress-striped ' + kind + '">'+
|
||||
'<div class="bar" style="width:' + percent + '%;"></div>'+
|
||||
'</div>'+
|
||||
'<div class="bar-text-left">' + totalsize + '</div>'+
|
||||
'<div class="bar-text-right">' + remaining + '</div>'+
|
||||
'</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
progress =
|
||||
'<div class="progress-block">'+
|
||||
'<div class="progress progress-striped ' + kind + '">'+
|
||||
'<div class="bar" style="width:' + percent + '%;"></div>'+
|
||||
'</div>'+
|
||||
'<div class="bar-text-left">' + (totalsize !== '' ? 'total ' : '') + totalsize + '</div>'+
|
||||
'<div class="bar-text-center">' + (estimated !== '' ? '[' + estimated + ']': '') + '</div>'+
|
||||
'<div class="bar-text-right">' + remaining + (remaining !== '' ? ' left' : '') + '</div>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
this.buildEstimated = function(group)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
if (group.post.StageProgress > 0)
|
||||
{
|
||||
return Util.formatTimeHMS(group.post.StageTimeSec / group.post.StageProgress * (1000 - group.post.StageProgress));
|
||||
}
|
||||
}
|
||||
else if (!group.paused && Status.status.DownloadRate > 0)
|
||||
{
|
||||
return Util.formatTimeLeft((group.RemainingSizeMB-group.PausedSizeMB)*1024/(Status.status.DownloadRate/1024));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
this.buildProgressLabel = function(group)
|
||||
{
|
||||
var text = '';
|
||||
if (group.postprocess && !Status.status.PostPaused && group.post.Stage !== 'REPAIRING')
|
||||
{
|
||||
if (group.post.Log && group.post.Log.length > 0)
|
||||
{
|
||||
text = group.post.Log[group.post.Log.length-1].Text;
|
||||
}
|
||||
else if (group.post.ProgressLabel !== '')
|
||||
{
|
||||
text = group.post.ProgressLabel;
|
||||
}
|
||||
}
|
||||
|
||||
return text !== '' ? ' <span class="label label-success">' + text + '</span>' : '';
|
||||
}
|
||||
|
||||
this.buildPriorityText = function(priority)
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case 0: return '';
|
||||
case 100: return 'very high priority';
|
||||
case 50: return 'high priority';
|
||||
case -50: return 'low priority';
|
||||
case -100: return 'very low priority';
|
||||
default: return 'priority: ' + priority;
|
||||
}
|
||||
}
|
||||
|
||||
this.buildPriority = function(priority)
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case 0: return '';
|
||||
case 100: return ' <span class="label label-priority label-important">very high priority</span>';
|
||||
case 50: return ' <span class="label label-priority label-important">high priority</span>';
|
||||
case -50: return ' <span class="label label-priority label-info">low priority</span>';
|
||||
case -100: return ' <span class="label label-priority label-info">very low priority</span>';
|
||||
}
|
||||
if (priority > 0)
|
||||
{
|
||||
return ' <span class="label label-priority label-important">priority: ' + priority + '</span>';
|
||||
}
|
||||
else if (priority < 0)
|
||||
{
|
||||
return ' <span class="label label-priority label-info">priority: ' + priority + '</span>';
|
||||
}
|
||||
}
|
||||
}(jQuery));
|
||||
1048
webui/edit.js
Normal file
787
webui/fasttable.js
Normal file
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Some code was borrowed from:
|
||||
* 1. Greg Weber's uiTableFilter jQuery plugin (http://gregweber.info/projects/uitablefilter)
|
||||
* 2. Denny Ferrassoli & Charles Christolini's TypeWatch jQuery plugin (http://github.com/dennyferra/TypeWatch)
|
||||
* 3. Justin Britten's tablesorterFilter jQuery plugin (http://www.justinbritten.com/work/2008/08/tablesorter-filter-results-based-on-search-string/)
|
||||
* 4. Allan Jardine's Bootstrap Pagination jQuery plugin for DataTables (http://datatables.net/)
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* HTML tables with:
|
||||
* 1) very fast content updates;
|
||||
* 2) automatic pagination;
|
||||
* 3) search/filtering.
|
||||
*
|
||||
* What makes it unique and fast?
|
||||
* The tables are designed to be updated very often (up to 10 times per second). This has two challenges:
|
||||
* 1) updating of whole content is slow because the DOM updates are slow.
|
||||
* 2) if the DOM is updated during user interaction the user input is not processed correctly.
|
||||
* For example if the table is updated after the user pressed mouse key but before he/she released
|
||||
* the key, the click is not processed because the element, on which the click was performed,
|
||||
* doesn't exist after the update of DOM anymore.
|
||||
*
|
||||
* How Fasttable solves these problems? The solutions is to update only rows and cells,
|
||||
* which were changed by keeping the unchanged DOM-elements.
|
||||
*
|
||||
* Important: the UI of table must be designed in a way, that the cells which are frequently changed
|
||||
* (like remaining download size) should not be clickable, whereas the cells which are rarely changed
|
||||
* (e. g. Download name) can be clickable.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.fn.fasttable = function(method)
|
||||
{
|
||||
if (methods[method])
|
||||
{
|
||||
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
|
||||
}
|
||||
else if ( typeof method === 'object' || ! method )
|
||||
{
|
||||
return methods.init.apply( this, arguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
$.error( 'Method ' + method + ' does not exist on jQuery.fasttable' );
|
||||
}
|
||||
};
|
||||
|
||||
var methods =
|
||||
{
|
||||
init : function(options)
|
||||
{
|
||||
return this.each(function()
|
||||
{
|
||||
var $this = $(this);
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
// If the plugin hasn't been initialized yet
|
||||
if (!data)
|
||||
{
|
||||
/*
|
||||
Do more setup stuff here
|
||||
*/
|
||||
|
||||
var config = {};
|
||||
config = $.extend(config, defaults, options);
|
||||
|
||||
config.filterInput = $(config.filterInput);
|
||||
config.filterClearButton = $(config.filterClearButton);
|
||||
config.pagerContainer = $(config.pagerContainer);
|
||||
config.infoContainer = $(config.infoContainer);
|
||||
config.headerCheck = $(config.headerCheck);
|
||||
|
||||
// Create a timer which gets reset upon every keyup event.
|
||||
// Perform filter only when the timer's wait is reached (user finished typing or paused long enough to elapse the timer).
|
||||
// Do not perform the filter is the query has not changed.
|
||||
// Immediately perform the filter if the ENTER key is pressed.
|
||||
|
||||
var timer;
|
||||
|
||||
config.filterInput.keyup(function()
|
||||
{
|
||||
var timerWait = 500;
|
||||
var overrideBool = false;
|
||||
var inputBox = this;
|
||||
|
||||
// Was ENTER pushed?
|
||||
if (inputBox.keyCode == 13)
|
||||
{
|
||||
timerWait = 1;
|
||||
overrideBool = true;
|
||||
}
|
||||
|
||||
var timerCallback = function()
|
||||
{
|
||||
var value = inputBox.value;
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
if ((value != data.lastFilter) || (overrideBool))
|
||||
{
|
||||
data.lastFilter = value;
|
||||
if (data.content)
|
||||
{
|
||||
data.curPage = 1;
|
||||
refresh(data);
|
||||
}
|
||||
if (data.config.filterInputCallback)
|
||||
{
|
||||
data.config.filterInputCallback(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Reset the timer
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(timerCallback, timerWait);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
config.filterClearButton.click(function()
|
||||
{
|
||||
var data = $this.data('fasttable');
|
||||
data.lastFilter = '';
|
||||
data.config.filterInput.val('');
|
||||
if (data.content)
|
||||
{
|
||||
refresh(data);
|
||||
}
|
||||
if (data.config.filterClearCallback)
|
||||
{
|
||||
data.config.filterClearCallback();
|
||||
}
|
||||
});
|
||||
|
||||
config.pagerContainer.on('click', 'li', function (e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var data = $this.data('fasttable');
|
||||
var pageNum = $(this).text();
|
||||
if (pageNum.indexOf('Prev') > -1)
|
||||
{
|
||||
data.curPage--;
|
||||
}
|
||||
else if (pageNum.indexOf('Next') > -1)
|
||||
{
|
||||
data.curPage++;
|
||||
}
|
||||
else if (isNaN(parseInt(pageNum)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.curPage = parseInt(pageNum);
|
||||
}
|
||||
refresh(data);
|
||||
});
|
||||
|
||||
$this.data('fasttable', {
|
||||
target : $this,
|
||||
config : config,
|
||||
pageSize : parseInt(config.pageSize),
|
||||
maxPages : parseInt(config.maxPages),
|
||||
pageDots : Util.parseBool(config.pageDots),
|
||||
curPage : 1,
|
||||
checkedRows: [],
|
||||
lastClickedRowID: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy : function()
|
||||
{
|
||||
return this.each(function()
|
||||
{
|
||||
var $this = $(this);
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
// Namespacing FTW
|
||||
$(window).unbind('.fasttable');
|
||||
$this.removeData('fasttable');
|
||||
});
|
||||
},
|
||||
|
||||
update : updateContent,
|
||||
|
||||
setPageSize : setPageSize,
|
||||
|
||||
setCurPage : setCurPage,
|
||||
|
||||
filteredContent : function()
|
||||
{
|
||||
return $(this).data('fasttable').filteredContent;
|
||||
},
|
||||
|
||||
checkedRows : function()
|
||||
{
|
||||
return $(this).data('fasttable').checkedRows;
|
||||
},
|
||||
|
||||
itemCheckClick : itemCheckClick,
|
||||
|
||||
titleCheckClick : titleCheckClick
|
||||
};
|
||||
|
||||
function has_words(str, words, caseSensitive)
|
||||
{
|
||||
var text = caseSensitive ? str : str.toLowerCase();
|
||||
|
||||
for (var i = 0; i < words.length; i++)
|
||||
{
|
||||
if (text.indexOf(words[i]) === -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateContent(content)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
if (content)
|
||||
{
|
||||
data.content = content;
|
||||
}
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function refresh(data)
|
||||
{
|
||||
refilter(data);
|
||||
validateChecks(data);
|
||||
updatePager(data);
|
||||
updateInfo(data);
|
||||
updateTable(data);
|
||||
}
|
||||
|
||||
function refilter(data)
|
||||
{
|
||||
var filterInput = data.config.filterInput;
|
||||
var phrase = filterInput.length > 0 ? filterInput.val() : '';
|
||||
var caseSensitive = data.config.filterCaseSensitive;
|
||||
var words = caseSensitive ? phrase.split(' ') : phrase.toLowerCase().split(' ');
|
||||
|
||||
if (words.length === 1 && words[0] === '')
|
||||
{
|
||||
data.filteredContent = data.content;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.filteredContent = [];
|
||||
for (var i = 0; i < data.content.length; i++)
|
||||
{
|
||||
var item = data.content[i];
|
||||
if (item.search === undefined && data.config.fillSearchCallback)
|
||||
{
|
||||
data.config.fillSearchCallback(item);
|
||||
}
|
||||
|
||||
if (has_words(item.search, words, caseSensitive))
|
||||
{
|
||||
data.filteredContent.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTable(data)
|
||||
{
|
||||
var oldTable = data.target[0];
|
||||
var newTable = buildTBody(data);
|
||||
updateTBody(data, oldTable, newTable);
|
||||
}
|
||||
|
||||
function buildTBody(data)
|
||||
{
|
||||
var table = $('<table><tbody></tbody></table>')[0];
|
||||
for (var i=0; i < data.pageContent.length; i++)
|
||||
{
|
||||
var item = data.pageContent[i];
|
||||
|
||||
var row = table.insertRow(table.rows.length);
|
||||
|
||||
row.fasttableID = item.id;
|
||||
if (data.checkedRows.indexOf(item.id) > -1)
|
||||
{
|
||||
row.className = 'checked';
|
||||
}
|
||||
if (data.config.renderRowCallback)
|
||||
{
|
||||
data.config.renderRowCallback(row, item);
|
||||
}
|
||||
|
||||
if (!item.fields)
|
||||
{
|
||||
if (data.config.fillFieldsCallback)
|
||||
{
|
||||
data.config.fillFieldsCallback(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.fields = [];
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=0; j < item.fields.length; j++)
|
||||
{
|
||||
var cell = row.insertCell(row.cells.length);
|
||||
cell.innerHTML = item.fields[j];
|
||||
if (data.config.renderCellCallback)
|
||||
{
|
||||
data.config.renderCellCallback(cell, j, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
titleCheckRedraw(data);
|
||||
|
||||
if (data.config.renderTableCallback)
|
||||
{
|
||||
data.config.renderTableCallback(table);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
function updateTBody(data, oldTable, newTable)
|
||||
{
|
||||
var oldTRs = oldTable.rows;
|
||||
var newTRs = newTable.rows;
|
||||
var oldTBody = $('tbody', oldTable)[0];
|
||||
var oldTRsLength = oldTRs.length - (data.config.hasHeader ? 1 : 0); // evlt. skip header row
|
||||
var newTRsLength = newTRs.length;
|
||||
|
||||
for (var i=0; i < newTRs.length; )
|
||||
{
|
||||
var newTR = newTRs[i];
|
||||
|
||||
if (i < oldTRsLength)
|
||||
{
|
||||
// update existing row
|
||||
var oldTR = oldTRs[i + (data.config.hasHeader ? 1 : 0)]; // evlt. skip header row
|
||||
var oldTDs = oldTR.cells;
|
||||
var newTDs = newTR.cells;
|
||||
|
||||
oldTR.className = newTR.className;
|
||||
oldTR.fasttableID = newTR.fasttableID;
|
||||
|
||||
for (var j=0, n = 0; j < oldTDs.length; j++, n++)
|
||||
{
|
||||
var oldTD = oldTDs[j];
|
||||
var newTD = newTDs[n];
|
||||
var oldHtml = oldTD.outerHTML;
|
||||
var newHtml = newTD.outerHTML;
|
||||
if (oldHtml !== newHtml)
|
||||
{
|
||||
oldTR.replaceChild(newTD, oldTD);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new row
|
||||
oldTBody.appendChild(newTR);
|
||||
}
|
||||
}
|
||||
|
||||
var maxTRs = newTRsLength + (data.config.hasHeader ? 1 : 0); // evlt. skip header row;
|
||||
while (oldTRs.length > maxTRs)
|
||||
{
|
||||
oldTable.deleteRow(oldTRs.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function updatePager(data)
|
||||
{
|
||||
data.pageCount = Math.ceil(data.filteredContent.length / data.pageSize);
|
||||
if (data.curPage < 1)
|
||||
{
|
||||
data.curPage = 1;
|
||||
}
|
||||
if (data.curPage > data.pageCount)
|
||||
{
|
||||
data.curPage = data.pageCount;
|
||||
}
|
||||
|
||||
var startIndex = (data.curPage - 1) * data.pageSize;
|
||||
data.pageContent = data.filteredContent.slice(startIndex, startIndex + data.pageSize);
|
||||
|
||||
var pagerObj = data.config.pagerContainer;
|
||||
var pagerHtml = buildPagerHtml(data);
|
||||
|
||||
var oldPager = pagerObj[0];
|
||||
var newPager = $(pagerHtml)[0];
|
||||
|
||||
updatePagerContent(data, oldPager, newPager);
|
||||
}
|
||||
|
||||
function buildPagerHtml(data)
|
||||
{
|
||||
var iListLength = data.maxPages;
|
||||
var iStart, iEnd, iHalf = Math.floor(iListLength/2);
|
||||
|
||||
if (data.pageCount < iListLength)
|
||||
{
|
||||
iStart = 1;
|
||||
iEnd = data.pageCount;
|
||||
}
|
||||
else if (data.curPage -1 <= iHalf)
|
||||
{
|
||||
iStart = 1;
|
||||
iEnd = iListLength;
|
||||
}
|
||||
else if (data.curPage - 1 >= (data.pageCount-iHalf))
|
||||
{
|
||||
iStart = data.pageCount - iListLength + 1;
|
||||
iEnd = data.pageCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = data.curPage - 1 - iHalf + 1;
|
||||
iEnd = iStart + iListLength - 1;
|
||||
}
|
||||
|
||||
var pager = '<ul>';
|
||||
pager += '<li' + (data.curPage === 1 || data.curPage === 0 ? ' class="disabled"' : '') + '><a href="#">← Prev</a></li>';
|
||||
|
||||
if (iStart > 1)
|
||||
{
|
||||
pager += '<li><a href="#">1</a></li>';
|
||||
if (iStart > 2 && data.pageDots)
|
||||
{
|
||||
pager += '<li class="disabled"><a href="#">…</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=iStart; j<=iEnd; j++)
|
||||
{
|
||||
pager += '<li' + ((j===data.curPage) ? ' class="active"' : '') + '><a href="#">' + j + '</a></li>';
|
||||
}
|
||||
|
||||
if (iEnd != data.pageCount)
|
||||
{
|
||||
if (iEnd < data.pageCount - 1 && data.pageDots)
|
||||
{
|
||||
pager += '<li class="disabled"><a href="#">…</a></li>';
|
||||
}
|
||||
pager += '<li><a href="#">' + data.pageCount + '</a></li>';
|
||||
}
|
||||
|
||||
pager += '<li' + (data.curPage === data.pageCount || data.pageCount === 0 ? ' class="disabled"' : '') + '><a href="#">Next →</a></li>';
|
||||
pager += '</ul>';
|
||||
|
||||
return pager;
|
||||
}
|
||||
|
||||
function updatePagerContent(data, oldPager, newPager)
|
||||
{
|
||||
var oldLIs = oldPager.getElementsByTagName('li');
|
||||
var newLIs = newPager.getElementsByTagName('li');
|
||||
|
||||
var oldLIsLength = oldLIs.length;
|
||||
var newLIsLength = newLIs.length;
|
||||
|
||||
for (var i=0, n=0; i < newLIs.length; i++, n++)
|
||||
{
|
||||
var newLI = newLIs[i];
|
||||
|
||||
if (n < oldLIsLength)
|
||||
{
|
||||
// update existing LI
|
||||
var oldLI = oldLIs[n];
|
||||
|
||||
var oldHtml = oldLI.outerHTML;
|
||||
var newHtml = newLI.outerHTML;
|
||||
if (oldHtml !== newHtml)
|
||||
{
|
||||
oldPager.replaceChild(newLI, oldLI);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new LI
|
||||
oldPager.appendChild(newLI);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
while (oldLIs.length > newLIsLength)
|
||||
{
|
||||
oldPager.removeChild(oldPager.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function updateInfo(data)
|
||||
{
|
||||
if (data.content.length === 0)
|
||||
{
|
||||
var infoText = data.config.infoEmpty;
|
||||
}
|
||||
else if (data.curPage === 0)
|
||||
{
|
||||
var infoText = 'No matching records found (total ' + data.content.length + ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstRecord = (data.curPage - 1) * data.pageSize + 1;
|
||||
var lastRecord = firstRecord + data.pageContent.length - 1;
|
||||
var infoText = 'Showing records ' + firstRecord + '-' + lastRecord + ' from ' + data.filteredContent.length;
|
||||
if (data.filteredContent != data.content)
|
||||
{
|
||||
infoText += ' filtered (total ' + data.content.length + ')';
|
||||
}
|
||||
}
|
||||
data.config.infoContainer.html(infoText);
|
||||
|
||||
if (data.config.updateInfoCallback)
|
||||
{
|
||||
data.config.updateInfoCallback({
|
||||
total: data.content.length,
|
||||
available: data.filteredContent.length,
|
||||
filter: data.filteredContent != data.content,
|
||||
firstRecord: firstRecord,
|
||||
lastRecord: lastRecord
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setPageSize(pageSize, maxPages, pageDots)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
data.pageSize = parseInt(pageSize);
|
||||
data.curPage = 1;
|
||||
if (maxPages !== undefined)
|
||||
{
|
||||
data.maxPages = maxPages;
|
||||
}
|
||||
if (pageDots !== undefined)
|
||||
{
|
||||
data.pageDots = pageDots;
|
||||
}
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function setCurPage(page)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
data.curPage = parseInt(page);
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function titleCheckRedraw(data)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var hasSelectedItems = false;
|
||||
var hasUnselectedItems = false;
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
if (checkedRows.indexOf(filteredContent[i].id) === -1)
|
||||
{
|
||||
hasUnselectedItems = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasSelectedItems = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSelectedItems && hasUnselectedItems)
|
||||
{
|
||||
data.config.headerCheck.removeClass('checked').addClass('checkremove');
|
||||
}
|
||||
else if (hasSelectedItems)
|
||||
{
|
||||
data.config.headerCheck.removeClass('checkremove').addClass('checked');
|
||||
}
|
||||
else
|
||||
{
|
||||
data.config.headerCheck.removeClass('checked').removeClass('checkremove');
|
||||
}
|
||||
}
|
||||
|
||||
function itemCheckClick(row, event)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var id = row.fasttableID;
|
||||
var doToggle = true;
|
||||
|
||||
if (event.shiftKey && data.lastClickedRowID > 0)
|
||||
{
|
||||
var checked = checkedRows.indexOf(id) > -1;
|
||||
doToggle = !checkRange(data, id, data.lastClickedRowID, !checked);
|
||||
}
|
||||
|
||||
if (doToggle)
|
||||
{
|
||||
toggleCheck(data, id);
|
||||
}
|
||||
|
||||
data.lastClickedRowID = id;
|
||||
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function titleCheckClick()
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var hasSelectedItems = false;
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
if (checkedRows.indexOf(filteredContent[i].id) > -1)
|
||||
{
|
||||
hasSelectedItems = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.lastClickedRowID = 0;
|
||||
checkAll(data, !hasSelectedItems);
|
||||
}
|
||||
|
||||
function toggleCheck(data, id)
|
||||
{
|
||||
var checkedRows = data.checkedRows;
|
||||
var index = checkedRows.indexOf(id);
|
||||
if (index > -1)
|
||||
{
|
||||
checkedRows.splice(index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkedRows.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAll(data, checked)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
checkRow(data, filteredContent[i].id, checked);
|
||||
}
|
||||
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function checkRange(data, from, to, checked)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var indexFrom = indexOfID(filteredContent, from);
|
||||
var indexTo = indexOfID(filteredContent, to);
|
||||
if (indexFrom === -1 || indexTo === -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (indexTo < indexFrom)
|
||||
{
|
||||
var tmp = indexTo; indexTo = indexFrom; indexFrom = tmp;
|
||||
}
|
||||
|
||||
for (var i = indexFrom; i <= indexTo; i++)
|
||||
{
|
||||
checkRow(data, filteredContent[i].id, checked);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkRow(data, id, checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
if (data.checkedRows.indexOf(id) === -1)
|
||||
{
|
||||
data.checkedRows.push(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = data.checkedRows.indexOf(id);
|
||||
if (index > -1)
|
||||
{
|
||||
data.checkedRows.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indexOfID(content, id)
|
||||
{
|
||||
for (var i = 0; i < content.length; i++)
|
||||
{
|
||||
if (id === content[i].id)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function validateChecks(data)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var ids = [];
|
||||
for (var i = 0; i < data.content.length; i++)
|
||||
{
|
||||
ids.push(data.content[i].id);
|
||||
}
|
||||
|
||||
for (var i = 0; i < checkedRows.length; i++)
|
||||
{
|
||||
if (ids.indexOf(checkedRows[i]) === -1)
|
||||
{
|
||||
checkedRows.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var defaults =
|
||||
{
|
||||
filterInput: '#table-filter',
|
||||
filterClearButton: '#table-clear',
|
||||
filterCaseSensitive: false,
|
||||
pagerContainer: '#table-pager',
|
||||
infoContainer: '#table-info',
|
||||
pageSize: 10,
|
||||
maxPages: 5,
|
||||
pageDots: true,
|
||||
hasHeader: true,
|
||||
infoEmpty: 'No records',
|
||||
renderRowCallback: undefined,
|
||||
renderCellCallback: undefined,
|
||||
renderTableCallback: undefined,
|
||||
fillFieldsCallback: undefined,
|
||||
updateInfoCallback: undefined,
|
||||
filterInputCallback: undefined,
|
||||
filterClearCallback: undefined,
|
||||
fillSearchCallback: undefined,
|
||||
headerCheck: '#table-header-check'
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
430
webui/history.js
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) History tab;
|
||||
* 2) History edit dialog.
|
||||
*/
|
||||
|
||||
/*** HISTORY TAB AND EDIT HISTORY DIALOG **********************************************/
|
||||
|
||||
var History = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $HistoryTable;
|
||||
var $HistoryTabBadge;
|
||||
var $HistoryTabBadgeEmpty;
|
||||
var $HistoryRecordsPerPage;
|
||||
|
||||
// State
|
||||
var history;
|
||||
var notification = null;
|
||||
var updateTabInfo;
|
||||
|
||||
this.init = function(options)
|
||||
{
|
||||
updateTabInfo = options.updateTabInfo;
|
||||
|
||||
$HistoryTable = $('#HistoryTable');
|
||||
$HistoryTabBadge = $('#HistoryTabBadge');
|
||||
$HistoryTabBadgeEmpty = $('#HistoryTabBadgeEmpty');
|
||||
$HistoryRecordsPerPage = $('#HistoryRecordsPerPage');
|
||||
|
||||
historyEditDialog.init();
|
||||
|
||||
var recordsPerPage = UISettings.read('HistoryRecordsPerPage', 10);
|
||||
$HistoryRecordsPerPage.val(recordsPerPage);
|
||||
|
||||
$HistoryTable.fasttable(
|
||||
{
|
||||
filterInput: $('#HistoryTable_filter'),
|
||||
filterClearButton: $("#HistoryTable_clearfilter"),
|
||||
pagerContainer: $('#HistoryTable_pager'),
|
||||
infoContainer: $('#HistoryTable_info'),
|
||||
headerCheck: $('#HistoryTable > thead > tr:first-child'),
|
||||
filterCaseSensitive: false,
|
||||
pageSize: recordsPerPage,
|
||||
maxPages: UISettings.miniTheme ? 1 : 5,
|
||||
pageDots: !UISettings.miniTheme,
|
||||
fillFieldsCallback: fillFieldsCallback,
|
||||
renderCellCallback: renderCellCallback,
|
||||
updateInfoCallback: updateInfo
|
||||
});
|
||||
|
||||
$HistoryTable.on('click', 'a', editClick);
|
||||
$HistoryTable.on('click', 'tbody div.check',
|
||||
function(event) { $HistoryTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
|
||||
$HistoryTable.on('click', 'thead div.check',
|
||||
function() { $HistoryTable.fasttable('titleCheckClick') });
|
||||
$HistoryTable.on('mousedown', Util.disableShiftMouseDown);
|
||||
}
|
||||
|
||||
this.applyTheme = function()
|
||||
{
|
||||
$HistoryTable.fasttable('setPageSize', UISettings.read('HistoryRecordsPerPage', 10),
|
||||
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
RPC.call('history', [], loaded);
|
||||
}
|
||||
|
||||
function loaded(curHistory)
|
||||
{
|
||||
history = curHistory;
|
||||
prepare();
|
||||
RPC.next();
|
||||
}
|
||||
|
||||
function prepare()
|
||||
{
|
||||
for (var j=0, jl=history.length; j < jl; j++)
|
||||
{
|
||||
detectStatus(history[j]);
|
||||
}
|
||||
}
|
||||
|
||||
function detectStatus(hist)
|
||||
{
|
||||
if (hist.Kind === 'NZB')
|
||||
{
|
||||
switch (hist.ScriptStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'UNKNOWN': hist.status = 'unknown'; break;
|
||||
case 'NONE':
|
||||
switch (hist.ParStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'NONE': hist.status = 'none'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hist.Kind === 'URL')
|
||||
{
|
||||
switch (hist.UrlStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'UNKNOWN': hist.status = 'unknown'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i < history.length; i++)
|
||||
{
|
||||
var hist = history[i];
|
||||
|
||||
var kind = hist.Kind;
|
||||
var statustext = hist.status === 'none' ? 'unknown' : hist.status;
|
||||
var size = kind === 'NZB' ? Util.formatSizeMB(hist.FileSizeMB) : '';
|
||||
|
||||
var textname = hist.Name;
|
||||
if (kind === 'URL')
|
||||
{
|
||||
textname += ' URL';
|
||||
}
|
||||
|
||||
var time = Util.formatDateTime(hist.HistoryTime + UISettings.timeZoneCorrection*60*60);
|
||||
|
||||
var item =
|
||||
{
|
||||
id: hist.ID,
|
||||
hist: hist,
|
||||
data: {time: time, size: size},
|
||||
search: statustext + ' ' + time + ' ' + textname + ' ' + hist.Category + ' ' + size
|
||||
};
|
||||
|
||||
data.push(item);
|
||||
}
|
||||
|
||||
$HistoryTable.fasttable('update', data);
|
||||
|
||||
Util.show($HistoryTabBadge, history.length > 0);
|
||||
Util.show($HistoryTabBadgeEmpty, history.length === 0 && UISettings.miniTheme);
|
||||
}
|
||||
|
||||
function fillFieldsCallback(item)
|
||||
{
|
||||
var hist = item.hist;
|
||||
|
||||
var status = buildStatus(hist);
|
||||
|
||||
var name = '<a href="#" histid="' + hist.ID + '">' + Util.textToHtml(Util.formatNZBName(hist.Name)) + '</a>';
|
||||
var category = Util.textToHtml(hist.Category);
|
||||
|
||||
if (hist.Kind === 'URL')
|
||||
{
|
||||
name += ' <span class="label label-info">URL</span>';
|
||||
}
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
item.fields = ['<div class="check img-check"></div>', status, item.data.time, name, category, item.data.size];
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' +
|
||||
' ' + status + ' <span class="label">' + item.data.time + '</span>';
|
||||
if (category)
|
||||
{
|
||||
info += ' <span class="label label-status">' + category + '</span>';
|
||||
}
|
||||
if (hist.Kind === 'NZB')
|
||||
{
|
||||
info += ' <span class="label">' + item.data.size + '</span>';
|
||||
}
|
||||
item.fields = [info];
|
||||
}
|
||||
}
|
||||
|
||||
function renderCellCallback(cell, index, item)
|
||||
{
|
||||
if (index === 2)
|
||||
{
|
||||
cell.className = 'text-center';
|
||||
}
|
||||
else if (index === 5)
|
||||
{
|
||||
cell.className = 'text-right';
|
||||
}
|
||||
}
|
||||
|
||||
function buildStatus(hist)
|
||||
{
|
||||
switch (hist.status)
|
||||
{
|
||||
case 'success': return '<span class="label label-status label-success">success</span>';
|
||||
case 'failure': return '<span class="label label-status label-important">failure</span>';
|
||||
case 'unknown': return '<span class="label label-status label-info">unknown</span>';
|
||||
case 'repairable': return '<span class="label label-status label-success">repairable</span>';
|
||||
case 'none': return '<span class="label label-status">unknown</span>';
|
||||
default: return '<span class="label label-status label-important">internal error(' + hist.status + ')</span>';
|
||||
}
|
||||
}
|
||||
|
||||
this.recordsPerPageChange = function()
|
||||
{
|
||||
var val = $HistoryRecordsPerPage.val();
|
||||
UISettings.write('HistoryRecordsPerPage', val);
|
||||
$HistoryTable.fasttable('setPageSize', val);
|
||||
}
|
||||
|
||||
function updateInfo(stat)
|
||||
{
|
||||
updateTabInfo($HistoryTabBadge, stat);
|
||||
}
|
||||
|
||||
this.deleteClick = function()
|
||||
{
|
||||
if (history.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var checkedRows = $HistoryTable.fasttable('checkedRows');
|
||||
if (checkedRows.length > 0)
|
||||
{
|
||||
ConfirmDialog.showModal('HistoryDeleteConfirmDialog', historyDelete);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfirmDialog.showModal('HistoryClearConfirmDialog', historyClear);
|
||||
}
|
||||
}
|
||||
|
||||
function historyDelete()
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
var IDs = $HistoryTable.fasttable('checkedRows');
|
||||
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [IDs]], function()
|
||||
{
|
||||
notification = '#Notif_History_Deleted';
|
||||
editCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
function historyClear()
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
var IDs = [];
|
||||
for (var i=0; i<history.length; i++)
|
||||
{
|
||||
IDs.push(history[i].ID);
|
||||
}
|
||||
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [IDs]], function()
|
||||
{
|
||||
notification = '#Notif_History_Cleared';
|
||||
editCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
function editCompleted()
|
||||
{
|
||||
Refresher.update();
|
||||
if (notification)
|
||||
{
|
||||
Notification.show(notification);
|
||||
notification = null;
|
||||
}
|
||||
}
|
||||
|
||||
function editClick()
|
||||
{
|
||||
var histid = $(this).attr('histid');
|
||||
$(this).blur();
|
||||
historyEditDialog.showModal(histid);
|
||||
}
|
||||
|
||||
/*** EDIT HISTORY DIALOG *************************************************************************/
|
||||
|
||||
var historyEditDialog = new function()
|
||||
{
|
||||
// Controls
|
||||
var $HistoryEditDialog;
|
||||
|
||||
// State
|
||||
var curHist;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
$HistoryEditDialog = $('#HistoryEditDialog');
|
||||
|
||||
$('#HistoryEdit_Delete').click(itemDelete);
|
||||
$('#HistoryEdit_Return').click(itemReturn);
|
||||
$('#HistoryEdit_Reprocess').click(itemReprocess);
|
||||
|
||||
$HistoryEditDialog.on('hidden', function () {
|
||||
Refresher.resume();
|
||||
});
|
||||
}
|
||||
|
||||
this.showModal = function(histid)
|
||||
{
|
||||
var hist = null;
|
||||
|
||||
// find history object
|
||||
for (var i=0; i<history.length; i++)
|
||||
{
|
||||
var gr = history[i];
|
||||
if (gr.ID == histid)
|
||||
{
|
||||
hist = gr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hist == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Refresher.pause();
|
||||
|
||||
curHist = hist;
|
||||
|
||||
var status = buildStatus(hist);
|
||||
|
||||
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
|
||||
if (hist.Kind === 'URL')
|
||||
{
|
||||
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + ' ' + '<span class="label label-info">URL</span>');
|
||||
}
|
||||
|
||||
$('#HistoryEdit_Status').html(status);
|
||||
$('#HistoryEdit_Category').text(hist.Category !== '' ? hist.Category : '<empty>');
|
||||
$('#HistoryEdit_Path').text(hist.DestDir);
|
||||
|
||||
var size = Util.formatSizeMB(hist.FileSizeMB, hist.FileSizeLo);
|
||||
|
||||
var table = '';
|
||||
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
|
||||
table += '<tr><td>Files (total/parked)</td><td class="text-right">' + hist.FileCount + '/' + hist.RemainingFileCount + '</td></tr>';
|
||||
$('#HistoryEdit_Statistics').html(table);
|
||||
|
||||
Util.show($('#HistoryEdit_ReturnGroup'), hist.RemainingFileCount > 0 || hist.Kind === 'URL');
|
||||
Util.show($('#HistoryEdit_PathGroup, #HistoryEdit_StatisticsGroup, #HistoryEdit_ReprocessGroup'), hist.Kind === 'NZB');
|
||||
|
||||
enableAllButtons();
|
||||
|
||||
$HistoryEditDialog.modal({backdrop: 'static'});
|
||||
}
|
||||
|
||||
function disableAllButtons()
|
||||
{
|
||||
$('#HistoryEditDialog .modal-footer .btn').attr('disabled', 'disabled');
|
||||
setTimeout(function()
|
||||
{
|
||||
$('#HistoryEdit_Transmit').show();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function enableAllButtons()
|
||||
{
|
||||
$('#HistoryEditDialog .modal-footer .btn').removeAttr('disabled');
|
||||
$('#HistoryEdit_Transmit').hide();
|
||||
}
|
||||
|
||||
function itemDelete()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Deleted';
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function itemReturn()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Returned';
|
||||
RPC.call('editqueue', ['HistoryReturn', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function itemReprocess()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Reproces';
|
||||
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function completed()
|
||||
{
|
||||
$HistoryEditDialog.modal('hide');
|
||||
editCompleted();
|
||||
}
|
||||
}();
|
||||
}(jQuery));
|
||||
BIN
webui/img/download-anim-green-2x.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
webui/img/download-anim-orange-2x.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
webui/img/favicon.ico
Normal file
|
After Width: | Height: | Size: 198 B |
BIN
webui/img/icons-2x.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
webui/img/icons.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
webui/img/transmit-file.gif
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
webui/img/transmit-reload-2x.gif
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
webui/img/transmit.gif
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
1361
webui/index.html
Normal file
778
webui/index.js
Normal file
@@ -0,0 +1,778 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Web-interface intialization;
|
||||
* 2) Web-interface settings;
|
||||
* 3) RPC queue;
|
||||
* 4) Refresh handling;
|
||||
* 5) Window resize handling including automatic theme switching (desktop/phone);
|
||||
* 6) Confirmation dialog;
|
||||
* 7) Popup notifications.
|
||||
*/
|
||||
|
||||
/*** WEB-INTERFACE SETTINGS (THIS IS NOT NZBGET CONFIG!) ***********************************/
|
||||
|
||||
var UISettings = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
/*** Web-interface configuration (edit if neccessary) *************/
|
||||
|
||||
// Animation on refresh button.
|
||||
this.refreshAnimation = true;
|
||||
|
||||
// Animation on play/pause button.
|
||||
this.activityAnimation = true;
|
||||
|
||||
// Animation of tab changes in tabbed dialogs.
|
||||
this.slideAnimation = true;
|
||||
|
||||
// Automatically set focus to the first control in dialogs.
|
||||
// Not good on touch devices, because may pop up the on-screen-keyboard.
|
||||
this.setFocus = false;
|
||||
|
||||
// Show popup notifications.
|
||||
this.showNotifications = true;
|
||||
|
||||
// Time zone correction in hours.
|
||||
// You shouldn't require this unless you can't set the time zone on your computer/device properly.
|
||||
this.timeZoneCorrection = 0;
|
||||
|
||||
// Default refresh interval.
|
||||
// The choosen interval is saved in web-browser and then restored.
|
||||
// The default value sets the interval on first use only.
|
||||
this.refreshInterval = 1;
|
||||
|
||||
// URL for communication with NZBGet via JSON-RPC
|
||||
this.rpcUrl = './jsonrpc';
|
||||
|
||||
|
||||
/*** No user configurable settings below this line (do not edit) *************/
|
||||
|
||||
// Current state
|
||||
this.miniTheme = false;
|
||||
this.showEditButtons = true;
|
||||
this.connectionError = false;
|
||||
|
||||
this.load = function()
|
||||
{
|
||||
this.refreshInterval = parseFloat(this.read('RefreshInterval', this.refreshInterval));
|
||||
}
|
||||
|
||||
this.save = function()
|
||||
{
|
||||
this.write('RefreshInterval', this.refreshInterval);
|
||||
}
|
||||
|
||||
this.read = function(key, def)
|
||||
{
|
||||
var v = localStorage.getItem(key);
|
||||
if (v === null || v === '')
|
||||
{
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
this.write = function(key, value)
|
||||
{
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** START WEB-APPLICATION ***********************************************************/
|
||||
|
||||
$(document).ready(function()
|
||||
{
|
||||
Frontend.init();
|
||||
});
|
||||
|
||||
|
||||
/*** FRONTEND MAIN PAGE ***********************************************************/
|
||||
|
||||
var Frontend = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// State
|
||||
var initialized = false;
|
||||
var firstLoad = true;
|
||||
var mobileSafari = false;
|
||||
var scrollbarWidth = 0;
|
||||
var switchingTheme = false;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
window.onerror = error;
|
||||
|
||||
if (!checkBrowser())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$('#FirstUpdateInfo').show();
|
||||
|
||||
UISettings.load();
|
||||
RPCController.init();
|
||||
|
||||
initControls();
|
||||
switchTheme();
|
||||
windowResized();
|
||||
|
||||
Options.init();
|
||||
Status.init();
|
||||
Downloads.init({ updateTabInfo: updateTabInfo });
|
||||
DownloadsEditDialog.init();
|
||||
DownloadsMultiDialog.init();
|
||||
DownloadsMergeDialog.init();
|
||||
Messages.init({ updateTabInfo: updateTabInfo });
|
||||
History.init({ updateTabInfo: updateTabInfo });
|
||||
Upload.init();
|
||||
Config.init({ updateTabInfo: updateTabInfo });
|
||||
ConfirmDialog.init();
|
||||
|
||||
Refresher.init(RPCController.refresh);
|
||||
|
||||
$(window).resize(windowResized);
|
||||
|
||||
initialized = true;
|
||||
|
||||
Refresher.update();
|
||||
|
||||
// DEBUG: activate config tab
|
||||
//$('#DownloadsTab').removeClass('fade').removeClass('in');
|
||||
//$('#ConfigTabLink').tab('show');
|
||||
}
|
||||
|
||||
function initControls()
|
||||
{
|
||||
mobileSafari = $.browser.safari && navigator.userAgent.toLowerCase().match(/(iphone|ipod|ipad)/) != null;
|
||||
scrollbarWidth = calcScrollbarWidth();
|
||||
|
||||
var FadeMainTabs = !$.browser.opera;
|
||||
if (!FadeMainTabs)
|
||||
{
|
||||
$('#DownloadsTab').removeClass('fade').removeClass('in');
|
||||
}
|
||||
|
||||
$('#Navbar a[data-toggle="tab"]').on('show', beforeTabShow);
|
||||
$('#Navbar a[data-toggle="tab"]').on('shown', afterTabShow);
|
||||
setupSearch();
|
||||
|
||||
$(window).scroll(windowScrolled);
|
||||
}
|
||||
|
||||
function checkBrowser()
|
||||
{
|
||||
if ($.browser.msie && parseInt($.browser.version, 10) < 9)
|
||||
{
|
||||
$('#FirstUpdateInfo').hide();
|
||||
$('#UnsupportedBrowserIE8Alert').show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function error(message, source, lineno)
|
||||
{
|
||||
if (source == "")
|
||||
{
|
||||
// ignore false errors without source information (sometimes happen in Safari)
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#FirstUpdateInfo').hide();
|
||||
$('#ErrorAlert-title').text('Error in ' + source + ' (line ' + lineno + ')');
|
||||
$('#ErrorAlert-text').text(message);
|
||||
$('#ErrorAlert').show();
|
||||
|
||||
if (Refresher)
|
||||
{
|
||||
Refresher.pause();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this.loadCompleted = function()
|
||||
{
|
||||
Downloads.redraw();
|
||||
Status.redraw();
|
||||
Messages.redraw();
|
||||
History.redraw();
|
||||
|
||||
if (firstLoad)
|
||||
{
|
||||
$('#FirstUpdateInfo').hide();
|
||||
$('#Navbar').show();
|
||||
$('#MainTabContent').show();
|
||||
$('#version').text(Options.option('Version'));
|
||||
windowResized();
|
||||
firstLoad = false;
|
||||
}
|
||||
|
||||
Refresher.refreshCompleted();
|
||||
}
|
||||
|
||||
function beforeTabShow(e)
|
||||
{
|
||||
var tabname = $(e.target).attr('href');
|
||||
tabname = tabname.substr(1, tabname.length - 4);
|
||||
$('#SearchBlock .search-query, #SearchBlock .search-clear').hide();
|
||||
$('#' + tabname + 'Table_filter, #' + tabname + 'Table_clearfilter').show();
|
||||
}
|
||||
|
||||
function afterTabShow(e)
|
||||
{
|
||||
if ($(e.target).attr('href') !== '#ConfigTab')
|
||||
{
|
||||
Config.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
function setupSearch()
|
||||
{
|
||||
$('.navbar-search .search-query').on('focus', function()
|
||||
{
|
||||
$(this).next().removeClass('icon-remove-white').addClass('icon-remove');
|
||||
});
|
||||
|
||||
$('.navbar-search .search-query').on('blur', function()
|
||||
{
|
||||
$(this).next().removeClass('icon-remove').addClass('icon-remove-white');
|
||||
});
|
||||
|
||||
$('.navbar-search').show();
|
||||
beforeTabShow({target: $('#DownloadsTabLink')});
|
||||
}
|
||||
|
||||
function windowScrolled()
|
||||
{
|
||||
$('body').toggleClass('scrolled', $(window).scrollTop() > 0 && !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
function calcScrollbarWidth()
|
||||
{
|
||||
var div = $('<div style="width:50px;height:50px;overflow:hidden;position:absolute;top:-200px;left:-200px;"><div style="height:100px;"></div>');
|
||||
// Append our div, do our calculation and then remove it
|
||||
$('body').append(div);
|
||||
var w1 = $('div', div).innerWidth();
|
||||
div.css('overflow-y', 'scroll');
|
||||
var w2 = $('div', div).innerWidth();
|
||||
$(div).remove();
|
||||
return (w1 - w2);
|
||||
}
|
||||
|
||||
function windowResized()
|
||||
{
|
||||
var oldMiniTheme = UISettings.miniTheme;
|
||||
UISettings.miniTheme = $(window).width() < 560;
|
||||
if (oldMiniTheme !== UISettings.miniTheme)
|
||||
{
|
||||
switchTheme();
|
||||
}
|
||||
|
||||
resizeNavbar();
|
||||
|
||||
if (UISettings.miniTheme)
|
||||
{
|
||||
centerPopupMenu('#PlayMenu', true);
|
||||
centerPopupMenu('#RefreshMenu', true);
|
||||
}
|
||||
|
||||
centerCenterDialogs();
|
||||
}
|
||||
|
||||
function centerPopupMenu(menu, center)
|
||||
{
|
||||
var $elem = $(menu);
|
||||
if (center)
|
||||
{
|
||||
$elem.removeClass('pull-right');
|
||||
var top = ($(window).height() - $elem.outerHeight())/2;
|
||||
top = top > 0 ? top : 0;
|
||||
var off = $elem.parent().offset();
|
||||
top -= off.top;
|
||||
var left = ($(window).width() - $elem.outerWidth())/2;
|
||||
left -= off.left;
|
||||
$elem.css({
|
||||
left: left,
|
||||
top: top,
|
||||
right: 'inherit'
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
$elem.css({
|
||||
left: '',
|
||||
top: '',
|
||||
right: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function centerCenterDialogs()
|
||||
{
|
||||
$.each($('.modal-center'), function(index, element) {
|
||||
Util.centerDialog(element, true);
|
||||
});
|
||||
}
|
||||
|
||||
function resizeNavbar()
|
||||
{
|
||||
var ScrollDelta = scrollbarWidth;
|
||||
if ($(document).height() > $(window).height())
|
||||
{
|
||||
// scrollbar is already visible, not need to acount on it
|
||||
ScrollDelta = 0;
|
||||
}
|
||||
|
||||
if (UISettings.miniTheme)
|
||||
{
|
||||
var w = $('#NavbarContainer').width() - $('#RefreshBlockPhone').outerWidth() - ScrollDelta;
|
||||
var $btns = $('#Navbar ul.nav > li');
|
||||
var buttonWidth = w / $btns.length;
|
||||
$btns.css('min-width', buttonWidth + 'px');
|
||||
$('#NavLinks').css('margin-left', 0);
|
||||
$('body').toggleClass('navfixed', false);
|
||||
}
|
||||
else
|
||||
{
|
||||
var InfoBlockMargin = 10;
|
||||
var w = $('#SearchBlock').position().left - $('#InfoBlock').position().left - $('#InfoBlock').width() - InfoBlockMargin * 2 - ScrollDelta;
|
||||
var n = $('#NavLinks').width();
|
||||
var offset = (w - n) / 2;
|
||||
var fixed = true;
|
||||
if (offset < 0)
|
||||
{
|
||||
w = $('#NavbarContainer').width() - ScrollDelta;
|
||||
offset = (w - n) / 2;
|
||||
fixed = false;
|
||||
}
|
||||
offset = offset > 0 ? offset : 0;
|
||||
$('#NavLinks').css('margin-left', offset);
|
||||
|
||||
// as of Aug 2012 Mobile Safari does not support "position:fixed"
|
||||
$('body').toggleClass('navfixed', fixed && !mobileSafari);
|
||||
|
||||
if (switchingTheme)
|
||||
{
|
||||
$('#Navbar ul.nav > li').css('min-width', '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTabInfo(control, stat)
|
||||
{
|
||||
if (stat.filter)
|
||||
{
|
||||
control.removeClass('badge-info').addClass('badge-warning');
|
||||
}
|
||||
else
|
||||
{
|
||||
control.removeClass('badge-warning').addClass('badge-info');
|
||||
}
|
||||
|
||||
control.html(stat.available);
|
||||
control.toggleClass('badge2', stat.total > 9);
|
||||
control.toggleClass('badge3', stat.total > 99);
|
||||
|
||||
if (control.lastOuterWidth !== control.outerWidth())
|
||||
{
|
||||
resizeNavbar();
|
||||
control.lastOuterWidth = control.outerWidth();
|
||||
}
|
||||
}
|
||||
|
||||
function switchTheme()
|
||||
{
|
||||
switchingTheme = true;
|
||||
|
||||
$('#DownloadsTable tbody').empty();
|
||||
$('#HistoryTable tbody').empty();
|
||||
$('#MessagesTable tbody').empty();
|
||||
|
||||
$('body').toggleClass('phone', UISettings.miniTheme);
|
||||
$('.datatable').toggleClass('table-bordered', !UISettings.miniTheme);
|
||||
$('#DownloadsTable').toggleClass('table-check', !UISettings.miniTheme || UISettings.showEditButtons);
|
||||
$('#HistoryTable').toggleClass('table-check', !UISettings.miniTheme || UISettings.showEditButtons);
|
||||
|
||||
centerPopupMenu('#PlayMenu', UISettings.miniTheme);
|
||||
centerPopupMenu('#RefreshMenu', UISettings.miniTheme);
|
||||
|
||||
if (UISettings.miniTheme)
|
||||
{
|
||||
$('#RefreshBlock').appendTo($('#RefreshBlockPhone'));
|
||||
$('#DownloadsRecordsPerPageBlock').appendTo($('#DownloadsRecordsPerPageBlockPhone'));
|
||||
$('#HistoryRecordsPerPageBlock').appendTo($('#HistoryRecordsPerPageBlockPhone'));
|
||||
$('#MessagesRecordsPerPageBlock').appendTo($('#MessagesRecordsPerPageBlockPhone'));
|
||||
}
|
||||
else
|
||||
{
|
||||
$('#RefreshBlock').appendTo($('#RefreshBlockDesktop'));
|
||||
$('#DownloadsRecordsPerPageBlock').appendTo($('#DownloadsTableTopBlock'));
|
||||
$('#HistoryRecordsPerPageBlock').appendTo($('#HistoryTableTopBlock'));
|
||||
$('#MessagesRecordsPerPageBlock').appendTo($('#MessagesTableTopBlock'));
|
||||
}
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
Downloads.redraw();
|
||||
History.redraw();
|
||||
Messages.redraw();
|
||||
|
||||
Downloads.applyTheme();
|
||||
History.applyTheme();
|
||||
Messages.applyTheme();
|
||||
windowResized();
|
||||
}
|
||||
|
||||
switchingTheme = false;
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** RPC CONTROL *********************************************************/
|
||||
|
||||
var RPCController = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// State
|
||||
var loadQueue;
|
||||
var firstLoad = true;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
RPC.rpcUrl = UISettings.rpcUrl;
|
||||
RPC.connectErrorMessage = 'Cannot establish connection to NZBGet.'
|
||||
RPC.defaultFailureCallback = rpcFailure;
|
||||
RPC.next = loadNext;
|
||||
}
|
||||
|
||||
this.refresh = function()
|
||||
{
|
||||
UISettings.connectionError = false;
|
||||
$('#ErrorAlert').hide();
|
||||
Refresher.refreshStarted();
|
||||
|
||||
loadQueue = new Array(
|
||||
function() { Options.update(); },
|
||||
function() { Status.update(); },
|
||||
function() { Downloads.update(); },
|
||||
function() { Messages.update(); },
|
||||
function() { History.update(); });
|
||||
|
||||
if (!firstLoad)
|
||||
{
|
||||
// query NZBGet configuration only on first refresh
|
||||
loadQueue.shift();
|
||||
}
|
||||
|
||||
loadNext();
|
||||
}
|
||||
|
||||
function loadNext()
|
||||
{
|
||||
if (loadQueue.length > 0)
|
||||
{
|
||||
var nextStep = loadQueue[0];
|
||||
loadQueue.shift();
|
||||
nextStep();
|
||||
}
|
||||
else
|
||||
{
|
||||
firstLoad = false;
|
||||
Frontend.loadCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
function rpcFailure(res)
|
||||
{
|
||||
UISettings.connectionError = true;
|
||||
$('#FirstUpdateInfo').hide();
|
||||
$('#ErrorAlert-text').html(res);
|
||||
$('#ErrorAlert').show();
|
||||
if (Status.status)
|
||||
{
|
||||
// stop animations
|
||||
Status.redraw();
|
||||
}
|
||||
};
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** REFRESH CONTROL *********************************************************/
|
||||
|
||||
var Refresher = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// State
|
||||
var secondsToUpdate = -1;
|
||||
var refreshTimer = 0;
|
||||
var indicatorTimer = 0;
|
||||
var indicatorFrame=0;
|
||||
var refreshPaused = 0;
|
||||
var refreshing = false;
|
||||
var refreshNeeded = false;
|
||||
var refreshCallback;
|
||||
|
||||
this.init = function(refresh)
|
||||
{
|
||||
refreshCallback = refresh;
|
||||
$('#RefreshMenu li a').click(refreshIntervalClick);
|
||||
$('#RefreshButton').click(refreshClick);
|
||||
updateRefreshMenu();
|
||||
}
|
||||
|
||||
this.refreshStarted = function()
|
||||
{
|
||||
clearTimeout(refreshTimer);
|
||||
refreshPaused = 0;
|
||||
refreshing = true;
|
||||
refreshNeeded = false;
|
||||
refreshAnimationShow();
|
||||
}
|
||||
|
||||
this.refreshCompleted = function()
|
||||
{
|
||||
refreshing = false;
|
||||
scheduleNextRefresh();
|
||||
}
|
||||
|
||||
this.pause = function()
|
||||
{
|
||||
clearTimeout(refreshTimer);
|
||||
refreshPaused++;
|
||||
}
|
||||
|
||||
this.resume = function()
|
||||
{
|
||||
refreshPaused--;
|
||||
|
||||
if (refreshPaused === 0 && UISettings.refreshInterval > 0)
|
||||
{
|
||||
countSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
refreshNeeded = true;
|
||||
refreshPaused = 0;
|
||||
if (!refreshing)
|
||||
{
|
||||
scheduleNextRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshClick()
|
||||
{
|
||||
if (indicatorFrame > 10)
|
||||
{
|
||||
// force animation restart
|
||||
indicatorFrame = 0;
|
||||
}
|
||||
refreshCallback();
|
||||
}
|
||||
|
||||
function scheduleNextRefresh()
|
||||
{
|
||||
clearTimeout(refreshTimer);
|
||||
secondsToUpdate = refreshNeeded ? 0 : UISettings.refreshInterval;
|
||||
if (secondsToUpdate > 0 || refreshNeeded)
|
||||
{
|
||||
secondsToUpdate += 0.1;
|
||||
countSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
function countSeconds()
|
||||
{
|
||||
if (refreshPaused > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
secondsToUpdate -= 0.1;
|
||||
if (secondsToUpdate <= 0)
|
||||
{
|
||||
refreshCallback();
|
||||
}
|
||||
else
|
||||
{
|
||||
refreshTimer = setTimeout(countSeconds, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshAnimationShow()
|
||||
{
|
||||
if (UISettings.refreshAnimation && indicatorTimer === 0)
|
||||
{
|
||||
refreshAnimationFrame();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshAnimationFrame()
|
||||
{
|
||||
// animate next frame
|
||||
indicatorFrame++;
|
||||
|
||||
if (indicatorFrame === 20)
|
||||
{
|
||||
indicatorFrame = 0;
|
||||
}
|
||||
|
||||
var f = indicatorFrame <= 10 ? indicatorFrame : 0;
|
||||
|
||||
var degree = 360 * f / 10;
|
||||
|
||||
$('#RefreshAnimation').css({
|
||||
'-webkit-transform': 'rotate(' + degree + 'deg)',
|
||||
'-moz-transform': 'rotate(' + degree + 'deg)',
|
||||
'-ms-transform': 'rotate(' + degree + 'deg)',
|
||||
'-o-transform': 'rotate(' + degree + 'deg)',
|
||||
'transform': 'rotate(' + degree + 'deg)'
|
||||
});
|
||||
|
||||
if (!refreshing && indicatorFrame === 0 && (UISettings.refreshInterval === 0 || UISettings.refreshInterval > 1 || !UISettings.refreshAnimation) || UISettings.connectionError)
|
||||
{
|
||||
indicatorTimer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// schedule next frame update
|
||||
indicatorTimer = setTimeout(refreshAnimationFrame, 100);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshIntervalClick()
|
||||
{
|
||||
var data = $(this).parent().attr('data');
|
||||
UISettings.refreshInterval = parseFloat(data);
|
||||
scheduleNextRefresh();
|
||||
updateRefreshMenu();
|
||||
UISettings.save();
|
||||
|
||||
if (UISettings.refreshInterval === 0)
|
||||
{
|
||||
// stop animation
|
||||
Status.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
function updateRefreshMenu()
|
||||
{
|
||||
Util.setMenuMark($('#RefreshMenu'), UISettings.refreshInterval);
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
function TODO()
|
||||
{
|
||||
Notification.show('#Notif_NotImplemented');
|
||||
}
|
||||
|
||||
|
||||
/*** CONFIRMATION DIALOG *****************************************************/
|
||||
|
||||
var ConfirmDialog = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $ConfirmDialog;
|
||||
|
||||
// State
|
||||
var actionCallback;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
$ConfirmDialog = $('#ConfirmDialog');
|
||||
$ConfirmDialog.on('hidden', hidden);
|
||||
$('#ConfirmDialog_OK').click(click);
|
||||
}
|
||||
|
||||
this.showModal = function(id, callback)
|
||||
{
|
||||
$('#ConfirmDialog_Title').html($('#' + id + '_Title').html());
|
||||
$('#ConfirmDialog_Text').html($('#' + id + '_Text').html());
|
||||
$('#ConfirmDialog_OK').html($('#' + id + '_OK').html());
|
||||
Util.centerDialog($ConfirmDialog, true);
|
||||
actionCallback = callback;
|
||||
$ConfirmDialog.modal();
|
||||
}
|
||||
|
||||
function hidden()
|
||||
{
|
||||
// confirm dialog copies data from other nodes
|
||||
// the copied DOM nodes must be destroyed
|
||||
$('#ConfirmDialog_Title').empty();
|
||||
$('#ConfirmDialog_Text').empty();
|
||||
$('#ConfirmDialog_OK').empty();
|
||||
}
|
||||
|
||||
function click(event)
|
||||
{
|
||||
event.preventDefault(); // avoid scrolling
|
||||
actionCallback();
|
||||
$ConfirmDialog.modal('hide');
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** NOTIFICATIONS *********************************************************/
|
||||
|
||||
var Notification = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
this.show = function(alert, completeFunc)
|
||||
{
|
||||
if (UISettings.showNotifications || $(alert).hasClass('alert-error'))
|
||||
{
|
||||
$(alert).animate({'opacity':'toggle'});
|
||||
var duration = $(alert).attr('data-duration');
|
||||
if (duration == null)
|
||||
{
|
||||
duration = 1000;
|
||||
}
|
||||
window.setTimeout(function()
|
||||
{
|
||||
$(alert).animate({'opacity':'toggle'}, completeFunc);
|
||||
}, duration);
|
||||
}
|
||||
else if (completeFunc)
|
||||
{
|
||||
completeFunc();
|
||||
}
|
||||
}
|
||||
}(jQuery));
|
||||
4960
webui/lib/bootstrap.css
vendored
Normal file
1824
webui/lib/bootstrap.js
vendored
Normal file
6
webui/lib/bootstrap.min.js
vendored
Normal file
9404
webui/lib/jquery.js
vendored
Normal file
4
webui/lib/jquery.min.js
vendored
Normal file
239
webui/messages.js
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Messages tab.
|
||||
*/
|
||||
|
||||
/*** MESSAGES TAB *********************************************************************/
|
||||
|
||||
var Messages = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $MessagesTable;
|
||||
var $MessagesTabBadge;
|
||||
var $MessagesTabBadgeEmpty;
|
||||
var $MessagesRecordsPerPage;
|
||||
|
||||
// State
|
||||
var messages;
|
||||
var maxMessages = null;
|
||||
var lastID = 0;
|
||||
var updateTabInfo;
|
||||
var notification = null;
|
||||
|
||||
this.init = function(options)
|
||||
{
|
||||
updateTabInfo = options.updateTabInfo;
|
||||
|
||||
$MessagesTable = $('#MessagesTable');
|
||||
$MessagesTabBadge = $('#MessagesTabBadge');
|
||||
$MessagesTabBadgeEmpty = $('#MessagesTabBadgeEmpty');
|
||||
$MessagesRecordsPerPage = $('#MessagesRecordsPerPage');
|
||||
|
||||
var recordsPerPage = UISettings.read('MessagesRecordsPerPage', 10);
|
||||
$MessagesRecordsPerPage.val(recordsPerPage);
|
||||
|
||||
$MessagesTable.fasttable(
|
||||
{
|
||||
filterInput: '#MessagesTable_filter',
|
||||
filterClearButton: '#MessagesTable_clearfilter',
|
||||
pagerContainer: '#MessagesTable_pager',
|
||||
infoContainer: '#MessagesTable_info',
|
||||
filterCaseSensitive: false,
|
||||
pageSize: recordsPerPage,
|
||||
maxPages: UISettings.miniTheme ? 1 : 5,
|
||||
pageDots: !UISettings.miniTheme,
|
||||
fillFieldsCallback: fillFieldsCallback,
|
||||
fillSearchCallback: fillSearchCallback,
|
||||
renderCellCallback: renderCellCallback,
|
||||
updateInfoCallback: updateInfo
|
||||
});
|
||||
}
|
||||
|
||||
this.applyTheme = function()
|
||||
{
|
||||
$MessagesTable.fasttable('setPageSize', UISettings.read('MessagesRecordsPerPage', 10),
|
||||
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
if (maxMessages === null)
|
||||
{
|
||||
maxMessages = parseInt(Options.option('LogBufferSize'));
|
||||
}
|
||||
|
||||
if (lastID === 0)
|
||||
{
|
||||
RPC.call('log', [0, maxMessages], loaded);
|
||||
}
|
||||
else
|
||||
{
|
||||
RPC.call('log', [lastID+1, 0], loaded);
|
||||
}
|
||||
}
|
||||
|
||||
function loaded(newMessages)
|
||||
{
|
||||
merge(newMessages);
|
||||
RPC.next();
|
||||
}
|
||||
|
||||
function merge(newMessages)
|
||||
{
|
||||
if (lastID === 0)
|
||||
{
|
||||
messages = newMessages;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages = messages.concat(newMessages);
|
||||
messages.splice(0, messages.length-maxMessages);
|
||||
}
|
||||
|
||||
if (messages.length > 0)
|
||||
{
|
||||
lastID = messages[messages.length-1].ID;
|
||||
}
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i < messages.length; i++)
|
||||
{
|
||||
var message = messages[i];
|
||||
|
||||
var item =
|
||||
{
|
||||
id: message.ID,
|
||||
message: message
|
||||
};
|
||||
|
||||
data.unshift(item);
|
||||
}
|
||||
|
||||
$MessagesTable.fasttable('update', data);
|
||||
|
||||
Util.show($MessagesTabBadge, messages.length > 0);
|
||||
Util.show($MessagesTabBadgeEmpty, messages.length === 0 && UISettings.miniTheme);
|
||||
}
|
||||
|
||||
function fillFieldsCallback(item)
|
||||
{
|
||||
var message = item.message;
|
||||
|
||||
var kind;
|
||||
switch (message.Kind)
|
||||
{
|
||||
case 'INFO': kind = '<span class="label label-status label-success">info</span>'; break;
|
||||
case 'DETAIL': kind = '<span class="label label-status label-info">detail</span>'; break;
|
||||
case 'WARNING': kind = '<span class="label label-status label-warning">warning</span>'; break;
|
||||
case 'ERROR': kind = '<span class="label label-status label-important">error</span>'; break;
|
||||
case 'DEBUG': kind = '<span class="label label-status">debug</span>'; break;
|
||||
}
|
||||
|
||||
var text = Util.textToHtml(message.Text);
|
||||
if (!item.time)
|
||||
{
|
||||
item.time = Util.formatDateTime(message.Time + UISettings.timeZoneCorrection*60*60);
|
||||
}
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
item.fields = [kind, item.time, text];
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = kind + ' <span class="label">' + item.time + '</span> ' + text;
|
||||
item.fields = [info];
|
||||
}
|
||||
}
|
||||
|
||||
function fillSearchCallback(item)
|
||||
{
|
||||
if (!item.time)
|
||||
{
|
||||
item.time = Util.formatDateTime(item.message.Time + UISettings.timeZoneCorrection*60*60);
|
||||
}
|
||||
|
||||
item.search = item.message.Kind + ' ' + item.time + ' ' + item.message.Text;
|
||||
}
|
||||
|
||||
function renderCellCallback(cell, index, item)
|
||||
{
|
||||
if (index === 1)
|
||||
{
|
||||
cell.className = 'text-center';
|
||||
}
|
||||
}
|
||||
|
||||
function updateInfo(stat)
|
||||
{
|
||||
updateTabInfo($MessagesTabBadge, stat);
|
||||
}
|
||||
|
||||
this.recordsPerPageChange = function()
|
||||
{
|
||||
var val = $MessagesRecordsPerPage.val();
|
||||
UISettings.write('MessagesRecordsPerPage', val);
|
||||
$MessagesTable.fasttable('setPageSize', val);
|
||||
}
|
||||
|
||||
this.clearClick = function()
|
||||
{
|
||||
ConfirmDialog.showModal('MessagesClearConfirmDialog', messagesClear);
|
||||
}
|
||||
|
||||
function messagesClear()
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
RPC.call('clearlog', [], function()
|
||||
{
|
||||
RPC.call('writelog', ['INFO', 'Messages have been deleted'], function()
|
||||
{
|
||||
notification = '#Notif_Messages_Cleared';
|
||||
lastID = 0;
|
||||
editCompleted();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function editCompleted()
|
||||
{
|
||||
Refresher.update();
|
||||
if (notification)
|
||||
{
|
||||
Notification.show(notification);
|
||||
notification = null;
|
||||
}
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
394
webui/status.js
Normal file
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Status Infos on main page (speed, time, paused state etc.);
|
||||
* 2) Statistics and Status dialog.
|
||||
*/
|
||||
|
||||
/*** STATUS INFOS ON MAIN PAGE AND STATISTICS DIALOG ****************************************/
|
||||
|
||||
var Status = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Properties (public)
|
||||
this.status;
|
||||
|
||||
// Controls
|
||||
var $SpeedLimitInput;
|
||||
var $CHPauseDownload;
|
||||
var $CHPausePostProcess;
|
||||
var $CHPauseScan;
|
||||
var $CHSoftPauseDownload;
|
||||
var $StatusPausing;
|
||||
var $StatusPaused;
|
||||
var $StatusSoftPaused;
|
||||
var $StatusLeft;
|
||||
var $StatusSpeed;
|
||||
var $StatusSpeedIcon;
|
||||
var $StatusTime;
|
||||
var $StatusURLs;
|
||||
var $PlayBlock;
|
||||
var $PlayButton;
|
||||
var $PauseButton;
|
||||
var $PlayAnimation;
|
||||
var $CurSpeedLimit;
|
||||
var $CurSpeedLimitBlock;
|
||||
var $LimitDialog;
|
||||
var $StatDialog;
|
||||
|
||||
// State
|
||||
var status;
|
||||
var lastPlayState = 0;
|
||||
var lastAnimState = 0;
|
||||
var playInitialized = false;
|
||||
var lastSoftPauseState = 0;
|
||||
var modalShown = false;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
$SpeedLimitInput = $('#SpeedLimitInput');
|
||||
$CHPauseDownload = $('#CHPauseDownload');
|
||||
$CHPausePostProcess = $('#CHPausePostProcess');
|
||||
$CHPauseScan = $('#CHPauseScan');
|
||||
$CHSoftPauseDownload = $('#CHSoftPauseDownload');
|
||||
$PlayBlock = $('#PlayBlock');
|
||||
$PlayButton = $('#PlayButton');
|
||||
$PauseButton = $('#PauseButton');
|
||||
$PlayAnimation = $('#PlayAnimation');
|
||||
$StatusPausing = $('#StatusPausing');
|
||||
$StatusPaused = $('#StatusPaused');
|
||||
$StatusSoftPaused = $('#StatusSoftPaused');
|
||||
$StatusLeft = $('#StatusLeft');
|
||||
$StatusSpeed = $('#StatusSpeed');
|
||||
$StatusSpeedIcon = $('#StatusSpeedIcon');
|
||||
$StatusTime = $('#StatusTime');
|
||||
$StatusURLs = $('#StatusURLs');
|
||||
$CurSpeedLimit = $('#CurSpeedLimit');
|
||||
$CurSpeedLimitBlock = $('#CurSpeedLimitBlock');
|
||||
$LimitDialog = $('#LimitDialog');
|
||||
$StatDialog = $('#StatDialog');
|
||||
|
||||
if (UISettings.setFocus)
|
||||
{
|
||||
$LimitDialog.on('shown', function()
|
||||
{
|
||||
$('#SpeedLimitInput').focus();
|
||||
});
|
||||
}
|
||||
|
||||
$PlayAnimation.hover(function() { $PlayBlock.addClass('hover'); }, function() { $PlayBlock.removeClass('hover'); });
|
||||
|
||||
// temporary pause the play animation if any modal is shown (to avoid artifacts in safari)
|
||||
$('body >.modal').on('show', modalShow);
|
||||
$('body > .modal').on('hide', modalHide);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
var _this = this;
|
||||
RPC.call('status', [],
|
||||
function(curStatus)
|
||||
{
|
||||
status = curStatus;
|
||||
_this.status = status;
|
||||
RPC.next();
|
||||
});
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
redrawStatistics();
|
||||
redrawInfo()
|
||||
}
|
||||
|
||||
function redrawStatistics()
|
||||
{
|
||||
var content = '';
|
||||
|
||||
content += '<tr><td>NZBGet version</td><td class="text-right">' + Options.option('Version') + '</td></tr>';
|
||||
content += '<tr><td>Uptime</td><td class="text-right">' + Util.formatTimeHMS(status.UpTimeSec) + '</td></tr>';
|
||||
content += '<tr><td>Download time</td><td class="text-right">' + Util.formatTimeHMS(status.DownloadTimeSec) + '</td></tr>';
|
||||
content += '<tr><td>Total downloaded</td><td class="text-right">' + Util.formatSizeMB(status.DownloadedSizeMB) + '</td></tr>';
|
||||
content += '<tr><td>Remaining</td><td class="text-right">' + Util.formatSizeMB(status.RemainingSizeMB) + '</td></tr>';
|
||||
content += '<tr><td>Free disk space</td><td class="text-right">' + Util.formatSizeMB(status.FreeDiskSpaceMB) + '</td></tr>';
|
||||
content += '<tr><td>Average download speed</td><td class="text-right">' + Util.round0(status.AverageDownloadRate / 1024) + ' KB/s</td></tr>';
|
||||
content += '<tr><td>Current download speed</td><td class="text-right">' + Util.round0(status.DownloadRate / 1024) + ' KB/s</td></tr>';
|
||||
content += '<tr><td>Current speed limit</td><td class="text-right">' + Util.round0(status.DownloadLimit / 1024) + ' KB/s</td></tr>';
|
||||
|
||||
$('#StatisticsTable tbody').html(content);
|
||||
|
||||
content = '';
|
||||
content += '<tr><td>Download</td><td class="text-right">';
|
||||
if (status.DownloadPaused || status.Download2Paused)
|
||||
{
|
||||
content += status.Download2Paused ? '<span class="label label-status label-warning">paused</span>' : '';
|
||||
content += status.Download2Paused && status.DownloadPaused ? ' + ' : '';
|
||||
content += status.DownloadPaused ? '<span class="label label-status label-warning">soft-paused</span>' : '';
|
||||
}
|
||||
else
|
||||
{
|
||||
content += '<span class="label label-status label-success">active</span>';
|
||||
}
|
||||
content += '</td></tr>';
|
||||
|
||||
content += '<tr><td>Post-processing</td><td class="text-right">' + (Options.option('PostProcess') === '' ?
|
||||
'<span class="label label-status">disabled</span>' :
|
||||
(status.PostPaused ?
|
||||
'<span class="label label-status label-warning">paused</span>' :
|
||||
'<span class="label label-status label-success">active</span>')) +
|
||||
'</td></tr>';
|
||||
|
||||
content += '<tr><td>NZB-Directory scan</td><td class="text-right">' + (Options.option('NzbDirInterval') === '0' ?
|
||||
'<span class="label label-status">disabled</span>' :
|
||||
(status.ScanPaused ?
|
||||
'<span class="label label-status label-warning">paused</span>' :
|
||||
'<span class="label label-status label-success">active</span>')) +
|
||||
'</td></tr>';
|
||||
|
||||
content += '</tbody>';
|
||||
content += '</table>';
|
||||
|
||||
$('#StatusTable tbody').html(content);
|
||||
}
|
||||
|
||||
function redrawInfo()
|
||||
{
|
||||
Util.show($CHPauseDownload, status.Download2Paused);
|
||||
Util.show($CHPausePostProcess, status.PostPaused);
|
||||
Util.show($CHPauseScan, status.ScanPaused);
|
||||
Util.show($CHSoftPauseDownload, status.DownloadPaused);
|
||||
|
||||
updatePlayAnim();
|
||||
updatePlayButton();
|
||||
|
||||
if (status.ServerStandBy)
|
||||
{
|
||||
$StatusSpeed.html('--- KB/s');
|
||||
if (status.RemainingSizeHi > 0 || status.RemainingSizeLo > 0)
|
||||
{
|
||||
if (status.AverageDownloadRate > 0)
|
||||
{
|
||||
$StatusTime.html(Util.formatTimeLeft(status.RemainingSizeMB*1024/(status.AverageDownloadRate/1024)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$StatusTime.html('--h --m');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$StatusTime.html('0h 0m');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$StatusSpeed.html(Util.round0(status.DownloadRate / 1024) + ' KB/s');
|
||||
if (status.DownloadRate > 0)
|
||||
{
|
||||
$StatusTime.html(Util.formatTimeLeft(status.RemainingSizeMB*1024/(status.DownloadRate/1024)));
|
||||
}
|
||||
else
|
||||
{
|
||||
$StatusTime.html('--h --m');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$StatusSpeedIcon.toggleClass('icon-plane', status.DownloadLimit === 0);
|
||||
$StatusSpeedIcon.toggleClass('icon-truck', status.DownloadLimit !== 0);
|
||||
}
|
||||
|
||||
function updatePlayButton()
|
||||
{
|
||||
var SoftPause = status.DownloadPaused && (!lastAnimState || !UISettings.activityAnimation);
|
||||
if (SoftPause !== lastSoftPauseState)
|
||||
{
|
||||
lastSoftPauseState = SoftPause;
|
||||
$PauseButton.removeClass('img-download-green').removeClass('img-download-green-orange').
|
||||
addClass(SoftPause ? 'img-download-green-orange' : 'img-download-green');
|
||||
$PlayButton.removeClass('img-download-orange').removeClass('img-download-orange-orange').
|
||||
addClass(SoftPause ? 'img-download-orange-orange' : 'img-download-orange');
|
||||
}
|
||||
|
||||
var Play = !status.Download2Paused;
|
||||
if (Play === lastPlayState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastPlayState = Play;
|
||||
|
||||
var hideBtn = Play ? $PlayButton : $PauseButton;
|
||||
var showBtn = !Play ? $PlayButton : $PauseButton;
|
||||
|
||||
if (playInitialized)
|
||||
{
|
||||
hideBtn.fadeOut(500);
|
||||
showBtn.fadeIn(500);
|
||||
if (!Play && !status.ServerStandBy)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_Pausing');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hideBtn.hide();
|
||||
showBtn.show();
|
||||
}
|
||||
|
||||
if (Play)
|
||||
{
|
||||
$PlayAnimation.removeClass('pause').addClass('play');
|
||||
}
|
||||
else
|
||||
{
|
||||
$PlayAnimation.removeClass('play').addClass('pause');
|
||||
}
|
||||
|
||||
playInitialized = true;
|
||||
}
|
||||
|
||||
function updatePlayAnim()
|
||||
{
|
||||
// Animate if either any downloads or post-processing is in progress
|
||||
var Anim = (!status.ServerStandBy || (status.PostJobCount > 0 && !status.PostPaused)) &&
|
||||
(UISettings.refreshInterval !== 0) && !UISettings.connectionError;
|
||||
if (Anim === lastAnimState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lastAnimState = Anim;
|
||||
|
||||
if (UISettings.activityAnimation && !modalShown)
|
||||
{
|
||||
if (Anim)
|
||||
{
|
||||
$PlayAnimation.fadeIn(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
$PlayAnimation.fadeOut(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.playClick = function()
|
||||
{
|
||||
//Notification.show('#Notif_Play');
|
||||
|
||||
if (lastPlayState)
|
||||
{
|
||||
// pause all activities
|
||||
RPC.call('pausedownload2', [],
|
||||
function(){RPC.call('pausepost', [],
|
||||
function(){RPC.call('pausescan', [], Refresher.update)})});
|
||||
}
|
||||
else
|
||||
{
|
||||
// resume all activities
|
||||
RPC.call('resumedownload2', [],
|
||||
function(){RPC.call('resumepost', [],
|
||||
function(){RPC.call('resumescan', [], Refresher.update)})});
|
||||
}
|
||||
}
|
||||
|
||||
this.pauseClick = function(data)
|
||||
{
|
||||
switch (data)
|
||||
{
|
||||
case 'download2':
|
||||
var method = status.Download2Paused ? 'resumedownload2' : 'pausedownload2';
|
||||
break;
|
||||
case 'post':
|
||||
var method = status.PostPaused ? 'resumepost' : 'pausepost';
|
||||
break;
|
||||
case 'scan':
|
||||
var method = status.ScanPaused ? 'resumescan' : 'pausescan';
|
||||
break;
|
||||
case 'download':
|
||||
var method = status.DownloadPaused ? 'resumedownload' : 'pausedownload';
|
||||
break;
|
||||
}
|
||||
RPC.call(method, [], Refresher.update);
|
||||
}
|
||||
|
||||
this.limitDialogClick = function()
|
||||
{
|
||||
$SpeedLimitInput.val('');
|
||||
$CurSpeedLimit.text(status.DownloadLimit === 0 ? 'none' : Util.round0(status.DownloadLimit / 1024) + ' KB/s');
|
||||
Util.show($CurSpeedLimitBlock, status.DownloadLimit !== 0);
|
||||
$LimitDialog.modal();
|
||||
}
|
||||
|
||||
this.statDialogClick = function()
|
||||
{
|
||||
$StatDialog.modal();
|
||||
}
|
||||
|
||||
this.setSpeedLimitClick = function()
|
||||
{
|
||||
var val = $SpeedLimitInput.val();
|
||||
var rate = 0;
|
||||
if (val == '')
|
||||
{
|
||||
rate = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
rate = parseInt(val);
|
||||
if (isNaN(rate))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
RPC.call('rate', [rate], function()
|
||||
{
|
||||
$LimitDialog.modal('hide');
|
||||
Notification.show('#Notif_SetSpeedLimit');
|
||||
Refresher.update();
|
||||
});
|
||||
}
|
||||
|
||||
function modalShow()
|
||||
{
|
||||
modalShown = true;
|
||||
if (lastAnimState)
|
||||
{
|
||||
$PlayAnimation.hide();
|
||||
}
|
||||
}
|
||||
|
||||
function modalHide()
|
||||
{
|
||||
if (lastAnimState)
|
||||
{
|
||||
$PlayAnimation.show();
|
||||
}
|
||||
modalShown = false;
|
||||
}
|
||||
}(jQuery));
|
||||
1844
webui/style.css
Normal file
413
webui/upload.js
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) File upload dialog (local files, urls, scan);
|
||||
* 2) Drag-n-drop events handling on main page.
|
||||
*/
|
||||
|
||||
/*** FILE UPLOAD (DRAG-N-DROP, URLS, SCAN) ************************************************/
|
||||
|
||||
var Upload = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $AddDialog;
|
||||
|
||||
// State
|
||||
var dragin = false;
|
||||
var files = [];
|
||||
var filesSuccess = [];
|
||||
var index;
|
||||
var errors = false;
|
||||
var needRefresh = false;
|
||||
var failure_message = null;
|
||||
var url = '';
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
var target = $('#DownloadsTab')[0];
|
||||
target.addEventListener('dragenter', bodyDragOver);
|
||||
target.addEventListener('dragover', bodyDragOver);
|
||||
target.addEventListener('dragleave', bodyDragLeave);
|
||||
target.addEventListener('drop', bodyDrop, false);
|
||||
|
||||
target = $('#AddDialog_Target')[0];
|
||||
target.addEventListener('dragenter', dialogDragOver);
|
||||
target.addEventListener('dragover', dialogDragOver);
|
||||
target.addEventListener('dragleave', dialogDragLeave);
|
||||
target.addEventListener('drop', dialogDrop, false);
|
||||
|
||||
$AddDialog = $('#AddDialog');
|
||||
|
||||
$AddDialog.on('hidden', function ()
|
||||
{
|
||||
Refresher.resume();
|
||||
files = [];
|
||||
filesSuccess = [];
|
||||
if (needRefresh)
|
||||
{
|
||||
Refresher.update();
|
||||
}
|
||||
});
|
||||
|
||||
if (UISettings.setFocus)
|
||||
{
|
||||
$AddDialog.on('shown', function ()
|
||||
{
|
||||
if (files.length === 0)
|
||||
{
|
||||
$('#AddDialog_URL').focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('#AddDialog_Select').click(selectFiles);
|
||||
$('#AddDialog_Submit').click(submit);
|
||||
$('#AddDialog_Input')[0].addEventListener("change", fileSelectHandler, false);
|
||||
$('#AddDialog_Scan').click(scan);
|
||||
}
|
||||
|
||||
function bodyDragOver(event)
|
||||
{
|
||||
if ((event.dataTransfer.types.contains && event.dataTransfer.types.contains('Files')) ||
|
||||
(event.dataTransfer.types.indexOf && event.dataTransfer.types.indexOf('Files') > -1) ||
|
||||
(event.dataTransfer.files && event.dataTransfer.files.length > 0))
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (!dragin)
|
||||
{
|
||||
dragin = true;
|
||||
$('body').addClass('dragover');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bodyDragLeave(event)
|
||||
{
|
||||
dragin = false;
|
||||
$('body').removeClass('dragover');
|
||||
}
|
||||
|
||||
function bodyDrop(event)
|
||||
{
|
||||
event.preventDefault();
|
||||
bodyDragLeave();
|
||||
|
||||
if (!event.dataTransfer.files)
|
||||
{
|
||||
showDnDUnsupportedAlert();
|
||||
return;
|
||||
}
|
||||
|
||||
showModal(event.dataTransfer.files);
|
||||
}
|
||||
|
||||
function dialogDragOver(event)
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (!dragin)
|
||||
{
|
||||
dragin = true;
|
||||
$('#AddDialog_Target').addClass('dragover');
|
||||
}
|
||||
}
|
||||
|
||||
function dialogDragLeave(event)
|
||||
{
|
||||
dragin = false;
|
||||
$('#AddDialog_Target').removeClass('dragover');
|
||||
}
|
||||
|
||||
function dialogDrop(event)
|
||||
{
|
||||
event.preventDefault();
|
||||
dialogDragLeave();
|
||||
|
||||
if (!event.dataTransfer.files)
|
||||
{
|
||||
showDnDUnsupportedAlert();
|
||||
return;
|
||||
}
|
||||
|
||||
addFiles(event.dataTransfer.files);
|
||||
}
|
||||
|
||||
function showDnDUnsupportedAlert()
|
||||
{
|
||||
setTimeout(function()
|
||||
{
|
||||
alert("Unfortunately your browser doesn't support drag and drop for files.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function selectFiles()
|
||||
{
|
||||
$('#AddDialog_Input').click();
|
||||
}
|
||||
|
||||
function fileSelectHandler(event)
|
||||
{
|
||||
addFiles(event.target.files);
|
||||
}
|
||||
|
||||
function addFiles(selectedFiles)
|
||||
{
|
||||
var list = '';
|
||||
for (var i = 0; i<selectedFiles.length; i++)
|
||||
{
|
||||
var file = selectedFiles[i];
|
||||
var html = '<table><tr><td width="18px" valign="top"><i class="icon-file" style="vertical-align:top;margin-top:2px;"></i><img class="hide" style="vertical-align:top;margin-top:1px;" src="img/transmit-file.gif" width="16px" height="16px"></td><td>' +
|
||||
Util.formatNZBName(file.name) + '</td></tr></table>';
|
||||
$('#AddDialog_Files').append(html);
|
||||
files.push(file);
|
||||
}
|
||||
$('#AddDialog_Files').show();
|
||||
$('#AddDialog_FilesHelp').hide();
|
||||
}
|
||||
|
||||
this.addClick = function()
|
||||
{
|
||||
showModal();
|
||||
}
|
||||
|
||||
function showModal(droppedFiles)
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
$('#AddDialog_Files').empty();
|
||||
$('#AddDialog_URL').val('');
|
||||
$('#AddDialog_FilesHelp').show();
|
||||
$('#AddDialog_URLLabel img').hide();
|
||||
$('#AddDialog_URLLabel i').hide();
|
||||
enableAllButtons();
|
||||
|
||||
var v = $('#AddDialog_Priority');
|
||||
DownloadsUI.fillPriorityCombo(v);
|
||||
v.val(0);
|
||||
DownloadsUI.fillCategoryCombo($('#AddDialog_Category'));
|
||||
|
||||
files = [];
|
||||
filesSuccess = [];
|
||||
|
||||
if (droppedFiles)
|
||||
{
|
||||
addFiles(droppedFiles);
|
||||
}
|
||||
|
||||
$AddDialog.modal({backdrop: 'static'});
|
||||
}
|
||||
|
||||
function disableAllButtons()
|
||||
{
|
||||
$('#AddDialog .modal-footer .btn, #AddDialog_Select, #AddDialog_Scan').attr('disabled', 'disabled');
|
||||
}
|
||||
|
||||
function enableAllButtons()
|
||||
{
|
||||
$('#AddDialog .modal-footer .btn, #AddDialog_Select, #AddDialog_Scan').removeAttr('disabled');
|
||||
$('#AddDialog_Transmit').hide();
|
||||
}
|
||||
|
||||
function submit()
|
||||
{
|
||||
disableAllButtons();
|
||||
|
||||
if (files.length > 0)
|
||||
{
|
||||
if (!window.FileReader)
|
||||
{
|
||||
$AddDialog.modal('hide');
|
||||
alert("Unfortunately your browser doesn't support FileReader API.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
|
||||
return;
|
||||
}
|
||||
|
||||
var testreader = new FileReader();
|
||||
if (!testreader.readAsBinaryString)
|
||||
{
|
||||
$AddDialog.modal('hide');
|
||||
alert("Unfortunately your browser doesn't support the function \"readAsBinaryString\" of FileReader API.\n\nPlease use alternative ways to add files to queue:\nadd via URL or put the files directly into incoming nzb-directory.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
needRefresh = false;
|
||||
errors = false;
|
||||
failure_message = null;
|
||||
index = 0;
|
||||
url = $('#AddDialog_URL').val();
|
||||
|
||||
/*
|
||||
setTimeout(function(){
|
||||
$('#AddDialog_Transmit').show();
|
||||
}, 500);
|
||||
*/
|
||||
|
||||
if (url.length > 0)
|
||||
{
|
||||
urlNext();
|
||||
}
|
||||
else
|
||||
{
|
||||
fileNext();
|
||||
}
|
||||
}
|
||||
|
||||
function fileNext()
|
||||
{
|
||||
if (index === files.length)
|
||||
{
|
||||
allCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
var file = files[index];
|
||||
|
||||
if (filesSuccess.indexOf(file) > -1)
|
||||
{
|
||||
// file already uploaded
|
||||
index++;
|
||||
setTimeout(next, 50);
|
||||
return;
|
||||
}
|
||||
|
||||
$('#AddDialog_Files table:eq(' + index + ') img').show();
|
||||
$('#AddDialog_Files table:eq(' + index + ') i').hide();
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (event)
|
||||
{
|
||||
var base64str = window.btoa(event.target.result);
|
||||
var category = $('#AddDialog_Category').val();
|
||||
var priority = parseInt($('#AddDialog_Priority').val());
|
||||
RPC.call('append', [file.name, category, priority, false, base64str], fileCompleted, fileFailure);
|
||||
};
|
||||
|
||||
reader.readAsBinaryString(file);
|
||||
}
|
||||
|
||||
function fileCompleted(result)
|
||||
{
|
||||
errors |= !result;
|
||||
needRefresh |= result;
|
||||
if (result)
|
||||
{
|
||||
filesSuccess.push(files[index]);
|
||||
}
|
||||
$('#AddDialog_Files table:eq(' + index + ') img').hide();
|
||||
$('#AddDialog_Files table:eq(' + index + ') i').removeClass('icon-file').addClass(
|
||||
result ? 'icon-ok' : 'icon-remove').show();
|
||||
index++;
|
||||
fileNext();
|
||||
}
|
||||
|
||||
function fileFailure(res)
|
||||
{
|
||||
failure_message = res;
|
||||
fileCompleted(false);
|
||||
}
|
||||
|
||||
function urlNext()
|
||||
{
|
||||
$('#AddDialog_URLLabel img').show();
|
||||
$('#AddDialog_URLLabel i').hide();
|
||||
|
||||
var category = $('#AddDialog_Category').val();
|
||||
var priority = parseInt($('#AddDialog_Priority').val());
|
||||
|
||||
RPC.call('appendurl', ['', category, priority, false, url], urlCompleted, urlFailure);
|
||||
}
|
||||
|
||||
function urlCompleted(result)
|
||||
{
|
||||
errors |= !result;
|
||||
needRefresh |= result;
|
||||
if (result)
|
||||
{
|
||||
$('#AddDialog_URL').empty();
|
||||
}
|
||||
$('#AddDialog_URLLabel img').hide();
|
||||
$('#AddDialog_URLLabel i').removeClass('icon-ok').removeClass('icon-remove').addClass(
|
||||
result ? 'icon-ok' : 'icon-remove').show();
|
||||
|
||||
fileNext();
|
||||
}
|
||||
|
||||
function urlFailure(res)
|
||||
{
|
||||
failure_message = res;
|
||||
urlCompleted(false);
|
||||
}
|
||||
|
||||
function allCompleted()
|
||||
{
|
||||
if (errors)
|
||||
{
|
||||
enableAllButtons();
|
||||
// using timeout for browser to update UI (buttons) before showing the alert
|
||||
setTimeout(function()
|
||||
{
|
||||
if (failure_message)
|
||||
{
|
||||
alert((index > 1 ? 'One or more files' : 'The file') + ' could not be added to the queue:\n' + failure_message);
|
||||
}
|
||||
else
|
||||
{
|
||||
alert((index > 1 ? 'One or more files' : 'The file') + ' could not be added to the queue.\nPlease check the messages tab for any error messages.');
|
||||
}
|
||||
needRefresh = true;
|
||||
}, 100);
|
||||
}
|
||||
else
|
||||
{
|
||||
$AddDialog.modal('hide');
|
||||
if (index > 0)
|
||||
{
|
||||
Notification.show('#Notif_AddFiles');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function scan()
|
||||
{
|
||||
disableAllButtons();
|
||||
|
||||
setTimeout(function(){
|
||||
$('#AddDialog_Transmit').show();
|
||||
}, 500);
|
||||
|
||||
RPC.call('scan', [true], function()
|
||||
{
|
||||
needRefresh = true;
|
||||
$AddDialog.modal('hide');
|
||||
Notification.show('#Notif_Scan');
|
||||
});
|
||||
}
|
||||
}(jQuery));
|
||||
502
webui/util.js
Normal file
@@ -0,0 +1,502 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Common utilitiy functions (format time, size, etc);
|
||||
* 2) Slideable tab dialog extension;
|
||||
* 3) Communication via JSON-RPC.
|
||||
*/
|
||||
|
||||
/*** UTILITY FUNCTIONS *********************************************************/
|
||||
|
||||
var Util = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
this.formatTimeHMS = function(sec)
|
||||
{
|
||||
var hms = '';
|
||||
var days = Math.floor(sec / 86400);
|
||||
if (days > 0)
|
||||
{
|
||||
hms = days + 'd ';
|
||||
}
|
||||
var hours = Math.floor((sec % 86400) / 3600);
|
||||
hms = hms + hours + ':';
|
||||
var minutes = Math.floor((sec / 60) % 60);
|
||||
if (minutes < 10)
|
||||
{
|
||||
hms = hms + '0';
|
||||
}
|
||||
hms = hms + minutes + ':';
|
||||
var seconds = Math.floor(sec % 60);
|
||||
if (seconds < 10)
|
||||
{
|
||||
hms = hms + '0';
|
||||
}
|
||||
hms = hms + seconds;
|
||||
return hms;
|
||||
}
|
||||
|
||||
this.formatTimeLeft = function(sec)
|
||||
{
|
||||
var hms = '';
|
||||
var days = Math.floor(sec / 86400);
|
||||
var hours = Math.floor((sec % 86400) / 3600);
|
||||
var minutes = Math.floor((sec / 60) % 60);
|
||||
var seconds = Math.floor(sec % 60);
|
||||
|
||||
if (days > 10)
|
||||
{
|
||||
return days + 'd';
|
||||
}
|
||||
if (days > 0)
|
||||
{
|
||||
return days + 'd ' + hours + 'h';
|
||||
}
|
||||
if (hours > 0)
|
||||
{
|
||||
return hours + 'h ' + (minutes < 10 ? '0' : '') + minutes + 'm';
|
||||
}
|
||||
if (minutes > 0)
|
||||
{
|
||||
return minutes + 'm ' + (seconds < 10 ? '0' : '') + seconds + 's';
|
||||
}
|
||||
|
||||
return seconds + 's';
|
||||
}
|
||||
|
||||
this.formatDateTime = function(unixTime)
|
||||
{
|
||||
var dt = new Date(unixTime * 1000);
|
||||
var h = dt.getHours();
|
||||
var m = dt.getMinutes();
|
||||
var s = dt.getSeconds();
|
||||
return dt.toDateString() + ' ' + (h < 10 ? '0' : '') + h + ':' + (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s;
|
||||
}
|
||||
|
||||
this.formatSizeMB = function(sizeMB, sizeLo)
|
||||
{
|
||||
if (sizeLo !== undefined && sizeMB < 100)
|
||||
{
|
||||
sizeMB = sizeLo / 1024 / 1024;
|
||||
}
|
||||
|
||||
if (sizeMB > 10240)
|
||||
{
|
||||
return this.round1(sizeMB / 1024.0) + ' GB';
|
||||
}
|
||||
else if (sizeMB > 1024)
|
||||
{
|
||||
return this.round2(sizeMB / 1024.0) + ' GB';
|
||||
}
|
||||
else if (sizeMB > 100)
|
||||
{
|
||||
return this.round0(sizeMB) + ' MB';
|
||||
}
|
||||
else if (sizeMB > 10)
|
||||
{
|
||||
return this.round1(sizeMB) + ' MB';
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.round2(sizeMB) + ' MB';
|
||||
}
|
||||
}
|
||||
|
||||
this.formatAge = function(time)
|
||||
{
|
||||
if (time == 0)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
var diff = new Date().getTime() / 1000 - time;
|
||||
if (diff > 60*60*24)
|
||||
{
|
||||
return this.round0(diff / (60*60*24)) +' d';
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.round0(diff / (60*60)) +' h';
|
||||
}
|
||||
}
|
||||
|
||||
this.round0 = function(arg)
|
||||
{
|
||||
return Math.round(arg);
|
||||
}
|
||||
|
||||
this.round1 = function(arg)
|
||||
{
|
||||
return arg.toFixed(1);
|
||||
}
|
||||
|
||||
this.round2 = function(arg)
|
||||
{
|
||||
return arg.toFixed(2);
|
||||
}
|
||||
|
||||
this.formatNZBName = function(NZBName)
|
||||
{
|
||||
return NZBName.replace(/\./g, ' ')
|
||||
.replace(/_/g, ' ');
|
||||
}
|
||||
|
||||
this.textToHtml = function(str)
|
||||
{
|
||||
return str.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
this.setMenuMark = function(menu, data)
|
||||
{
|
||||
// remove marks from all items
|
||||
$('li table tr td:first-child', menu).html('');
|
||||
// set mark on selected item
|
||||
var mark = $('li[data="mark"]', menu).html();
|
||||
$('li[data="' + data + '"] table tr td:first-child', menu).html(mark);
|
||||
}
|
||||
|
||||
this.show = function(jqSelector, visible, display)
|
||||
{
|
||||
if (display)
|
||||
{
|
||||
$(jqSelector).css({display: visible ? display : 'none'});
|
||||
}
|
||||
else
|
||||
{
|
||||
visible ? $(jqSelector).show() : $(jqSelector).hide();
|
||||
}
|
||||
}
|
||||
|
||||
this.parseBool = function(value)
|
||||
{
|
||||
return ''+value == 'true';
|
||||
}
|
||||
|
||||
this.disableShiftMouseDown = function(event)
|
||||
{
|
||||
// disable default shift+click behaviour, which is to select a text
|
||||
if (event.shiftKey)
|
||||
{
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
this.centerDialog = function(dialog, center)
|
||||
{
|
||||
var $elem = $(dialog);
|
||||
if (center)
|
||||
{
|
||||
var top = ($(window).height() - $elem.outerHeight())/2;
|
||||
top = top > 0 ? top : 0;
|
||||
$elem.css({ top: top});
|
||||
}
|
||||
else
|
||||
{
|
||||
$elem.css({ top: '' });
|
||||
}
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** MODAL DIALOG WITH SLIDEABLE TABS *********************************************************/
|
||||
|
||||
var TabDialog = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
this.extend = function(dialog)
|
||||
{
|
||||
dialog.restoreTab = restoreTab;
|
||||
dialog.switchTab = switchTab;
|
||||
}
|
||||
|
||||
function restoreTab()
|
||||
{
|
||||
var dialog = this;
|
||||
var body = $('.modal-body', dialog);
|
||||
var footer = $('.modal-footer', dialog);
|
||||
var header = $('.modal-header', dialog);
|
||||
dialog.css({margin: '', left: '', top: '', bottom: '', right: '', width: '', height: ''});
|
||||
body.css({position: '', height: '', left: '', right: '', top: '', bottom: '', 'max-height': ''});
|
||||
footer.css({position: '', left: '', right: '', bottom: ''});
|
||||
}
|
||||
|
||||
function switchTab(fromTab, toTab, duration, options)
|
||||
{
|
||||
var dialog = this;
|
||||
var sign = options.back ? -1 : 1;
|
||||
var fullscreen = options.fullscreen && !options.back;
|
||||
var bodyPadding = 30;
|
||||
var dialogMargin = options.mini ? 0 : 15;
|
||||
var dialogBorder = 2;
|
||||
|
||||
var body = $('.modal-body', dialog);
|
||||
var footer = $('.modal-footer', dialog);
|
||||
var header = $('.modal-header', dialog);
|
||||
|
||||
var oldBodyHeight = body.height();
|
||||
var oldWinHeight = dialog.height();
|
||||
var windowWidth = $(window).width();
|
||||
var windowHeight = $(window).height();
|
||||
var oldTabWidth = fromTab.width();
|
||||
var dialogStyleFS, bodyStyleFS, footerStyleFS;
|
||||
|
||||
if (options.fullscreen && options.back)
|
||||
{
|
||||
// save fullscreen state for later use
|
||||
dialogStyleFS = dialog.attr('style');
|
||||
bodyStyleFS = body.attr('style');
|
||||
footerStyleFS = footer.attr('style');
|
||||
// restore non-fullscreen state to calculate proper destination sizes
|
||||
dialog.restoreTab();
|
||||
}
|
||||
|
||||
fromTab.hide();
|
||||
toTab.show();
|
||||
|
||||
// CONTROL POINT: at this point the destination dialog size is active
|
||||
// store destination positions and sizes
|
||||
|
||||
var newBodyHeight = fullscreen ? windowHeight - header.outerHeight() - footer.outerHeight() - dialogMargin*2 - bodyPadding : body.height();
|
||||
var newTabWidth = fullscreen ? windowWidth - dialogMargin*2 - dialogBorder - bodyPadding : toTab.width();
|
||||
var leftPos = toTab.position().left;
|
||||
var newDialogPosition = dialog.position();
|
||||
var newDialogWidth = dialog.width();
|
||||
var newDialogHeight = dialog.height();
|
||||
var newDialogMarginLeft = dialog.css('margin-left');
|
||||
var newDialogMarginTop = dialog.css('margin-top');
|
||||
|
||||
// restore source dialog size
|
||||
|
||||
if (options.fullscreen && options.back)
|
||||
{
|
||||
// restore fullscreen state
|
||||
dialog.attr('style', dialogStyleFS);
|
||||
body.attr('style', bodyStyleFS);
|
||||
footer.attr('style', footerStyleFS);
|
||||
}
|
||||
|
||||
body.css({position: '', height: oldBodyHeight});
|
||||
dialog.css('overflow', 'hidden');
|
||||
fromTab.css({position: 'absolute', left: leftPos, width: oldTabWidth});
|
||||
toTab.css({position: 'absolute', width: newTabWidth, height: newBodyHeight,
|
||||
left: sign * ((options.back ? newTabWidth : oldTabWidth) + bodyPadding)});
|
||||
fromTab.show();
|
||||
|
||||
// animate dialog to destination position and sizes
|
||||
|
||||
if (options.fullscreen && options.back)
|
||||
{
|
||||
body.css({position: 'absolute'});
|
||||
dialog.animate({
|
||||
'margin-left': newDialogMarginLeft,
|
||||
'margin-top': newDialogMarginTop,
|
||||
left: newDialogPosition.left,
|
||||
top: newDialogPosition.top,
|
||||
right: newDialogPosition.left + newDialogWidth,
|
||||
bottom: newDialogPosition.top + newDialogHeight,
|
||||
width: newDialogWidth,
|
||||
height: newDialogHeight
|
||||
},
|
||||
duration);
|
||||
|
||||
body.animate({height: newBodyHeight, 'max-height': newBodyHeight}, duration);
|
||||
}
|
||||
else if (options.fullscreen)
|
||||
{
|
||||
dialog.css({height: dialog.height()});
|
||||
footer.css({position: 'absolute', left: 0, right: 0, bottom: 0});
|
||||
dialog.animate({
|
||||
margin: dialogMargin,
|
||||
left: '0%', top: '0%', bottom: '0%', right: '0%',
|
||||
width: windowWidth - dialogMargin*2,
|
||||
height: windowHeight - dialogMargin*2
|
||||
},
|
||||
duration);
|
||||
|
||||
body.animate({height: newBodyHeight, 'max-height': newBodyHeight}, duration);
|
||||
}
|
||||
else
|
||||
{
|
||||
body.animate({height: newBodyHeight}, duration);
|
||||
}
|
||||
|
||||
fromTab.animate({left: sign * -((options.back ? newTabWidth : oldTabWidth) + bodyPadding)}, duration);
|
||||
toTab.animate({left: leftPos}, duration, function()
|
||||
{
|
||||
fromTab.hide();
|
||||
fromTab.css({position: '', width: '', height: '', left: ''});
|
||||
toTab.css({position: '', width: '', height: '', left: ''});
|
||||
dialog.css({overflow: '', width: (fullscreen ? 'auto' : ''), height: (fullscreen ? 'auto' : '')});
|
||||
if (fullscreen)
|
||||
{
|
||||
body.css({position: 'absolute', height: '', left: 0, right: 0,
|
||||
top: header.outerHeight(),
|
||||
bottom: footer.outerHeight(),
|
||||
'max-height': 'inherit'});
|
||||
}
|
||||
else
|
||||
{
|
||||
body.css({position: '', height: ''});
|
||||
}
|
||||
if (options.fullscreen && options.back)
|
||||
{
|
||||
// restore non-fullscreen state
|
||||
dialog.restoreTab();
|
||||
}
|
||||
if (options.complete)
|
||||
{
|
||||
options.complete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** REMOTE PROCEDURE CALLS VIA JSON-RPC *************************************************/
|
||||
|
||||
var RPC = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Properties
|
||||
this.rpcUrl;
|
||||
this.defaultFailureCallback;
|
||||
this.connectErrorMessage = 'Cannot establish connection';
|
||||
|
||||
this.call = function(method, params, completed_callback, failure_callback)
|
||||
{
|
||||
var _this = this;
|
||||
|
||||
var request = JSON.stringify({nocache: new Date().getTime(), method: method, params: params});
|
||||
var xhr = createXMLHttpRequest();
|
||||
|
||||
xhr.open('post', this.rpcUrl);
|
||||
|
||||
// Example for cross-domain access:
|
||||
//xhr.open('post', 'http://localhost:6789/jsonrpc');
|
||||
//xhr.withCredentials = 'true';
|
||||
//xhr.setRequestHeader('Authorization', 'Basic ' + window.btoa('myusername:mypassword'));
|
||||
|
||||
xhr.onreadystatechange = function()
|
||||
{
|
||||
if (xhr.readyState === 4)
|
||||
{
|
||||
var res = 'Unknown error';
|
||||
var result;
|
||||
if (xhr.status === 200)
|
||||
{
|
||||
if (xhr.responseText != '')
|
||||
{
|
||||
try
|
||||
{
|
||||
result = JSON.parse(xhr.responseText);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
res = e;
|
||||
}
|
||||
if (result)
|
||||
{
|
||||
if (result.error == null)
|
||||
{
|
||||
res = result.result;
|
||||
completed_callback(res);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = result.error.message + '<br><br>Request: ' + request;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res = 'No response received.';
|
||||
}
|
||||
}
|
||||
else if (xhr.status === 0)
|
||||
{
|
||||
res = _this.connectErrorMessage;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = 'Invalid Status: ' + xhr.status;
|
||||
}
|
||||
|
||||
if (failure_callback)
|
||||
{
|
||||
failure_callback(res, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
_this.defaultFailureCallback(res, result);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(request);
|
||||
}
|
||||
|
||||
function createXMLHttpRequest()
|
||||
{
|
||||
var xmlHttp;
|
||||
|
||||
if (window.XMLHttpRequest)
|
||||
{
|
||||
xmlHttp = new XMLHttpRequest();
|
||||
}
|
||||
else if (window.ActiveXObject)
|
||||
{
|
||||
try
|
||||
{
|
||||
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
try
|
||||
{
|
||||
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
throw(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlHttp==null)
|
||||
{
|
||||
alert("Your browser does not support XMLHTTP.");
|
||||
throw("Your browser does not support XMLHTTP.");
|
||||
}
|
||||
|
||||
return xmlHttp;
|
||||
}
|
||||
}(jQuery));
|
||||
5
win32.h
@@ -63,10 +63,13 @@
|
||||
/* Determine what socket length (socklen_t) data type is */
|
||||
#define SOCKLEN_T socklen_t
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#define HAVE_REGEX_H 1
|
||||
|
||||
/* Define to 1 if spinlocks are supported */
|
||||
#define HAVE_SPINLOCK
|
||||
|
||||
#define VERSION "0.8.0"
|
||||
#define VERSION "9.0-testing"
|
||||
|
||||
/* Suppress warnings */
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
|
||||