Compare commits

...

1 Commits

Author SHA1 Message Date
Andrey Prygunkov
3d9d43a542 version 10.2 2013-06-30 20:54:16 +00:00
75 changed files with 3974 additions and 6447 deletions

View File

@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <direct.h>
#else
@@ -136,9 +136,6 @@ void ArticleDownloader::Run()
debug("Entering ArticleDownloader-loop");
SetStatus(adRunning);
BuildOutputFilename();
m_szResultFilename = m_pArticleInfo->GetResultFilename();
if (g_pOptions->GetContinuePartial())
@@ -369,7 +366,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
for (int retry = 3; retry > 0; retry--)
{
szResponse = m_pConnection->Request(tmp);
if ((szResponse && !strncmp(szResponse, "2", 1)) || m_pConnection->GetAuthError())
if (szResponse && !strncmp(szResponse, "2", 1))
{
break;
}
@@ -591,8 +588,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
if (g_pOptions->GetDupeCheck())
{
m_pFileInfo->LockOutputFile();
bool bOutputInitialized = m_pFileInfo->GetOutputInitialized();
if (!bOutputInitialized)
if (!m_pFileInfo->GetOutputInitialized())
{
char* pb = strstr(szLine, " name=");
if (pb)
@@ -606,6 +602,11 @@ bool ArticleDownloader::PrepareFile(char* szLine)
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
}
if (m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
}
if (!g_pOptions->GetDirectWrite())
@@ -613,11 +614,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pFileInfo->SetOutputInitialized(true);
}
m_pFileInfo->UnlockOutputFile();
if (!bOutputInitialized && m_szArticleFilename && m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
}
}
if (g_pOptions->GetDirectWrite())
@@ -632,7 +628,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
long iArticleFilesize = atol(pb);
if (!CreateOutputFile(iArticleFilesize))
{
m_pFileInfo->UnlockOutputFile();
return false;
}
m_pFileInfo->SetOutputInitialized(true);
@@ -694,58 +689,22 @@ bool ArticleDownloader::CreateOutputFile(int iSize)
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
strncpy(szDestDir, m_szOutputFilename, iMaxlen);
szDestDir[iMaxlen] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(szDestDir, szErrBuf, sizeof(szErrBuf)))
if (!Util::ForceDirectories(szDestDir))
{
error("Could not create directory %s: %s", szDestDir, szErrBuf);
error("Could not create directory %s! Errcode: %i", szDestDir, errno);
return false;
}
if (!Util::CreateSparseFile(m_szOutputFilename, iSize))
{
error("Could not create file %s", m_szOutputFilename);
error("Could not create file %s!", m_szOutputFilename);
return false;
}
return true;
}
void ArticleDownloader::BuildOutputFilename()
{
char szFilename[1024];
snprintf(szFilename, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), m_pFileInfo->GetID(), m_pArticleInfo->GetPartNumber());
szFilename[1024-1] = '\0';
m_pArticleInfo->SetResultFilename(szFilename);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", szFilename);
tmpname[1024-1] = '\0';
SetTempFilename(tmpname);
if (g_pOptions->GetDirectWrite())
{
m_pFileInfo->LockOutputFile();
if (m_pFileInfo->GetOutputFilename())
{
strncpy(szFilename, m_pFileInfo->GetOutputFilename(), 1024);
szFilename[1024-1] = '\0';
}
else
{
snprintf(szFilename, 1024, "%s%c%i.out.tmp", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetID());
szFilename[1024-1] = '\0';
m_pFileInfo->SetOutputFilename(szFilename);
}
m_pFileInfo->UnlockOutputFile();
SetOutputFilename(szFilename);
}
}
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
{
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
@@ -767,7 +726,7 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
else
{
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFailed;
return adFatalError;
}
Decoder::EStatus eStatus = pDecoder->Check();
@@ -945,10 +904,9 @@ void ArticleDownloader::CompleteFileParts()
}
// Ensure the DstDir is created
char szErrBuf[1024];
if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
if (!Util::ForceDirectories(szNZBDestDir))
{
error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
error("Could not create directory %s! Errcode: %i", szNZBDestDir, errno);
SetStatus(adJoined);
return;
}
@@ -1115,6 +1073,27 @@ void ArticleDownloader::CompleteFileParts()
{
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
if (g_pOptions->GetRenameBroken())
{
char brokenfn[1024];
snprintf(brokenfn, 1024, "%s_broken", ofn);
brokenfn[1024-1] = '\0';
if (Util::MoveFile(ofn, brokenfn))
{
detail("Renaming broken file from %s to %s", ofn, brokenfn);
}
else
{
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
}
strncpy(ofn, brokenfn, 1024);
ofn[1024-1] = '\0';
}
else
{
detail("Not renaming broken file %s", ofn);
}
if (g_pOptions->GetCreateBrokenLog())
{
char szBrokenLogName[1024];
@@ -1147,10 +1126,9 @@ bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldD
}
// Ensure the DstDir is created
char szErrBuf[1024];
if (!Util::ForceDirectories(pNZBInfo->GetDestDir(), szErrBuf, sizeof(szErrBuf)))
if (!Util::ForceDirectories(pNZBInfo->GetDestDir()))
{
error("Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
error("Could not create directory %s! Errcode: %i", pNZBInfo->GetDestDir(), errno);
return false;
}

View File

@@ -77,14 +77,10 @@ private:
bool Write(char* szLine, int iLen);
bool PrepareFile(char* szLine);
bool CreateOutputFile(int iSize);
void BuildOutputFilename();
EStatus DecodeCheck();
void FreeConnection(bool bKeepConnected);
EStatus CheckResponse(const char* szResponse, const char* szComment);
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
public:
ArticleDownloader();
@@ -99,6 +95,9 @@ public:
bool Terminate();
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
const char* GetTempFilename() { return m_szTempFilename; }
void SetTempFilename(const char* v);
void SetOutputFilename(const char* v);
const char* GetArticleFilename() { return m_szArticleFilename; }
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -34,7 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <sys/socket.h>
@@ -52,13 +53,11 @@
#include "PrePostProcessor.h"
#include "Util.h"
#include "DownloadInfo.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern UrlCoordinator* g_pUrlCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Scanner* g_pScanner;
extern void ExitProc();
extern void Reload();
@@ -104,10 +103,9 @@ void BinRpcProcessor::Execute()
return;
}
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(m_MessageBase.m_szUsername, g_pOptions->GetControlUsername())) ||
strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword()))
{
warn("nzbget request received on port %i from %s, but username or password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
return;
}
@@ -343,10 +341,9 @@ void DownloadBinCommand::Execute()
return;
}
int iBufLen = ntohl(DownloadRequest.m_iTrailingDataLength);
char* pRecvBuffer = (char*)malloc(iBufLen);
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
if (!m_pConnection->Recv(pRecvBuffer, iBufLen))
if (!m_pConnection->Recv(pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength)))
{
error("invalid request");
free(pRecvBuffer);
@@ -355,17 +352,35 @@ void DownloadBinCommand::Execute()
int iPriority = ntohl(DownloadRequest.m_iPriority);
bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused);
bool bAddTop = ntohl(DownloadRequest.m_bAddFirst);
bool bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory,
iPriority, NULL, bAddTop, bAddPaused, NULL, pRecvBuffer, iBufLen, true);
char tmp[1024];
snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s",
Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(bOK, tmp);
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;
char tmp[1024];
snprintf(tmp, 1024, "Collection %s added to queue", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(true, tmp);
}
else
{
char tmp[1024];
snprintf(tmp, 1024, "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szFilename));
tmp[1024-1] = '\0';
SendBoolResponse(false, tmp);
}
free(pRecvBuffer);
}
@@ -782,7 +797,7 @@ void EditQueueBinCommand::Execute()
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset, szText);
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
}
free(pBuf);
@@ -832,6 +847,7 @@ void PostQueueBinCommand::Execute()
{
PostInfo* pPostInfo = *it;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1;
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
bufsize += strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1;
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
@@ -854,12 +870,15 @@ void PostQueueBinCommand::Execute()
pPostQueueAnswer->m_iTotalTimeSec = htonl((int)(pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0));
pPostQueueAnswer->m_iStageTimeSec = htonl((int)(pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0));
pPostQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1);
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1);
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
bufptr += sizeof(SNZBPostQueueResponseEntry);
strcpy(bufptr, pPostInfo->GetNZBInfo()->GetFilename());
bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
strcpy(bufptr, pPostInfo->GetParFilename());
bufptr += ntohl(pPostQueueAnswer->m_iParFilename);
strcpy(bufptr, pPostInfo->GetInfoName());
bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen);
strcpy(bufptr, pPostInfo->GetNZBInfo()->GetDestDir());
@@ -945,7 +964,7 @@ void ScanBinCommand::Execute()
bool bSyncMode = ntohl(ScanRequest.m_bSyncMode);
g_pScanner->ScanNZBDir(bSyncMode);
g_pPrePostProcessor->ScanNZBDir(bSyncMode);
SendBoolResponse(true, bSyncMode ? "Scan-Command completed" : "Scan-Command scheduled successfully");
}
@@ -1007,7 +1026,7 @@ void HistoryBinCommand::Execute()
pListAnswer->m_iSizeHi = htonl(iSizeHi);
pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount());
pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus());
pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatus());
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
{

View File

@@ -1,7 +1,3 @@
nzbget-11.0:
- please see subversion log at
http://sourceforge.net/p/nzbget/code/log
nzbget-10.2:
- fixed potential segfault which could happen with file paths longer
than 1024 characters;

View File

@@ -37,7 +37,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -219,7 +219,7 @@ bool Connection::Connect()
}
else
{
DoDisconnect();
Connection::DoDisconnect();
}
return bRes;
@@ -243,120 +243,35 @@ bool Connection::Disconnect()
return bRes;
}
bool Connection::Bind()
int Connection::Bind()
{
debug("Binding");
if (m_eStatus == csListening)
{
return true;
return 0;
}
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error("Could not resolve hostname %s", m_szHost);
return false;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return false;
}
}
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return false;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return false;
}
if (listen(m_iSocket, 100) < 0)
{
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return false;
}
m_eStatus = csListening;
int iRes = DoBind();
return true;
if (iRes == 0)
{
m_eStatus = csListening;
}
return iRes;
}
int Connection::WriteLine(const char* pBuffer)
{
//debug("Connection::WriteLine");
//debug("Connection::write(char* line)");
if (m_eStatus != csConnected)
{
return -1;
}
int iRes = send(m_iSocket, pBuffer, strlen(pBuffer), 0);
int iRes = DoWriteLine(pBuffer);
return iRes;
}
@@ -391,73 +306,9 @@ char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
return NULL;
}
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
}
return pBuffer;
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
return res;
}
Connection* Connection::Accept()
@@ -469,17 +320,13 @@ Connection* Connection::Accept()
return NULL;
}
SOCKET iSocket = accept(m_iSocket, NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
if (iSocket == INVALID_SOCKET)
SOCKET iRes = DoAccept();
if (iRes == INVALID_SOCKET)
{
return NULL;
}
Connection* pCon = new Connection(iSocket, m_bTLS);
Connection* pCon = new Connection(iRes, m_bTLS);
return pCon;
}
@@ -661,12 +508,200 @@ bool Connection::DoDisconnect()
return true;
}
int Connection::DoWriteLine(const char* pBuffer)
{
//debug("Connection::doWrite()");
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
}
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
{
//debug( "Connection::DoReadLine()" );
char* pBufPtr = pBuffer;
iSize--; // for trailing '0'
int iBytesRead = 0;
int iBufAvail = m_iBufAvail; // local variable is faster
char* szBufPtr = m_szBufPtr; // local variable is faster
while (iSize)
{
if (!iBufAvail)
{
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
if (iBufAvail < 0)
{
ReportError("Could not receive data on socket", NULL, true, 0);
break;
}
else if (iBufAvail == 0)
{
break;
}
szBufPtr = m_szReadBuf;
m_szReadBuf[iBufAvail] = '\0';
}
int len = 0;
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
if (p)
{
len = (int)(p - szBufPtr + 1);
}
else
{
len = iBufAvail;
}
if (len > iSize)
{
len = iSize;
}
memcpy(pBufPtr, szBufPtr, len);
pBufPtr += len;
szBufPtr += len;
iBufAvail -= len;
iBytesRead += len;
iSize -= len;
if (p)
{
break;
}
}
*pBufPtr = '\0';
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
m_szBufPtr = szBufPtr; // copy back to member
if (pBytesRead)
{
*pBytesRead = iBytesRead;
}
if (pBufPtr == pBuffer)
{
return NULL;
}
return pBuffer;
}
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
{
*iBufLen = m_iBufAvail;
*pBuffer = m_szBufPtr;
m_iBufAvail = 0;
};
};
int Connection::DoBind()
{
debug("Do binding");
#ifdef HAVE_GETADDRINFO
struct addrinfo addr_hints, *addr_list, *addr;
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
memset(&addr_hints, 0, sizeof(addr_hints));
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
addr_hints.ai_socktype = SOCK_STREAM,
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
sprintf(iPortStr, "%d", m_iPort);
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
if (res != 0)
{
error("Could not resolve hostname %s", m_szHost);
return -1;
}
m_iSocket = INVALID_SOCKET;
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
{
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (m_iSocket != INVALID_SOCKET)
{
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
if (res != -1)
{
// Connection established
break;
}
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
}
freeaddrinfo(addr_list);
#else
struct sockaddr_in sSocketAddress;
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
sSocketAddress.sin_family = AF_INET;
if (!m_szHost || strlen(m_szHost) == 0)
{
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
}
else
{
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
{
return -1;
}
}
sSocketAddress.sin_port = htons(m_iPort);
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Socket creation failed for %s", m_szHost, true, 0);
return -1;
}
int opt = 1;
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
if (res == -1)
{
// Connection failed
closesocket(m_iSocket);
m_iSocket = INVALID_SOCKET;
}
#endif
if (m_iSocket == INVALID_SOCKET)
{
ReportError("Binding socket failed for %s", m_szHost, true, 0);
return -1;
}
if (listen(m_iSocket, 100) < 0)
{
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
return -1;
}
return 0;
}
SOCKET Connection::DoAccept()
{
SOCKET iSocket = accept(m_iSocket, NULL, NULL);
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
{
ReportError("Could not accept connection", NULL, true, 0);
}
return iSocket;
}
void Connection::Cancel()
{

View File

@@ -72,8 +72,12 @@ protected:
Connection(SOCKET iSocket, bool bTLS);
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
bool DoConnect();
bool DoDisconnect();
virtual bool DoConnect();
virtual bool DoDisconnect();
int DoBind();
int DoWriteLine(const char* pBuffer);
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
SOCKET DoAccept();
#ifndef HAVE_GETADDRINFO
unsigned int ResolveHostAddr(const char* szHost);
#endif
@@ -88,9 +92,9 @@ public:
virtual ~Connection();
static void Init();
static void Final();
virtual bool Connect();
virtual bool Disconnect();
bool Bind();
bool Connect();
bool Disconnect();
int Bind();
bool Send(const char* pBuffer, int iSize);
bool Recv(char* pBuffer, int iSize);
int TryRecv(char* pBuffer, int iSize);

View File

@@ -77,12 +77,12 @@ bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
if (!outfile)
{
error("Error saving diskstate: Could not create file %s", fileName);
error("Could not create file %s", fileName);
perror(fileName);
return false;
}
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 25);
fprintf(outfile, "%s%i\n", FORMATVERSION_SIGNATURE, 21);
// save nzb-infos
SaveNZBList(pDownloadQueue, outfile);
@@ -129,14 +129,14 @@ bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
if (!infile)
{
error("Error reading diskstate: could not open file %s", fileName);
error("Could not open file %s", fileName);
return false;
}
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (iFormatVersion < 3 || iFormatVersion > 25)
if (iFormatVersion < 3 || iFormatVersion > 21)
{
error("Could not load diskstate due to file version mismatch");
fclose(infile);
@@ -198,14 +198,13 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
for (NZBInfoList::iterator it = pDownloadQueue->GetNZBInfoList()->begin(); it != pDownloadQueue->GetNZBInfoList()->end(); it++)
{
NZBInfo* pNZBInfo = *it;
fprintf(outfile, "%i\n", pNZBInfo->GetID());
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
fprintf(outfile, "%s\n", pNZBInfo->GetQueuedFilename());
fprintf(outfile, "%s\n", pNZBInfo->GetName());
fprintf(outfile, "%s\n", pNZBInfo->GetCategory());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetPostProcess());
fprintf(outfile, "%i,%i,%i,%i\n", (int)pNZBInfo->GetParStatus(), (int)pNZBInfo->GetUnpackStatus(), (int)pNZBInfo->GetMoveStatus(), (int)pNZBInfo->GetRenameStatus());
fprintf(outfile, "%i,%i,%i,%i,%i\n", (int)pNZBInfo->GetParStatus(), (int)pNZBInfo->GetUnpackStatus(), (int)pNZBInfo->GetScriptStatus(), (int)pNZBInfo->GetMoveStatus(), (int)pNZBInfo->GetRenameStatus());
fprintf(outfile, "%i\n", (int)pNZBInfo->GetUnpackCleanedUpDisk());
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
fprintf(outfile, "%i\n", pNZBInfo->GetParkedFileCount());
@@ -240,13 +239,6 @@ void DiskState::SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile)
fprintf(outfile, "%s=%s\n", pParameter->GetName(), pParameter->GetValue());
}
fprintf(outfile, "%i\n", pNZBInfo->GetScriptStatuses()->size());
for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++)
{
ScriptStatus* pScriptStatus = *it;
fprintf(outfile, "%i,%s\n", pScriptStatus->GetStatus(), pScriptStatus->GetName());
}
NZBInfo::Messages* pMessages = pNZBInfo->LockMessages();
fprintf(outfile, "%i\n", pMessages->size());
for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++)
@@ -273,13 +265,6 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
pNZBInfo->AddReference();
pDownloadQueue->GetNZBInfoList()->Add(pNZBInfo);
if (iFormatVersion >= 24)
{
int iID;
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
pNZBInfo->SetID(iID);
}
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pNZBInfo->SetFilename(buf);
@@ -327,18 +312,13 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
{
int iScriptStatus;
if (fscanf(infile, "%i\n", &iScriptStatus) != 1) goto error;
if (iScriptStatus > 1) iScriptStatus--;
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
pNZBInfo->SetScriptStatus((NZBInfo::EScriptStatus)iScriptStatus);
}
if (iFormatVersion >= 18)
{
int iParStatus, iUnpackStatus, iScriptStatus, iMoveStatus = 0, iRenameStatus = 0;
if (iFormatVersion >= 23)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iMoveStatus, &iRenameStatus) != 4) goto error;
}
else if (iFormatVersion >= 21)
if (iFormatVersion >= 21)
{
if (fscanf(infile, "%i,%i,%i,%i,%i\n", &iParStatus, &iUnpackStatus, &iScriptStatus, &iMoveStatus, &iRenameStatus) != 5) goto error;
}
@@ -352,13 +332,9 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
}
pNZBInfo->SetParStatus((NZBInfo::EParStatus)iParStatus);
pNZBInfo->SetUnpackStatus((NZBInfo::EUnpackStatus)iUnpackStatus);
pNZBInfo->SetScriptStatus((NZBInfo::EScriptStatus)iScriptStatus);
pNZBInfo->SetMoveStatus((NZBInfo::EMoveStatus)iMoveStatus);
pNZBInfo->SetRenameStatus((NZBInfo::ERenameStatus)iRenameStatus);
if (iFormatVersion < 23)
{
if (iScriptStatus > 1) iScriptStatus--;
pNZBInfo->GetScriptStatuses()->Add("SCRIPT", (ScriptStatus::EStatus)iScriptStatus);
}
}
if (iFormatVersion >= 19)
@@ -423,26 +399,6 @@ bool DiskState::LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
}
}
if (iFormatVersion >= 23)
{
int iScriptCount;
if (fscanf(infile, "%i\n", &iScriptCount) != 1) goto error;
for (int i = 0; i < iScriptCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
char* szScriptName = strchr(buf, ',');
if (szScriptName)
{
szScriptName++;
int iStatus = atoi(buf);
if (iStatus > 1 && iFormatVersion < 25) iStatus--;
pNZBInfo->GetScriptStatuses()->Add(szScriptName, (ScriptStatus::EStatus)iStatus);
}
}
}
if (iFormatVersion >= 11)
{
int iLogCount;
@@ -534,6 +490,7 @@ bool DiskState::LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQue
}
else
{
warn("Could not load diskstate for file %s", fileName);
delete pFileInfo;
}
}
@@ -561,7 +518,7 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
if (!outfile)
{
error("Error saving diskstate: could not create file %s", szFilename);
error("Could not create file %s", szFilename);
return false;
}
@@ -606,7 +563,7 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool
if (!infile)
{
error("Error reading diskstate: could not open file %s", szFilename);
error("Could not open file %s", szFilename);
return false;
}
@@ -675,8 +632,10 @@ void DiskState::SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile)
{
PostInfo* pPostInfo = *it;
int iNZBIndex = FindNZBInfoIndex(pDownloadQueue, pPostInfo->GetNZBInfo());
fprintf(outfile, "%i,%i\n", iNZBIndex, (int)pPostInfo->GetStage());
fprintf(outfile, "%i,%i,%i,%i\n", iNZBIndex,
(int)pPostInfo->GetParStatus(), (int)pPostInfo->GetUnpackStatus(), (int)pPostInfo->GetStage());
fprintf(outfile, "%s\n", pPostInfo->GetInfoName());
fprintf(outfile, "%s\n", pPostInfo->GetParFilename());
}
}
@@ -693,18 +652,18 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int i
for (int i = 0; i < size; i++)
{
PostInfo* pPostInfo = NULL;
unsigned int iNZBIndex, iStage, iDummy;
unsigned int iNZBIndex, iParCheck, iParStatus = 0, iUnpackStatus = 0, iStage;
if (iFormatVersion < 19)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
}
else if (iFormatVersion < 22)
{
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iDummy, &iDummy, &iStage) != 4) goto error;
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iParCheck, &iParStatus, &iStage) != 4) goto error;
if (!iParCheck)
{
iParStatus = PostInfo::psSkipped;
}
}
else
{
if (fscanf(infile, "%i,%i\n", &iNZBIndex, &iStage) != 2) goto error;
if (fscanf(infile, "%i,%i,%i,%i\n", &iNZBIndex, &iParStatus, &iUnpackStatus, &iStage) != 4) goto error;
}
if (iFormatVersion < 18 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
if (iFormatVersion < 21 && iStage > (int)PostInfo::ptVerifyingRepaired) iStage++;
@@ -714,6 +673,8 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int i
{
pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pDownloadQueue->GetNZBInfoList()->at(iNZBIndex - 1));
pPostInfo->SetParStatus((PostInfo::EParStatus)iParStatus);
pPostInfo->SetUnpackStatus((PostInfo::EUnpackStatus)iUnpackStatus);
pPostInfo->SetStage((PostInfo::EStage)iStage);
}
@@ -721,11 +682,9 @@ bool DiskState::LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int i
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (!bSkipPostQueue) pPostInfo->SetInfoName(buf);
if (iFormatVersion < 22)
{
// ParFilename, ignore
if (!fgets(buf, sizeof(buf), infile)) goto error;
}
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
if (!bSkipPostQueue) pPostInfo->SetParFilename(buf);
if (!bSkipPostQueue)
{
@@ -761,7 +720,7 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
if (!infile)
{
error("Error reading diskstate: could not open file %s", fileName);
error("Could not open file %s", fileName);
return false;
}
@@ -818,8 +777,9 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
pNZBInfo->SetDestDir(buf);
}
// ParFilename, ignore
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
pPostInfo->SetParFilename(buf);
if (!fgets(buf, sizeof(buf), infile)) goto error;
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
@@ -863,7 +823,7 @@ bool DiskState::LoadOldPostQueue(DownloadQueue* pDownloadQueue)
if (fscanf(infile, "%i\n", &iParCheck) != 1) goto error; // ParCheck
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
pNZBInfo->SetParStatus(iParCheck ? (NZBInfo::EParStatus)iIntValue : NZBInfo::psSkipped);
pPostInfo->SetParStatus(iParCheck ? (PostInfo::EParStatus)iIntValue : PostInfo::psSkipped);
if (iFormatVersion < 7)
{
@@ -954,7 +914,6 @@ error:
void DiskState::SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile)
{
fprintf(outfile, "%i\n", pUrlInfo->GetID());
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());
@@ -966,13 +925,6 @@ bool DiskState::LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion)
{
char buf[10240];
if (iFormatVersion >= 24)
{
int iID;
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
pUrlInfo->SetID(iID);
}
int iStatus, iPriority;
if (fscanf(infile, "%i,%i\n", &iStatus, &iPriority) != 2) goto error;
if (pUrlInfo) pUrlInfo->SetStatus((UrlInfo::EStatus)iStatus);
@@ -1013,7 +965,6 @@ void DiskState::SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile)
{
HistoryInfo* pHistoryInfo = *it;
fprintf(outfile, "%i\n", pHistoryInfo->GetID());
fprintf(outfile, "%i\n", (int)pHistoryInfo->GetKind());
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
@@ -1039,13 +990,6 @@ bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
for (int i = 0; i < size; i++)
{
HistoryInfo* pHistoryInfo = NULL;
int iID = 0;
if (iFormatVersion >= 24)
{
if (fscanf(infile, "%i\n", &iID) != 1) goto error;
}
HistoryInfo::EKind eKind = HistoryInfo::hkNZBInfo;
if (iFormatVersion >= 15)
@@ -1069,11 +1013,6 @@ bool DiskState::LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFo
pHistoryInfo = new HistoryInfo(pUrlInfo);
}
if (iFormatVersion >= 24)
{
pHistoryInfo->SetID(iID);
}
int iTime;
if (fscanf(infile, "%i\n", &iTime) != 1) goto error;
pHistoryInfo->SetTime((time_t)iTime);
@@ -1104,37 +1043,136 @@ int DiskState::FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo
}
/*
* Deletes whole download queue including history.
* Delete all files from Queue.
* Returns true if successful, false if not
*/
void DiskState::DiscardDownloadQueue()
bool DiskState::DiscardDownloadQueue()
{
debug("Discarding queue");
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
szFullFilename[1024-1] = '\0';
remove(szFullFilename);
char fileName[1024];
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
fileName[1024-1] = '\0';
DirBrowser dir(g_pOptions->GetQueueDir());
while (const char* filename = dir.Next())
FILE* infile = fopen(fileName, "rb");
if (!infile)
{
// delete all files whose names have only characters '0'..'9'
bool bOnlyNums = true;
for (const char* p = filename; *p != '\0'; p++)
error("Could not open file %s", fileName);
return false;
}
bool res = false;
char FileSignatur[128];
fgets(FileSignatur, sizeof(FileSignatur), infile);
int iFormatVersion = ParseFormatVersion(FileSignatur);
if (3 <= iFormatVersion && iFormatVersion <= 21)
{
// skip nzb-infos
int size = 0;
char buf[1024];
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
{
if (!('0' <= *p && *p <= '9'))
if (!fgets(buf, sizeof(buf), infile)) break; // filename
if (!fgets(buf, sizeof(buf), infile)) break; // destdir
if (iFormatVersion >= 5)
{
bOnlyNums = false;
break;
if (!fgets(buf, sizeof(buf), infile)) break; // localfile
}
if (iFormatVersion >= 13)
{
if (!fgets(buf, sizeof(buf), infile)) break; // name
}
if (iFormatVersion >= 4)
{
if (!fgets(buf, sizeof(buf), infile)) break; // category
if (!fgets(buf, sizeof(buf), infile)) break; // postprocess
}
if (iFormatVersion >= 8 && iFormatVersion < 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ParStatus
}
if (iFormatVersion >= 9 && iFormatVersion < 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ScriptStatus
}
if (iFormatVersion >= 18)
{
if (!fgets(buf, sizeof(buf), infile)) break; // ParStatus, UnpackStatus, ScriptStatus
}
if (iFormatVersion >= 19)
{
if (!fgets(buf, sizeof(buf), infile)) break; // UnpackCleanedUpDisk
}
if (!fgets(buf, sizeof(buf), infile)) break; // file count
if (iFormatVersion >= 10)
{
if (!fgets(buf, sizeof(buf), infile)) break; // parked file count
}
if (!fgets(buf, sizeof(buf), infile)) break; // file size
if (iFormatVersion >= 4)
{
// completed files
int iFileCount;
if (fscanf(infile, "%i\n", &iFileCount) != 1) break;
for (int i = 0; i < iFileCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break; // filename
}
}
if (iFormatVersion >= 6)
{
// postprocess-parameters
int iParameterCount;
if (fscanf(infile, "%i\n", &iParameterCount) != 1) break;
for (int i = 0; i < iParameterCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break;
}
}
if (iFormatVersion >= 11)
{
// log-messages
int iLogCount;
if (fscanf(infile, "%i\n", &iLogCount) != 1) break;
for (int i = 0; i < iLogCount; i++)
{
if (!fgets(buf, sizeof(buf), infile)) break;
}
}
}
if (bOnlyNums)
// file-infos
fscanf(infile, "%i\n", &size);
for (int i = 0; i < size; i++)
{
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetQueueDir(), filename);
szFullFilename[1024-1] = '\0';
remove(szFullFilename);
int id;
char tr[100];
if (fscanf(infile, "%i,%s\n", &id, &tr) == 2)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
fileName[1024-1] = '\0';
remove(fileName);
}
}
res = true;
}
else
{
error("Could not discard diskstate due to file version mismatch");
res = false;
}
fclose(infile);
if (res)
{
remove(fileName);
}
return res;
}
bool DiskState::DownloadQueueExists()

View File

@@ -56,7 +56,7 @@ public:
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
void DiscardDownloadQueue();
bool DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

@@ -34,10 +34,10 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include <cctype>
#include <cstdio>
#include <map>
#include <sys/stat.h>
#include "nzbget.h"
#include "DownloadInfo.h"
@@ -117,59 +117,6 @@ void NZBParameterList::SetParameter(const char* szName, const char* szValue)
}
ScriptStatus::ScriptStatus(const char* szName, EStatus eStatus)
{
m_szName = strdup(szName);
m_eStatus = eStatus;
}
ScriptStatus::~ScriptStatus()
{
if (m_szName)
{
free(m_szName);
}
}
ScriptStatusList::~ScriptStatusList()
{
Clear();
}
void ScriptStatusList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void ScriptStatusList::Add(const char* szScriptName, ScriptStatus::EStatus eStatus)
{
push_back(new ScriptStatus(szScriptName, eStatus));
}
ScriptStatus::EStatus ScriptStatusList::CalcTotalStatus()
{
ScriptStatus::EStatus eStatus = ScriptStatus::srNone;
for (iterator it = begin(); it != end(); it++)
{
ScriptStatus* pScriptStatus = *it;
// Failure-Status overrides Success-Status
if ((pScriptStatus->GetStatus() == ScriptStatus::srSuccess && eStatus == ScriptStatus::srNone) ||
(pScriptStatus->GetStatus() == ScriptStatus::srFailure))
{
eStatus = pScriptStatus->GetStatus();
}
}
return eStatus;
}
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
@@ -186,8 +133,8 @@ NZBInfo::NZBInfo()
m_eRenameStatus = rsNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eCleanupStatus = csNone;
m_eMoveStatus = msNone;
m_eScriptStatus = srNone;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
@@ -259,15 +206,6 @@ void NZBInfo::Release()
}
}
void NZBInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
}
}
void NZBInfo::ClearCompletedFiles()
{
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
@@ -399,9 +337,12 @@ void NZBInfo::BuildFinalDirName(char* szFinalDirBuf, int iBufSize)
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
snprintf(szBuffer, 1024, "%s%s", szFinalDirBuf, GetName());
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
if (g_pOptions->GetAppendNZBDir())
{
snprintf(szBuffer, 1024, "%s%s", szFinalDirBuf, GetName());
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
}
}
void NZBInfo::SetParameter(const char* szName, const char* szValue)
@@ -519,7 +460,6 @@ FileInfo::FileInfo()
m_szSubject = NULL;
m_szFilename = NULL;
m_szOutputFilename = NULL;
m_pMutexOutputFile = NULL;
m_bFilenameConfirmed = false;
m_lSize = 0;
m_lRemainingSize = 0;
@@ -532,7 +472,6 @@ FileInfo::FileInfo()
m_iPriority = 0;
m_bExtraPriority = false;
m_iActiveDownloads = 0;
m_bAutoDeleted = false;
m_iIDGen++;
m_iID = m_iIDGen;
}
@@ -553,10 +492,6 @@ FileInfo::~ FileInfo()
{
free(m_szOutputFilename);
}
if (m_pMutexOutputFile)
{
delete m_pMutexOutputFile;
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
@@ -581,9 +516,9 @@ void FileInfo::ClearArticles()
m_Articles.clear();
}
void FileInfo::SetID(int iID)
void FileInfo::SetID(int s)
{
m_iID = iID;
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
@@ -621,12 +556,12 @@ void FileInfo::MakeValidFilename()
void FileInfo::LockOutputFile()
{
m_pMutexOutputFile->Lock();
m_mutexOutputFile.Lock();
}
void FileInfo::UnlockOutputFile()
{
m_pMutexOutputFile->Unlock();
m_mutexOutputFile.Unlock();
}
void FileInfo::SetOutputFilename(const char* szOutputFilename)
@@ -638,21 +573,6 @@ void FileInfo::SetOutputFilename(const char* szOutputFilename)
m_szOutputFilename = strdup(szOutputFilename);
}
void FileInfo::SetActiveDownloads(int iActiveDownloads)
{
m_iActiveDownloads = iActiveDownloads;
if (m_iActiveDownloads > 0 && !m_pMutexOutputFile)
{
m_pMutexOutputFile = new Mutex();
}
else if (m_iActiveDownloads == 0 && m_pMutexOutputFile)
{
delete m_pMutexOutputFile;
m_pMutexOutputFile = NULL;
}
}
bool FileInfo::IsDupe(const char* szFilename)
{
char fileName[1024];
@@ -701,11 +621,16 @@ PostInfo::PostInfo()
debug("Creating PostInfo");
m_pNZBInfo = NULL;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_bWorking = false;
m_bDeleted = false;
m_bRequestParCheck = false;
m_eRenameStatus = rsNone;
m_eParStatus = psNone;
m_eUnpackStatus = usNone;
m_eRequestParCheck = rpNone;
m_bRequestParRename = false;
m_eScriptStatus = srNone;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
@@ -723,6 +648,10 @@ PostInfo::~ PostInfo()
{
debug("Destroying PostInfo");
if (m_szParFilename)
{
free(m_szParFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
@@ -754,6 +683,11 @@ void PostInfo::SetNZBInfo(NZBInfo* pNZBInfo)
m_pNZBInfo->AddReference();
}
void PostInfo::SetParFilename(const char* szParFilename)
{
m_szParFilename = strdup(szParFilename);
}
void PostInfo::SetInfoName(const char* szInfoName)
{
m_szInfoName = strdup(szInfoName);
@@ -903,9 +837,9 @@ void UrlInfo::SetURL(const char* szURL)
m_szURL = strdup(szURL);
}
void UrlInfo::SetID(int iID)
void UrlInfo::SetID(int s)
{
m_iID = iID;
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;
@@ -985,9 +919,9 @@ HistoryInfo::~HistoryInfo()
}
}
void HistoryInfo::SetID(int iID)
void HistoryInfo::SetID(int s)
{
m_iID = iID;
m_iID = s;
if (m_iIDGen < m_iID)
{
m_iIDGen = m_iID;

View File

@@ -92,11 +92,10 @@ private:
int m_iCompleted;
bool m_bOutputInitialized;
char* m_szOutputFilename;
Mutex* m_pMutexOutputFile;
Mutex m_mutexOutputFile;
int m_iPriority;
bool m_bExtraPriority;
int m_iActiveDownloads;
bool m_bAutoDeleted;
static int m_iIDGen;
@@ -104,7 +103,7 @@ public:
FileInfo();
~FileInfo();
int GetID() { return m_iID; }
void SetID(int iID);
void SetID(int s);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
@@ -141,9 +140,7 @@ public:
bool GetExtraPriority() { return m_bExtraPriority; }
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
int GetActiveDownloads() { return m_iActiveDownloads; }
void SetActiveDownloads(int iActiveDownloads);
bool GetAutoDeleted() { return m_bAutoDeleted; }
void SetAutoDeleted(bool bAutoDeleted) { m_bAutoDeleted = bAutoDeleted; }
void SetActiveDownloads(int iActiveDownloads) { m_iActiveDownloads = iActiveDownloads; }
};
typedef std::deque<FileInfo*> FileQueue;
@@ -212,40 +209,6 @@ public:
void SetParameter(const char* szName, const char* szValue);
};
class ScriptStatus
{
public:
enum EStatus
{
srNone,
srFailure,
srSuccess
};
private:
char* m_szName;
EStatus m_eStatus;
friend class ScriptStatusList;
public:
ScriptStatus(const char* szName, EStatus eStatus);
~ScriptStatus();
const char* GetName() { return m_szName; }
EStatus GetStatus() { return m_eStatus; }
};
typedef std::deque<ScriptStatus*> ScriptStatusListBase;
class ScriptStatusList : public ScriptStatusListBase
{
public:
~ScriptStatusList();
void Add(const char* szScriptName, ScriptStatus::EStatus eStatus);
void Clear();
ScriptStatus::EStatus CalcTotalStatus();
};
class NZBInfoList;
class NZBInfo
@@ -265,8 +228,7 @@ public:
psSkipped,
psFailure,
psSuccess,
psRepairPossible,
psManual
psRepairPossible
};
enum EUnpackStatus
@@ -277,11 +239,12 @@ public:
usSuccess
};
enum ECleanupStatus
enum EScriptStatus
{
csNone,
csFailure,
csSuccess
srNone,
srUnknown,
srFailure,
srSuccess
};
enum EMoveStatus
@@ -309,17 +272,15 @@ private:
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
ECleanupStatus m_eCleanupStatus;
EScriptStatus m_eScriptStatus;
EMoveStatus m_eMoveStatus;
char* m_szQueuedFilename;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bParManual;
bool m_bCleanupDisk;
bool m_bUnpackCleanedUpDisk;
NZBInfoList* m_Owner;
NZBParameterList m_ppParameters;
ScriptStatusList m_scriptStatuses;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
@@ -334,7 +295,6 @@ public:
void AddReference();
void Release();
int GetID() { return m_iID; }
void SetID(int iID);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
@@ -362,10 +322,10 @@ public:
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ECleanupStatus GetCleanupStatus() { return m_eCleanupStatus; }
void SetCleanupStatus(ECleanupStatus eCleanupStatus) { m_eCleanupStatus = eCleanupStatus; }
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleted() { return m_bDeleted; }
@@ -378,7 +338,6 @@ public:
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
ScriptStatusList* GetScriptStatuses() { return &m_scriptStatuses; } // needs locking (for shared objects)
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
@@ -411,15 +370,60 @@ public:
ptFinished
};
enum ERenameStatus
{
rsNone,
rsSkipped,
rsFailure,
rsSuccess
};
enum EParStatus
{
psNone,
psSkipped,
psFailure,
psSuccess,
psRepairPossible
};
enum ERequestParCheck
{
rpNone,
rpCurrent,
rpAll
};
enum EUnpackStatus
{
usNone,
usSkipped,
usFailure,
usSuccess
};
enum EScriptStatus
{
srNone,
srUnknown,
srFailure,
srSuccess
};
typedef std::deque<Message*> Messages;
private:
int m_iID;
NZBInfo* m_pNZBInfo;
char* m_szParFilename;
char* m_szInfoName;
bool m_bWorking;
bool m_bDeleted;
bool m_bRequestParCheck;
ERenameStatus m_eRenameStatus;
EParStatus m_eParStatus;
EUnpackStatus m_eUnpackStatus;
EScriptStatus m_eScriptStatus;
ERequestParCheck m_eRequestParCheck;
bool m_bRequestParRename;
EStage m_eStage;
char* m_szProgressLabel;
@@ -441,6 +445,8 @@ public:
int GetID() { return m_iID; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
EStage GetStage() { return m_eStage; }
@@ -459,10 +465,18 @@ public:
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetRequestParCheck() { return m_bRequestParCheck; }
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
EParStatus GetParStatus() { return m_eParStatus; }
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
bool GetRequestParRename() { return m_bRequestParRename; }
void SetRequestParRename(bool bRequestParRename) { m_bRequestParRename = bRequestParRename; }
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
@@ -504,7 +518,7 @@ public:
UrlInfo();
~UrlInfo();
int GetID() { return m_iID; }
void SetID(int 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)
@@ -548,7 +562,7 @@ public:
HistoryInfo(UrlInfo* pUrlInfo);
~HistoryInfo();
int GetID() { return m_iID; }
void SetID(int iID);
void SetID(int s);
EKind GetKind() { return m_eKind; }
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -34,7 +34,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
@@ -235,10 +236,6 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -38,7 +38,7 @@
#include <string.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <stdio.h>
#include <cstdio>
#include "nzbget.h"
#include "Options.h"

View File

@@ -37,7 +37,7 @@ nzbget_SOURCES = \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
@@ -51,7 +51,7 @@ doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf
nzbget.conf nzbget-postprocess.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
@@ -64,18 +64,16 @@ webui_FILES = \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
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)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Note about "sed":
# We need to make some changes in installed files.
@@ -93,15 +91,12 @@ install-exec-hook:
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
# 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:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
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
@@ -110,9 +105,18 @@ 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:

View File

@@ -62,7 +62,7 @@ host_triplet = @host@
target_triplet = @target@
bin_PROGRAMS = nzbget$(EXEEXT)
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
$(dist_exampleconf_DATA) $(nobase_dist_ppscripts_SCRIPTS) \
$(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 \
@@ -77,9 +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)" "$(DESTDIR)$(ppscriptsdir)" \
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" \
"$(DESTDIR)$(webuidir)"
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
PROGRAMS = $(bin_PROGRAMS)
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
@@ -100,15 +101,9 @@ am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
nzbget.$(OBJEXT)
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
nzbget_LDADD = $(LDADD)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
nobase_dist_ppscriptsSCRIPT_INSTALL = $(install_sh_SCRIPT)
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
SCRIPTS = $(nobase_dist_ppscripts_SCRIPTS) $(sbin_SCRIPTS)
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -123,11 +118,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) \
$(nobase_dist_webui_DATA)
$(dist_webuiconf_DATA) $(nobase_dist_webui_DATA)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -255,7 +257,7 @@ nzbget_SOURCES = \
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
EXTRA_DIST = \
Makefile.cvs nzbgetd \
Makefile.cvs nzbgetd nzbget-postprocess.sh \
$(patches_FILES) $(windows_FILES)
patches_FILES = \
@@ -269,7 +271,7 @@ doc_FILES = \
README ChangeLog COPYING
exampleconf_FILES = \
nzbget.conf
nzbget.conf nzbget-postprocess.conf
webui_FILES = \
webui/index.html webui/index.js webui/downloads.js webui/edit.js webui/fasttable.js \
@@ -282,19 +284,17 @@ webui_FILES = \
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
webui/img/transmit-reload-2x.gif
ppscripts_FILES = \
ppscripts/EMail.py ppscripts/Logger.py
# Install
sbin_SCRIPTS = nzbgetd
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)
ppscriptsdir = $(datadir)/nzbget
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
# Ignore "svn_version.cpp" in distcleancheck
distcleancheck_listfiles = \
@@ -382,30 +382,24 @@ clean-binPROGRAMS:
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
@rm -f nzbget$(EXEEXT)
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
install-nobase_dist_ppscriptsSCRIPTS: $(nobase_dist_ppscripts_SCRIPTS)
install-binSCRIPTS: $(bin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(ppscriptsdir)" || $(mkdir_p) "$(DESTDIR)$(ppscriptsdir)"
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
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)'`; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " $(nobase_dist_ppscriptsSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
$(nobase_dist_ppscriptsSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ppscriptsdir)/$$f"; \
echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
else :; fi; \
done
uninstall-nobase_dist_ppscriptsSCRIPTS:
uninstall-binSCRIPTS:
@$(NORMAL_UNINSTALL)
@$(am__vpath_adj_setup) \
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
$(am__vpath_adj) p=$$f; \
@list='$(bin_SCRIPTS)'; for p in $$list; do \
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
echo " rm -f '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
rm -f "$(DESTDIR)$(ppscriptsdir)/$$f"; \
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
rm -f "$(DESTDIR)$(bindir)/$$f"; \
done
install-sbinSCRIPTS: $(sbin_SCRIPTS)
@$(NORMAL_INSTALL)
@@ -520,6 +514,23 @@ uninstall-dist_exampleconfDATA:
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)"
@@ -591,7 +602,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
$(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 \
@@ -722,7 +733,7 @@ check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
installdirs:
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; 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
@@ -771,12 +782,12 @@ info: info-am
info-am:
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA
install-dist_webuiconfDATA install-nobase_dist_webuiDATA
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
install-exec-am: install-binPROGRAMS install-sbinSCRIPTS
install-exec-am: install-binPROGRAMS install-binSCRIPTS \
install-sbinSCRIPTS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
@@ -805,9 +816,9 @@ ps: ps-am
ps-am:
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
uninstall-dist_exampleconfDATA uninstall-info-am \
uninstall-nobase_dist_ppscriptsSCRIPTS \
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 \
@@ -816,19 +827,19 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
distclean distclean-compile distclean-generic distclean-hdr \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
install-binPROGRAMS install-data install-data-am \
install-data-hook install-dist_docDATA \
install-dist_exampleconfDATA install-exec install-exec-am \
install-exec-hook install-info install-info-am install-man \
install-nobase_dist_ppscriptsSCRIPTS \
install-nobase_dist_webuiDATA install-sbinSCRIPTS \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-binPROGRAMS \
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
uninstall-info-am uninstall-nobase_dist_ppscriptsSCRIPTS \
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
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":
@@ -847,15 +858,12 @@ install-exec-hook:
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
# Prepare example configuration file
# 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:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
sed 's:typically installed to /usr/local/share/nzbget/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
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
@@ -864,9 +872,18 @@ 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:

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -27,7 +27,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6217; // = "nzb-XX" (protocol version)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6212; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -81,7 +81,6 @@ enum eRemoteEditAction
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
eRemoteEditActionFileSetPriority, // set priority for files
eRemoteEditActionFileReorder, // (not supported)
eRemoteEditActionFileSplit, // split - create new group from selected files
eRemoteEditActionGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
eRemoteEditActionGroupMoveTop, // move group to the top of download-queue
eRemoteEditActionGroupMoveBottom, // move group to the bottom of download-queue
@@ -101,8 +100,7 @@ enum eRemoteEditAction
eRemoteEditActionPostDelete, // delete post-job
eRemoteEditActionHistoryDelete, // delete history-item
eRemoteEditActionHistoryReturn, // move history-item back to download queue
eRemoteEditActionHistoryProcess, // move history-item back to download queue and start postprocessing
eRemoteEditActionHistorySetParameter // set post-process parameter for history-item
eRemoteEditActionHistoryProcess // move history-item back to download queue and start postprocessing
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
@@ -128,8 +126,7 @@ struct SNZBRequestBase
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
int32_t m_iStructSize; // Size of the entire struct
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
char m_szUsername[NZBREQUESTPASSWORDSIZE]; // User name
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
};
// The basic SNZBResposneBase struct, used in all responses
@@ -425,10 +422,12 @@ struct SNZBPostQueueResponseEntry
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
//char m_szParFilename[m_iParFilename]; // variable sized
//char m_szInfoName[m_iInfoNameLen]; // variable sized
//char m_szDestDir[m_iDestDirLen]; // variable sized
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized

View File

@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include "nzbget.h"
#include "Log.h"
@@ -85,14 +85,17 @@ const char* NNTPConnection::Request(const char* req)
{
debug("%s requested authorization", GetHost());
//authentication required!
if (!Authenticate())
{
m_bAuthError = true;
return NULL;
}
//try again
WriteLine(req);
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
return answer;
}
return answer;
@@ -100,16 +103,13 @@ const char* NNTPConnection::Request(const char* req)
bool NNTPConnection::Authenticate()
{
if (!m_pNewsServer->GetUser() || strlen(m_pNewsServer->GetUser()) == 0 ||
!m_pNewsServer->GetPassword() || strlen(m_pNewsServer->GetPassword()) == 0)
if (!(m_pNewsServer)->GetUser() ||
!(m_pNewsServer)->GetPassword())
{
error("Server%i (%s) requested authorization but username/password are not set in settings", m_pNewsServer->GetID(), m_pNewsServer->GetHost());
m_bAuthError = true;
return false;
return true;
}
m_bAuthError = !AuthInfoUser(0);
return !m_bAuthError;
return AuthInfoUser();
}
bool NNTPConnection::AuthInfoUser(int iRecur)
@@ -128,7 +128,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportError("Authorization for %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
@@ -150,7 +150,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
@@ -171,7 +171,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
ReportError("Authorization for %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
else if (!strncmp(answer, "2", 1))
@@ -188,7 +188,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
if (GetStatus() != csCancelled)
{
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
}
return false;
}
@@ -207,6 +207,10 @@ const char* NNTPConnection::JoinGroup(const char* grp)
tmp[1024-1] = '\0';
const char* answer = Request(tmp);
if (m_bAuthError)
{
return answer;
}
if (answer && !strncmp(answer, "2", 1))
{
@@ -226,40 +230,26 @@ const char* NNTPConnection::JoinGroup(const char* grp)
return answer;
}
bool NNTPConnection::Connect()
bool NNTPConnection::DoConnect()
{
debug("Opening connection to %s", GetHost());
if (m_eStatus == csConnected)
bool res = Connection::DoConnect();
if (!res)
{
return true;
return res;
}
if (!Connection::Connect())
{
return false;
}
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
if (!answer)
{
ReportErrorAnswer("Connection to server%i (%s) failed: Connection closed by remote host", NULL);
Disconnect();
ReportError("Connection to %s failed: Connection closed by remote host", GetHost(), false, 0);
return false;
}
if (strncmp(answer, "2", 1))
{
ReportErrorAnswer("Connection to server%i (%s) failed (Answer: %s)", answer);
Disconnect();
return false;
}
if ((m_pNewsServer->GetUser() && strlen(m_pNewsServer->GetUser()) > 0 &&
m_pNewsServer->GetPassword() && strlen(m_pNewsServer->GetPassword()) > 0) &&
!Authenticate())
{
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
return false;
}
@@ -268,7 +258,7 @@ bool NNTPConnection::Connect()
return true;
}
bool NNTPConnection::Disconnect()
bool NNTPConnection::DoDisconnect()
{
if (m_eStatus == csConnected)
{
@@ -279,13 +269,13 @@ bool NNTPConnection::Disconnect()
m_szActiveGroup = NULL;
}
}
return Connection::Disconnect();
return Connection::DoDisconnect();
}
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
{
char szErrStr[1024];
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetID(), m_pNewsServer->GetHost(), szAnswer);
snprintf(szErrStr, 1024, szMsgPrefix, GetHost(), szAnswer);
szErrStr[1024-1] = '\0';
ReportError(szErrStr, NULL, false, 0);

View File

@@ -33,26 +33,26 @@
class NNTPConnection : public Connection
{
private:
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
NewsServer* m_pNewsServer;
char* m_szActiveGroup;
char* m_szLineBuf;
bool m_bAuthError;
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
bool Authenticate();
bool AuthInfoUser(int iRecur);
bool AuthInfoPass(int iRecur);
virtual bool DoConnect();
virtual bool DoDisconnect();
void Clear();
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
public:
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
virtual bool Connect();
virtual bool Disconnect();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
NNTPConnection(NewsServer* pNewsServer);
virtual ~NNTPConnection();
NewsServer* GetNewsServer() { return m_pNewsServer; }
const char* Request(const char* req);
bool Authenticate();
bool AuthInfoUser(int iRecur = 0);
bool AuthInfoPass(int iRecur = 0);
const char* JoinGroup(const char* grp);
bool GetAuthError() { return m_bAuthError; }
};
#endif

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -121,6 +121,16 @@ void NZBFile::DetachFileInfos()
m_FileInfos.clear();
}
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
{
return Create(szFileName, szCategory, szBuffer, iSize, true);
}
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
{
return Create(szFileName, szCategory, NULL, 0, false);
}
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
// make Article-List big enough
@@ -347,7 +357,7 @@ void NZBFile::ProcessFilenames()
}
#ifdef WIN32
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
CoInitialize(NULL);
@@ -364,15 +374,21 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
doc->put_resolveExternals(VARIANT_FALSE);
doc->put_validateOnParse(VARIANT_FALSE);
doc->put_async(VARIANT_FALSE);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
VARIANT_BOOL success = doc->load(v);
VARIANT_BOOL success;
if (bFromBuffer)
{
success = doc->loadXML(szBuffer);
}
else
{
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
success = doc->load(v);
}
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
@@ -466,14 +482,11 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
int partNumber = atoi(number);
int lsize = atoi(bytes);
if (partNumber > 0)
{
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
ArticleInfo* pArticle = new ArticleInfo();
pArticle->SetPartNumber(partNumber);
pArticle->SetMessageID(szId);
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
if (lsize > 0)
{
@@ -488,7 +501,7 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
#else
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
{
NZBFile* pFile = new NZBFile(szFileName, szCategory);
@@ -499,10 +512,18 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
int ret = 0;
pFile->m_bIgnoreNextError = false;
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (bFromBuffer)
{
ret = xmlSAXUserParseMemory(&SAX_handler, pFile, szBuffer, iSize);
}
else
{
ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
}
if (ret == 0)
{
pFile->ProcessFilenames();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -66,10 +66,12 @@ private:
void Parse_EndElement(const char *name);
void Parse_Content(const char *buf, int len);
#endif
static NZBFile* Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer);
public:
virtual ~NZBFile();
static NZBFile* Create(const char* szFileName, const char* szCategory);
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize);
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }

View File

File diff suppressed because it is too large Load Diff

123
Options.h
View File

@@ -28,11 +28,8 @@
#define OPTIONS_H
#include <vector>
#include <list>
#include <time.h>
#include "Thread.h"
#include "Util.h"
class Options
{
@@ -80,11 +77,11 @@ public:
omColored,
omNCurses
};
enum EParCheck
enum ELoadPars
{
pcAuto,
pcForce,
pcManual
lpNone,
lpOne,
lpAll
};
enum EParScan
{
@@ -109,6 +106,12 @@ public:
mmRegEx
};
enum EDomain
{
dmServer = 1,
dmPostProcess
};
class OptEntry
{
private:
@@ -142,31 +145,6 @@ public:
OptEntry* FindOption(const char* szName);
};
class ConfigTemplate
{
private:
char* m_szName;
char* m_szDisplayName;
char* m_szTemplate;
friend class Options;
public:
ConfigTemplate(const char* szName, const char* szDisplayName, const char* szTemplate);
~ConfigTemplate();
const char* GetName() { return m_szName; }
const char* GetDisplayName() { return m_szDisplayName; }
const char* GetTemplate() { return m_szTemplate; }
};
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
class ConfigTemplates: public ConfigTemplatesBase
{
public:
~ConfigTemplates();
};
typedef std::vector<char*> NameList;
class Category
@@ -174,14 +152,12 @@ public:
private:
char* m_szName;
char* m_szDestDir;
char* m_szDefScript;
public:
Category(const char* szName, const char* szDestDir, const char* szDefScript);
Category(const char* szName, const char* szDestDir);
~Category();
const char* GetName() { return m_szName; }
const char* GetDestDir() { return m_szDestDir; }
const char* GetDefScript() { return m_szDefScript; }
};
typedef std::vector<Category*> CategoriesBase;
@@ -193,31 +169,6 @@ public:
Category* FindCategory(const char* szName);
};
class Script
{
private:
char* m_szName;
char* m_szLocation;
char* m_szDisplayName;
public:
Script(const char* szName, const char* szLocation);
~Script();
const char* GetName() { return m_szName; }
const char* GetLocation() { return m_szLocation; }
void SetDisplayName(const char* szDisplayName);
const char* GetDisplayName() { return m_szDisplayName; }
};
typedef std::list<Script*> ScriptListBase;
class ScriptList: public ScriptListBase
{
public:
~ScriptList();
Script* Find(const char* szName);
};
private:
OptEntries m_OptEntries;
bool m_bConfigInitialized;
@@ -234,8 +185,6 @@ private:
char* m_szQueueDir;
char* m_szNzbDir;
char* m_szWebDir;
char* m_szConfigTemplate;
char* m_szScriptDir;
EMessageTarget m_eInfoTarget;
EMessageTarget m_eWarningTarget;
EMessageTarget m_eErrorTarget;
@@ -246,14 +195,15 @@ private:
bool m_bResetLog;
int m_iConnectionTimeout;
int m_iTerminateTimeout;
bool m_bAppendNZBDir;
bool m_bAppendCategoryDir;
bool m_bContinuePartial;
bool m_bRenameBroken;
int m_iRetries;
int m_iRetryInterval;
bool m_bSaveQueue;
bool m_bDupeCheck;
char* m_szControlIP;
char* m_szControlUsername;
char* m_szControlPassword;
int m_iControlPort;
bool m_bSecureControl;
@@ -261,7 +211,7 @@ private:
char* m_szSecureCert;
char* m_szSecureKey;
char* m_szLockFile;
char* m_szDaemonUsername;
char* m_szDaemonUserName;
EOutputMode m_eOutputMode;
bool m_bReloadQueue;
bool m_bReloadUrlQueue;
@@ -270,11 +220,12 @@ private:
int m_iLogBufferSize;
bool m_bCreateLog;
char* m_szLogFile;
EParCheck m_eParCheck;
ELoadPars m_eLoadPars;
bool m_bParCheck;
bool m_bParRepair;
EParScan m_eParScan;
char* m_szDefScript;
char* m_szScriptOrder;
char* m_szPostProcess;
char* m_szPostConfigFilename;
char* m_szNZBProcess;
char* m_szNZBAddedProcess;
bool m_bStrictParName;
@@ -285,16 +236,19 @@ private:
bool m_bCursesTime;
bool m_bCursesGroup;
bool m_bCrcCheck;
int m_iThreadLimit;
bool m_bDirectWrite;
int m_iWriteBufferSize;
int m_iNzbDirInterval;
int m_iNzbDirFileAge;
bool m_bParCleanupQueue;
int m_iDiskSpace;
EScriptLogKind m_eProcessLogKind;
bool m_bAllowReProcess;
bool m_bTLS;
bool m_bDumpCore;
bool m_bParPauseQueue;
bool m_bScriptPauseQueue;
bool m_bPostPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
@@ -306,7 +260,6 @@ private:
char* m_szUnrarCmd;
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
// Parsed command-line parameters
bool m_bServerMode;
@@ -345,6 +298,7 @@ private:
void InitOptFile();
void InitCommandLine(int argc, char* argv[]);
void InitOptions();
void InitPostConfig();
void InitFileArg(int argc, char* argv[]);
void InitServers();
void InitCategories();
@@ -369,19 +323,14 @@ private:
void ConfigError(const char* msg, ...);
void ConfigWarn(const char* msg, ...);
void LocateOptionSrcPos(const char *szOptionName);
void ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue, int iValueBufLen);
static bool CompareScripts(Script* pScript1, Script* pScript2);
void LoadScriptDir(ScriptList* pScriptList, const char* szDirectory, bool bIsSubDir);
void BuildScriptDisplayNames(ScriptList* pScriptList);
void ConvertOldOptionName(char *szOption, int iBufLen);
public:
Options(int argc, char* argv[]);
~Options();
bool LoadConfig(OptEntries* pOptEntries);
bool SaveConfig(OptEntries* pOptEntries);
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
void LoadScriptList(ScriptList* pScriptList);
bool LoadConfig(EDomain eDomain, OptEntries* pOptEntries);
bool SaveConfig(EDomain eDomain, OptEntries* pOptEntries);
// Options
OptEntries* LockOptEntries();
@@ -393,8 +342,6 @@ public:
const char* GetQueueDir() { return m_szQueueDir; }
const char* GetNzbDir() { return m_szNzbDir; }
const char* GetWebDir() { return m_szWebDir; }
const char* GetConfigTemplate() { return m_szConfigTemplate; }
const char* GetScriptDir() { return m_szScriptDir; }
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
bool GetResetLog() const { return m_bResetLog; }
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
@@ -405,14 +352,15 @@ public:
int GetConnectionTimeout() { return m_iConnectionTimeout; }
int GetTerminateTimeout() { return m_iTerminateTimeout; }
bool GetDecode() { return m_bDecode; };
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
bool GetContinuePartial() { return m_bContinuePartial; }
bool GetRenameBroken() { return m_bRenameBroken; }
int GetRetries() { return m_iRetries; }
int GetRetryInterval() { return m_iRetryInterval; }
bool GetSaveQueue() { return m_bSaveQueue; }
bool GetDupeCheck() { return m_bDupeCheck; }
const char* GetControlIP() { return m_szControlIP; }
const char* GetControlUsername() { return m_szControlUsername; }
const char* GetControlPassword() { return m_szControlPassword; }
int GetControlPort() { return m_iControlPort; }
bool GetSecureControl() { return m_bSecureControl; }
@@ -420,7 +368,7 @@ public:
const char* GetSecureCert() { return m_szSecureCert; }
const char* GetSecureKey() { return m_szSecureKey; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUsername() { return m_szDaemonUsername; }
const char* GetDaemonUserName() { return m_szDaemonUserName; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
bool GetReloadQueue() { return m_bReloadQueue; }
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
@@ -429,11 +377,12 @@ public:
int GetLogBufferSize() { return m_iLogBufferSize; }
bool GetCreateLog() { return m_bCreateLog; }
const char* GetLogFile() { return m_szLogFile; }
EParCheck GetParCheck() { return m_eParCheck; }
ELoadPars GetLoadPars() { return m_eLoadPars; }
bool GetParCheck() { return m_bParCheck; }
bool GetParRepair() { return m_bParRepair; }
EParScan GetParScan() { return m_eParScan; }
const char* GetScriptOrder() { return m_szScriptOrder; }
const char* GetDefScript() { return m_szDefScript; }
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; }
@@ -443,16 +392,19 @@ public:
bool GetCursesTime() { return m_bCursesTime; }
bool GetCursesGroup() { return m_bCursesGroup; }
bool GetCrcCheck() { return m_bCrcCheck; }
int GetThreadLimit() { return m_iThreadLimit; }
bool GetDirectWrite() { return m_bDirectWrite; }
int GetWriteBufferSize() { return m_iWriteBufferSize; }
int GetNzbDirInterval() { return m_iNzbDirInterval; }
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
int GetDiskSpace() { return m_iDiskSpace; }
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
bool GetAllowReProcess() { return m_bAllowReProcess; }
bool GetTLS() { return m_bTLS; }
bool GetDumpCore() { return m_bDumpCore; }
bool GetParPauseQueue() { return m_bParPauseQueue; }
bool GetScriptPauseQueue() { return m_bScriptPauseQueue; }
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
@@ -464,7 +416,6 @@ public:
const char* GetUnrarCmd() { return m_szUnrarCmd; }
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
Category* FindCategory(const char* szName) { return m_Categories.FindCategory(szName); }

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -35,8 +35,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
@@ -45,11 +45,9 @@
#include <libpar2/par2cmdline.h>
#include <libpar2/par2repairer.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "ParChecker.h"
#include "ParCoordinator.h"
#include "Log.h"
#include "Options.h"
#include "Util.h"
@@ -79,6 +77,7 @@ public:
friend class ParChecker;
};
Result Repairer::PreProcess(const char *szParFilename)
{
#ifdef HAVE_PAR2_BUGFIXES_V2
@@ -156,9 +155,7 @@ ParChecker::ParChecker()
{
debug("Creating ParChecker");
m_eStatus = psFailed;
m_szDestDir = NULL;
m_szNZBName = NULL;
m_eStatus = psUndefined;
m_szParFilename = NULL;
m_szInfoName = NULL;
m_szErrMsg = NULL;
@@ -175,13 +172,9 @@ ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
if (m_szDestDir)
if (m_szParFilename)
{
free(m_szDestDir);
}
if (m_szNZBName)
{
free(m_szNZBName);
free(m_szParFilename);
}
if (m_szInfoName)
{
@@ -209,26 +202,15 @@ void ParChecker::Cleanup()
free(*it);
}
m_ProcessedFiles.clear();
m_sourceFiles.clear();
}
void ParChecker::SetDestDir(const char * szDestDir)
void ParChecker::SetParFilename(const char * szParFilename)
{
if (m_szDestDir)
if (m_szParFilename)
{
free(m_szDestDir);
free(m_szParFilename);
}
m_szDestDir = strdup(szDestDir);
}
void ParChecker::SetNZBName(const char * szNZBName)
{
if (m_szNZBName)
{
free(m_szNZBName);
}
m_szNZBName = strdup(szNZBName);
m_szParFilename = strdup(szParFilename);
}
void ParChecker::SetInfoName(const char * szInfoName)
@@ -240,118 +222,24 @@ void ParChecker::SetInfoName(const char * szInfoName)
m_szInfoName = strdup(szInfoName);
}
void ParChecker::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParChecker::Run()
{
ParCoordinator::FileList fileList;
if (!ParCoordinator::FindMainPars(m_szDestDir, &fileList))
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Could not find any par-files", m_szNZBName);
m_eStatus = psFailed;
Completed();
return;
}
m_eStatus = psRepairNotNeeded;
m_bCancelled = false;
for (ParCoordinator::FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
char* szParFilename = *it;
debug("Found par: %s", szParFilename);
if (!IsStopped() && !m_bCancelled)
{
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
char szInfoName[1024];
int iBaseLen = 0;
ParCoordinator::ParseParFilename(szParFilename, &iBaseLen, NULL);
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
strncpy(szInfoName, szParFilename, maxlen);
szInfoName[maxlen] = '\0';
char szParInfoName[1024];
snprintf(szParInfoName, 1024, "%s%c%s", m_szNZBName, (int)PATH_SEPARATOR, szInfoName);
szParInfoName[1024-1] = '\0';
SetInfoName(szParInfoName);
EStatus eStatus = RunParCheck(szFullParFilename);
// accumulate total status, the worst status has priority
if (m_eStatus > eStatus)
{
m_eStatus = eStatus;
}
if (g_pOptions->GetCreateBrokenLog())
{
WriteBrokenLog(eStatus);
}
}
free(szParFilename);
}
Completed();
}
void ParChecker::WriteBrokenLog(EStatus eStatus)
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_szDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (eStatus != psRepairNotNeeded || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (eStatus == psFailed)
{
if (m_bCancelled)
{
fprintf(file, "Repair cancelled for %s\n", m_szInfoName);
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
}
else if (eStatus == psRepairPossible)
{
fprintf(file, "Repair possible for %s\n", m_szInfoName);
}
else if (eStatus == psRepaired)
{
fprintf(file, "Successfully repaired %s\n", m_szInfoName);
}
else if (eStatus == psRepairNotNeeded)
{
fprintf(file, "Repair not needed for %s\n", m_szInfoName);
}
fclose(file);
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
}
ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
{
Cleanup();
m_szParFilename = szParFilename;
m_bRepairNotNeeded = false;
m_eStage = ptLoadingPars;
m_iProcessedFiles = 0;
m_iExtraFiles = 0;
m_bVerifyingExtraFiles = false;
EStatus eStatus = psFailed;
m_bCancelled = false;
PrintMessage(Message::mkInfo, "Verifying %s", m_szInfoName);
info("Verifying %s", m_szInfoName);
SetStatus(psWorking);
debug("par: %s", m_szParFilename);
@@ -377,17 +265,18 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
{
if (res == eInvalidCommandLineArguments)
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
}
else
{
PrintMessage(Message::mkError, "Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
}
SetStatus(psFailed);
delete pRepairer;
Cleanup();
return psFailed;
return;
}
char BufReason[1024];
@@ -421,7 +310,7 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
PrintMessage(Message::mkInfo, "Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
@@ -483,26 +372,23 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
if (IsStopped())
{
SetStatus(psFailed);
delete pRepairer;
Cleanup();
return psFailed;
return;
}
eStatus = psFailed;
if (res == eSuccess)
{
PrintMessage(Message::mkInfo, "Repair not needed for %s", m_szInfoName);
eStatus = psRepairNotNeeded;
info("Repair not needed for %s", m_szInfoName);
m_bRepairNotNeeded = true;
}
else if (res == eRepairPossible)
{
eStatus = psRepairPossible;
if (g_pOptions->GetParRepair())
{
PrintMessage(Message::mkInfo, "Repairing %s", m_szInfoName);
info("Repairing %s", m_szInfoName);
SaveSourceList();
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
@@ -516,44 +402,38 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
debug("ParChecker: Process-result=%i", res);
if (res == eSuccess)
{
PrintMessage(Message::mkInfo, "Successfully repaired %s", m_szInfoName);
eStatus = psRepaired;
DeleteLeftovers();
info("Successfully repaired %s", m_szInfoName);
}
}
else
{
PrintMessage(Message::mkInfo, "Repair possible for %s", m_szInfoName);
info("Repair possible for %s", m_szInfoName);
res = eSuccess;
}
}
if (m_bCancelled)
{
if (m_eStage >= ptRepairing)
{
PrintMessage(Message::mkWarning, "Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
eStatus = psRepairPossible;
}
else
{
PrintMessage(Message::mkWarning, "Par-check cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("par-check cancelled");
eStatus = psFailed;
}
warn("Repair cancelled for %s", m_szInfoName);
m_szErrMsg = strdup("repair cancelled");
SetStatus(psFailed);
}
else if (eStatus == psFailed)
else if (res == eSuccess)
{
SetStatus(psFinished);
}
else
{
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
{
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
}
PrintMessage(Message::mkError, "Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
SetStatus(psFailed);
}
delete pRepairer;
Cleanup();
return eStatus;
}
bool ParChecker::LoadMorePars()
@@ -570,11 +450,11 @@ bool ParChecker::LoadMorePars()
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
if (loadedOK)
{
PrintMessage(Message::mkInfo, "File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
else
{
PrintMessage(Message::mkInfo, "Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
}
free(szParFilename);
}
@@ -605,7 +485,7 @@ bool ParChecker::CheckSplittedFragments()
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile *sourcefile = *it;
if (AddSplittedFragments(sourcefile->TargetFileName().c_str()))
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
{
bFragmentsAdded = true;
}
@@ -669,7 +549,7 @@ bool ParChecker::AddSplittedFragments(const char* szFilename)
bool ParChecker::AddMissingFiles()
{
PrintMessage(Message::mkInfo, "Performing extra par-scan for %s", m_szInfoName);
info("Performing extra par-scan for %s", m_szInfoName);
char szDirectory[1024];
strncpy(szDirectory, m_szParFilename, 1024);
@@ -763,7 +643,7 @@ void ParChecker::signal_filename(std::string str)
m_eStage = ptVerifyingRepaired;
}
PrintMessage(Message::mkInfo, "%s %s", szStageMessage[m_eStage], str.c_str());
info("%s %s", szStageMessage[m_eStage], str.c_str());
if (m_eStage == ptLoadingPars || m_eStage == ptVerifyingSources)
{
@@ -847,11 +727,11 @@ void ParChecker::signal_done(std::string str, int available, int total)
if (bFileExists)
{
PrintMessage(Message::mkWarning, "File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
}
else
{
PrintMessage(Message::mkWarning, "File %s with %i block(s) is missing", str.c_str(), total);
warn("File %s with %i block(s) is missing", str.c_str(), total);
}
}
}
@@ -863,60 +743,8 @@ void ParChecker::Cancel()
((Repairer*)m_pRepairer)->cancelled = true;
m_bCancelled = true;
#else
PrintMessage(Message::mkError, "Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details).");
error("Could not cancel par-repair. The program was compiled using version of libpar2 which doesn't support cancelling of par-repair. Please apply libpar2-patches supplied with NZBGet and recompile libpar2 and NZBGet (see README for details).");
#endif
}
void ParChecker::SaveSourceList()
{
// Buliding a list of DiskFile-objects, marked as source-files
for (std::vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
{
Par2RepairerSourceFile* sourcefile = (Par2RepairerSourceFile*)*it;
vector<DataBlock>::iterator it2 = sourcefile->SourceBlocks();
for (int i = 0; i < (int)sourcefile->BlockCount(); i++, it2++)
{
DataBlock block = *it2;
DiskFile* pSourceFile = block.GetDiskFile();
if (pSourceFile &&
std::find(m_sourceFiles.begin(), m_sourceFiles.end(), pSourceFile) == m_sourceFiles.end())
{
m_sourceFiles.push_back(pSourceFile);
}
}
}
}
void ParChecker::DeleteLeftovers()
{
// After repairing check if all DiskFile-objects saved by "SaveSourceList()" have
// corresponding target-files. If not - the source file was replaced. In this case
// the DiskFile-object points to the renamed bak-file, which we can delete.
for (SourceList::iterator it = m_sourceFiles.begin(); it != m_sourceFiles.end(); it++)
{
DiskFile* pSourceFile = (DiskFile*)*it;
bool bFound = false;
for (std::vector<Par2RepairerSourceFile*>::iterator it2 = ((Repairer*)m_pRepairer)->sourcefiles.begin();
it2 != ((Repairer*)m_pRepairer)->sourcefiles.end(); it2++)
{
Par2RepairerSourceFile* sourcefile = *it2;
if (sourcefile->GetTargetFile() == pSourceFile)
{
bFound = true;
break;
}
}
if (!bFound)
{
PrintMessage(Message::mkInfo, "Deleting file %s", Util::BaseFileName(pSourceFile->FileName().c_str()));
remove(pSourceFile->FileName().c_str());
}
}
}
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -29,20 +29,19 @@
#ifndef DISABLE_PARCHECK
#include <deque>
#include <string>
#include "Thread.h"
#include "Log.h"
#include "Observer.h"
class ParChecker : public Thread
class ParChecker : public Thread, public Subject
{
public:
enum EStatus
{
psUndefined,
psWorking,
psFailed,
psRepairPossible,
psRepaired,
psRepairNotNeeded
psFinished
};
enum EStage
@@ -54,17 +53,15 @@ public:
};
typedef std::deque<char*> FileList;
typedef std::deque<void*> SourceList;
private:
char* m_szInfoName;
char* m_szDestDir;
char* m_szNZBName;
const char* m_szParFilename;
char* m_szParFilename;
EStatus m_eStatus;
EStage m_eStage;
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
char* m_szErrMsg;
bool m_bRepairNotNeeded;
FileList m_QueuedParFiles;
Mutex m_mutexQueuedParFiles;
bool m_bQueuedParFilesChanged;
@@ -77,17 +74,12 @@ private:
int m_iFileProgress;
int m_iStageProgress;
bool m_bCancelled;
SourceList m_sourceFiles;
EStatus RunParCheck(const char* szParFilename);
void WriteBrokenLog(EStatus eStatus);
void Cleanup();
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
bool AddMissingFiles();
void SaveSourceList();
void DeleteLeftovers();
void signal_filename(std::string str);
void signal_progress(double progress);
void signal_done(std::string str, int available, int total);
@@ -100,8 +92,6 @@ protected:
*/
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
EStage GetStage() { return m_eStage; }
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetFileProgress() { return m_iFileProgress; }
@@ -111,12 +101,14 @@ public:
ParChecker();
virtual ~ParChecker();
virtual void Run();
void SetDestDir(const char* szDestDir);
const char* GetParFilename() { return m_szParFilename; }
void SetParFilename(const char* szParFilename);
const char* GetInfoName() { return m_szInfoName; }
void SetInfoName(const char* szInfoName);
void SetNZBName(const char* szNZBName);
void SetStatus(EStatus eStatus);
EStatus GetStatus() { return m_eStatus; }
const char* GetErrMsg() { return m_szErrMsg; }
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
void AddParFile(const char* szParFilename);
void QueueChanged();
void Cancel();

View File

@@ -33,8 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
@@ -64,34 +63,10 @@ void ParCoordinator::PostParChecker::UpdateProgress()
m_pOwner->UpdateParCheckProgress();
}
void ParCoordinator::PostParChecker::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
void ParCoordinator::PostParRenamer::UpdateProgress()
{
m_pOwner->UpdateParRenameProgress();
}
void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
}
#endif
ParCoordinator::ParCoordinator()
@@ -99,9 +74,18 @@ ParCoordinator::ParCoordinator()
debug("Creating ParCoordinator");
#ifndef DISABLE_PARCHECK
m_bStopped = false;
m_ParCheckerObserver.m_pOwner = this;
m_ParChecker.Attach(&m_ParCheckerObserver);
m_ParChecker.m_pOwner = this;
m_ParRenamerObserver.m_pOwner = this;
m_ParRenamer.Attach(&m_ParRenamerObserver);
m_ParRenamer.m_pOwner = this;
m_bStopped = false;
const char* szPostScript = g_pOptions->GetPostProcess();
m_bPostScript = szPostScript && strlen(szPostScript) > 0;
#endif
}
@@ -145,7 +129,10 @@ void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
QueueEditor::eaGroupPauseExtraPars, 0, NULL);
(g_pOptions->GetLoadPars() == Options::lpOne ||
(g_pOptions->GetLoadPars() == Options::lpNone && g_pOptions->GetParCheck()))
? QueueEditor::eaGroupPauseExtraPars : QueueEditor::eaGroupPauseAllPars,
0, NULL);
break;
}
}
@@ -258,11 +245,11 @@ bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameL
*/
void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
{
info("Checking pars for %s", pPostInfo->GetInfoName());
m_eCurrentJob = jkParCheck;
m_ParChecker.SetPostInfo(pPostInfo);
m_ParChecker.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParChecker.SetNZBName(pPostInfo->GetNZBInfo()->GetName());
m_ParChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", pPostInfo->GetInfoName());
m_ParChecker.SetParFilename(pPostInfo->GetParFilename());
m_ParChecker.SetInfoName(pPostInfo->GetInfoName());
pPostInfo->SetWorking(true);
m_ParChecker.Start();
}
@@ -272,11 +259,11 @@ void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
*/
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
{
info("Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
m_eCurrentJob = jkParRename;
m_ParRenamer.SetPostInfo(pPostInfo);
m_ParRenamer.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
m_ParRenamer.Start();
}
@@ -335,38 +322,106 @@ bool ParCoordinator::AddPar(FileInfo* pFileInfo, bool bDeleted)
return bSameCollection;
}
void ParCoordinator::ParCheckCompleted()
void ParCoordinator::ParCheckerUpdate(Subject* Caller, void* Aspect)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
// Update ParStatus (accumulate result)
if ((m_ParChecker.GetStatus() == ParChecker::psRepaired ||
m_ParChecker.GetStatus() == ParChecker::psRepairNotNeeded) &&
pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
if (m_ParChecker.GetStatus() == ParChecker::psFinished ||
m_ParChecker.GetStatus() == ParChecker::psFailed)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
else if (m_ParChecker.GetStatus() == ParChecker::psRepairPossible &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
else
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
char szPath[1024];
strncpy(szPath, m_ParChecker.GetParFilename(), 1024);
szPath[1024-1] = '\0';
if (char* p = strrchr(szPath, PATH_SEPARATOR)) *p = '\0';
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetCreateBrokenLog())
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szPath, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (!m_ParChecker.GetRepairNotNeeded() || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
{
if (m_ParChecker.GetCancelled())
{
fprintf(file, "Repair cancelled for %s\n", m_ParChecker.GetInfoName());
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_ParChecker.GetInfoName(), m_ParChecker.GetErrMsg() ? m_ParChecker.GetErrMsg() : "");
}
}
else if (m_ParChecker.GetRepairNotNeeded())
{
fprintf(file, "Repair not needed for %s\n", m_ParChecker.GetInfoName());
}
else
{
if (g_pOptions->GetParRepair())
{
fprintf(file, "Successfully repaired %s\n", m_ParChecker.GetInfoName());
}
else
{
fprintf(file, "Repair possible for %s\n", m_ParChecker.GetInfoName());
}
}
fclose(file);
}
else
{
error("Could not open file %s", szBrokenLogName);
}
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
pPostInfo->SetWorking(false);
if (pPostInfo->GetDeleted())
{
pPostInfo->SetStage(PostInfo::ptFinished);
}
else
{
pPostInfo->SetStage(PostInfo::ptQueued);
}
// Update ParStatus by NZBInfo (accumulate result)
if (m_ParChecker.GetStatus() == ParChecker::psFailed && !m_ParChecker.GetCancelled())
{
pPostInfo->SetParStatus(PostInfo::psFailure);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
}
else if (m_ParChecker.GetStatus() == ParChecker::psFinished &&
(g_pOptions->GetParRepair() || m_ParChecker.GetRepairNotNeeded()))
{
pPostInfo->SetParStatus(PostInfo::psSuccess);
if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
}
}
else
{
pPostInfo->SetParStatus(PostInfo::psRepairPossible);
if (pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
}
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
g_pQueueCoordinator->UnlockQueue();
}
/**
@@ -418,7 +473,7 @@ bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilenam
{
if (pBestBlockInfo->m_pFileInfo->GetPaused())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
pBestBlockInfo->m_pFileInfo->SetPaused(false);
pBestBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
@@ -442,7 +497,7 @@ bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilenam
BlockInfo* pBlockInfo = blocks.front();
if (pBlockInfo->m_pFileInfo->GetPaused())
{
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
info("Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
pBlockInfo->m_pFileInfo->SetPaused(false);
pBlockInfo->m_pFileInfo->SetExtraPriority(true);
}
@@ -560,9 +615,9 @@ void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
void ParCoordinator::UpdateParCheckProgress()
{
g_pQueueCoordinator->LockQueue();
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
PostInfo* pPostInfo = pDownloadQueue->GetPostQueue()->front();
if (m_ParChecker.GetFileProgress() == 0)
{
pPostInfo->SetProgressLabel(m_ParChecker.GetProgressLabel());
@@ -599,7 +654,7 @@ void ParCoordinator::UpdateParCheckProgress()
if (iEstimatedRepairTime > g_pOptions->GetParTimeLimit() * 60)
{
debug("Estimated repair time %i seconds", iEstimatedRepairTime);
m_ParChecker.PrintMessage(Message::mkWarning, "Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
warn("Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
bParCancel = true;
}
}
@@ -646,28 +701,50 @@ void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
}
}
void ParCoordinator::ParRenameCompleted()
void ParCoordinator::ParRenamerUpdate(Subject* Caller, void* Aspect)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->GetNZBInfo()->SetRenameStatus(m_ParRenamer.GetStatus() == ParRenamer::psSuccess ? NZBInfo::rsSuccess : NZBInfo::rsFailure);
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
if (m_ParRenamer.GetStatus() == ParRenamer::psFinished ||
m_ParRenamer.GetStatus() == ParRenamer::psFailed)
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
pPostInfo->SetWorking(false);
if (pPostInfo->GetDeleted())
{
pPostInfo->SetStage(PostInfo::ptFinished);
}
else
{
pPostInfo->SetStage(PostInfo::ptQueued);
}
// Update ParStatus by NZBInfo
if (m_ParRenamer.GetStatus() == ParRenamer::psFailed && !m_ParRenamer.GetCancelled())
{
pPostInfo->SetRenameStatus(PostInfo::rsFailure);
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsFailure);
}
else if (m_ParRenamer.GetStatus() == ParRenamer::psFinished)
{
pPostInfo->SetRenameStatus(PostInfo::rsSuccess);
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsSuccess);
}
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
}
g_pQueueCoordinator->UnlockQueue();
}
void ParCoordinator::UpdateParRenameProgress()
{
g_pQueueCoordinator->LockQueue();
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
PostInfo* pPostInfo = pDownloadQueue->GetPostQueue()->front();
pPostInfo->SetProgressLabel(m_ParRenamer.GetProgressLabel());
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
time_t tCurrent = time(NULL);
@@ -688,39 +765,4 @@ void ParCoordinator::UpdateParRenameProgress()
CheckPauseState(pPostInfo);
}
void ParCoordinator::PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...)
{
char szText[1024];
va_list args;
va_start(args, szFormat);
vsnprintf(szText, 1024, szFormat, args);
va_end(args);
szText[1024-1] = '\0';
pPostInfo->AppendMessage(eKind, szText);
switch (eKind)
{
case Message::mkDetail:
detail("%s", szText);
break;
case Message::mkInfo:
info("%s", szText);
break;
case Message::mkWarning:
warn("%s", szText);
break;
case Message::mkError:
error("%s", szText);
break;
case Message::mkDebug:
debug("%s", szText);
break;
}
}
#endif

View File

@@ -29,6 +29,7 @@
#include <list>
#include <deque>
#include "Observer.h"
#include "DownloadInfo.h"
#ifndef DISABLE_PARCHECK
@@ -40,6 +41,13 @@ class ParCoordinator
{
private:
#ifndef DISABLE_PARCHECK
class ParCheckerObserver: public Observer
{
public:
ParCoordinator* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->ParCheckerUpdate(Caller, Aspect); }
};
class PostParChecker: public ParChecker
{
private:
@@ -48,8 +56,6 @@ private:
protected:
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParCheckCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
@@ -57,6 +63,13 @@ private:
friend class ParCoordinator;
};
class ParRenamerObserver: public Observer
{
public:
ParCoordinator* m_pOwner;
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->ParRenamerUpdate(Caller, Aspect); }
};
class PostParRenamer: public ParRenamer
{
private:
@@ -64,8 +77,6 @@ private:
PostInfo* m_pPostInfo;
protected:
virtual void UpdateProgress();
virtual void Completed() { m_pOwner->ParRenameCompleted(); }
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
public:
PostInfo* GetPostInfo() { return m_pPostInfo; }
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
@@ -89,20 +100,16 @@ private:
private:
PostParChecker m_ParChecker;
ParCheckerObserver m_ParCheckerObserver;
bool m_bStopped;
bool m_bPostScript;
PostParRenamer m_ParRenamer;
ParRenamerObserver m_ParRenamerObserver;
EJobKind m_eCurrentJob;
protected:
virtual bool PauseDownload() = 0;
virtual bool UnpauseDownload() = 0;
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void ParCheckCompleted();
void ParRenameCompleted();
void CheckPauseState(PostInfo* pPostInfo);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...);
#endif
public:
@@ -117,9 +124,15 @@ public:
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
#ifndef DISABLE_PARCHECK
void ParCheckerUpdate(Subject* Caller, void* Aspect);
void ParRenamerUpdate(Subject* Caller, void* Aspect);
void CheckPauseState(PostInfo* pPostInfo);
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
void UpdateParCheckProgress();
void UpdateParRenameProgress();
void StartParCheckJob(PostInfo* pPostInfo);
void StartParRenameJob(PostInfo* pPostInfo);
void Stop();

View File

@@ -35,8 +35,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fstream>
#ifdef WIN32
#include <par2cmdline.h>
#include <par2repairer.h>
@@ -79,7 +79,7 @@ ParRenamer::ParRenamer()
{
debug("Creating ParRenamer");
m_eStatus = psFailed;
m_eStatus = psUnknown;
m_szDestDir = NULL;
m_szInfoName = NULL;
m_szProgressLabel = (char*)malloc(1024);
@@ -131,6 +131,12 @@ void ParRenamer::SetInfoName(const char * szInfoName)
m_szInfoName = strdup(szInfoName);
}
void ParRenamer::SetStatus(EStatus eStatus)
{
m_eStatus = eStatus;
Notify(NULL);
}
void ParRenamer::Cancel()
{
m_bCancelled = true;
@@ -141,7 +147,8 @@ void ParRenamer::Run()
Cleanup();
m_bCancelled = false;
m_iRenamedCount = 0;
m_eStatus = psFailed;
SetStatus(psUnknown);
snprintf(m_szProgressLabel, 1024, "Checking renamed files for %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
@@ -153,20 +160,21 @@ void ParRenamer::Run()
if (m_bCancelled)
{
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", m_szInfoName);
warn("Renaming cancelled for %s", m_szInfoName);
SetStatus(psFailed);
}
else if (m_iRenamedCount > 0)
{
PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
m_eStatus = psSuccess;
info("Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
SetStatus(psFinished);
}
else
{
PrintMessage(Message::mkInfo, "No renamed files found for %s", m_szInfoName);
info("Could not rename any files for %s", m_szInfoName);
SetStatus(psFailed);
}
Cleanup();
Completed();
}
void ParRenamer::LoadParFiles()
@@ -194,7 +202,7 @@ void ParRenamer::LoadParFile(const char* szParFilename)
if (!pRepairer->LoadPacketsFromFile(szParFilename))
{
PrintMessage(Message::mkWarning, "Could not load par2-file %s", szParFilename);
warn("Could not load par2-file %s", szParFilename);
delete pRepairer;
return;
}
@@ -256,7 +264,7 @@ void ParRenamer::CheckFile(const char* szFilename)
FILE* pFile = fopen(szFilename, "rb");
if (!pFile)
{
PrintMessage(Message::mkError, "Could not open file %s", szFilename);
error("Could not open file %s", szFilename);
return;
}
@@ -268,7 +276,7 @@ void ParRenamer::CheckFile(const char* szFilename)
int iError = ferror(pFile);
if (iReadBytes != iBlockSize && iError)
{
PrintMessage(Message::mkError, "Could not read file %s", szFilename);
error("Could not read file %s", szFilename);
return;
}
@@ -296,14 +304,14 @@ void ParRenamer::CheckFile(const char* szFilename)
if (!Util::FileExists(szDstFilename))
{
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
info("Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
if (Util::MoveFile(szFilename, szDstFilename))
{
m_iRenamedCount++;
}
else
{
PrintMessage(Message::mkError, "Could not rename %s to %s", szFilename, szDstFilename);
error("Could not rename %s to %s", szFilename, szDstFilename);
}
}

View File

@@ -31,15 +31,16 @@
#include <deque>
#include "Thread.h"
#include "Log.h"
#include "Observer.h"
class ParRenamer : public Thread
class ParRenamer : public Thread, public Subject
{
public:
enum EStatus
{
psUnknown,
psFailed,
psSuccess
psFinished
};
class FileHash
@@ -75,8 +76,6 @@ private:
protected:
virtual void UpdateProgress() {}
virtual void Completed() {}
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
const char* GetProgressLabel() { return m_szProgressLabel; }
int GetStageProgress() { return m_iStageProgress; }

View File

@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
@@ -49,15 +49,12 @@
#include "DiskState.h"
#include "Util.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "Unpack.h"
#include "NZBFile.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
extern Scheduler* g_pScheduler;
extern Scanner* g_pScanner;
PrePostProcessor::PrePostProcessor()
{
@@ -69,6 +66,9 @@ PrePostProcessor::PrePostProcessor()
m_QueueCoordinatorObserver.m_pOwner = this;
g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver);
const char* szPostScript = g_pOptions->GetPostProcess();
m_bPostScript = szPostScript && strlen(szPostScript) > 0;
#ifndef DISABLE_PARCHECK
m_ParCoordinator.m_pOwner = this;
#endif
@@ -125,11 +125,12 @@ void PrePostProcessor::Run()
int iSchedulerInterval = 1000;
int iHistoryInterval = 60000;
const int iStepMSec = 200;
m_Scanner.SetStepInterval(iStepMSec);
while (!IsStopped())
{
// check incoming nzb directory
g_pScanner->Check();
m_Scanner.Check();
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) &&
g_pOptions->GetDiskSpace() > 0 && !g_pQueueCoordinator->GetStandBy() &&
@@ -215,19 +216,17 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
#ifndef DISABLE_PARCHECK
!m_ParCoordinator.AddPar(pAspect->pFileInfo, pAspect->eAction == QueueCoordinator::eaFileDeleted) &&
#endif
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, true, false) &&
(!pAspect->pFileInfo->GetPaused() || IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false)))
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, true, false, false) &&
(!pAspect->pFileInfo->GetPaused() || IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, false)))
{
if (pAspect->eAction == QueueCoordinator::eaFileCompleted ||
(pAspect->pFileInfo->GetAutoDeleted() &&
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true)))
if (pAspect->eAction == QueueCoordinator::eaFileCompleted)
{
info("Collection %s completely downloaded", pAspect->pNZBInfo->GetName());
NZBDownloaded(pAspect->pDownloadQueue, pAspect->pNZBInfo);
}
else if (pAspect->eAction == QueueCoordinator::eaFileDeleted &&
else if (pAspect->pNZBInfo->GetDeleted() &&
!pAspect->pNZBInfo->GetParCleanup() &&
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true))
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false, true))
{
info("Collection %s deleted from queue", pAspect->pNZBInfo->GetName());
NZBDeleted(pAspect->pDownloadQueue, pAspect->pNZBInfo);
@@ -238,14 +237,12 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
void PrePostProcessor::NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
PostScriptController::InitParamsForNewNZB(pNZBInfo);
if (g_pOptions->GetMergeNzb())
{
pNZBInfo = MergeGroups(pDownloadQueue, pNZBInfo);
}
if (g_pOptions->GetParCheck() != Options::pcForce)
if (g_pOptions->GetLoadPars() != Options::lpAll)
{
m_ParCoordinator.PausePars(pDownloadQueue, pNZBInfo);
}
@@ -258,30 +255,24 @@ void PrePostProcessor::NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo
void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
if (!pNZBInfo->GetPostProcess() && g_pOptions->GetDecode())
bool bPostProcessed = false;
if (!pNZBInfo->GetPostProcess() || (m_bPostScript && g_pOptions->GetAllowReProcess()))
{
info("Queueing %s for post-processing", pNZBInfo->GetName());
PostInfo* pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pNZBInfo);
pPostInfo->SetInfoName(pNZBInfo->GetName());
if (pNZBInfo->GetParStatus() == NZBInfo::psNone && g_pOptions->GetParCheck() != Options::pcForce)
#ifdef DISABLE_PARCHECK
bool bParCheck = false;
#else
bool bParCheck = g_pOptions->GetParCheck() && g_pOptions->GetDecode();
#endif
if ((bParCheck || m_bPostScript || g_pOptions->GetUnpack() || strlen(g_pOptions->GetInterDir()) > 0) &&
CreatePostJobs(pDownloadQueue, pNZBInfo, bParCheck, true, false))
{
pNZBInfo->SetParStatus(NZBInfo::psSkipped);
pNZBInfo->SetPostProcess(true);
bPostProcessed = true;
}
if (pNZBInfo->GetRenameStatus() == NZBInfo::rsNone)
{
pNZBInfo->SetRenameStatus(NZBInfo::rsSkipped);
}
pNZBInfo->SetPostProcess(true);
pDownloadQueue->GetPostQueue()->push_back(pPostInfo);
SaveQueue(pDownloadQueue);
m_bHasMoreJobs = true;
}
else
if (!bPostProcessed)
{
NZBCompleted(pDownloadQueue, pNZBInfo, true);
}
@@ -297,33 +288,15 @@ void PrePostProcessor::NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBIn
char* szFilename = *it;
if (Util::FileExists(szFilename))
{
detail("Deleting file %s", Util::BaseFileName(szFilename));
remove(szFilename);
}
}
// delete .out.tmp-files and _brokenlog.txt
DirBrowser dir(pNZBInfo->GetDestDir());
while (const char* szFilename = dir.Next())
{
int iLen = strlen(szFilename);
if ((iLen > 8 && !strcmp(szFilename + iLen - 8, ".out.tmp")) || !strcmp(szFilename, "_brokenlog.txt"))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", pNZBInfo->GetDestDir(), PATH_SEPARATOR, szFilename);
szFullFilename[1024-1] = '\0';
detail("Deleting file %s", szFilename);
remove(szFullFilename);
remove(szFilename);
// delete old directory (if empty)
if (g_pOptions->GetAppendNZBDir() && Util::DirEmpty(pNZBInfo->GetDestDir()))
{
rmdir(pNZBInfo->GetDestDir());
}
}
}
// delete old directory (if empty)
if (Util::DirEmpty(pNZBInfo->GetDestDir()))
{
rmdir(pNZBInfo->GetDestDir());
}
if (g_pOptions->GetNzbCleanupDisk())
{
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
@@ -489,6 +462,11 @@ NZBInfo* PrePostProcessor::MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* p
return pNZBInfo;
}
void PrePostProcessor::ScanNZBDir(bool bSyncMode)
{
m_Scanner.ScanNZBDir(bSyncMode);
}
void PrePostProcessor::CheckDiskSpace()
{
long long lFreeSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
@@ -509,54 +487,44 @@ void PrePostProcessor::CheckPostQueue()
if (!pPostInfo->GetWorking())
{
#ifndef DISABLE_PARCHECK
if (pPostInfo->GetRequestParCheck() && pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
g_pOptions->GetParCheck() != Options::pcManual)
if (pPostInfo->GetRequestParCheck() == PostInfo::rpAll)
{
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psNone);
pPostInfo->SetRequestParCheck(false);
pPostInfo->SetStage(PostInfo::ptQueued);
pPostInfo->GetNZBInfo()->GetScriptStatuses()->Clear();
DeletePostThread(pPostInfo);
if (!CreatePostJobs(pDownloadQueue, pPostInfo->GetNZBInfo(), true, false, true))
{
error("Could not par-check %s: there are no par-files", pPostInfo->GetNZBInfo()->GetName());
}
}
else if (pPostInfo->GetRequestParCheck() && pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
g_pOptions->GetParCheck() == Options::pcManual)
else if (pPostInfo->GetRequestParCheck() == PostInfo::rpCurrent && pPostInfo->GetParStatus() <= PostInfo::psSkipped)
{
pPostInfo->SetRequestParCheck(false);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psManual);
pPostInfo->SetParStatus(PostInfo::psNone);
pPostInfo->SetRequestParCheck(PostInfo::rpNone);
pPostInfo->SetStage(PostInfo::ptQueued);
DeletePostThread(pPostInfo);
FileInfo* pFileInfo = GetQueueGroup(pDownloadQueue, pPostInfo->GetNZBInfo());
if (pFileInfo)
{
info("Downloading all remaining files for manual par-check for %s", pPostInfo->GetNZBInfo()->GetName());
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupResume, 0, NULL);
pPostInfo->SetStage(PostInfo::ptFinished);
pPostInfo->GetNZBInfo()->SetPostProcess(false);
}
else
{
info("There are no par-files remain for download for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetStage(PostInfo::ptQueued);
}
}
else if (pPostInfo->GetRequestParRename())
{
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsNone);
pPostInfo->SetRenameStatus(PostInfo::rsNone);
pPostInfo->SetRequestParRename(false);
pPostInfo->SetStage(PostInfo::ptQueued);
DeletePostThread(pPostInfo);
}
#endif
if (pPostInfo->GetDeleted())
if (pPostInfo->GetStage() == PostInfo::ptQueued && pPostInfo->GetRenameStatus() == PostInfo::rsNone && !g_pOptions->GetPausePostProcess())
{
pPostInfo->SetStage(PostInfo::ptFinished);
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-rename");
m_ParCoordinator.StartParRenameJob(pPostInfo);
}
else if (pPostInfo->GetStage() == PostInfo::ptQueued && pPostInfo->GetParStatus() == PostInfo::psNone && !g_pOptions->GetPausePostProcess())
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-check");
m_ParCoordinator.StartParCheckJob(pPostInfo);
}
else
#endif
if (pPostInfo->GetStage() == PostInfo::ptQueued && !g_pOptions->GetPausePostProcess())
{
DeletePostThread(pPostInfo);
StartJob(pDownloadQueue, pPostInfo);
StartProcessJob(pDownloadQueue, pPostInfo);
}
else if (pPostInfo->GetStage() == PostInfo::ptFinished)
{
@@ -611,75 +579,37 @@ void PrePostProcessor::DeletePostThread(PostInfo* pPostInfo)
}
}
void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
void PrePostProcessor::StartProcessJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
{
#ifndef DISABLE_PARCHECK
if (pPostInfo->GetNZBInfo()->GetRenameStatus() == NZBInfo::rsNone)
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-rename");
m_ParCoordinator.StartParRenameJob(pPostInfo);
return;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psNone)
{
if (m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
UpdatePauseState(g_pOptions->GetParPauseQueue(), "par-check");
m_ParCoordinator.StartParCheckJob(pPostInfo);
}
else
{
info("Nothing to par-check for %s", pPostInfo->GetInfoName());
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSkipped);
pPostInfo->SetWorking(false);
pPostInfo->SetStage(PostInfo::ptQueued);
}
return;
}
#endif
bool bUnpack = g_pOptions->GetUnpack() && (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone);
bool bParFailed = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psManual;
bool bCleanup = !bUnpack &&
pPostInfo->GetNZBInfo()->GetCleanupStatus() == NZBInfo::csNone &&
(pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
(pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)) &&
strlen(g_pOptions->GetExtCleanupDisk()) > 0;
bool bMoveInter = !bUnpack &&
bool bUnpack = g_pOptions->GetUnpack() && (pPostInfo->GetUnpackStatus() == PostInfo::usNone);
bool bNZBFileCompleted = IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false);
bool bHasFailedParJobs = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible;
bool bMoveInterToDest = !bUnpack &&
pPostInfo->GetNZBInfo()->GetMoveStatus() == NZBInfo::msNone &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usFailure &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psManual &&
pPostInfo->GetUnpackStatus() != PostInfo::usFailure &&
pPostInfo->GetParStatus() != PostInfo::psFailure &&
strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(pPostInfo->GetNZBInfo()->GetDestDir(), g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
// TODO: check if download has pp-scripts defined
bool bPostScript = true;
if (bUnpack && bParFailed)
if (bUnpack && bHasFailedParJobs)
{
warn("Skipping unpack due to %s for %s",
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psManual ? "required par-repair" : "par-failure",
pPostInfo->GetInfoName());
warn("Skipping unpack due to par-failure for %s", pPostInfo->GetInfoName());
pPostInfo->SetUnpackStatus(PostInfo::usSkipped);
pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
bUnpack = false;
}
if (!bUnpack && !bMoveInter && !bPostScript)
if ((!bNZBFileCompleted && !(m_bPostScript && g_pOptions->GetAllowReProcess())) ||
(!bUnpack && !m_bPostScript && !bMoveInterToDest))
{
pPostInfo->SetStage(PostInfo::ptFinished);
return;
}
pPostInfo->SetProgressLabel(bUnpack ? "Unpacking" : bMoveInter ? "Moving" : "Executing post-process-script");
pPostInfo->SetProgressLabel(bUnpack ? "Unpacking" : bMoveInterToDest ? "Moving" : "Executing post-process-script");
pPostInfo->SetWorking(true);
pPostInfo->SetStage(bUnpack ? PostInfo::ptUnpacking : bMoveInter ? PostInfo::ptMoving : PostInfo::ptExecutingScript);
pPostInfo->SetStage(bUnpack ? PostInfo::ptUnpacking : bMoveInterToDest ? PostInfo::ptMoving : PostInfo::ptExecutingScript);
pPostInfo->SetFileProgress(0);
pPostInfo->SetStageProgress(0);
SaveQueue(pDownloadQueue);
@@ -693,22 +623,17 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
if (bUnpack)
{
UpdatePauseState(g_pOptions->GetUnpackPauseQueue(), "unpack");
UnpackController::StartJob(pPostInfo);
UnpackController::StartUnpackJob(pPostInfo);
}
else if (bCleanup)
else if (bMoveInterToDest)
{
UpdatePauseState(g_pOptions->GetUnpackPauseQueue() || g_pOptions->GetScriptPauseQueue(), "cleanup");
CleanupController::StartJob(pPostInfo);
}
else if (bMoveInter)
{
UpdatePauseState(g_pOptions->GetUnpackPauseQueue() || g_pOptions->GetScriptPauseQueue(), "move");
MoveController::StartJob(pPostInfo);
UpdatePauseState(g_pOptions->GetUnpackPauseQueue() || g_pOptions->GetPostPauseQueue(), "move");
MoveController::StartMoveJob(pPostInfo);
}
else
{
UpdatePauseState(g_pOptions->GetScriptPauseQueue(), "post-process-script");
PostScriptController::StartJob(pPostInfo);
UpdatePauseState(g_pOptions->GetPostPauseQueue(), "post-process-script");
PostScriptController::StartScriptJob(pPostInfo, bNZBFileCompleted, bHasFailedParJobs);
}
}
@@ -720,15 +645,31 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
DeletePostThread(pPostInfo);
if (IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, false))
// Update ScriptStatus by NZBInfo (accumulate result)
if (pPostInfo->GetScriptStatus() == PostInfo::srUnknown &&
pPostInfo->GetNZBInfo()->GetScriptStatus() != NZBInfo::srFailure)
{
// Cleaning up queue if par-check was successful or unpack was successful or
// script was successful (if unpack was not performed)
pPostInfo->GetNZBInfo()->SetScriptStatus(NZBInfo::srUnknown);
}
else if (pPostInfo->GetScriptStatus() == PostInfo::srFailure)
{
pPostInfo->GetNZBInfo()->SetScriptStatus(NZBInfo::srFailure);
}
else if (pPostInfo->GetScriptStatus() == PostInfo::srSuccess &&
pPostInfo->GetNZBInfo()->GetScriptStatus() == NZBInfo::srNone)
{
pPostInfo->GetNZBInfo()->SetScriptStatus(NZBInfo::srSuccess);
}
if (IsNZBFileCompleted(pDownloadQueue, pPostInfo->GetNZBInfo(), true, true, false))
{
// Cleaning up queue if all par-checks were successful or all unpacks were successful or
// all scripts were successful (if unpack was not performed)
bool bCanCleanupQueue = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible ||
pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess ||
(pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone &&
pPostInfo->GetNZBInfo()->GetScriptStatuses()->CalcTotalStatus() == ScriptStatus::srSuccess);
pPostInfo->GetNZBInfo()->GetScriptStatus() == NZBInfo::srSuccess);
if ((g_pOptions->GetParCleanupQueue() || g_pOptions->GetNzbCleanupDisk()) && bCanCleanupQueue)
{
if (g_pOptions->GetParCleanupQueue())
@@ -768,7 +709,7 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
}
bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bAllowOnlyOneDeleted)
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted)
{
bool bNZBFileCompleted = true;
int iDeleted = 0;
@@ -782,8 +723,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 ||
!(m_ParCoordinator.ParseParFilename(pFileInfo->GetFilename(), NULL, NULL))) &&
!(m_ParCoordinator.ParseParFilename(pFileInfo->GetFilename(), NULL, NULL) || g_pOptions->GetAllowReProcess())) &&
!pFileInfo->GetDeleted()) ||
(bAllowOnlyOneDeleted && iDeleted > 1))
{
@@ -793,9 +737,116 @@ bool PrePostProcessor::IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo
}
}
if (bNZBFileCompleted && bCheckPostQueue)
{
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin() + 1; it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
if (pPostInfo->GetNZBInfo() == pNZBInfo)
{
bNZBFileCompleted = false;
break;
}
}
}
return bNZBFileCompleted;
}
bool PrePostProcessor::CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bParCheck, bool bUnpackOrScript, bool bAddTop)
{
debug("Queueing post-process-jobs");
PostQueue cPostQueue;
bool bJobsAdded = false;
ParCoordinator::FileList fileList;
if (m_ParCoordinator.FindMainPars(pNZBInfo->GetDestDir(), &fileList))
{
debug("Found pars");
for (ParCoordinator::FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
char* szParFilename = *it;
debug("Found par: %s", szParFilename);
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
char szInfoName[1024];
int iBaseLen = 0;
m_ParCoordinator.ParseParFilename(szParFilename, &iBaseLen, NULL);
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
strncpy(szInfoName, szParFilename, maxlen);
szInfoName[maxlen] = '\0';
char szParInfoName[1024];
snprintf(szParInfoName, 1024, "%s%c%s", pNZBInfo->GetName(), (int)PATH_SEPARATOR, szInfoName);
szParInfoName[1024-1] = '\0';
if (cPostQueue.empty())
{
info("Queueing %s for post-processing", pNZBInfo->GetName());
}
PostInfo* pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pNZBInfo);
pPostInfo->SetParFilename(szFullParFilename);
pPostInfo->SetInfoName(szParInfoName);
pPostInfo->SetRenameStatus(PostInfo::rsSkipped);
pPostInfo->SetParStatus(bParCheck && !pNZBInfo->GetUnpackCleanedUpDisk() ? PostInfo::psNone : PostInfo::psSkipped);
pPostInfo->SetUnpackStatus(!pNZBInfo->GetUnpackCleanedUpDisk() ? PostInfo::usNone : PostInfo::usSkipped);
if (bAddTop)
{
cPostQueue.push_front(pPostInfo);
}
else
{
cPostQueue.push_back(pPostInfo);
}
bJobsAdded = true;
free(szParFilename);
}
}
if (cPostQueue.empty() && bUnpackOrScript && (m_bPostScript || g_pOptions->GetUnpack()))
{
info("Queueing %s for post-processing", pNZBInfo->GetName());
PostInfo* pPostInfo = new PostInfo();
pPostInfo->SetNZBInfo(pNZBInfo);
pPostInfo->SetParFilename("");
pPostInfo->SetInfoName(pNZBInfo->GetName());
pPostInfo->SetRenameStatus(PostInfo::rsSkipped);
pPostInfo->SetParStatus(PostInfo::psSkipped);
pPostInfo->SetUnpackStatus(!pNZBInfo->GetUnpackCleanedUpDisk() ? PostInfo::usNone : PostInfo::usSkipped);
cPostQueue.push_back(pPostInfo);
bJobsAdded = true;
}
for (PostQueue::iterator it = cPostQueue.begin(); it != cPostQueue.end(); it++)
{
if (bAddTop)
{
pDownloadQueue->GetPostQueue()->push_front(*it);
}
else
{
pDownloadQueue->GetPostQueue()->push_back(*it);
}
}
if (bJobsAdded)
{
SaveQueue(pDownloadQueue);
m_bHasMoreJobs = true;
}
return bJobsAdded;
}
/**
* Returns the first FileInfo belonging to given NZBInfo.
*/
@@ -901,7 +952,7 @@ void PrePostProcessor::CheckScheduledResume()
}
}
bool PrePostProcessor::QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText)
bool PrePostProcessor::QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset)
{
debug("Edit-command for post-processor received");
switch (eAction)
@@ -915,10 +966,11 @@ bool PrePostProcessor::QueueEditList(IDList* pIDList, EEditAction eAction, int i
return PostQueueDelete(pIDList);
case eaHistoryDelete:
return HistoryDelete(pIDList);
case eaHistoryReturn:
case eaHistoryProcess:
case eaHistorySetParameter:
return HistoryEdit(pIDList, eAction, iOffset, szText);
return HistoryReturn(pIDList, eAction == eaHistoryProcess);
default:
return false;
@@ -1052,7 +1104,7 @@ bool PrePostProcessor::PostQueueMove(IDList* pIDList, EEditAction eAction, int i
return bOK;
}
bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText)
bool PrePostProcessor::HistoryDelete(IDList* pIDList)
{
bool bOK = false;
@@ -1066,26 +1118,39 @@ bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOf
HistoryInfo* pHistoryInfo = *itHistory;
if (pHistoryInfo->GetID() == iID)
{
switch (eAction)
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
info("Deleting %s from history", szNiceName);
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
case eaHistoryDelete:
HistoryDelete(pDownloadQueue, itHistory, pHistoryInfo);
break;
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
case eaHistoryReturn:
case eaHistoryProcess:
HistoryReturn(pDownloadQueue, itHistory, pHistoryInfo, eAction == eaHistoryProcess);
break;
case eaHistorySetParameter:
HistorySetParameter(pHistoryInfo, szText);
break;
default:
// nothing, just to avoid compiler warning
break;
// delete parked files
int index = 0;
for (FileQueue::iterator it = pDownloadQueue->GetParkedFiles()->begin(); it != pDownloadQueue->GetParkedFiles()->end(); )
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
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++;
}
}
}
pDownloadQueue->GetHistoryList()->erase(itHistory);
delete pHistoryInfo;
bOK = true;
break;
}
@@ -1102,152 +1167,112 @@ bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOf
return bOK;
}
void PrePostProcessor::HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo)
bool PrePostProcessor::HistoryReturn(IDList* pIDList, bool bReprocess)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
info("Deleting %s from history", szNiceName);
bool bOK = false;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
for (IDList::iterator itID = pIDList->begin(); itID != pIDList->end(); itID++)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
// delete parked files
int index = 0;
for (FileQueue::iterator it = pDownloadQueue->GetParkedFiles()->begin(); it != pDownloadQueue->GetParkedFiles()->end(); )
int iID = *itID;
for (HistoryList::iterator itHistory = pDownloadQueue->GetHistoryList()->begin(); itHistory != pDownloadQueue->GetHistoryList()->end(); itHistory++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
HistoryInfo* pHistoryInfo = *itHistory;
if (pHistoryInfo->GetID() == iID)
{
pDownloadQueue->GetParkedFiles()->erase(it);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Returning %s from history back to download queue", szNiceName);
bool bUnparked = false;
if (bReprocess && pHistoryInfo->GetKind() != HistoryInfo::hkNZBInfo)
{
g_pDiskState->DiscardFile(pFileInfo);
error("Could not restart postprocessing for %s: history item has wrong type", szNiceName);
break;
}
delete pFileInfo;
it = pDownloadQueue->GetParkedFiles()->begin() + index;
}
else
{
it++;
index++;
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->SetParCleanup(false);
if (!pNZBInfo->GetUnpackCleanedUpDisk())
{
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->SetUnpackStatus(NZBInfo::usNone);
}
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 "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", szNiceName);
}
if (bReprocess)
{
// start postprocessing
debug("Restarting postprocessing for %s", szNiceName);
NZBDownloaded(pDownloadQueue, pHistoryInfo->GetNZBInfo());
}
if (bUnparked || bReprocess)
{
delete pHistoryInfo;
}
break;
}
}
}
pDownloadQueue->GetHistoryList()->erase(itHistory);
delete pHistoryInfo;
}
void PrePostProcessor::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Returning %s from history back to download queue", szNiceName);
bool bUnparked = false;
if (bReprocess && pHistoryInfo->GetKind() != HistoryInfo::hkNZBInfo)
{
error("Could not restart postprocessing for %s: history item has wrong type", szNiceName);
return;
}
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->SetParCleanup(false);
if (!pNZBInfo->GetUnpackCleanedUpDisk())
{
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->SetUnpackStatus(NZBInfo::usNone);
pNZBInfo->SetCleanupStatus(NZBInfo::csNone);
}
pNZBInfo->GetScriptStatuses()->Clear();
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 "pHistoryInfo" is released few lines later, after the call to "NZBDownloaded"
info("%s returned from history back to download queue", szNiceName);
}
else
{
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", szNiceName);
NZBDownloaded(pDownloadQueue, pHistoryInfo->GetNZBInfo());
}
if (bUnparked || bReprocess)
{
delete pHistoryInfo;
}
}
void PrePostProcessor::HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Setting post-process-parameter '%s' for '%s'", szText, szNiceName);
if (pHistoryInfo->GetKind() != HistoryInfo::hkNZBInfo)
{
error("Could not set post-process-parameter for %s: history item has wrong type", szNiceName);
return;
}
char* szStr = strdup(szText);
char* szValue = strchr(szStr, '=');
if (szValue)
{
*szValue = '\0';
szValue++;
pHistoryInfo->GetNZBInfo()->SetParameter(szStr, szValue);
}
else
{
error("Could not set post-process-parameter for %s: invalid argument: %s", pHistoryInfo->GetNZBInfo()->GetName(), szText);
}
free(szStr);
if (bOK)
{
SaveQueue(pDownloadQueue);
}
g_pQueueCoordinator->UnlockQueue();
return bOK;
}

View File

@@ -31,6 +31,7 @@
#include "Thread.h"
#include "Observer.h"
#include "DownloadInfo.h"
#include "Scanner.h"
#include "ParCoordinator.h"
class PrePostProcessor : public Thread
@@ -44,8 +45,7 @@ public:
eaPostDelete,
eaHistoryDelete,
eaHistoryReturn,
eaHistoryProcess,
eaHistorySetParameter
eaHistoryProcess
};
private:
@@ -71,16 +71,18 @@ private:
PostParCoordinator m_ParCoordinator;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bPostScript;
bool m_bSchedulerPauseChanged;
bool m_bSchedulerPause;
bool m_bPostPause;
Scanner m_Scanner;
const char* m_szPauseReason;
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
bool bIgnorePausedPars, bool bAllowOnlyOneDeleted);
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
void CheckPostQueue();
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void StartProcessJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
void SaveQueue(DownloadQueue* pDownloadQueue);
void SanitisePostQueue(PostQueue* pPostQueue);
void CheckDiskSpace();
@@ -93,14 +95,13 @@ private:
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bUnpackOrScript, bool bAddTop);
void DeleteQueuedFile(const char* szQueuedFile);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
bool PostQueueDelete(IDList* pIDList);
bool HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo);
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
bool HistoryDelete(IDList* pIDList);
bool HistoryReturn(IDList* pIDList, bool bReprocess);
void Cleanup();
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void CheckHistory();
@@ -113,7 +114,8 @@ public:
virtual void Stop();
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void ScanNZBDir(bool bSyncMode);
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
};
#endif

View File

@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
@@ -117,17 +117,6 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Unlock();
// compute maximum number of allowed download threads
m_iDownloadsLimit = 2; // two extra threads for completing files (when connections are not needed)
for (ServerPool::Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() == 0)
{
m_iDownloadsLimit += pNewsServer->GetMaxConnections();
}
}
m_tStartServer = time(NULL);
m_tLastCheck = m_tStartServer;
bool bWasStandBy = true;
@@ -144,27 +133,21 @@ void QueueCoordinator::Run()
// start download for next article
FileInfo* pFileInfo;
ArticleInfo* pArticleInfo;
bool bFreeConnection = false;
m_mutexDownloadQueue.Lock();
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
if (bHasMoreArticles && !IsStopped() && (int)m_ActiveDownloads.size() < m_iDownloadsLimit)
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
{
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
bArticeDownloadsRunning = true;
}
else
{
bFreeConnection = true;
}
m_mutexDownloadQueue.Unlock();
if (bFreeConnection)
{
g_pServerPool->FreeConnection(pConnection, false);
}
m_mutexDownloadQueue.Unlock();
}
}
else
@@ -565,11 +548,7 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
pArticleDownloader->SetFileInfo(pFileInfo);
pArticleDownloader->SetArticleInfo(pArticleInfo);
pArticleDownloader->SetConnection(pConnection);
char szInfoName[1024];
snprintf(szInfoName, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
szInfoName[1024-1] = '\0';
pArticleDownloader->SetInfoName(szInfoName);
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() + 1);
@@ -578,6 +557,45 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
pArticleDownloader->Start();
}
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
{
char name[1024];
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
name[1024-1] = '\0';
pArticleInfo->SetResultFilename(name);
char tmpname[1024];
snprintf(tmpname, 1024, "%s.tmp", name);
tmpname[1024-1] = '\0';
pArticleDownloader->SetTempFilename(tmpname);
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);
if (g_pOptions->GetDirectWrite())
{
pFileInfo->LockOutputFile();
if (pFileInfo->GetOutputFilename())
{
strncpy(name, pFileInfo->GetOutputFilename(), 1024);
name[1024-1] = '\0';
}
else
{
snprintf(name, 1024, "%s%c%i.out.tmp", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetID());
name[1024-1] = '\0';
pFileInfo->SetOutputFilename(name);
}
pFileInfo->UnlockOutputFile();
pArticleDownloader->SetOutputFilename(name);
}
}
DownloadQueue* QueueCoordinator::LockQueue()
{
m_mutexDownloadQueue.Lock();
@@ -644,7 +662,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
fileCompleted = false;
pFileInfo->SetAutoDeleted(true);
DeleteQueueEntry(pFileInfo);
}
}
@@ -926,7 +943,7 @@ void QueueCoordinator::AdjustStartTime()
if (tDiff > 60 || tDiff < 0)
{
m_tStartServer += tDiff + 1; // "1" because the method is called once per second
if (m_tStartDownload != 0 && !m_bStandBy)
if (m_tStartDownload != 0)
{
m_tStartDownload += tDiff + 1;
}
@@ -1031,76 +1048,3 @@ bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZB
return true;
}
/*
* Creates new nzb-item out of existing files from other nzb-items.
* If any of file-items is being downloaded the command fail.
* For each file-item an event "eaFileDeleted" is fired.
*
* NOTE: DownloadQueue must be locked prior to call of this function
*/
bool QueueCoordinator::SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo)
{
if (pFileList->empty())
{
return false;
}
NZBInfo* pSrcNZBInfo = NULL;
for (FileQueue::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetActiveDownloads() > 0 || pFileInfo->GetCompleted() > 0)
{
error("Could not split %s. File is already (partially) downloaded", pFileInfo->GetFilename());
return false;
}
if (pFileInfo->GetNZBInfo()->GetPostProcess())
{
error("Could not split %s. File in post-process-stage", pFileInfo->GetFilename());
return false;
}
if (!pSrcNZBInfo)
{
pSrcNZBInfo = pFileInfo->GetNZBInfo();
}
}
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->AddReference();
m_DownloadQueue.GetNZBInfoList()->Add(pNZBInfo);
pNZBInfo->SetFilename(pSrcNZBInfo->GetFilename());
pNZBInfo->SetName(szName);
pNZBInfo->SetCategory(pSrcNZBInfo->GetCategory());
pNZBInfo->BuildDestDirName();
pNZBInfo->SetQueuedFilename(pSrcNZBInfo->GetQueuedFilename());
for (NZBParameterList::iterator it = pSrcNZBInfo->GetParameters()->begin(); it != pSrcNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
pNZBInfo->SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
for (FileQueue::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
FileInfo* pFileInfo = *it;
Aspect aspect = { eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
pFileInfo->SetNZBInfo(pNZBInfo);
pSrcNZBInfo->SetFileCount(pSrcNZBInfo->GetFileCount() - 1);
pSrcNZBInfo->SetSize(pSrcNZBInfo->GetSize() - pFileInfo->GetSize());
pNZBInfo->SetFileCount(pNZBInfo->GetFileCount() + 1);
pNZBInfo->SetSize(pNZBInfo->GetSize() + pFileInfo->GetSize());
}
pNZBInfo->Release();
*pNewNZBInfo = pNZBInfo;
return true;
}

View File

@@ -63,7 +63,6 @@ private:
QueueEditor m_QueueEditor;
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
int m_iDownloadsLimit;
// statistics
static const int SPEEDMETER_SLOTS = 30;
@@ -90,6 +89,7 @@ private:
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
@@ -121,7 +121,6 @@ public:
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
bool SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo);
void DiscardDiskFile(FileInfo* pFileInfo);
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -33,8 +33,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <cctype>
#include <cstdio>
#include <sys/stat.h>
#include <set>
#ifndef WIN32
@@ -227,11 +227,7 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
}
else if (eAction == eaGroupMerge)
{
return MergeGroups(pDownloadQueue, &cItemList);
}
else if (eAction == eaFileSplit)
{
return SplitGroup(pDownloadQueue, &cItemList, szText);
MergeGroups(pDownloadQueue, &cItemList);
}
else if (eAction == eaFileReorder)
{
@@ -294,7 +290,6 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
case eaFileSplit:
// remove compiler warning "enumeration not handled in switch"
break;
}
@@ -548,8 +543,8 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause,
eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder, eaFileSplit,
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom,
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
@@ -839,15 +834,13 @@ bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInf
return false;
}
bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
{
if (pItemList->size() == 0)
{
return false;
return;
}
bool bOK = true;
EditItem* pDestItem = pItemList->front();
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
@@ -856,45 +849,15 @@ bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
{
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
if (g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo()))
{
bOK = false;
}
g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo());
}
delete pItem;
}
// align group
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
delete pDestItem;
return bOK;
}
bool QueueEditor::SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName)
{
if (pItemList->size() == 0)
{
return false;
}
FileQueue* pFileList = new FileQueue();
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
{
EditItem* pItem = *it;
pFileList->push_back(pItem->m_pFileInfo);
delete pItem;
}
NZBInfo* pNewNZBInfo = NULL;
bool bOK = g_pQueueCoordinator->SplitQueueEntries(pFileList, szName, &pNewNZBInfo);
if (bOK)
{
AlignGroup(pDownloadQueue, pNewNZBInfo);
}
delete pFileList;
return bOK;
}
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)

View File

@@ -1,7 +1,7 @@
/*
* This file if part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -45,7 +45,6 @@ public:
eaFilePauseExtraPars,
eaFileSetPriority,
eaFileReorder,
eaFileSplit,
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
eaGroupMoveTop,
eaGroupMoveBottom,
@@ -97,8 +96,7 @@ private:
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
bool SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName);
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);

43
README
View File

@@ -429,28 +429,40 @@ same computer)
Security warning
----------------
NZBGet communicates via unsecured socket connections. This makes it vulnerable.
Although server checks the password passed by client, this password is still
transmitted in unsecured way. For this reason it is highly recommended
to configure your Firewall to not expose the port used by NZBGet to WAN.
NZBGet client communicates with NZBGet server via unsecured socket connections.
This makes it vulnerable. Although server checks the password passed by client,
this password is still transmitted in unsecured way. For this reason it is
highly recommended to configure your firewall to not expose the port used by
NZBGet (option <ControlPort>) to WAN.
If you need to control server from WAN it is better to connect to server's
terminal via SSH (POSIX) or remote desktop (Windows) and then run
nzbget-client-commands in this terminal.
If you need to control server from WAN it is better to use web-interface via HTTPS
or (if you prefer remote commands) connect to server's terminal via SSH (POSIX)
or remote desktop (Windows) and then run nzbget-client-commands in this terminal.
Post processing scripts
-----------------------
After the download of nzb-file is completed nzbget can call post-processing
scripts, defined in configuration file.
script, defined in configuration file. See example configuration file for
the description of parameters passed to the script (option "PostProcess").
Example post-processing scripts are provided in directory "ppscripts".
An example script for unraring of downloaded files is provided in file
"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").
To use the scripts copy them into your local directory and set options
<ScriptDir>, <DefScript> and <ScriptOrder>.
Set the option "PostProcess" in "nzbget.conf" to point to the post-
processing script.
For information on writing your own post-processing scripts please
visit NZBGet web site.
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
-------------
@@ -512,8 +524,11 @@ Binary distribution for Windows contains code from the following libraries:
- libpar2 (http://parchive.sourceforge.net)
- libsigc++ (http://libsigc.sourceforge.net)
- GnuTLS (http://www.gnu.org/software/gnutls)
- zlib (http://www.zlib.net)
- regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
libpar2 is distributed under GPL; libsigc++, GnuTLS and regex - under LGPL;
zlib - under zlib license.
=====================================
10. Contact

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -34,7 +34,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <windows.h>
#else
@@ -126,10 +126,6 @@ void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest,
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
pMessageBase->m_iType = htonl(iRequest);
pMessageBase->m_iStructSize = htonl(iSize);
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE - 1);
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
}
@@ -188,7 +184,7 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
DownloadRequest.m_bAddPaused = htonl(bAddPaused);
DownloadRequest.m_iPriority = htonl(iPriority);
DownloadRequest.m_iTrailingDataLength = htonl(iLength - 1);
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
strncpy(DownloadRequest.m_szFilename, szFilename, NZBREQUESTFILENAMESIZE - 1);
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
@@ -1039,13 +1035,13 @@ bool RemoteClient::RequestPostQueue()
}
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Unpacking", ", Executing postprocess-script", "" };
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
ntohl(pPostQueueAnswer->m_iInfoNameLen) + ntohl(pPostQueueAnswer->m_iDestDirLen) +
ntohl(pPostQueueAnswer->m_iProgressLabelLen);
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
}
free(pBuf);
@@ -1164,7 +1160,7 @@ bool RemoteClient::RequestHistory()
char szSize[20];
Util::FormatFileSize(szSize, sizeof(szSize), lSize);
const char* szParStatusText[] = { "", "", ", Par failed", ", Par successful", ", Repair possible", ", Repair needed" };
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,

View File

@@ -104,7 +104,7 @@ void RemoteServer::Run()
m_bTLS);
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
m_pConnection->SetSuppressErrors(false);
bBind = m_pConnection->Bind();
bBind = m_pConnection->Bind() == 0;
}
// Accept connections and store the new Connection

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -33,13 +33,14 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fstream>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <sys/stat.h>
#include <errno.h>
#include "nzbget.h"
#include "Scanner.h"
@@ -66,41 +67,6 @@ Scanner::FileData::~FileData()
free(m_szFilename);
}
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
{
m_szFilename = strdup(szFilename);
m_szNZBName = strdup(szNZBName);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iPriority = iPriority;
m_bAddTop = bAddTop;
m_bAddPaused = bAddPaused;
if (pParameters)
{
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pNZBParameter = *it;
m_Parameters.SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
}
}
Scanner::QueueData::~QueueData()
{
free(m_szFilename);
free(m_szNZBName);
free(m_szCategory);
for (NZBParameterList::iterator it = m_Parameters.begin(); it != m_Parameters.end(); it++)
{
delete *it;
}
m_Parameters.clear();
}
Scanner::Scanner()
{
debug("Creating Scanner");
@@ -109,6 +75,7 @@ Scanner::Scanner()
m_bScanning = false;
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
m_iPass = 0;
m_iStepMSec = 0;
const char* szNZBScript = g_pOptions->GetNZBProcess();
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
@@ -123,26 +90,13 @@ Scanner::~Scanner()
delete *it;
}
m_FileList.clear();
ClearQueueList();
}
void Scanner::ClearQueueList()
{
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
delete *it;
}
m_QueueList.clear();
}
void Scanner::Check()
{
m_mutexScan.Lock();
if (m_bRequestedNZBDirScan ||
if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan ||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000))
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
{
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
bool bCheckStat = !m_bRequestedNZBDirScan;
@@ -151,7 +105,7 @@ void Scanner::Check()
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
if (!bCheckStat && m_bNZBScript)
{
// if immediate scan requested, we need second scan to process files extracted by NzbProcess-script
// if immediate scan requesten, we need second scan to process files extracted by NzbProcess-script
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
}
m_bScanning = false;
@@ -162,7 +116,7 @@ void Scanner::Check()
// - one additional scan is neccessary to check sizes of detected files;
// - another scan is required to check files which were extracted by NzbProcess-script;
// - third scan is needed to check sizes of extracted files.
if (g_pOptions->GetNzbDirInterval() > 0 && g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
if (g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
{
int iMaxPass = m_bNZBScript ? 3 : 1;
if (m_iPass < iMaxPass)
@@ -179,10 +133,7 @@ void Scanner::Check()
DropOldFiles();
}
m_iNZBDirInterval += 200;
ClearQueueList();
m_mutexScan.Unlock();
m_iNZBDirInterval += m_iStepMSec;
}
/**
@@ -327,34 +278,16 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
return;
}
char* szNZBName = strdup("");
char* szNZBCategory = strdup(szCategory);
NZBParameterList* pParameters = new NZBParameterList();
NZBParameterList* pParameterList = new NZBParameterList;
int iPriority = 0;
bool bAddTop = false;
bool bAddPaused = false;
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
QueueData* pQueueData = *it;
if (Util::SameFilename(pQueueData->GetFilename(), szFullFilename))
{
free(szNZBName);
szNZBName = strdup(pQueueData->GetNZBName());
free(szNZBCategory);
szNZBCategory = strdup(pQueueData->GetCategory());
iPriority = pQueueData->GetPriority();
bAddTop = pQueueData->GetAddTop();
bAddPaused = pQueueData->GetAddPaused();
}
}
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
{
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
&szNZBName, &szNZBCategory, &iPriority, pParameters, &bAddTop, &bAddPaused);
&szNZBCategory, &iPriority, pParameterList);
bExists = Util::FileExists(szFullFilename);
if (bExists && strcasecmp(szExtension, ".nzb"))
{
@@ -362,8 +295,7 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
if (!bRenameOK)
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
}
}
}
@@ -374,38 +306,35 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
if (bRenameOK)
{
AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szRenamedName, szNZBCategory, iPriority, pParameterList);
}
else
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, szRenamedName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szFullFilename, szNZBCategory, iPriority, pParameterList);
}
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
delete *it;
}
pParameters->clear();
delete pParameters;
pParameterList->clear();
delete pParameterList;
free(szNZBName);
free(szNZBCategory);
}
void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList)
{
const char* szBasename = Util::BaseFileName(szFilename);
info("Collection %s found", szBasename);
NZBFile* pNZBFile = NZBFile::Create(szFilename, szCategory);
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
if (!pNZBFile)
{
error("Could not add collection %s to queue", szBasename);
@@ -415,22 +344,14 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, cons
bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
if (!bRenameOK)
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not rename file %s to %s! Errcode: %i", szFilename, bakname2, errno);
}
if (pNZBFile && bRenameOK)
{
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
if (szNZBName && strlen(szNZBName) > 0)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
pNZBFile->GetNZBInfo()->SetFilename(szNZBName);
pNZBFile->GetNZBInfo()->BuildDestDirName();
}
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
{
NZBParameter* pParameter = *it;
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
@@ -440,10 +361,9 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, cons
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
pFileInfo->SetPaused(bAddPaused);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
info("Collection %s added to queue", szBasename);
}
@@ -455,91 +375,13 @@ void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, cons
void Scanner::ScanNZBDir(bool bSyncMode)
{
m_mutexScan.Lock();
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
// but it's not critical here.
m_bScanning = true;
m_bRequestedNZBDirScan = true;
m_mutexScan.Unlock();
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
{
usleep(100 * 1000);
}
}
bool Scanner::AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode)
{
char szTempFileName[1024];
if (szFileName)
{
strncpy(szTempFileName, szFileName, 1024);
szTempFileName[1024-1] = '\0';
}
else
{
int iNum = 1;
while (iNum == 1 || Util::FileExists(szTempFileName))
{
snprintf(szTempFileName, 1024, "%snzb-%i.tmp", g_pOptions->GetTempDir(), iNum);
szTempFileName[1024-1] = '\0';
iNum++;
}
if (!Util::SaveBufferIntoFile(szTempFileName, szBuffer, iBufSize))
{
error("Could not create file %s", szTempFileName);
return false;
}
}
// move file into NzbDir, make sure the file name is unique
char szValidNZBName[1024];
strncpy(szValidNZBName, Util::BaseFileName(szNZBName), 1024);
szValidNZBName[1024-1] = '\0';
Util::MakeValidFilename(szValidNZBName, '_', false);
char szScanFileName[1024];
snprintf(szScanFileName, 1024, "%s%s", g_pOptions->GetNzbDir(), szValidNZBName);
char *szExt = strrchr(szValidNZBName, '.');
if (szExt)
{
*szExt = '\0';
szExt++;
}
int iNum = 2;
while (Util::FileExists(szScanFileName))
{
if (szExt)
{
snprintf(szScanFileName, 1024, "%s%s_%i.%s", g_pOptions->GetNzbDir(), szValidNZBName, iNum, szExt);
}
else
{
snprintf(szScanFileName, 1024, "%s%s_%i", g_pOptions->GetNzbDir(), szValidNZBName, iNum);
}
szScanFileName[1024-1] = '\0';
iNum++;
}
if (!Util::MoveFile(szTempFileName, szScanFileName))
{
char szSysErrStr[256];
error("Could not move file %s to %s: %s", szTempFileName, szScanFileName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
remove(szTempFileName);
return false;
}
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szCategory, iPriority, pParameters, bAddTop, bAddPaused);
m_mutexScan.Lock();
m_QueueList.push_back(pQueueData);
m_mutexScan.Unlock();
ScanNZBDir(bSyncMode);
return true;
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2011 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
@@ -29,7 +29,6 @@
#include <deque>
#include <time.h>
#include "DownloadInfo.h"
#include "Thread.h"
class Scanner
{
@@ -53,57 +52,26 @@ private:
typedef std::deque<FileData*> FileList;
class QueueData
{
private:
char* m_szFilename;
char* m_szNZBName;
char* m_szCategory;
int m_iPriority;
NZBParameterList m_Parameters;
bool m_bAddTop;
bool m_bAddPaused;
public:
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
~QueueData();
const char* GetFilename() { return m_szFilename; }
const char* GetNZBName() { return m_szNZBName; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
NZBParameterList* GetParameters() { return &m_Parameters; }
bool GetAddTop() { return m_bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
};
typedef std::deque<QueueData*> QueueList;
bool m_bRequestedNZBDirScan;
int m_iNZBDirInterval;
bool m_bNZBScript;
int m_iPass;
int m_iStepMSec;
FileList m_FileList;
QueueList m_QueueList;
bool m_bScanning;
Mutex m_mutexScan;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
void AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
void AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
void DropOldFiles();
void ClearQueueList();
public:
Scanner();
~Scanner();
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
void ScanNZBDir(bool bSyncMode);
void Check();
bool AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddPaused, bool bAddTop,
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2009 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
@@ -35,7 +35,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "nzbget.h"
#include "Scheduler.h"

View File

@@ -57,7 +57,8 @@ extern Options* g_pOptions;
extern char* (*g_szEnvironmentVariables)[];
extern DownloadQueueHolder* g_pDownloadQueueHolder;
static const int POSTPROCESS_PARCHECK = 92;
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
static const int POSTPROCESS_PARCHECK_ALL = 92;
static const int POSTPROCESS_SUCCESS = 93;
static const int POSTPROCESS_ERROR = 94;
static const int POSTPROCESS_NONE = 95;
@@ -113,11 +114,6 @@ EnvironmentStrings::EnvironmentStrings()
}
EnvironmentStrings::~EnvironmentStrings()
{
Clear();
}
void EnvironmentStrings::Clear()
{
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
{
@@ -197,7 +193,7 @@ ScriptController::ScriptController()
m_szArgs = NULL;
m_bFreeArgs = false;
m_szInfoName = NULL;
m_szLogPrefix = NULL;
m_szDefaultKindPrefix = NULL;
m_bTerminated = false;
m_environmentStrings.InitFromCurrentProcess();
}
@@ -214,12 +210,6 @@ ScriptController::~ScriptController()
}
}
void ScriptController::ResetEnv()
{
m_environmentStrings.Clear();
m_environmentStrings.InitFromCurrentProcess();
}
void ScriptController::SetEnvVar(const char* szName, const char* szValue)
{
int iLen = strlen(szName) + strlen(szValue) + 2;
@@ -228,139 +218,64 @@ void ScriptController::SetEnvVar(const char* szName, const char* szValue)
m_environmentStrings.Append(szVar);
}
/**
* If szStripPrefix is not NULL, only options, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all options are processed; without stripping.
*/
void ScriptController::PrepareEnvOptions(const char* szStripPrefix)
void ScriptController::PrepareEnvOptions()
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries();
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
{
Options::OptEntry* pOptEntry = *it;
if (szStripPrefix && !strncmp(pOptEntry->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pOptEntry->GetName()) > iPrefixLen)
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBOP_%s", pOptEntry->GetName());
// convert to upper case; replace "." with "_".
for (char* szPtr = szVarname; *szPtr; szPtr++)
{
SetEnvVarSpecial("NZBPO", pOptEntry->GetName() + iPrefixLen, pOptEntry->GetValue());
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBOP", pOptEntry->GetName(), pOptEntry->GetValue());
if (*szPtr == '.') *szPtr = '_';
*szPtr = toupper(*szPtr);
}
szVarname[1024-1] = '\0';
SetEnvVar(szVarname, pOptEntry->GetValue());
}
g_pOptions->UnlockOptEntries();
}
/**
* If szStripPrefix is not NULL, only pp-parameters, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
* If szStripPrefix is NULL, all pp-parameters are processed; without stripping.
*/
void ScriptController::PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix)
void ScriptController::PrepareEnvParameters(NZBInfo* pNZBInfo)
{
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, pParameter->GetValue());
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName(), pParameter->GetValue());
}
}
}
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
szVarname[1024-1] = '\0';
void ScriptController::SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue)
{
char szVarname[1024];
snprintf(szVarname, sizeof(szVarname), "%s_%s", szPrefix, szName);
szVarname[1024-1] = '\0';
// Original name
SetEnvVar(szVarname, szValue);
char szNormVarname[1024];
strncpy(szNormVarname, szVarname, sizeof(szVarname));
szNormVarname[1024-1] = '\0';
// Replace special characters with "_" and convert to upper case
for (char* szPtr = szNormVarname; *szPtr; szPtr++)
{
if (strchr(".:*!\"$%&/()=`+~#'{}[]@- ", *szPtr)) *szPtr = '_';
*szPtr = toupper(*szPtr);
}
// Another env var with normalized name (replaced special chars and converted to upper case)
if (strcmp(szVarname, szNormVarname))
{
SetEnvVar(szNormVarname, szValue);
}
}
// Original name
SetEnvVar(szVarname, pParameter->GetValue());
void ScriptController::PrepareArgs()
{
#ifdef WIN32
if (!m_szArgs)
{
// Special support for script languages:
// automatically find the app registered for this extension and run it
const char* szExtension = strrchr(GetScript(), '.');
if (szExtension && strcasecmp(szExtension, ".exe") && strcasecmp(szExtension, ".bat") && strcasecmp(szExtension, ".cmd"))
char szNormVarname[1024];
strncpy(szNormVarname, szVarname, sizeof(szVarname));
szNormVarname[1024-1] = '\0';
// replace ".*:" with "_".
for (char* szPtr = szNormVarname; *szPtr; szPtr++)
{
debug("Looking for associated program for %s", szExtension);
char szCommand[512];
int iBufLen = 512-1;
if (Util::RegReadStr(HKEY_CLASSES_ROOT, szExtension, NULL, szCommand, &iBufLen))
{
szCommand[iBufLen] = '\0';
debug("Extension: %s", szCommand);
char szRegPath[512];
snprintf(szRegPath, 512, "%s\\shell\\open\\command", szCommand);
szRegPath[512-1] = '\0';
iBufLen = 512-1;
if (Util::RegReadStr(HKEY_CLASSES_ROOT, szRegPath, NULL, szCommand, &iBufLen))
{
szCommand[iBufLen] = '\0';
debug("Command: %s", szCommand);
DWORD_PTR pArgs[] = { (DWORD_PTR)GetScript(), (DWORD_PTR)0 };
if (FormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, szCommand, 0, 0,
m_szCmdLine, sizeof(m_szCmdLine), (va_list*)pArgs))
{
debug("CmdLine: %s", m_szCmdLine);
return;
}
}
}
warn("Could not found associated program for %s. Trying to execute %s directly", szExtension, Util::BaseFileName(GetScript()));
if (*szPtr == '.' || *szPtr == ':' || *szPtr == '*') *szPtr = '_';
*szPtr = toupper(*szPtr);
}
}
#endif
if (!m_szArgs)
{
m_szStdArgs[0] = GetScript();
m_szStdArgs[1] = NULL;
SetArgs(m_szStdArgs, false);
// Another env var with normalized name (replaced special chars and converted to upper case)
if (strcmp(szVarname, szNormVarname))
{
SetEnvVar(szNormVarname, pParameter->GetValue());
}
}
}
int ScriptController::Execute()
{
PrepareEnvOptions(NULL);
PrepareArgs();
PrepareEnvOptions();
int iExitCode = 0;
int pipein;
@@ -373,24 +288,14 @@ int ScriptController::Execute()
#ifdef WIN32
// build command line
char* szCmdLine = NULL;
if (m_szArgs)
char szCmdLine[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
char szCmdLineBuf[2048];
int iUsedLen = 0;
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
{
snprintf(szCmdLineBuf + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
}
szCmdLineBuf[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
szCmdLine = szCmdLineBuf;
}
else
{
szCmdLine = m_szCmdLine;
snprintf(szCmdLine + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
iUsedLen += strlen(*szArgPtr) + 3;
}
szCmdLine[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
// create pipes to write and read data
HANDLE hReadPipe, hWritePipe;
@@ -605,7 +510,11 @@ int ScriptController::Execute()
} // while (!bChildConfirmed && !m_bTerminated)
#endif
debug("Exit code %i", iExitCode);
if (!m_bTerminated)
{
info("Completed %s", m_szInfoName);
debug("Exit code %i", iExitCode);
}
return iExitCode;
}
@@ -650,7 +559,7 @@ void ScriptController::ProcessOutput(char* szText)
for (char* pend = szText + strlen(szText) - 1; pend >= szText && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
if (szText[0] == '\0')
if (strlen(szText) == 0)
{
// skip empty lines
return;
@@ -658,54 +567,78 @@ void ScriptController::ProcessOutput(char* szText)
if (!strncmp(szText, "[INFO] ", 7))
{
PrintMessage(Message::mkInfo, szText + 7);
AddMessage(Message::mkInfo, false, szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
PrintMessage(Message::mkWarning, szText + 10);
AddMessage(Message::mkWarning, false, szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
PrintMessage(Message::mkError, szText + 8);
AddMessage(Message::mkError, false, szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
PrintMessage(Message::mkDetail, szText + 9);
AddMessage(Message::mkDetail, false, szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
PrintMessage(Message::mkDebug, szText + 8);
AddMessage(Message::mkDebug, false, szText + 8);
}
else
{
PrintMessage(Message::mkInfo, szText);
{
switch (m_eDefaultLogKind)
{
case Options::slNone:
break;
case Options::slDetail:
AddMessage(Message::mkDetail, true, szText);
break;
case Options::slInfo:
AddMessage(Message::mkInfo, true, szText);
break;
case Options::slWarning:
AddMessage(Message::mkWarning, true, szText);
break;
case Options::slError:
AddMessage(Message::mkError, true, szText);
break;
case Options::slDebug:
AddMessage(Message::mkDebug, true, szText);
break;
}
}
debug("Processing output received from script - completed");
}
void ScriptController::AddMessage(Message::EKind eKind, const char* szText)
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
switch (eKind)
{
case Message::mkDetail:
detail("%s", szText);
detail("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkInfo:
info("%s", szText);
info("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkWarning:
warn("%s", szText);
warn("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkError:
error("%s", szText);
error("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
case Message::mkDebug:
debug("%s", szText);
debug("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
break;
}
}
@@ -719,26 +652,18 @@ void ScriptController::PrintMessage(Message::EKind eKind, const char* szFormat,
vsnprintf(tmp2, 1024, szFormat, ap);
tmp2[1024-1] = '\0';
va_end(ap);
char tmp3[1024];
if (m_szLogPrefix)
{
snprintf(tmp3, 1024, "%s: %s", m_szLogPrefix, tmp2);
}
else
{
strncpy(tmp3, tmp2, 1024);
}
tmp3[1024-1] = '\0';
AddMessage(eKind, tmp3);
AddMessage(eKind, false, tmp2);
}
void PostScriptController::StartJob(PostInfo* pPostInfo)
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, bool bNZBFileCompleted, bool bHasFailedParJobs)
{
PostScriptController* pScriptController = new PostScriptController();
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->SetScript(g_pOptions->GetPostProcess());
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
pScriptController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pScriptController);
@@ -747,81 +672,6 @@ void PostScriptController::StartJob(PostInfo* pPostInfo)
}
void PostScriptController::Run()
{
FileList activeList;
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szVarname = pParameter->GetName();
if (strlen(szVarname) > 0 && szVarname[0] != '*' && szVarname[strlen(szVarname)-1] == ':' &&
(!strcasecmp(pParameter->GetValue(), "yes") || !strcasecmp(pParameter->GetValue(), "on") || !strcasecmp(pParameter->GetValue(), "1")))
{
char* szScriptName = strdup(szVarname);
szScriptName[strlen(szScriptName)-1] = '\0'; // remove trailing ':'
activeList.push_back(szScriptName);
}
}
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Clear();
g_pDownloadQueueHolder->UnlockQueue();
Options::ScriptList scriptList;
g_pOptions->LoadScriptList(&scriptList);
for (Options::ScriptList::iterator it = scriptList.begin(); it != scriptList.end(); it++)
{
Options::Script* pScript = *it;
for (FileList::iterator it2 = activeList.begin(); it2 != activeList.end(); it2++)
{
char* szActiveName = *it2;
// if any script has requested par-check, do not execute other scripts
if (Util::SameFilename(pScript->GetName(), szActiveName) && !m_pPostInfo->GetRequestParCheck())
{
ExecuteScript(pScript->GetName(), pScript->GetDisplayName(), pScript->GetLocation());
}
}
}
for (FileList::iterator it = activeList.begin(); it != activeList.end(); it++)
{
free(*it);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation)
{
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", szScriptName, m_pPostInfo->GetInfoName());
SetScript(szLocation);
SetArgs(NULL, false);
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script %s for %s", szScriptName, m_pPostInfo->GetInfoName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetLogPrefix(szDisplayName);
PrepareParams(szScriptName);
int iExitCode = Execute();
szInfoName[0] = 'P'; // uppercase
SetLogPrefix(NULL);
ScriptStatus::EStatus eStatus = AnalyseExitCode(iExitCode);
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Add(szScriptName, eStatus);
g_pDownloadQueueHolder->UnlockQueue();
}
void PostScriptController::PrepareParams(const char* szScriptName)
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
@@ -830,105 +680,156 @@ void PostScriptController::PrepareParams(const char* szScriptName)
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
int iParStatus[] = { 0, 0, 1, 2, 3 };
char szParStatus[10];
snprintf(szParStatus, 10, "%i", iParStatus[m_pPostInfo->GetNZBInfo()->GetParStatus()]);
snprintf(szParStatus, 10, "%i", iParStatus[g_pOptions->GetAllowReProcess() ? (int)m_pPostInfo->GetParStatus() : (int)m_pPostInfo->GetNZBInfo()->GetParStatus()]);
szParStatus[10-1] = '\0';
int iUnpackStatus[] = { 0, 0, 1, 2 };
char szUnpackStatus[10];
snprintf(szUnpackStatus, 10, "%i", iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
snprintf(szUnpackStatus, 10, "%i", g_pOptions->GetAllowReProcess() ? 0 : iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
szUnpackStatus[10-1] = '\0';
char szCollectionCompleted[10];
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
szCollectionCompleted[10-1] = '\0';
char szHasFailedParJobs[10];
snprintf(szHasFailedParJobs, 10, "%i", (int)m_bHasFailedParJobs);
szHasFailedParJobs[10-1] = '\0';
char szDestDir[1024];
strncpy(szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
szDestDir[1024-1] = '\0';
char szNZBID[10];
snprintf(szNZBID, 10, "%i", m_pPostInfo->GetNZBInfo()->GetID());
szNZBID[10-1] = '\0';
char szNZBFilename[1024];
strncpy(szNZBFilename, m_pPostInfo->GetNZBInfo()->GetFilename(), 1024);
szNZBFilename[1024-1] = '\0';
char szParFilename[1024];
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
szParFilename[1024-1] = '\0';
char szCategory[1024];
strncpy(szCategory, m_pPostInfo->GetNZBInfo()->GetCategory(), 1024);
szCategory[1024-1] = '\0';
// Reset
ResetEnv();
char szInfoName[1024];
snprintf(szInfoName, 1024, "post-process-script for %s", g_pOptions->GetAllowReProcess() ? m_pPostInfo->GetInfoName() : m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Post-Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[9];
szArgs[0] = GetScript();
szArgs[1] = szDestDir;
szArgs[2] = szNZBFilename;
szArgs[3] = szParFilename;
szArgs[4] = szParStatus;
szArgs[5] = szCollectionCompleted;
szArgs[6] = szHasFailedParJobs;
szArgs[7] = szCategory;
szArgs[8] = NULL;
SetArgs(szArgs, false);
SetEnvVar("NZBPP_NZBNAME", szNZBName);
SetEnvVar("NZBPP_NZBID", szNZBID);
SetEnvVar("NZBPP_DIRECTORY", szDestDir);
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
SetEnvVar("NZBPP_UNPACKSTATUS", szUnpackStatus);
SetEnvVar("NZBPP_NZBCOMPLETED", szCollectionCompleted);
SetEnvVar("NZBPP_PARFAILED", szHasFailedParJobs);
SetEnvVar("NZBPP_CATEGORY", szCategory);
PrepareEnvParameters(m_pPostInfo->GetNZBInfo(), NULL);
PrepareEnvParameters(m_pPostInfo->GetNZBInfo());
char szParamPrefix[1024];
snprintf(szParamPrefix, 1024, "%s:", szScriptName);
szParamPrefix[1024-1] = '\0';
PrepareEnvParameters(m_pPostInfo->GetNZBInfo(), szParamPrefix);
PrepareEnvOptions(szParamPrefix);
g_pDownloadQueueHolder->UnlockQueue();
}
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int iExitCode)
{
// The ScriptStatus is accumulated for all scripts:
// If any script has failed the status is "failure", etc.
info("Executing post-process-script for %s", g_pOptions->GetAllowReProcess() ? m_pPostInfo->GetInfoName() : szNZBName);
switch (iExitCode)
int iResult = Execute();
szInfoName[0] = 'P'; // uppercase
switch (iResult)
{
case POSTPROCESS_SUCCESS:
PrintMessage(Message::mkInfo, "%s successful", GetInfoName());
return ScriptStatus::srSuccess;
info("%s successful", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
break;
case POSTPROCESS_ERROR:
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
PrintMessage(Message::mkError, "%s failed", GetInfoName());
return ScriptStatus::srFailure;
info("%s failed", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
break;
case POSTPROCESS_NONE:
PrintMessage(Message::mkInfo, "%s skipped", GetInfoName());
return ScriptStatus::srNone;
info("%s skipped", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srNone);
break;
#ifndef DISABLE_PARCHECK
case POSTPROCESS_PARCHECK:
if (m_pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped)
case POSTPROCESS_PARCHECK_ALL:
if (m_pPostInfo->GetParStatus() > PostInfo::psSkipped)
{
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", GetInfoName());
return ScriptStatus::srFailure;
error("%s requested par-check/repair for all collections, but they were already checked", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else if (!m_bNZBFileCompleted)
{
error("%s requested par-check/repair for all collections, but it was not the call for the last collection", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", GetInfoName());
m_pPostInfo->SetRequestParCheck(true);
return ScriptStatus::srSuccess;
info("%s requested par-check/repair for all collections", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
}
break;
case POSTPROCESS_PARCHECK_CURRENT:
if (m_pPostInfo->GetParStatus() > PostInfo::psSkipped)
{
error("%s requested par-check/repair for current collection, but it was already checked", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else if (strlen(m_pPostInfo->GetParFilename()) == 0)
{
error("%s requested par-check/repair for current collection, but it doesn't have any par-files", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
}
else
{
info("%s requested par-check/repair for current collection", szInfoName);
m_pPostInfo->SetRequestParCheck(PostInfo::rpCurrent);
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
}
break;
#endif
default:
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", GetInfoName());
return ScriptStatus::srFailure;
info("%s terminated with unknown status", szInfoName);
m_pPostInfo->SetScriptStatus(PostInfo::srUnknown);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
m_pPostInfo->SetWorking(false);
}
void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
if (!strncmp(szText, "[HISTORY] ", 10))
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText);
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
}
else
{
ScriptController::AddMessage(eKind, szText);
ScriptController::AddMessage(eKind, bDefaultKind, szText);
m_pPostInfo->AppendMessage(eKind, szText);
}
@@ -967,59 +868,16 @@ void PostScriptController::Stop()
Terminate();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void PostScriptController::InitParamsForNewNZB(NZBInfo* pNZBInfo)
{
const char* szDefScript = g_pOptions->GetDefScript();
if (pNZBInfo->GetCategory() && strlen(pNZBInfo->GetCategory()) > 0)
{
Options::Category* pCategory = g_pOptions->FindCategory(pNZBInfo->GetCategory());
if (pCategory && pCategory->GetDefScript() && strlen(pCategory->GetDefScript()) > 0)
{
szDefScript = pCategory->GetDefScript();
}
}
if (!szDefScript || strlen(szDefScript) == 0)
{
return;
}
// split szDefScript into tokens and create pp-parameter for each token
char* szDefScript2 = strdup(szDefScript);
char* saveptr;
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
while (szScriptName)
{
szScriptName = Util::Trim(szScriptName);
if (szScriptName[0] != '\0')
{
char szParam[1024];
snprintf(szParam, 1024, "%s:", szScriptName);
szParam[1024-1] = '\0';
pNZBInfo->GetParameters()->SetParameter(szParam, "yes");
}
szScriptName = strtok_r(NULL, ",;", &saveptr);
}
free(szDefScript2);
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused)
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename,
const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList)
{
info("Executing nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
NZBScriptController* pScriptController = new NZBScriptController();
pScriptController->SetScript(szScript);
pScriptController->m_pNZBName = pNZBName;
pScriptController->m_pCategory = pCategory;
pScriptController->m_pParameters = pParameters;
pScriptController->m_pParameterList = pParameterList;
pScriptController->m_iPriority = iPriority;
pScriptController->m_bAddTop = bAddTop;
pScriptController->m_bAddPaused = bAddPaused;
char szInfoName[1024];
snprintf(szInfoName, 1024, "nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
@@ -1036,51 +894,30 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
szDir[iLen-1] = '\0';
}
char szPriority[20];
snprintf(szPriority, 20, "%i", *iPriority);
szPriority[20-1] = '\0';
pScriptController->SetDefaultKindPrefix("NZB-Process: ");
pScriptController->SetDefaultLogKind(g_pOptions->GetProcessLogKind());
char szAddTop[10];
snprintf(szAddTop, 10, "%i", *bAddTop ? 1 : 0);
szAddTop[10-1] = '\0';
char szAddPaused[10];
snprintf(szAddPaused, 10, "%i", *bAddPaused ? 1 : 0);
szAddPaused[10-1] = '\0';
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
pScriptController->SetLogPrefix(szLogPrefix);
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
const char* szArgs[4];
szArgs[0] = szScript;
szArgs[1] = szDir;
szArgs[2] = szNZBFilename;
szArgs[3] = NULL;
pScriptController->SetArgs(szArgs, false);
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->SetEnvVar("NZBNP_NZBNAME", strlen(*pNZBName) > 0 ? *pNZBName : Util::BaseFileName(szNZBFilename));
pScriptController->SetEnvVar("NZBNP_CATEGORY", *pCategory);
pScriptController->SetEnvVar("NZBNP_PRIORITY", szPriority);
pScriptController->SetEnvVar("NZBNP_TOP", szAddTop);
pScriptController->SetEnvVar("NZBNP_PAUSED", szAddPaused);
pScriptController->Execute();
delete pScriptController;
}
void NZBScriptController::AddMessage(Message::EKind eKind, const char* szText)
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
szText = szText + m_iPrefixLen;
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strncmp(szText + 6, "NZBNAME=", 8))
{
free(*m_pNZBName);
*m_pNZBName = strdup(szText + 6 + 8);
}
else if (!strncmp(szText + 6, "CATEGORY=", 9))
if (!strncmp(szText + 6, "CATEGORY=", 9))
{
free(*m_pCategory);
*m_pCategory = strdup(szText + 6 + 9);
@@ -1092,7 +929,7 @@ void NZBScriptController::AddMessage(Message::EKind eKind, const char* szText)
if (szValue)
{
*szValue = '\0';
m_pParameters->SetParameter(szParam, szValue + 1);
m_pParameterList->SetParameter(szParam, szValue + 1);
}
else
{
@@ -1104,14 +941,6 @@ void NZBScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
*m_iPriority = atoi(szText + 6 + 9);
}
else if (!strncmp(szText + 6, "TOP=", 4))
{
*m_bAddTop = atoi(szText + 6 + 4) != 0;
}
else if (!strncmp(szText + 6, "PAUSED=", 7))
{
*m_bAddPaused = atoi(szText + 6 + 7) != 0;
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
@@ -1119,7 +948,7 @@ void NZBScriptController::AddMessage(Message::EKind eKind, const char* szText)
}
else
{
ScriptController::AddMessage(eKind, szText);
ScriptController::AddMessage(eKind, bDefaultKind, szText);
}
}
@@ -1128,8 +957,6 @@ void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInf
NZBAddedScriptController* pScriptController = new NZBAddedScriptController();
pScriptController->SetScript(szScript);
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
pScriptController->SetEnvVar("NZBNA_NZBNAME", pNZBInfo->GetName());
// "NZBNA_NAME" is not correct but kept for compatibility with older versions where this name was used by mistake
pScriptController->SetEnvVar("NZBNA_NAME", pNZBInfo->GetName());
pScriptController->SetEnvVar("NZBNA_FILENAME", pNZBInfo->GetFilename());
pScriptController->SetEnvVar("NZBNA_CATEGORY", pNZBInfo->GetCategory());
@@ -1158,7 +985,7 @@ void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInf
snprintf(buf, 100, "%i", iMaxPriority);
pScriptController->SetEnvVar("NZBNA_PRIORITY", buf);
pScriptController->PrepareEnvParameters(pNZBInfo, NULL);
pScriptController->PrepareEnvParameters(pNZBInfo);
pScriptController->SetAutoDestroy(true);
@@ -1174,11 +1001,13 @@ void NZBAddedScriptController::Run()
info("Executing %s", szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
SetDefaultKindPrefix("NZB-Added Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
const char* szArgs[2];
szArgs[0] = GetScript();
szArgs[1] = NULL;
SetArgs(szArgs, false);
Execute();
@@ -1211,11 +1040,8 @@ void SchedulerScriptController::Run()
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
SetDefaultKindPrefix("Scheduled Process: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
Execute();
}

View File

@@ -27,6 +27,7 @@
#define SCRIPTCONTROLLER_H
#include <list>
#include <fstream>
#include "Log.h"
#include "Thread.h"
@@ -43,7 +44,6 @@ private:
public:
EnvironmentStrings();
~EnvironmentStrings();
void Clear();
void InitFromCurrentProcess();
void Append(char* szString);
#ifdef WIN32
@@ -60,28 +60,26 @@ private:
const char* m_szWorkingDir;
const char** m_szArgs;
bool m_bFreeArgs;
const char* m_szStdArgs[2];
const char* m_szInfoName;
const char* m_szLogPrefix;
const char* m_szDefaultKindPrefix;
EnvironmentStrings m_environmentStrings;
Options::EScriptLogKind m_eDefaultLogKind;
bool m_bTerminated;
#ifdef WIN32
HANDLE m_hProcess;
char m_szCmdLine[2048];
#else
pid_t m_hProcess;
#endif
void PrepareEnvOptions();
protected:
void ProcessOutput(char* szText);
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
bool GetTerminated() { return m_bTerminated; }
void ResetEnv();
void PrepareEnvOptions(const char* szStripPrefix);
void PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix);
void PrepareArgs();
void PrepareEnvParameters(NZBInfo* pNZBInfo);
public:
ScriptController();
@@ -95,51 +93,39 @@ public:
void SetArgs(const char** szArgs, bool bFreeArgs) { m_szArgs = szArgs; m_bFreeArgs = bFreeArgs; }
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
const char* GetInfoName() { return m_szInfoName; }
void SetLogPrefix(const char* szLogPrefix) { m_szLogPrefix = szLogPrefix; }
void SetDefaultKindPrefix(const char* szDefaultKindPrefix) { m_szDefaultKindPrefix = szDefaultKindPrefix; }
void SetDefaultLogKind(Options::EScriptLogKind eDefaultLogKind) { m_eDefaultLogKind = eDefaultLogKind; }
void SetEnvVar(const char* szName, const char* szValue);
void SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue);
};
class PostScriptController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szNZBName[1024];
void ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation);
void PrepareParams(const char* szScriptName);
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
typedef std::deque<char*> FileList;
bool m_bNZBFileCompleted;
bool m_bHasFailedParJobs;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);
static void InitParamsForNewNZB(NZBInfo* pNZBInfo);
static void StartScriptJob(PostInfo* pPostInfo, bool bNZBFileCompleted, bool bHasFailedParJobs);
};
class NZBScriptController : public ScriptController
{
private:
char** m_pNZBName;
char** m_pCategory;
int* m_iPriority;
NZBParameterList* m_pParameters;
bool* m_bAddTop;
bool* m_bAddPaused;
int m_iPrefixLen;
NZBParameterList* m_pParameterList;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters,
bool* bAddTop, bool* bAddPaused);
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
};
class NZBAddedScriptController : public Thread, public ScriptController

View File

@@ -26,15 +26,16 @@
# include "config.h"
#endif
#ifndef DISABLE_TLS
#ifdef WIN32
#define SKIP_DEFAULT_WINDOWS_HEADERS
#include "win32.h"
#endif
#ifndef DISABLE_TLS
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#ifdef WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
@@ -42,6 +43,7 @@
#include <strings.h>
#endif
#include <ctype.h>
#include <stdarg.h>
#include <limits.h>
#include <time.h>
#include <errno.h>

View File

@@ -33,8 +33,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#endif
@@ -77,7 +77,7 @@ UnpackController::~UnpackController()
m_archiveFiles.Clear();
}
void UnpackController::StartJob(PostInfo* pPostInfo)
void UnpackController::StartUnpackJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
pUnpackController->m_pPostInfo = pPostInfo;
@@ -125,13 +125,11 @@ void UnpackController::Run()
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
CheckStateFiles();
#ifndef DISABLE_PARCHECK
if (bUnpack && m_bHasBrokenFiles && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
if (bUnpack && HasBrokenFiles() && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && HasParFiles())
{
PrintMessage(Message::mkInfo, "%s has broken files", m_szName);
info("%s has broken files", m_szName);
RequestParCheck(false);
m_pPostInfo->SetWorking(false);
return;
@@ -140,15 +138,13 @@ void UnpackController::Run()
if (bUnpack)
{
bool bScanNonStdFiles = m_pPostInfo->GetNZBInfo()->GetRenameStatus() > NZBInfo::rsSkipped ||
m_pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
!m_bHasParFiles;
CheckArchiveFiles(bScanNonStdFiles);
CheckArchiveFiles();
}
if (bUnpack && (m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
if (bUnpack && (m_bHasRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
{
SetInfoName(m_szInfoName);
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
SetWorkingDir(m_szDestDir);
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
@@ -158,7 +154,7 @@ void UnpackController::Run()
m_bUnpackOK = true;
m_bUnpackStartError = false;
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
if (m_bHasRarFiles)
{
ExecuteUnrar();
}
@@ -180,13 +176,14 @@ void UnpackController::Run()
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && HasParFiles())
{
RequestParCheck(m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped);
}
else
#endif
{
m_pPostInfo->SetUnpackStatus(PostInfo::usSkipped);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
@@ -212,20 +209,19 @@ void UnpackController::ExecuteUnrar()
szArgs[3] = szPasswordParam;
}
szArgs[4] = "-o+";
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
szArgs[5] = "*.rar";
szArgs[6] = m_szUnpackDir;
szArgs[7] = NULL;
SetArgs(szArgs, false);
SetScript(g_pOptions->GetUnrarCmd());
SetLogPrefix("Unrar");
SetDefaultKindPrefix("Unrar: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upUnrar;
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
@@ -266,15 +262,14 @@ void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
SetArgs(szArgs, false);
SetScript(g_pOptions->GetSevenZipCmd());
SetDefaultKindPrefix("7-Zip: ");
m_bAllOKMessageReceived = false;
m_eUnpacker = upSevenZip;
PrintMessage(Message::mkInfo, "Executing 7-Zip");
SetLogPrefix("7-Zip");
SetProgressLabel("");
int iExitCode = Execute();
SetLogPrefix(NULL);
SetProgressLabel("");
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
@@ -293,6 +288,7 @@ void UnpackController::Completed()
if (m_bUnpackOK && bCleanupSuccess)
{
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->SetUnpackStatus(PostInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
m_pPostInfo->SetStage(PostInfo::ptQueued);
@@ -300,7 +296,7 @@ void UnpackController::Completed()
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && !m_bUnpackStartError && !GetTerminated() && m_bHasParFiles)
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && !m_bUnpackStartError && !GetTerminated() && HasParFiles())
{
RequestParCheck(false);
}
@@ -308,6 +304,7 @@ void UnpackController::Completed()
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->SetUnpackStatus(PostInfo::usFailure);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
@@ -324,30 +321,33 @@ void UnpackController::RequestParCheck(bool bRename)
}
else
{
m_pPostInfo->SetRequestParCheck(true);
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
void UnpackController::CheckStateFiles()
bool UnpackController::HasParFiles()
{
return ParCoordinator::FindMainPars(m_szDestDir, NULL);
}
bool UnpackController::HasBrokenFiles()
{
char szBrokenLog[1024];
snprintf(szBrokenLog, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_brokenlog.txt");
szBrokenLog[1024-1] = '\0';
m_bHasBrokenFiles = Util::FileExists(szBrokenLog);
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
return Util::FileExists(szBrokenLog);
}
void UnpackController::CreateUnpackDir()
{
m_bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (m_bInterDir)
if (strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir())))
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
Util::ForceDirectories(m_szFinalDir);
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
}
else
@@ -355,24 +355,17 @@ void UnpackController::CreateUnpackDir()
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
}
m_szUnpackDir[1024-1] = '\0';
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szUnpackDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
}
Util::ForceDirectories(m_szUnpackDir);
}
void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
void UnpackController::CheckArchiveFiles()
{
m_bHasRarFiles = false;
m_bHasNonStdRarFiles = false;
m_bHasSevenZipFiles = false;
m_bHasSevenZipMultiFiles = false;
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]*$");
@@ -389,32 +382,14 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
{
m_bHasRarFiles = true;
}
else if (regExSevenZip.Match(filename))
if (regExSevenZip.Match(filename))
{
m_bHasSevenZipFiles = true;
}
else if (regExSevenZipMulti.Match(filename))
if (regExSevenZipMulti.Match(filename))
{
m_bHasSevenZipMultiFiles = true;
}
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles && !regExRarMultiSeq.Match(filename))
{
// Check if file has RAR signature
char rarSignature[] = {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00};
char fileSignature[7];
FILE* infile;
infile = fopen(szFullFilename, "rb");
if (infile)
{
int cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
fclose(infile);
if (cnt == sizeof(fileSignature) && !strcmp(rarSignature, fileSignature))
{
m_bHasNonStdRarFiles = true;
}
}
}
}
}
}
@@ -476,7 +451,7 @@ bool UnpackController::Cleanup()
{
char* szFilename = *it;
if (m_bInterDir || !extractedFiles.Exists(szFilename))
if (!extractedFiles.Exists(szFilename))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFilename);
@@ -505,7 +480,7 @@ bool UnpackController::Cleanup()
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename)
&& regExSevenZip.Match(filename) && (m_bInterDir || !extractedFiles.Exists(filename)))
&& regExSevenZip.Match(filename) && !extractedFiles.Exists(filename))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
@@ -587,7 +562,7 @@ bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
return i > 0;
}
void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
void UnpackController::AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText)
{
char szMsgText[1024];
strncpy(szMsgText, szText, 1024);
@@ -595,39 +570,31 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
// Modify unrar messages for better readability:
// remove the destination path part from message "Extracting file.xxx"
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting ", 19) &&
!strncmp(szText + 19, m_szUnpackDir, strlen(m_szUnpackDir)))
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting ", 12) &&
!strncmp(szText + 12, m_szUnpackDir, strlen(m_szUnpackDir)))
{
snprintf(szMsgText, 1024, "Unrar: Extracting %s", szText + 19 + strlen(m_szUnpackDir) + 1);
snprintf(szMsgText, 1024, "Extracting %s", szText + 12 + strlen(m_szUnpackDir) + 1);
szMsgText[1024-1] = '\0';
}
ScriptController::AddMessage(eKind, szMsgText);
ScriptController::AddMessage(eKind, bDefaultKind, szMsgText);
m_pPostInfo->AppendMessage(eKind, szMsgText);
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: UNRAR ", 6) &&
strstr(szMsgText, " Copyright ") && strstr(szMsgText, " Alexander Roshal"))
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Extracting ", 11))
{
// reset start time for a case if user uses unpack-script to do some things
// (like sending Wake-On-Lan message) before executing unrar
m_pPostInfo->SetStageTime(time(NULL));
SetProgressLabel(szMsgText);
}
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: Extracting ", 18))
if (m_eUnpacker == upUnrar && !strncmp(szText, "Extracting from ", 16))
{
SetProgressLabel(szMsgText + 7);
}
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting from ", 23))
{
const char *szFilename = szText + 23;
const char *szFilename = szText + 16;
debug("Filename: %s", szFilename);
m_archiveFiles.push_back(strdup(szFilename));
SetProgressLabel(szText + 7);
SetProgressLabel(szText);
}
if ((m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: All OK", 13)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "7-Zip: Everything is Ok", 23)))
if ((m_eUnpacker == upUnrar && !strncmp(szText, "All OK", 6)) ||
(m_eUnpacker == upSevenZip && !strncmp(szText, "Everything is Ok", 16)))
{
m_bAllOKMessageReceived = true;
}
@@ -648,7 +615,7 @@ void UnpackController::SetProgressLabel(const char* szProgressLabel)
}
void MoveController::StartJob(PostInfo* pPostInfo)
void MoveController::StartMoveJob(PostInfo* pPostInfo)
{
MoveController* pMoveController = new MoveController();
pMoveController->m_pPostInfo = pPostInfo;
@@ -673,6 +640,9 @@ void MoveController::Run()
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
SetDefaultKindPrefix("Move: ");
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szInterDir[1024-1] = '\0';
@@ -708,14 +678,10 @@ void MoveController::Run()
bool MoveController::MoveFiles()
{
char szErrBuf[1024];
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
{
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
return false;
}
bool bOK = true;
bOK = Util::ForceDirectories(m_szDestDir);
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
@@ -751,138 +717,3 @@ bool MoveController::MoveFiles()
return bOK;
}
void CleanupController::StartJob(PostInfo* pPostInfo)
{
CleanupController* pCleanupController = new CleanupController();
pCleanupController->m_pPostInfo = pPostInfo;
pCleanupController->SetAutoDestroy(false);
pPostInfo->SetPostThread(pCleanupController);
pCleanupController->Start();
}
void CleanupController::Run()
{
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
char szInfoName[1024];
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
m_szDestDir[1024-1] = '\0';
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
if (bInterDir)
{
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
}
else
{
m_szFinalDir[0] = '\0';
}
g_pDownloadQueueHolder->UnlockQueue();
info("Cleaning up %s", szNZBName);
bool bDeleted = false;
bool bOK = Cleanup(m_szDestDir, &bDeleted);
if (bOK && m_szFinalDir[0] != '\0')
{
bool bDeleted2 = false;
bOK = Cleanup(m_szFinalDir, &bDeleted2);
bDeleted = bDeleted || bDeleted2;
}
szInfoName[0] = 'C'; // uppercase
if (bOK && bDeleted)
{
info("%s successful", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else if (bOK)
{
info("Nothing to cleanup for %s", szNZBName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
}
else
{
error("%s failed", szInfoName);
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
m_pPostInfo->SetWorking(false);
}
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
{
*bDeleted = false;
bool bOK = true;
ExtList extList;
// split ExtCleanupDisk into tokens and create a list
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
char* saveptr;
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
while (szExt)
{
extList.push_back(szExt);
szExt = strtok_r(NULL, ",; ", &saveptr);
}
DirBrowser dir(szDestDir);
while (const char* filename = dir.Next())
{
// check file extension
int iFilenameLen = strlen(filename);
bool bDeleteIt = false;
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
{
const char* szExt = *it;
int iExtLen = strlen(szExt);
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, filename + iFilenameLen - iExtLen))
{
bDeleteIt = true;
break;
}
}
if (bDeleteIt)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s! Errcode: %i", szFullFilename, errno);
bOK = false;
}
*bDeleted = true;
}
}
free(szExtCleanupDisk);
return bOK;
}

View File

@@ -59,13 +59,9 @@ private:
char m_szFinalDir[1024];
char m_szUnpackDir[1024];
char m_szPassword[1024];
bool m_bInterDir;
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasParFiles;
bool m_bHasBrokenFiles;
bool m_bHasRarFiles;
bool m_bHasNonStdRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
@@ -76,14 +72,15 @@ private:
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
virtual void AddMessage(Message::EKind eKind, const char* szText);
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, const char* szText);
void ExecuteUnrar();
void ExecuteSevenZip(bool bMultiVolumes);
void Completed();
void CreateUnpackDir();
bool Cleanup();
void CheckStateFiles();
void CheckArchiveFiles(bool bScanNonStdFiles);
bool HasParFiles();
bool HasBrokenFiles();
void CheckArchiveFiles();
void SetProgressLabel(const char* szProgressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck(bool bRename);
@@ -93,7 +90,7 @@ public:
virtual ~UnpackController();
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);
static void StartUnpackJob(PostInfo* pPostInfo);
};
class MoveController : public Thread, public ScriptController
@@ -107,23 +104,7 @@ private:
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
};
class CleanupController : public Thread, public ScriptController
{
private:
PostInfo* m_pPostInfo;
char m_szDestDir[1024];
char m_szFinalDir[1024];
bool Cleanup(const char* szDestDir, bool *bDeleted);
typedef std::deque<char*> ExtList;
public:
virtual void Run();
static void StartJob(PostInfo* pPostInfo);
static void StartMoveJob(PostInfo* pPostInfo);
};
#endif

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
@@ -49,13 +49,10 @@
#include "Util.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
#include "Scanner.h"
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
extern QueueCoordinator* g_pQueueCoordinator;
extern Scanner* g_pScanner;
UrlDownloader::UrlDownloader() : WebDownloader()
{
@@ -145,7 +142,7 @@ void UrlCoordinator::Run()
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
if (bHasMoreUrls && !IsStopped())
if (bHasMoreUrls && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
{
StartUrlDownload(pUrlInfo);
}
@@ -421,8 +418,38 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
{
g_pScanner->AddExternalFile(
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename,
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : szOriginalCategory,
pUrlInfo->GetPriority(), NULL, pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(), szTempFilename, NULL, 0, false);
}
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);
}
}

252
Util.cpp
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -140,7 +140,15 @@ int getopt(int argc, char *argv[], char *optstring)
DirBrowser::DirBrowser(const char* szPath)
{
char szMask[MAX_PATH + 1];
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
int len = strlen(szPath);
if (szPath[len] == '\\' || szPath[len] == '/')
{
snprintf(szMask, MAX_PATH + 1, "%s*.*", szPath);
}
else
{
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
}
szMask[MAX_PATH] = '\0';
m_hFile = _findfirst(szMask, &m_FindData);
m_bFirst = true;
@@ -203,36 +211,6 @@ const char* DirBrowser::Next()
#endif
StringBuilder::StringBuilder()
{
m_szBuffer = NULL;
m_iBufferSize = 0;
m_iUsedSize = 0;
}
StringBuilder::~StringBuilder()
{
if (m_szBuffer)
{
free(m_szBuffer);
}
}
void StringBuilder::Append(const char* szStr)
{
int iPartLen = strlen(szStr);
if (m_iUsedSize + iPartLen + 1 > m_iBufferSize)
{
m_iBufferSize += iPartLen + 10240;
m_szBuffer = (char*)realloc(m_szBuffer, m_iBufferSize);
}
strcpy(m_szBuffer + m_iUsedSize, szStr);
m_iUsedSize += iPartLen;
m_szBuffer[m_iUsedSize] = '\0';
}
char Util::VersionRevisionBuf[40];
char* Util::BaseFileName(const char* filename)
@@ -267,13 +245,9 @@ void Util::NormalizePathSeparators(char* szPath)
}
}
bool Util::ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize)
bool Util::ForceDirectories(const char* szPath)
{
*szErrBuf = '\0';
char szSysErrStr[256];
char szNormPath[1024];
strncpy(szNormPath, szPath, 1024);
szNormPath[1024-1] = '\0';
char* szNormPath = strdup(szPath);
NormalizePathSeparators(szNormPath);
int iLen = strlen(szNormPath);
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
@@ -286,30 +260,15 @@ bool Util::ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize)
}
struct stat buffer;
bool bOK = !stat(szNormPath, &buffer);
if (!bOK && errno != ENOENT)
{
snprintf(szErrBuf, iBufSize, "could not read information for directory %s: errno %i, %s", szNormPath, errno, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (bOK && !S_ISDIR(buffer.st_mode))
{
snprintf(szErrBuf, iBufSize, "path %s is not a directory", szNormPath);
szErrBuf[iBufSize-1] = 0;
return false;
}
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
if (!bOK
#ifdef WIN32
&& strlen(szNormPath) > 2
#endif
)
{
char szParentPath[1024];
strncpy(szParentPath, szNormPath, 1024);
szParentPath[1024-1] = '\0';
char* szParentPath = strdup(szNormPath);
bOK = true;
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
if (p)
{
@@ -323,35 +282,20 @@ bool Util::ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize)
{
*p = '\0';
}
if (strlen(szParentPath) != strlen(szPath) && !ForceDirectories(szParentPath, szErrBuf, iBufSize))
if (strlen(szParentPath) != strlen(szPath))
{
return false;
bOK = ForceDirectories(szParentPath);
}
}
if (mkdir(szNormPath, S_DIRMODE) != 0 && errno != EEXIST)
if (bOK)
{
snprintf(szErrBuf, iBufSize, "could not create directory %s: %s", szNormPath, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (stat(szNormPath, &buffer) != 0)
{
snprintf(szErrBuf, iBufSize, "could not read information for directory %s: %s", szNormPath, GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
szErrBuf[iBufSize-1] = 0;
return false;
}
if (!S_ISDIR(buffer.st_mode))
{
snprintf(szErrBuf, iBufSize, "path %s is not a directory", szNormPath);
szErrBuf[iBufSize-1] = 0;
return false;
mkdir(szNormPath, S_DIRMODE);
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
}
free(szParentPath);
}
return true;
free(szNormPath);
return bOK;
}
bool Util::GetCurrentDirectory(char* szBuffer, int iBufSize)
@@ -417,20 +361,6 @@ bool Util::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBuff
return true;
}
bool Util::SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen)
{
FILE* pFile = fopen(szFileName, "wb");
if (!pFile)
{
return false;
}
int iWrittenBytes = fwrite(szBuffer, 1, iBufLen, pFile);
fclose(pFile);
return iWrittenBytes = iBufLen;
}
bool Util::CreateSparseFile(const char* szFilename, int iSize)
{
bool bOK = false;
@@ -710,9 +640,14 @@ long long Util::FileSize(const char* szFilename)
#ifdef WIN32
struct _stat32i64 buffer;
_stat32i64(szFilename, &buffer);
#else
#ifdef HAVE_STAT64
struct stat64 buffer;
stat64(szFilename, &buffer);
#else
struct stat buffer;
stat(szFilename, &buffer);
#endif
#endif
return buffer.st_size;
}
@@ -858,23 +793,6 @@ void Util::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
szBuffer[iBufLen - 1] = '\0';
}
bool Util::SameFilename(const char* szFilename1, const char* szFilename2)
{
#ifdef WIN32
return strcasecmp(szFilename1, szFilename2) == 0;
#else
return strcmp(szFilename1, szFilename2) == 0;
#endif
}
char* Util::GetLastErrorMessage(char* szBuffer, int iBufLen)
{
szBuffer[0] = '\0';
strerror_r(errno, szBuffer, iBufLen);
szBuffer[iBufLen-1] = '\0';
return szBuffer;
}
void Util::InitVersionRevision()
{
#ifndef WIN32
@@ -973,7 +891,7 @@ void Util::TrimRight(char* szStr)
{
int iLen = strlen(szStr);
char ch = szStr[iLen-1];
while (iLen > 0 && (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'))
while (*szStr && (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'))
{
szStr[iLen-1] = 0;
iLen--;
@@ -981,34 +899,6 @@ void Util::TrimRight(char* szStr)
}
}
char* Util::Trim(char* szStr)
{
TrimRight(szStr);
char ch = *szStr;
while (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
{
szStr++;
ch = *szStr;
}
return szStr;
}
#ifdef WIN32
bool Util::RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen)
{
HKEY hSubKey;
if (!RegOpenKeyEx(hKey, szKeyName, 0, KEY_READ, &hSubKey))
{
DWORD iRetBytes = *iBufLen;
LONG iRet = RegQueryValueEx(hSubKey, szValueName, NULL, NULL, (LPBYTE)szBuffer, &iRetBytes);
*iBufLen = iRetBytes;
RegCloseKey(hSubKey);
return iRet == 0;
}
return false;
}
#endif
unsigned int WebUtil::DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer)
{
@@ -1070,9 +960,9 @@ char* WebUtil::XmlEncode(const char* raw)
iReqSize += 6;
break;
default:
if (ch < 0x20 || ch >= 0x80)
if (ch >= 0x80)
{
iReqSize += 10;
iReqSize += 6;
break;
}
}
@@ -1110,51 +1000,10 @@ char* WebUtil::XmlEncode(const char* raw)
output += 6;
break;
default:
if (ch < 0x20 || ch > 0x80)
if (ch >= 0x80)
{
unsigned int cp = ch;
// decode utf8
if ((cp >> 5) == 0x6 && (p[1] & 0xc0) == 0x80)
{
// 2 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 6) & 0x7ff) + (ch & 0x3f);
}
else if ((cp >> 4) == 0xe && (p[1] & 0xc0) == 0x80)
{
// 3 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 12) & 0xffff) + ((ch << 6) & 0xfff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
else if ((cp >> 3) == 0x1e && (p[1] & 0xc0) == 0x80)
{
// 4 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 18) & 0x1fffff) + ((ch << 12) & 0x3ffff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += (ch << 6) & 0xfff;
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
// accept only valid XML 1.0 characters
if (cp == 0x9 || cp == 0xA || cp == 0xD ||
(0x20 <= cp && cp <= 0xD7FF) ||
(0xE000 <= cp && cp <= 0xFFFD) ||
(0x10000 <= cp && cp <= 0x10FFFF))
{
sprintf(output, "&#x%06x;", cp);
output += 10;
}
else
{
// replace invalid characters with dots
sprintf(output, ".");
output += 1;
}
sprintf(output, "&#%i;", ch);
output += 6;
}
else
{
@@ -1348,38 +1197,9 @@ char* WebUtil::JsonEncode(const char* raw)
output += 2;
break;
default:
if (ch < 0x20 || ch > 0x80)
if (ch < 0x20 || ch >= 0x80)
{
unsigned int cp = ch;
// decode utf8
if ((cp >> 5) == 0x6 && (p[1] & 0xc0) == 0x80)
{
// 2 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 6) & 0x7ff) + (ch & 0x3f);
}
else if ((cp >> 4) == 0xe && (p[1] & 0xc0) == 0x80)
{
// 3 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 12) & 0xffff) + ((ch << 6) & 0xfff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
else if ((cp >> 3) == 0x1e && (p[1] & 0xc0) == 0x80)
{
// 4 bytes
if (!(ch = *++p)) goto BreakLoop; // read next char
cp = ((cp << 18) & 0x1fffff) + ((ch << 12) & 0x3ffff);
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += (ch << 6) & 0xfff;
if (!(ch = *++p)) goto BreakLoop; // read next char
cp += ch & 0x3f;
}
// we support only Unicode range U+0000-U+FFFF
sprintf(output, "\\u%04x", cp <= 0xFFFF ? cp : '.');
sprintf(output, "\\u%04x", ch);
output += 6;
}
else

26
Util.h
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 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
@@ -57,19 +57,6 @@ public:
const char* Next();
};
class StringBuilder
{
private:
char* m_szBuffer;
int m_iBufferSize;
int m_iUsedSize;
public:
StringBuilder();
~StringBuilder();
void Append(const char* szStr);
const char* GetBuffer() { return m_szBuffer; }
};
class Util
{
public:
@@ -77,7 +64,6 @@ public:
static char* BaseFileName(const char* filename);
static void NormalizePathSeparators(char* szPath);
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
static bool SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen);
static bool CreateSparseFile(const char* szFilename, int iSize);
static bool TruncateFile(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
@@ -87,7 +73,7 @@ public:
static bool CreateDirectory(const char* szDirFilename);
static bool RemoveDirectory(const char* szDirFilename);
static bool DeleteDirectoryWithContent(const char* szDirFilename);
static bool ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize);
static bool ForceDirectories(const char* szPath);
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
static bool SetCurrentDirectory(const char* szDirFilename);
static long long FileSize(const char* szFilename);
@@ -99,8 +85,6 @@ public:
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
static bool SameFilename(const char* szFilename1, const char* szFilename2);
static char* GetLastErrorMessage(char* szBuffer, int iBufLen);
/*
* Split command line int arguments.
@@ -128,12 +112,6 @@ public:
static void TrimRight(char* szStr);
static char* Trim(char* szStr);
#ifdef WIN32
static bool RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen);
#endif
/*
* Returns program version and revision number as string formatted like "0.7.0-r295".
* If revision number is not available only version is returned ("0.7.0").

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifdef WIN32
#include <direct.h>
#else

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -33,7 +33,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#ifndef WIN32
#include <unistd.h>
#endif
@@ -107,7 +107,7 @@ void WebProcessor::Execute()
char* szAuthInfo64 = p + 21;
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
{
error("Invalid-request: auth-info too big");
error("invalid-request: auth-info too big");
return;
}
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
@@ -131,7 +131,7 @@ void WebProcessor::Execute()
if (m_eHttpMethod == hmPost && iContentLen <= 0)
{
error("Invalid-request: content length is 0");
error("invalid-request: content length is 0");
return;
}
@@ -185,25 +185,20 @@ void WebProcessor::Execute()
debug("Final URL=%s", m_szUrl);
if (strlen(g_pOptions->GetControlPassword()) > 0)
if (strlen(szAuthInfo) == 0)
{
if (strlen(szAuthInfo) == 0)
{
SendAuthResponse();
return;
}
SendAuthResponse();
return;
}
// Authorization
char* pw = strchr(szAuthInfo, ':');
if (pw) *pw++ = '\0';
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(szAuthInfo, g_pOptions->GetControlUsername())) ||
strcmp(pw, g_pOptions->GetControlPassword()))
{
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr(), szAuthInfo, pw);
SendAuthResponse();
return;
}
// 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_pConnection->GetRemoteAddr());
SendAuthResponse();
return;
}
if (m_eHttpMethod == hmPost)
@@ -214,7 +209,8 @@ void WebProcessor::Execute()
if (!m_pConnection->Recv(m_szRequest, iContentLen))
{
error("Invalid-request: could not read data");
free(m_szRequest);
error("invalid-request: could not read data");
return;
}
debug("Request=%s", m_szRequest);

View File

@@ -33,7 +33,8 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <fstream>
#include <stdarg.h>
#ifndef WIN32
#include <unistd.h>
@@ -47,17 +48,45 @@
#include "UrlCoordinator.h"
#include "QueueEditor.h"
#include "PrePostProcessor.h"
#include "Scanner.h"
#include "Util.h"
extern Options* g_pOptions;
extern QueueCoordinator* g_pQueueCoordinator;
extern UrlCoordinator* g_pUrlCoordinator;
extern PrePostProcessor* g_pPrePostProcessor;
extern Scanner* g_pScanner;
extern void ExitProc();
extern void Reload();
//*****************************************************************
// StringBuilder
StringBuilder::StringBuilder()
{
m_szBuffer = NULL;
m_iBufferSize = 0;
m_iUsedSize = 0;
}
StringBuilder::~StringBuilder()
{
if (m_szBuffer)
{
free(m_szBuffer);
}
}
void StringBuilder::Append(const char* szStr)
{
int iPartLen = strlen(szStr);
if (m_iUsedSize + iPartLen + 1 > m_iBufferSize)
{
m_iBufferSize += iPartLen + 10240;
m_szBuffer = (char*)realloc(m_szBuffer, m_iBufferSize);
}
strcpy(m_szBuffer + m_iUsedSize, szStr);
m_iUsedSize += iPartLen;
m_szBuffer[m_iUsedSize] = '\0';
}
//*****************************************************************
// XmlRpcProcessor
@@ -414,10 +443,6 @@ XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName)
{
command = new SaveConfigXmlCommand();
}
else if (!strcasecmp(szMethodName, "configtemplates"))
{
command = new ConfigTemplatesXmlCommand();
}
else
{
command = new ErrorXmlCommand(1, "Invalid procedure");
@@ -1354,7 +1379,6 @@ EditCommandEntry EditCommandNameMap[] = {
{ QueueEditor::eaFilePauseExtraPars, "FilePauseExtraPars" },
{ QueueEditor::eaFileSetPriority, "FileSetPriority" },
{ QueueEditor::eaFileReorder, "FileReorder" },
{ QueueEditor::eaFileSplit, "FileSplit" },
{ QueueEditor::eaGroupMoveOffset, "GroupMoveOffset" },
{ QueueEditor::eaGroupMoveTop, "GroupMoveTop" },
{ QueueEditor::eaGroupMoveBottom, "GroupMoveBottom" },
@@ -1375,7 +1399,6 @@ EditCommandEntry EditCommandNameMap[] = {
{ PrePostProcessor::eaHistoryDelete, "HistoryDelete" },
{ PrePostProcessor::eaHistoryReturn, "HistoryReturn" },
{ PrePostProcessor::eaHistoryProcess, "HistoryProcess" },
{ PrePostProcessor::eaHistorySetParameter, "HistorySetParameter" },
{ 0, NULL }
};
@@ -1442,7 +1465,7 @@ void EditQueueXmlCommand::Execute()
}
else
{
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset, szEditText);
bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset);
}
BuildBoolResponse(bOK);
@@ -1504,10 +1527,26 @@ void DownloadXmlCommand::Execute()
szFileContent[iLen] = '\0';
//debug("FileContent=%s", szFileContent);
bool bOK = g_pScanner->AddExternalFile(szFileName, szCategory, iPriority, NULL, bAddTop,
false, NULL, szFileContent, iLen, true);
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(szFileName, szCategory, szFileContent, iLen + 1);
BuildBoolResponse(bOK);
if (pNZBFile)
{
info("Request: Queue collection %s", szFileName);
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetPriority(iPriority);
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
delete pNZBFile;
BuildBoolResponse(true);
}
else
{
BuildBoolResponse(false);
}
}
void PostQueueXmlCommand::Execute()
@@ -1525,7 +1564,7 @@ void PostQueueXmlCommand::Execute()
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use "NZBName" instead
"<member><name>NZBFilename</name><value><string>%s</string></value></member>\n"
"<member><name>DestDir</name><value><string>%s</string></value></member>\n"
"<member><name>ParFilename</name><value><string>%s</string></value></member>\n" // deprecated, always empty
"<member><name>ParFilename</name><value><string>%s</string></value></member>\n"
"<member><name>InfoName</name><value><string>%s</string></value></member>\n"
"<member><name>Stage</name><value><string>%s</string></value></member>\n"
"<member><name>ProgressLabel</name><value><string>%s</string></value></member>\n"
@@ -1547,7 +1586,7 @@ void PostQueueXmlCommand::Execute()
"\"NZBNicename\" : \"%s\",\n" // deprecated, use "NZBName" instead
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
"\"ParFilename\" : \"%s\",\n" // deprecated, always empty
"\"ParFilename\" : \"%s\",\n"
"\"InfoName\" : \"%s\",\n"
"\"Stage\" : \"%s\",\n"
"\"ProgressLabel\" : \"%s\",\n"
@@ -1595,12 +1634,13 @@ void PostQueueXmlCommand::Execute()
char* xmlNZBNicename = EncodeStr(pPostInfo->GetNZBInfo()->GetName());
char* xmlNZBFilename = EncodeStr(pPostInfo->GetNZBInfo()->GetFilename());
char* xmlDestDir = EncodeStr(pPostInfo->GetNZBInfo()->GetDestDir());
char* xmlParFilename = EncodeStr(pPostInfo->GetParFilename());
char* xmlInfoName = EncodeStr(pPostInfo->GetInfoName());
char* xmlProgressLabel = EncodeStr(pPostInfo->GetProgressLabel());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_POSTQUEUE_ITEM_START : XML_POSTQUEUE_ITEM_START,
pPostInfo->GetID(), pPostInfo->GetNZBInfo()->GetID(), xmlNZBNicename,
xmlNZBNicename, xmlNZBFilename, xmlDestDir, "",
xmlNZBNicename, xmlNZBFilename, xmlDestDir, xmlParFilename,
xmlInfoName, szPostStageName[pPostInfo->GetStage()], xmlProgressLabel,
pPostInfo->GetFileProgress(), pPostInfo->GetStageProgress(),
pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0,
@@ -1610,6 +1650,7 @@ void PostQueueXmlCommand::Execute()
free(xmlNZBNicename);
free(xmlNZBFilename);
free(xmlDestDir);
free(xmlParFilename);
free(xmlInfoName);
free(xmlProgressLabel);
@@ -1725,7 +1766,7 @@ void ScanXmlCommand::Execute()
// optional parameter "SyncMode"
NextParamAsBool(&bSyncMode);
g_pScanner->ScanNZBDir(bSyncMode);
g_pPrePostProcessor->ScanNZBDir(bSyncMode);
BuildBoolResponse(true);
}
@@ -1736,7 +1777,7 @@ void HistoryXmlCommand::Execute()
const char* XML_HISTORY_ITEM_START =
"<value><struct>\n"
"<member><name>ID</name><value><i4>%i</i4></value></member>\n"
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n"
"<member><name>NZBID</name><value><i4>%i</i4></value></member>\n" // deprecated, use ID instead
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>NZBNicename</name><value><string>%s</string></value></member>\n" // deprecated, use Name instead
@@ -1757,24 +1798,20 @@ void HistoryXmlCommand::Execute()
"<member><name>UrlStatus</name><value><string>%s</string></value></member>\n"
"<member><name>Parameters</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_SCRIPT_START =
"</data></array></value></member>\n"
"<member><name>ScriptStatuses</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_LOG_START =
const char* XML_HISTORY_ITEM_LOG_START =
"</data></array></value></member>\n"
"<member><name>Log</name><value><array><data>\n";
const char* XML_HISTORY_ITEM_END =
const char* XML_HISTORY_ITEM_END =
"</data></array></value></member>\n"
"</struct></value>\n";
const char* JSON_HISTORY_ITEM_START =
"{\n"
"\"ID\" : %i,\n"
"\"NZBID\" : %i,\n"
"\"NZBID\" : %i,\n" // deprecated, use ID instead
"\"Kind\" : \"%s\",\n"
"\"Name\" : \"%s\",\n"
"\"Name\" : \"%s\",\n" // deprecated, use Name instead
"\"NZBNicename\" : \"%s\",\n" // deprecated, use Name instead
"\"NZBFilename\" : \"%s\",\n"
"\"DestDir\" : \"%s\",\n"
@@ -1793,11 +1830,7 @@ void HistoryXmlCommand::Execute()
"\"UrlStatus\" : \"%s\",\n"
"\"Parameters\" : [\n";
const char* JSON_HISTORY_ITEM_SCRIPT_START =
"],\n"
"\"ScriptStatuses\" : [\n";
const char* JSON_HISTORY_ITEM_LOG_START =
const char* JSON_HISTORY_ITEM_LOG_START =
"],\n"
"\"Log\" : [\n";
@@ -1817,19 +1850,7 @@ void HistoryXmlCommand::Execute()
"\"Value\" : \"%s\"\n"
"}";
const char* XML_SCRIPT_ITEM =
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>Status</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_SCRIPT_ITEM =
"{\n"
"\"Name\" : \"%s\",\n"
"\"Status\" : \"%s\"\n"
"}";
const char* XML_LOG_ITEM =
const char* XML_LOG_ITEM =
"<value><struct>\n"
"<member><name>ID</name><value><i4>%i</i4></value></member>\n"
"<member><name>Kind</name><value><string>%s</string></value></member>\n"
@@ -1845,10 +1866,10 @@ void HistoryXmlCommand::Execute()
"\"Text\" : \"%s\"\n"
"}";
const char* szParStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "REPAIR_POSSIBLE", "MANUAL" };
const char* szParStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "REPAIR_POSSIBLE" };
const char* szUnpackStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS" };
const char* szMoveStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
const char* szScriptStatusName[] = { "NONE", "UNKNOWN", "FAILURE", "SUCCESS" };
const char* szUrlStatusName[] = { "UNKNOWN", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN" };
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
@@ -1882,10 +1903,10 @@ void HistoryXmlCommand::Execute()
xmlCategory = EncodeStr(pNZBInfo->GetCategory());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
pHistoryInfo->GetID(), pNZBInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename,
pHistoryInfo->GetID(), pHistoryInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename,
xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()],
szUnpackStatusName[pNZBInfo->GetUnpackStatus()], szMoveStatusName[pNZBInfo->GetMoveStatus()],
szScriptStatusName[pNZBInfo->GetScriptStatuses()->CalcTotalStatus()],
szScriptStatusName[pNZBInfo->GetScriptStatus()],
iFileSizeLo, iFileSizeHi, iFileSizeMB, pNZBInfo->GetFileCount(),
pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", "");
@@ -1900,7 +1921,7 @@ void HistoryXmlCommand::Execute()
char* xmlURL = EncodeStr(pUrlInfo->GetURL());
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START,
pHistoryInfo->GetID(), 0, "URL", xmlNicename, xmlNicename, xmlNZBFilename,
pHistoryInfo->GetID(), pHistoryInfo->GetID(), "URL", xmlNicename, xmlNicename, xmlNZBFilename,
"", xmlCategory, "", "", "", "", 0, 0, 0, 0, 0, pHistoryInfo->GetTime(), xmlURL,
szUrlStatusName[pUrlInfo->GetStatus()]);
@@ -1944,33 +1965,6 @@ void HistoryXmlCommand::Execute()
}
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_SCRIPT_START : XML_HISTORY_ITEM_SCRIPT_START);
if (pNZBInfo)
{
// Script statuses
int iScriptIndex = 0;
for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++)
{
ScriptStatus* pScriptStatus = *it;
char* xmlName = EncodeStr(pScriptStatus->GetName());
char* xmlStatus = EncodeStr(szScriptStatusName[pScriptStatus->GetStatus()]);
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_SCRIPT_ITEM : XML_SCRIPT_ITEM, xmlName, xmlStatus);
szItemBuf[szItemBufSize-1] = '\0';
free(xmlName);
free(xmlStatus);
if (IsJson() && iScriptIndex++ > 0)
{
AppendResponse(",\n");
}
AppendResponse(szItemBuf);
}
}
AppendResponse(IsJson() ? JSON_HISTORY_ITEM_LOG_START : XML_HISTORY_ITEM_LOG_START);
if (pNZBInfo)
@@ -2065,7 +2059,7 @@ void DownloadUrlXmlCommand::Execute()
char szNicename[1024];
pUrlInfo->GetName(szNicename, sizeof(szNicename));
info("Queue %s", szNicename);
info("Request: Queue %s", szNicename);
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, bAddTop);
@@ -2193,7 +2187,7 @@ void ConfigXmlCommand::Execute()
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}
// struct[] loadconfig()
// struct[] loadconfig(string domain)
void LoadConfigXmlCommand::Execute()
{
const char* XML_CONFIG_ITEM =
@@ -2208,10 +2202,36 @@ void LoadConfigXmlCommand::Execute()
"\"Value\" : \"%s\"\n"
"}";
Options::OptEntries* pOptEntries = new Options::OptEntries();
if (!g_pOptions->LoadConfig(pOptEntries))
char* szDomain;
if (!NextParamAsStr(&szDomain))
{
BuildErrorResponse(3, "Could not read configuration file");
BuildErrorResponse(2, "Invalid parameter");
return;
}
const char* szConfigFile = NULL;
Options::EDomain eDomain;
if (!strcasecmp(szDomain, "SERVER"))
{
eDomain = Options::dmServer;
szConfigFile = g_pOptions->GetConfigFilename();
}
else if (!strcasecmp(szDomain, "POST"))
{
eDomain = Options::dmPostProcess;
szConfigFile = g_pOptions->GetPostConfigFilename();
}
else
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
Options::OptEntries* pOptEntries = new Options::OptEntries();
if (!g_pOptions->LoadConfig(eDomain, pOptEntries))
{
BuildErrorResponse(3, "Could not read configuration file %s", szConfigFile);
delete pOptEntries;
return;
}
@@ -2249,9 +2269,38 @@ void LoadConfigXmlCommand::Execute()
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}
// bool saveconfig(struct[] data)
// bool saveconfig(string domain, struct[] data)
void SaveConfigXmlCommand::Execute()
{
char* szDomain;
if (!NextParamAsStr(&szDomain))
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
Options::EDomain eDomain;
if (!strcasecmp(szDomain, "SERVER"))
{
eDomain = Options::dmServer;
}
else if (!strcasecmp(szDomain, "POST"))
{
eDomain = Options::dmPostProcess;
const char* szConfigFile = g_pOptions->GetPostConfigFilename();
if (!szConfigFile)
{
BuildErrorResponse(3, "Post-processing script configuration file is not defined");
return;
}
}
else
{
BuildErrorResponse(2, "Invalid parameter");
return;
}
Options::OptEntries* pOptEntries = new Options::OptEntries();
char* szName;
@@ -2265,71 +2314,9 @@ void SaveConfigXmlCommand::Execute()
}
// save to config file
bool bOK = g_pOptions->SaveConfig(pOptEntries);
bool bOK = g_pOptions->SaveConfig(eDomain, pOptEntries);
delete pOptEntries;
BuildBoolResponse(bOK);
}
// struct[] configtemplates()
void ConfigTemplatesXmlCommand::Execute()
{
const char* XML_CONFIG_ITEM =
"<value><struct>\n"
"<member><name>Name</name><value><string>%s</string></value></member>\n"
"<member><name>DisplayName</name><value><string>%s</string></value></member>\n"
"<member><name>Template</name><value><string>%s</string></value></member>\n"
"</struct></value>\n";
const char* JSON_CONFIG_ITEM =
"{\n"
"\"Name\" : \"%s\",\n"
"\"DisplayName\" : \"%s\",\n"
"\"Template\" : \"%s\"\n"
"}";
Options::ConfigTemplates* pConfigTemplates = new Options::ConfigTemplates();
if (!g_pOptions->LoadConfigTemplates(pConfigTemplates))
{
BuildErrorResponse(3, "Could not read configuration templates");
delete pConfigTemplates;
return;
}
AppendResponse(IsJson() ? "[\n" : "<array><data>\n");
int index = 0;
for (Options::ConfigTemplates::iterator it = pConfigTemplates->begin(); it != pConfigTemplates->end(); it++)
{
Options::ConfigTemplate* pConfigTemplate = *it;
char* xmlName = EncodeStr(pConfigTemplate->GetName());
char* xmlDisplayName = EncodeStr(pConfigTemplate->GetDisplayName());
char* xmlTemplate = EncodeStr(pConfigTemplate->GetTemplate());
int szItemBufSize = strlen(xmlName) + strlen(xmlTemplate) + 1024;
char* szItemBuf = (char*)malloc(szItemBufSize);
snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlDisplayName, xmlTemplate);
szItemBuf[szItemBufSize-1] = '\0';
free(xmlName);
free(xmlDisplayName);
free(xmlTemplate);
if (IsJson() && index++ > 0)
{
AppendResponse(",\n");
}
AppendResponse(szItemBuf);
free(szItemBuf);
}
delete pConfigTemplates;
AppendResponse(IsJson() ? "\n]" : "</data></array>\n");
}

View File

@@ -27,7 +27,19 @@
#define XMLRPC_H
#include "Connection.h"
#include "Util.h"
class StringBuilder
{
private:
char* m_szBuffer;
int m_iBufferSize;
int m_iUsedSize;
public:
StringBuilder();
~StringBuilder();
void Append(const char* szStr);
const char* GetBuffer() { return m_szBuffer; }
};
class XmlCommand;
@@ -270,12 +282,7 @@ class SaveConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ConfigTemplatesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
void Save(const char *szFilename);
};
#endif

View File

@@ -79,6 +79,9 @@
/* Define to 1 if spinlocks are supported */
#undef HAVE_SPINLOCK
/* Define to 1 if stat64 is supported */
#undef HAVE_STAT64
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@@ -135,9 +138,3 @@
/* Version number of package */
#undef VERSION
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES

400
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 11.0-testing.
# Generated by GNU Autoconf 2.61 for nzbget 10.2.
#
# 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='11.0-testing'
PACKAGE_STRING='nzbget 11.0-testing'
PACKAGE_VERSION='10.2'
PACKAGE_STRING='nzbget 10.2'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="nzbget.cpp"
@@ -1235,7 +1235,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 11.0-testing to adapt to many kinds of systems.
\`configure' configures nzbget 10.2 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1306,7 +1306,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 11.0-testing:";;
short | recursive ) echo "Configuration of nzbget 10.2:";;
esac
cat <<\_ACEOF
@@ -1315,7 +1315,6 @@ Optional Features:
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-dependency-tracking speeds up one-time build
--enable-dependency-tracking do not reject slow dependency extractors
--disable-largefile omit support for large files
--disable-curses do not use curses (removes dependency from
curses-library)
--disable-parcheck do not include par-check/-repair-support (removes
@@ -1328,8 +1327,8 @@ Optional Features:
--disable-gzip disable gzip-compression/decompression (removes
dependency from zlib-library)
--disable-sigchld-handler
do not use sigchld-handler (the disabling may be
neccessary on 32-Bit BSD)
do not use sigchld-handler (the disabling is
recommended for BSD)
--enable-debug enable debugging
Optional Packages:
@@ -1453,7 +1452,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 11.0-testing
nzbget configure 10.2
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1467,7 +1466,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 11.0-testing, which was
It was created by nzbget $as_me 10.2, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2263,7 +2262,7 @@ fi
# Define the identity of the package.
PACKAGE=nzbget
VERSION=11.0-testing
VERSION=10.2
cat >>confdefs.h <<_ACEOF
@@ -3830,7 +3829,7 @@ if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
else
ac_cv_header_stdc=no
fi
rm -f -r conftest*
rm -f conftest*
fi
@@ -3851,7 +3850,7 @@ if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
else
ac_cv_header_stdc=no
fi
rm -f -r conftest*
rm -f conftest*
fi
@@ -4722,149 +4721,64 @@ fi
# Check whether --enable-largefile was given.
if test "${enable_largefile+set}" = set; then
enableval=$enable_largefile;
fi
if test "$enable_largefile" != no; then
{ echo "$as_me:$LINENO: checking for special C compiler options needed for large files" >&5
echo $ECHO_N "checking for special C compiler options needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_largefile_CC+set}" = set; then
{ echo "$as_me:$LINENO: checking for stat64" >&5
echo $ECHO_N "checking for stat64... $ECHO_C" >&6; }
if test "${ac_cv_func_stat64+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
ac_cv_sys_largefile_CC=no
if test "$GCC" != yes; then
ac_save_CC=$CC
while :; do
# IRIX 6.2 and later do not support large files by default,
# so use the C compiler's -n32 option if that helps.
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext
CC="$CC -n32"
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_cv_sys_largefile_CC=' -n32'; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext
break
done
CC=$ac_save_CC
rm -f conftest.$ac_ext
fi
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_largefile_CC" >&5
echo "${ECHO_T}$ac_cv_sys_largefile_CC" >&6; }
if test "$ac_cv_sys_largefile_CC" != no; then
CC=$CC$ac_cv_sys_largefile_CC
fi
{ echo "$as_me:$LINENO: checking for _FILE_OFFSET_BITS value needed for large files" >&5
echo $ECHO_N "checking for _FILE_OFFSET_BITS value needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_file_offset_bits+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
while :; do
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
/* Define stat64 to an innocuous variant, in case <limits.h> declares stat64.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define stat64 innocuous_stat64
/* System header to define __stub macros and hopefully few prototypes,
which can conflict with char stat64 (); below.
Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
<limits.h> exists even on freestanding compilers. */
#ifdef __STDC__
# include <limits.h>
#else
# include <assert.h>
#endif
#undef stat64
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char stat64 ();
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
#if defined __stub_stat64 || defined __stub___stat64
choke me
#endif
int
main ()
{
return stat64 ();
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
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_compile") 2>conftest.er1
(eval "$ac_link") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
@@ -4873,203 +4787,27 @@ eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_file_offset_bits=no; break
} && test -s conftest$ac_exeext &&
$as_test_x conftest$ac_exeext; then
ac_cv_func_stat64=yes
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_cv_func_stat64=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_file_offset_bits=64; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
conftest$ac_exeext conftest.$ac_ext
fi
{ echo "$as_me:$LINENO: result: $ac_cv_func_stat64" >&5
echo "${ECHO_T}$ac_cv_func_stat64" >&6; }
if test $ac_cv_func_stat64 = yes; then
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_file_offset_bits=unknown
break
done
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_file_offset_bits" >&5
echo "${ECHO_T}$ac_cv_sys_file_offset_bits" >&6; }
case $ac_cv_sys_file_offset_bits in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits
cat >>confdefs.h <<\_ACEOF
#define HAVE_STAT64 1
_ACEOF
;;
esac
rm -f -r conftest*
if test $ac_cv_sys_file_offset_bits = unknown; then
{ echo "$as_me:$LINENO: checking for _LARGE_FILES value needed for large files" >&5
echo $ECHO_N "checking for _LARGE_FILES value needed for large files... $ECHO_C" >&6; }
if test "${ac_cv_sys_large_files+set}" = set; then
echo $ECHO_N "(cached) $ECHO_C" >&6
else
while :; do
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_large_files=no; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
cat >conftest.$ac_ext <<_ACEOF
/* confdefs.h. */
_ACEOF
cat confdefs.h >>conftest.$ac_ext
cat >>conftest.$ac_ext <<_ACEOF
/* end confdefs.h. */
#define _LARGE_FILES 1
#include <sys/types.h>
/* Check that off_t can represent 2**63 - 1 correctly.
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
int
main ()
{
;
return 0;
}
_ACEOF
rm -f conftest.$ac_objext
if { (ac_try="$ac_compile"
case "(($ac_try" in
*\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
*) ac_try_echo=$ac_try;;
esac
eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
(eval "$ac_compile") 2>conftest.er1
ac_status=$?
grep -v '^ *+' conftest.er1 >conftest.err
rm -f conftest.er1
cat conftest.err >&5
echo "$as_me:$LINENO: \$? = $ac_status" >&5
(exit $ac_status); } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
} && test -s conftest.$ac_objext; then
ac_cv_sys_large_files=1; break
else
echo "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
ac_cv_sys_large_files=unknown
break
done
fi
{ echo "$as_me:$LINENO: result: $ac_cv_sys_large_files" >&5
echo "${ECHO_T}$ac_cv_sys_large_files" >&6; }
case $ac_cv_sys_large_files in #(
no | unknown) ;;
*)
cat >>confdefs.h <<_ACEOF
#define _LARGE_FILES $ac_cv_sys_large_files
_ACEOF
;;
esac
rm -f -r conftest*
fi
fi
@@ -8828,9 +8566,17 @@ echo $ECHO_N "checking whether to use an empty SIGCHLD handler... $ECHO_C" >&6;
if test "${enable_sigchld_handler+set}" = set; then
enableval=$enable_sigchld_handler; SIGCHLDHANDLER=$enableval
else
SIGCHLDHANDLER=yes
SIGCHLDHANDLER=auto
fi
if test "$SIGCHLDHANDLER" = "auto"; then
SIGCHLDHANDLER=yes
case "$target" in
*bsd*)
SIGCHLDHANDLER=no
;;
esac
fi
{ echo "$as_me:$LINENO: result: $SIGCHLDHANDLER" >&5
echo "${ECHO_T}$SIGCHLDHANDLER" >&6; }
if test "$SIGCHLDHANDLER" = "yes"; then
@@ -9537,7 +9283,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 11.0-testing, which was
This file was extended by nzbget $as_me 10.2, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -9590,7 +9336,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 11.0-testing
nzbget config.status 10.2
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"
@@ -10272,7 +10018,7 @@ do
cat >>$CONFIG_STATUS <<_ACEOF
# First, check the format of the line:
cat >"\$tmp/defines.sed" <<\\CEOF
/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*/b def
/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def
/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def
b
:def

View File

@@ -2,9 +2,9 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 11.0-testing, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 10.2, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 11.0-testing)
AM_INIT_AUTOMAKE(nzbget, 10.2)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])
@@ -56,9 +56,10 @@ AC_CHECK_FUNC(getopt_long,
dnl
dnl use 64-Bits for file sizes
dnl stat64
dnl
AC_SYS_LARGEFILE
AC_CHECK_FUNC(stat64,
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],)
dnl
@@ -502,14 +503,22 @@ fi
dnl
dnl Some Linux systems require an empty signal handler for SIGCHLD
dnl in order for exit codes to be correctly delivered to parent process.
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to install the handler.
dnl Some BSD systems however may not function properly if the handler is installed.
dnl The default behavior is to check the target and disable the handler on BSD but keep it enabled on other systems.
dnl
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
AC_ARG_ENABLE(sigchld-handler,
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling is recommended for BSD)])],
[SIGCHLDHANDLER=$enableval],
[SIGCHLDHANDLER=yes])
[SIGCHLDHANDLER=auto])
if test "$SIGCHLDHANDLER" = "auto"; then
SIGCHLDHANDLER=yes
case "$target" in
*bsd*)
SIGCHLDHANDLER=no
;;
esac
fi
AC_MSG_RESULT($SIGCHLDHANDLER)
if test "$SIGCHLDHANDLER" = "yes"; then
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])

50
nzbget-postprocess.conf Normal file
View File

@@ -0,0 +1,50 @@
#
# This file if part of nzbget
#
# Template configuration file for post-processing script "nzbget-postprocess.sh".
# Please refer to "nzbget-postprocess.sh" for usage instructions.
#
# Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
##############################################################################
### OPTIONS ###
# Rename img-files to iso (yes, no).
RenameIMG=yes
# Joint TS-files (yes, no).
JoinTS=yes
##############################################################################
### POSTPROCESSING-PARAMETERS ###
# This section defines parameters, which can be set for each nzb-file
# individually using either web-interface or command line.
# Example command line for setting parameter "PostProcess" to value "no" for
# nzb-file with id=2:
# nzbget -E G O PostProcess=no 2
# Perform postprocessing (yes, no).
#
# Set to "no" to skip postprocessing for this nzb-file.
PostProcess=yes
# Destination directory.
DestDir=

231
nzbget-postprocess.sh Executable file
View File

@@ -0,0 +1,231 @@
#!/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-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#
####################### Usage instructions #######################
# o Script will cleanup, join ts-files and rename img-files to iso.
#
# o To use this script with nzbget set the option "PostProcess" in
# nzbget configuration file to point to this script file. E.g.:
# PostProcess=/home/user/nzbget/nzbget-postprocess.sh
#
# o The script needs a configuration file. An example configuration file
# 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.
#
# o There are few options, which can be ajdusted for each nzb-file individually.
#
####################### End of Usage instructions #######################
# NZBGet passes following arguments to postprocess-programm as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled.
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
# Name of script's configuration file
SCRIPT_CONFIG_FILE="nzbget-postprocess.conf"
# Exit codes
POSTPROCESS_PARCHECK_CURRENT=91
POSTPROCESS_PARCHECK_ALL=92
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
POSTPROCESS_NONE=95
# Check if the script is called from nzbget 10.0 or later
if [ "$NZBPP_DIRECTORY" = "" -o "$NZBOP_CONFIGFILE" = "" ]; then
echo "*** NZBGet post-processing script ***"
echo "This script is supposed to be called from nzbget (10.0 or later)."
exit $POSTPROCESS_ERROR
fi
if [ "$NZBOP_UNPACK" = "" ]; then
echo "[ERROR] This script requires nzbget version at least 10.0-testing-r555 or 10.0-stable."
exit $POSTPROCESS_ERROR
fi
# Check if postprocessing was disabled in postprocessing parameters
# (for current nzb-file) via web-interface or via command line with
# "nzbget -E G O PostProcess=no <ID>"
if [ "$NZBPR_PostProcess" = "no" ]; then
echo "[WARNING] Post-Process: Post-processing disabled for this nzb-file, exiting"
exit $POSTPROCESS_NONE
fi
echo "[INFO] Post-Process: Post-processing script successfully started"
cd "$NZBPP_DIRECTORY"
# Determine the location of configuration file (it must be stored in
# the directory with nzbget.conf).
ConfigDir="${NZBOP_CONFIGFILE%/*}"
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
if [ ! -f "$ScriptConfigFile" ]; then
echo "[ERROR] Post-Process: Configuration file $ScriptConfigFile not found, exiting"
exit $POSTPROCESS_ERROR
fi
# Readg configuration file
while read line; do eval "$line"; done < $ScriptConfigFile
# Check nzbget.conf options
BadConfig=0
if [ "$NZBOP_ALLOWREPROCESS" = "yes" ]; then
echo "[ERROR] Post-Process: Please disable option \"AllowReProcess\" in nzbget configuration file"
BadConfig=1
fi
if [ "$NZBOP_UNPACK" != "yes" ]; then
echo "[ERROR] Post-Process: Please enable option \"Unpack\" in nzbget configuration file"
BadConfig=1
fi
if [ "$BadConfig" -eq 1 ]; then
echo "[ERROR] Post-Process: Exiting due to incompatible nzbget configuration"
exit $POSTPROCESS_ERROR
fi
# Check par status
if [ "$NZBPP_PARSTATUS" -eq 3 ]; then
echo "[WARNING] Post-Process: Par-check successful, but Par-repair disabled, exiting"
exit $POSTPROCESS_NONE
fi
if [ "$NZBPP_PARSTATUS" -eq 1 ]; then
echo "[WARNING] Post-Process: Par-check failed, exiting"
exit $POSTPROCESS_NONE
fi
# Check unpack status
if [ "$NZBPP_UNPACKSTATUS" -eq 1 ]; then
echo "[WARNING] Post-Process: Unpack failed, exiting"
exit $POSTPROCESS_NONE
fi
if [ "$NZBPP_UNPACKSTATUS" -eq 0 -a "$NZBPP_PARSTATUS" -ne 2 ]; then
# Unpack is disabled or was skipped due to nzb-file properties or due to errors during par-check
if (ls *.rar *.7z *.7z.??? >/dev/null 2>&1); then
echo "[WARNING] Post-Process: Archive files exist but unpack skipped, exiting"
exit $POSTPROCESS_NONE
fi
if (ls *.par2 >/dev/null 2>&1); then
echo "[WARNING] Post-Process: Unpack skipped and par-check skipped (although par2-files exist), exiting"
exit $POSTPROCESS_NONE
fi
if [ -f "_brokenlog.txt" ]; then
echo "[WARNING] Post-Process: _brokenlog.txt exists, download is probably damaged, exiting"
exit $POSTPROCESS_NONE
fi
echo "[INFO] Post-Process: Neither archive- nor par2-files found, _brokenlog.txt doesn't exist, considering download successful"
fi
# Check if destination directory exists (important for reprocessing of history items)
if [ ! -d "$NZBPP_DIRECTORY" ]; then
echo "[ERROR] Post-Process: Nothing to post-process: destination directory $NZBPP_DIRECTORY doesn't exist"
exit $POSTPROCESS_ERROR
fi
# All checks done, now processing the files
# If download contains only nzb-files move them into nzb-directory
# for further download
# Check if command "wc" exists
wc -l . >/dev/null 2>&1
if [ "$?" -ne 127 ]; then
AllFilesCount=`ls -1 2>/dev/null | wc -l`
NZBFilesCount=`ls -1 *.nzb 2>/dev/null | wc -l`
if [ "$AllFilesCount" -eq "$NZBFilesCount" ]; then
echo "[INFO] Moving downloaded nzb-files into incoming nzb-directory for further download"
mv *.nzb $NZBOP_NZBDIR
fi
fi
# Clean up
echo "[INFO] Post-Process: Cleaning up"
chmod -R a+rw .
rm *.nzb >/dev/null 2>&1
rm *.sfv >/dev/null 2>&1
rm *.1 >/dev/null 2>&1
rm _brokenlog.txt >/dev/null 2>&1
rm *.[pP][aA][rR]2 >/dev/null 2>&1
if [ "$JoinTS" = "yes" ]; then
# Join any split .ts files if they are named xxxx.0000.ts xxxx.0001.ts
# They will be joined together to a file called xxxx.0001.ts
if (ls *.ts >/dev/null 2>&1); then
echo "[INFO] Post-Process: Joining ts-files"
tsname=`find . -name "*0001.ts" |awk -F/ '{print $NF}'`
cat *0???.ts > ./$tsname
# Remove all the split .ts files
echo "[INFO] Post-Process: Deleting source ts-files"
rm *0???.ts >/dev/null 2>&1
fi
fi
if [ "$RenameIMG" = "yes" ]; then
# Rename img file to iso
# It will be renamed to .img.iso so you can see that it has been renamed
if (ls *.img >/dev/null 2>&1); then
echo "[INFO] Post-Process: Renaming img-files to iso"
imgname=`find . -name "*.img" |awk -F/ '{print $NF}'`
mv $imgname $imgname.iso
fi
fi
# Check if destination directory was set in postprocessing parameters
# (for current nzb-file) via web-interface or via command line with
# "nzbget -E G O DestDir=/new/path <ID>"
if [ "$NZBPR_DestDir" != "" ]; then
mkdir $NZBPR_DestDir
mv * $NZBPR_DestDir >/dev/null 2>&1
cd ..
rmdir $NZBPP_DIRECTORY
fi
# All OK, requesting cleaning up of download queue
exit $POSTPROCESS_SUCCESS

View File

@@ -61,21 +61,6 @@ QueueDir=${MainDir}/queue
# Directory to store temporary files.
TempDir=${MainDir}/tmp
# Directory with web-interface files.
#
# Example: /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=
# Directory with post-processing scripts.
#
# NOTE: For information on writing post-processing scripts visit
# http://nzbget.sourceforge.net/Post-processing_scripts.
ScriptDir=${MainDir}/ppscripts
# Lock-file for daemon-mode, POSIX only.
#
# If the option is not empty, NZBGet creates the file and writes process-id
@@ -87,19 +72,14 @@ LockFile=${MainDir}/nzbget.lock
# NOTE: See also option <CreateLog>.
LogFile=${DestDir}/nzbget.log
# Configuration file template.
# Directory with web-interface files.
#
# Put the path to the example configuration file which comes with
# NZBGet. Web-interface needs this file to read option descriptions.
# Example: "WebDir=/usr/local/share/nzbget/webui".
#
# Do not put here your actual configuration file (typically stored
# in your home directory or in /etc/nzbget.conf) but instead the unchanged
# example configuration file (typically installed to
# /usr/local/share/nzbget/nzbget.conf).
#
# Example: /usr/local/share/nzbget/nzbget.conf.
ConfigTemplate=
# 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 ###
@@ -175,7 +155,7 @@ Server1.Encryption=no
# select cipher for TLS" if the cipher string is not valid.
Server1.Cipher=
# Maximum number of simultaneous connections to this server (0-999).
# Maximal number of simultaneous connections to this server (0-999).
Server1.Connections=4
# Second server, on level 0.
@@ -224,20 +204,10 @@ ControlIP=0.0.0.0
# communication see option <SecurePort>.
ControlPort=6789
# User name which NZBGet server and remote client use.
#
# Set to empty value to disable user name check (check only password).
#
# NOTE: this option was added in NZBGet 11. Older versions used predefined
# not changeable user name "nzbget". Third-party tools or web-sites written
# for older NZBGet versions may not have an option to define user name. In
# this case you should set option <ControlUsername> to the default value
# "nzbget" or use empty value.
ControlUsername=nzbget
# Password which NZBGet server and remote client use.
#
# Set to empty value to disable authorization request.
# For authorization to web-interface use predefined username "nzbget" (not configurable)
# and the password defined here.
ControlPassword=tegbzn6789
# Secure control of NZBGet server (yes, no).
@@ -271,7 +241,7 @@ SecureKey=
# 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
DaemonUserName=root
# Specify default umask (affects file permissions) for newly created
# files, POSIX only (000-1000).
@@ -289,6 +259,9 @@ UMask=1000
# Create subdirectory with category-name in destination-directory (yes, no).
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
# nzb-files (seconds).
#
@@ -314,18 +287,14 @@ NzbDirFileAge=60
# save the second file under the same filename as the first one.
MergeNzb=no
# Set path to program, that must be executed before a nzb-file is added
# to queue.
# Set path to program, that must be executed before any file in incoming
# directory (option <NzbDir>) is processed.
#
# This program is called each time a new file is found in incoming
# directory (option <NzbDir>) or a file is received via RPC (web-interface,
# command "nzbget --append", etc.).
#
# Example: ~/nzbprocess.sh.
# Example: "NzbProcess=~/nzbprocess.sh".
#
# That program can unpack archives which were put in incoming directory, make
# filename cleanup, change nzb-name, category, priority and post-processing
# parameters of the nzb-file or do other things.
# 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
@@ -333,13 +302,6 @@ MergeNzb=no
# 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;
# NZBNP_NZBNAME - nzb-name (without path but with extension);
# NZBNP_CATEGORY - category of nzb-file;
# NZBNP_PRIORITY - priority of nzb-file;
# NZBNP_TOP - flag indicating that the file will be added to the top
# of queue: 0 or 1;
# NZBNP_PAUSED - flag indicating that the file will be added as
# paused: 0 or 1.
#
# In addition to these arguments NZBGet passes all
# nzbget.conf-options to nzbprocess-program as environment variables. These
@@ -349,18 +311,14 @@ MergeNzb=no
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
# the values are passed always in lower case.
#
# The nzbprocess-script can change nzb-name, category, priority,
# post-processing parameters and top-/paused-flags of the nzb-file
# by printing special messages into standard output (which is processed
# by NZBGet).
# The nzbprocess-script can assign category, priority and post-processing
# parameters to the current nzb-file by printing special messages into
# standard output (which is processed by NZBGet).
#
# To change nzb-name use following syntax:
# echo "[NZB] NZBNAME=my download";
#
# To change category:
# To assign category use following syntax:
# echo "[NZB] CATEGORY=my category";
#
# To change priority:
# To assign priority:
# echo "[NZB] PRIORITY=signed_integer_value";
#
# for example: to set priority higher than normal:
@@ -369,14 +327,6 @@ MergeNzb=no
# another example: use a negative value for "lower than normal" priority:
# echo "[NZB] PRIORITY=-100";
#
# Although priority can be any integer value, the web-interface operates
# with five predefined priorities:
# -100 - very low priority;
# -50 - low priority;
# 0 - normal priority (default);
# 50 - high priority;
# 100 - very high priority.
#
# To assign post-processing parameters:
# echo "[NZB] NZBPR_myvar=my value";
#
@@ -384,12 +334,6 @@ MergeNzb=no
# parameter with name "myvar" and value "my value" will be associated
# with nzb-file.
#
# To change top-flag (nzb-file will be added to the top of queue):
# echo "[NZB] TOP=1";
#
# To change paused-flag (nzb-file will be added in paused state):
# echo "[NZB] PAUSED=1";
#
# 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
# (if it was an nzb-file) or renamed by adding the extension ".processed".
@@ -401,17 +345,16 @@ MergeNzb=no
# 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.
#
# NOTE: Files added via RPC calls in particular from web-interface are
# saved into incoming nzb-directory and then 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.
# 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.sh.
# 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.
@@ -440,23 +383,25 @@ NzbProcess=
#
# Examples:
# 1) pausing nzb-file using file-id:
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E G P $NZBNA_LASTID;
# "$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";
# "$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_NZBNAME/.*\.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).
# 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.
# In last two cases: if the file exists it will not be added to queue;
#
# If this option is disabled, all files are downloaded and duplicate files
# are renamed to "filename_duplicate1".
@@ -493,11 +438,17 @@ ReloadPostQueue=yes
# the file was completed.
ContinuePartial=yes
# Visibly rename broken files on download appending "_broken" (yes, no).
#
# Do not activate this option if par-check is enabled.
RenameBroken=no
# Decode articles (yes, no).
#
# yes - decode articles using internal decoder (supports yEnc and UU formats);
# no - the articles will not be decoded and joined. Useful for debugging to
# look at article's source text.
# no - the articles will not be decoded and joined. External programs
# (like "uudeview") can be used to decode and join downloaded articles.
# Also useful for debugging to look at article's source text.
Decode=yes
# Write decoded articles directly into destination output file (yes, no).
@@ -580,6 +531,29 @@ ConnectionTimeout=60
# Do not use small values!
TerminateTimeout=600
# Set the (approximate) maximum number of allowed threads (10-999).
#
# 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
# 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
# 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
# many threads. But the most OSes should not have problems with 100 threads.
ThreadLimit=100
# Set the maximum download rate on program start (kilobytes/sec).
#
# Value "0" means no speed control.
@@ -658,7 +632,7 @@ DeleteCleanupDisk=no
# that can be achieved with the option <ParCleanupQueue>.
KeepHistory=7
# Maximum number of simultaneous connections for nzb URL downloads (0-999).
# 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
@@ -686,11 +660,6 @@ Category1.Name=Movies
# destination directory.
Category1.DestDir=
# Default list of post-processing scripts.
#
# For more information see global option <DefScript>.
Category1.DefScript=
Category2.Name=Series
Category3.Name=Music
Category4.Name=Software
@@ -723,6 +692,22 @@ DetailTarget=both
# debug-mode: "./configure --enable-debug".
DebugTarget=both
# Set the default message-kind for output received from process-scripts
# (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.:
# [INFO] bla-bla.
# [DETAIL] bla-bla.
# [WARNING] bla-bla.
# [ERROR] bla-bla.
# [DEBUG] bla-bla.
#
# If the message-kind was detected the text is added to log with detected type.
# Otherwise the message becomes the default kind, specified in this option.
ProcessLogKind=detail
# Number of messages stored in buffer and available for remote
# clients (messages).
LogBufferSize=1000
@@ -782,6 +767,306 @@ CursesTime=no
UpdateInterval=200
##############################################################################
### PAR CHECK/REPAIR ###
# How many par2-files to load (one, all, none).
#
# one - only one main par2-file must be downloaded and other must be paused;
# all - all par2-files must be downloaded;
# none - all par2-files must be automatically paused.
# Paused files remain in queue and can be unpaused by parchecker when needed.
LoadPars=one
# Force par-verification (yes, no).
#
# Force par-check for every download. When set to "no" the par-check is
# performed only if the unpacker or the post-processing script detect a
# damaged download.
#
# To download only needed par2-files (smart par-files loading) set also
# the option <LoadPars> to "one". If option <LoadPars> is set to "all",
# all par2-files will be downloaded before verification and repair starts.
# The option <RenameBroken> must be set to "no", otherwise the par-checker
# may not find renamed files and fail.
ParCheck=no
# Automatic par-repair (yes, no).
#
# If option <ParCheck> is enabled and <ParRepair> is not, the program
# only verifies downloaded files and downloads needed par2-files, but does
# not start repair-process. This is useful if computer does not have
# enough CPU power, since repairing of large files may take too much
# resources and time on a slow computers.
ParRepair=yes
# What files should be scanned during par-verification (limited,
# full, auto).
#
# limited - scan only files belonging to the par-set;
# full - scan all files in the directory. This helps if the
# files were renamed after creating of par-set.
# auto - a limited scan is performed first. If the par-checker
# detects missing files, it scans other files in the
# directory until all required files are found.
#
# NOTE: for par-check/repair NZBGet uses library libpar2. The last and
# widely used version 0.2 of the library has few bugs, sometimes causing
# a crash of the program. This is especially true when using "full" or
# "auto" par-scan. NZBGet is supplied with patches addressing these
# issues. Please apply the patches to libpar2 and recompile it.
ParScan=auto
# 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
# to use these files by setting <StrictParName> to "no".
# This has however a side effect: if NZB-file contains more than one collection
# of files (with different par-sets), par-checker may download par-files from
# a wrong collection. This increases you traffic (but not harm par-check).
#
# NOTE: Par-checker always uses only par-files added from the same NZB-file
# and the option <StrictParName> does not change this behavior.
StrictParName=yes
# Maximum allowed time for par-repair (minutes).
#
# 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
# required for par-repair. If the estimated value exceeds the limit defined
# here, NZBGet cancels the repair.
#
# 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
# 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
# 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 the patch to libpar2.
ParTimeLimit=0
# Pause download queue during check/repair (yes, no).
#
# 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
# the queue.
#
# NOTE: See also options <PostPauseQueue> and <UnpackPauseQueue>.
ParPauseQueue=no
# Cleanup download queue after successful check/repair (yes, no).
#
# Enable this option for automatic deletion of unneeded (paused) par-files
# from download queue after successful check/repair.
ParCleanupQueue=yes
# Delete source nzb-file after successful check/repair (yes, no).
#
# Enable this option for automatic deletion of nzb-file from incoming directory
# after successful check/repair.
NzbCleanupDisk=no
##############################################################################
### UNPACK ###
# Unpack downloaded nzb-files (yes, no).
#
# If the download is damaged and could not be repaired using par-files
# the unpacking is not performed.
#
# If the option <ParCheck> is disabled the program will try to unpack
# downloaded files first. If the unpacking fails the par-check/repair
# is performed and the unpack will be executed again.
Unpack=yes
# Pause download queue during unpack (yes, no).
#
# Enable the option to give CPU more time for unpacking. That helps
# to speed up unpacking on slow CPUs.
#
# NOTE: See also options <ParPauseQueue> and <PostPauseQueue>.
UnpackPauseQueue=no
# Delete archive files after successful unpacking (yes, no).
UnpackCleanupDisk=yes
# Full path to unrar executable.
#
# Example: "/usr/bin/unrar".
#
# If unrar is in your PATH you may leave the path part and set only
# the executable name ("unrar" on POSIX or "unrar.exe" on Windows).
UnrarCmd=unrar
# Full path to 7-Zip executable.
#
# Example: "/usr/bin/7z".
#
# If 7-Zip binary is in your PATH you may leave the path part and set only
# the executable name ("7z" or "7za" on POSIX or "7z.exe" on Windows).
SevenZipCmd=7z
##############################################################################
### POST-PROCESSING ###
# Set path to program, that must be executed after the download of nzb-file
# is completed and possibly par-checked/repaired and unpacked, depending
# on other options.
#
# Example: "PostProcess=~/nzbget-postprocess.sh".
#
# NOTE: An example script is provided within distribution in file
# "nzbget-postprocess.sh".
#
# NOTE: Since version 10.0 NZBGet has a built-in support for unpack
# (option <Unpack>). In the previous versions unpack was performed by
# post-processing scripts. If you use a script created for older NZBGet
# version you need to disable the built-in unpack for script to operate
# properly.
#
# INFO FOR DEVELOPERS:
# If the option <AllowReProcess> is disabled (that's the default setting)
# the post-processing script is executed once per nzb-file after
# par-check/repair (if needed) and unpacking (if enabled).
#
# If the option <AllowReProcess> is active the post-processing script is
# executed for each collection within nzb-file (for nzb-files having multiple
# collections but at least once). Depending on option <ParCheck> the collection
# could be already par-checked/repaired or the script could be called without
# par-check taken place. In the latest case if the script detects errors
# (such as unpack failed) it has an ability to request par-check from
# NZBGet. After par-check/repair NZBGet calls the script once again.
#
# NZBGet passes following arguments to post-processing script as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled.
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
#
# In addition the following arguments are passed if the option <AllowReProcess>
# is active:
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
# found);
# NZBPP_PARFAILED - indication of failed par-jobs for current nzb-file:
# 0 = no failed par-jobs;
# 1 = current par-job or any of the previous par-jobs for
# the same nzb-files failed;
# NZBPP_NZBCOMPLETED - state of nzb-job:
# 0 = there are more collections in this nzb-file queued;
# 1 = this was the last collection in nzb-file.
#
# 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
# as environment variables. These variables have prefix "NZBPR_" in their names.
# For example, pp-parameter "myvar" will be passed as environment
# variable "NZBPR_myvar".
#
# 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
# "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:
# 93 - post-process successful (status = SUCCESS);
# 94 - post-process failed (status = FAILURE);
# 95 - post-process skipped (status = NONE);
# 91 - request NZBGet to do par-check/repair for current collection in the
# current nzb-file. This return code is accepted only if the
# option <AllowReProcess> is active;
# 92 - request NZBGet to do par-check/repair for all collections (par-sets)
# in the current nzb-file.
# All other return codes are interpreted as "status unknown".
#
# The return value is used to display the status of post-processing in
# a history view. In addition to status one or more text messages can be
# passed to history using a special prefix "[HISTORY]" by printing messages
# to standard output. For example:
# echo "[ERROR] [HISTORY] Unpack failed, not enough disk space";
#
# NOTE: If the option <AllowReProcess> is active NZBGet calls the script
# for each collection within nzb-file. The term "collection" actually means
# "par-set". To determine what "collections" are present in nzb-file NZBGet
# looks for par-sets. If any collection of files within nzb-file does
# not have any par-files, this collection will not be detected.
# For example, for nzb-file containing three collections but only two par-sets,
# the postprocess will be called two times - after processing of each par-set.
# If NZBGet doesn't find any collections it calls PostProcess once
# with empty string for parameter NZBPP_PARFILENAME.
PostProcess=
# Allow multiple post-processing for the same nzb-file (yes, no).
#
# NOTE: Enable this option only if you were advised to do that by the author
# of the post-processing script.
#
# NOTE: By enabling <AllowReProcess> you should disable the option <ParCheck>
# to prevent multiple par-checking.
#
# INFO FOR DEVELOPERS:
# This option affects the post-processing in several ways:
# 1) If the option is active the post-processing script (option <PostProcess>)
# is called for each collection (par-set) within nzb-file;
# 2) The post-processing script may be called multiple times when nzb-file
# reaches the state "completely downloaded". This can be needed if
# the par-check/-repair is performed by the post-processing script
# (instead of relying on NZBGet's built-in par-check-feature).
#
# NOTE: If the built-in unpacking is active (option <Unpack>) this option
# is ignored (as if it were set to "no").
#
# NOTE: If you develop a script depending on this option you should check
# if the option is active when your script is started and generate an
# error message if the option is not set correctly. You should also check
# the option <Unpack> because if it's active the option <AllowReProcess>
# doesn't work too.
AllowReProcess=no
# Pause download queue during executing of postprocess-script (yes, no).
#
# Enable the option to give CPU more time for postprocess-script. That helps
# to speed up postprocess on slow CPUs with fast connection (e.g. NAS-devices).
#
# NOTE: See also options <ParPauseQueue> and <UnpackPauseQueue>.
PostPauseQueue=no
##############################################################################
### SCHEDULER ###
@@ -830,7 +1115,7 @@ UpdateInterval=200
# Path to the program to execute if the command is "Process".
#
# Example: /home/user/fetch-nzb.sh.
# Example: "Task1.Process=/home/user/fetch-nzb.sh".
#
# If the option <TaskX.Command> is not set to "Process" this option
# is ignored and can be omitted.
@@ -849,275 +1134,3 @@ UpdateInterval=200
#Task2.WeekDays=1-7
#Task2.Command=DownloadRate
#Task2.DownloadRate=0
##############################################################################
### PAR CHECK/REPAIR ###
# Whether and how par-verification must be performed (auto, force, manual).
#
# Auto - par-check is performed when needed. One par2-file is always
# downloaded. Additional par2-files are downloaded if needed
# for repair. Repair is performed if the option <ParRepair>
# is enabled;
# Force - force par-check for every download (even undamaged). All
# par2-files are always downloaded. Repair is performed if
# the option <ParRepair> is enabled;
# Manual - par-check is skipped. One par2-file is always
# downloaded. If a damaged download is detected, all
# par2-files are downloaded but neithet par-check nor par-repair
# take place. The download can be then repaired manually
# (possibly on another, faster computer).
ParCheck=auto
# Automatic par-repair after par-verification (yes, no).
#
# If option <ParCheck> is set to "Auto" or "Force" this option defines
# if the download must be repaired when needed. The option can be
# disabled if computer does not have enough CPU power, since repairing
# may take too much resources and time on a slow computers.
ParRepair=yes
# What files should be scanned during par-verification (limited,
# full, auto).
#
# Limited - scan only files belonging to the par-set;
# Full - scan all files in the directory. This helps if the
# files were renamed after creating of par-set;
# Auto - a limited scan is performed first. If the par-checker
# detects missing files, it scans other files in the
# directory until all required files are found.
#
# NOTE: for par-check/repair NZBGet uses library libpar2. The last and
# widely used version 0.2 of the library has few bugs, sometimes causing
# a crash of the program. This is especially true when using "full" or
# "auto" par-scan. NZBGet is supplied with patches addressing these
# issues. Please apply the patches to libpar2 and recompile it.
ParScan=auto
# Use only par2-files with matching names (yes, no).
#
# If par-check needs extra par-blocks it looks for paused par2-files
# in the download queue. These par2-files should have the same base name
# as the main par2-file, currently loaded in par-checker. Sometimes extra
# par2-files have non-matching names (especially if they were uploaded
# by a different poster). Normally par-checker does not use these files, but
# you can allow it to use them by setting <StrictParName> to "no".
# There is a small side effect then: if NZB-file contains more than one
# collection of files (with different par-sets), par-checker may download
# par2-files from a wrong collection and will need to unpause other
# par2-files until all required files are downloaded. This increases the
# traffic (but not harm the par-check).
#
# NOTE: Par-checker always uses only par-files added from the same NZB-file
# and the option <StrictParName> does not change this behavior.
StrictParName=yes
# Maximum allowed time for par-repair (minutes).
#
# 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
# required for par-repair. If the estimated value exceeds the limit defined
# here, NZBGet cancels the repair.
#
# 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
# 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
# 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 the patch to libpar2.
ParTimeLimit=0
# Pause download queue during check/repair (yes, no).
#
# 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
# the queue.
#
# NOTE: See also options <ScriptPauseQueue> and <UnpackPauseQueue>.
ParPauseQueue=no
# Cleanup download queue after successful check/repair (yes, no).
#
# Enable this option for automatic deletion of unneeded (paused) par-files
# from download queue after successful check/repair.
ParCleanupQueue=yes
# Delete source nzb-file after successful check/repair (yes, no).
#
# Enable this option for automatic deletion of nzb-file from incoming directory
# after successful check/repair.
NzbCleanupDisk=no
# Files to delete after successful check/repair.
#
# List of file extensions or file names to delete after successful
# check/repair. The entries must be separated with commas. The entries
# can be file extensions or any text the file name may end with.
#
# Example: .par2, .sfv
ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
##############################################################################
### UNPACK ###
# Unpack downloaded nzb-files (yes, no).
#
# If the download is damaged and could not be repaired using par-files
# the unpacking is not performed.
#
# If the option <ParCheck> is disabled the program will try to unpack
# downloaded files first. If the unpacking fails the par-check/repair
# is performed and the unpack will be executed again.
Unpack=yes
# Pause download queue during unpack (yes, no).
#
# Enable the option to give CPU more time for unpacking. That helps
# to speed up unpacking on slow CPUs.
#
# NOTE: See also options <ParPauseQueue> and <ScriptPauseQueue>.
UnpackPauseQueue=no
# Delete archive files after successful unpacking (yes, no).
UnpackCleanupDisk=yes
# Full path to unrar executable.
#
# Example: /usr/bin/unrar.
#
# If unrar is in your PATH you may leave the path part and set only
# the executable name ("unrar" on POSIX or "unrar.exe" on Windows).
UnrarCmd=unrar
# Full path to 7-Zip executable.
#
# Example: /usr/bin/7z.
#
# If 7-Zip binary is in your PATH you may leave the path part and set only
# the executable name ("7z" or "7za" on POSIX or "7z.exe" on Windows).
SevenZipCmd=7z
##############################################################################
### POST-PROCESSING SCRIPTS ###
# Default list of post-processing scripts to execute after the download
# of nzb-file is completed and possibly par-checked/repaired and unpacked,
# depending on other options.
#
# The scripts in the list must be separated with commas or semicolons. Only
# filenames without path must be used. All scripts must be stored in directory
# pointed by option <ScriptDir>.
#
# Example: Cleanup.sh, Move.sh, EMail.py.
#
# Each download (nzb-file) has its own list of post-processing scripts. The option
# <DefScript> is the default value assigned to download when it is added to
# queue. The list of post-processing scripts for a particular download can be
# changed in the edit dialog in web-interface or using remote command "--edit/-E".
#
# When nzb-file is added to queue it can have a category assigned to it. In this
# case the option <CategoryX.DefScript> (if not empty) overrides the
# global option <DefScript>.
#
# NOTE: The script execution order is controlled by option <ScriptOrder>, not
# by their order in option <DefScript>.
#
# NOTE: Changing options <DefScript> and <CategoryX.DefScript> doesn't affect
# already queued downloads.
#
# NOTE: For the list of interesting post-processing scripts see
# http://nzbget.sourceforge.net/Catalog_of_post-processing_scripts.
#
# INFO FOR DEVELOPERS:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.sourceforge.net/Post-processing_scripts.
#
# NZBGet passes following arguments to post-processing script as environment
# variables:
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
# NZBPP_NZBNAME - user-friendly name of processed nzb-file as it is displayed
# by the program. The file path and extension are removed.
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled;
# 4 = par-check needed but skipped (option ParCheck=manual);
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
#
# If the script defines own options they are also passed as environment
# variables. These variables have prefix "NZBPO_" in their names. For
# example, option "myoption" will be passed as environment variable
# "NZBPO_myoption" and in addition in uppercase as "NZBPO_MYOPTION".
#
# If the script defines own post-processing parameters, they are also passed as
# environment variables. These variables have prefix "NZBPR_" in their
# names. For example, pp-parameter "myparam" will be passed as environment
# variable "NZBPR_myparam" and in addition in uppercase as "NZBPR_MYPARAM".
#
# In addition to arguments, pp-options and pp-parameters NZBGet passes all
# nzbget.conf-options to pp-script 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.
#
# Return value: NZBGet processes the exit code returned by the script:
# 93 - post-process successful (status = SUCCESS);
# 94 - post-process failed (status = FAILURE);
# 95 - post-process skipped (status = NONE). Use this code when you script
# terminates immediateley without doing any job and when this is not
# a failure termination;
# 92 - request NZBGet to do par-check/repair for current nzb-file.
#
# All other return codes are interpreted as failure (status = FAILURE).
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.sourceforge.net/Post-processing_scripts.
DefScript=
# Execution order for scripts.
#
# If you assign multiple scripts to one nzb-file, they are executed in the
# order defined by this option. Scripts not listed here are executed at
# the end in their alphabetical order.
#
# The scripts in the list must be separated with commas or semicolons. Only
# filenames without path must be used. All scripts must be stored in directory
# pointed by option <ScriptDir>.
#
# Example: Cleanup.sh, Move.sh.
ScriptOrder=
# Pause download queue during executing of postprocess-script (yes, no).
#
# Enable the option to give CPU more time for postprocess-script. That helps
# to speed up postprocess on slow CPUs with fast connection (e.g. NAS-devices).
#
# NOTE: See also options <ParPauseQueue> and <UnpackPauseQueue>.
ScriptPauseQueue=no

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-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
@@ -74,7 +74,6 @@
#include "PrePostProcessor.h"
#include "ParChecker.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "Util.h"
#ifdef WIN32
#include "NTService.h"
@@ -114,7 +113,6 @@ Log* g_pLog = NULL;
PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
Scheduler* g_pScheduler = NULL;
Scanner* g_pScanner = NULL;
int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
@@ -225,19 +223,16 @@ void Run(bool bReload)
g_pLog->InitOptions();
if (g_pOptions->GetDaemonMode())
if (g_pOptions->GetDaemonMode() && !bReload)
{
#ifdef WIN32
info("nzbget %s service-mode", Util::VersionRevision());
#else
if (!bReload)
{
Daemonize();
}
Daemonize();
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());
}
@@ -312,7 +307,6 @@ void Run(bool bReload)
// Creating PrePostProcessor
if (!g_pOptions->GetRemoteClientMode())
{
g_pScanner = new Scanner();
g_pPrePostProcessor = new PrePostProcessor();
}
@@ -347,7 +341,7 @@ void Run(bool bReload)
// Standalone-mode
if (!g_pOptions->GetServerMode())
{
NZBFile* pNZBFile = NZBFile::Create(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
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");
@@ -748,11 +742,6 @@ void Cleanup()
delete g_pPrePostProcessor;
g_pPrePostProcessor = NULL;
}
if (g_pScanner)
{
delete g_pScanner;
g_pScanner = NULL;
}
debug("PrePostProcessor deleted");
debug("Deleting Frontend");
@@ -844,14 +833,14 @@ void Daemonize()
/* Drop user if there is one, and we were run as root */
if ( getuid() == 0 || geteuid() == 0 )
{
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUsername());
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
if (pw)
{
fchown(lfp, pw->pw_uid, pw->pw_gid); /* change owner of lock file */
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
setgid(pw->pw_gid); /* Set primary group. */
/* Try setting aux groups correctly - not critical if this fails. */
initgroups( g_pOptions->GetDaemonUsername(),pw->pw_gid);
initgroups( g_pOptions->GetDaemonUserName(),pw->pw_gid);
/* Finally, set uid. */
setuid(pw->pw_uid);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 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
@@ -37,8 +37,6 @@
#define fdopen _fdopen
#define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep)
#define localtime_r(time, tm) localtime_s(tm, time)
#define strtok_r(str, delim, saveptr) strtok_s(str, delim, saveptr)
#define strerror_r(errnum, buffer, size) strerror_s(buffer, size, errnum)
#define int32_t __int32
#define mkdir(dir, flags) _mkdir(dir)
#define rmdir _rmdir

274
nzbget.kdevelop Normal file
View File

@@ -0,0 +1,274 @@
<?xml version = '1.0'?>
<kdevelop>
<general>
<author>gnu</author>
<email/>
<version>0.3.0</version>
<projectmanagement>KDevAutoProject</projectmanagement>
<primarylanguage>C++</primarylanguage>
<keywords>
<keyword>C++</keyword>
<keyword>Code</keyword>
</keywords>
<ignoreparts>
<part>kdevabbrev</part>
<part>kdevbookmarks</part>
<part>kdevsnippet</part>
<part>kdevctags2</part>
<part>kdevdoxygen</part>
<part>kdevkonsoleview</part>
<part>kdevfilegroups</part>
<part>kdevfilelist</part>
<part>kdevfileview</part>
<part>kdevdistpart</part>
<part>kdevopenwith</part>
<part>kdevregexptest</part>
<part>kdevscripting</part>
<part>kdevfilter</part>
<part>kdevtexttools</part>
</ignoreparts>
<projectdirectory>.</projectdirectory>
<absoluteprojectpath>false</absoluteprojectpath>
<description/>
<projectname>nzbget</projectname>
<defaultencoding/>
<versioncontrol>kdevsubversion</versioncontrol>
</general>
<kdevautoproject>
<general>
<activetarget>src/nzbget</activetarget>
<useconfiguration>default</useconfiguration>
</general>
<run>
<mainprogram>/home/user/nzbget/nzbget</mainprogram>
<terminal>false</terminal>
<directoryradio>executable</directoryradio>
<customdirectory>/</customdirectory>
<programargs/>
<autocompile>true</autocompile>
<envvars/>
<globaldebugarguments></globaldebugarguments>
<globalcwd>/home/user/nzbget</globalcwd>
<useglobalprogram>true</useglobalprogram>
<autoinstall>false</autoinstall>
<autokdesu>false</autokdesu>
</run>
<configurations>
<default>
<envvars/>
<configargs>--enable-debug</configargs>
<builddir/>
<topsourcedir/>
<cppflags/>
<ldflags/>
<ccompiler>kdevgccoptions</ccompiler>
<cxxcompiler>kdevgppoptions</cxxcompiler>
<f77compiler>kdevpgf77options</f77compiler>
<ccompilerbinary/>
<cxxcompilerbinary/>
<f77compilerbinary/>
<cflags/>
<cxxflags/>
<f77flags/>
</default>
</configurations>
<make>
<envvars>
<envvar value="1" name="WANT_AUTOCONF_2_5" />
<envvar value="1" name="WANT_AUTOMAKE_1_6" />
</envvars>
<abortonerror>true</abortonerror>
<runmultiplejobs>false</runmultiplejobs>
<numberofjobs>1</numberofjobs>
<dontact>false</dontact>
<makebin/>
<prio>0</prio>
</make>
</kdevautoproject>
<kdevdoctreeview>
<ignoretocs>
<toc>ada</toc>
<toc>ada_bugs_gcc</toc>
<toc>bash</toc>
<toc>bash_bugs</toc>
<toc>clanlib</toc>
<toc>w3c-dom-level2-html</toc>
<toc>fortran_bugs_gcc</toc>
<toc>gnome1</toc>
<toc>gnustep</toc>
<toc>gtk</toc>
<toc>gtk_bugs</toc>
<toc>haskell</toc>
<toc>haskell_bugs_ghc</toc>
<toc>java_bugs_gcc</toc>
<toc>java_bugs_sun</toc>
<toc>kde2book</toc>
<toc>opengl</toc>
<toc>pascal_bugs_fp</toc>
<toc>php</toc>
<toc>php_bugs</toc>
<toc>perl</toc>
<toc>perl_bugs</toc>
<toc>python</toc>
<toc>python_bugs</toc>
<toc>qt-kdev3</toc>
<toc>ruby</toc>
<toc>ruby_bugs</toc>
<toc>sdl</toc>
<toc>w3c-svg</toc>
<toc>sw</toc>
<toc>w3c-uaag10</toc>
<toc>wxwidgets_bugs</toc>
</ignoretocs>
<ignoreqt_xml>
<toc>Guide to the Qt Translation Tools</toc>
<toc>Qt Assistant Manual</toc>
<toc>Qt Designer Manual</toc>
<toc>Qt Reference Documentation</toc>
<toc>qmake User Guide</toc>
</ignoreqt_xml>
<ignoredoxygen>
<toc>KDE Libraries (Doxygen)</toc>
</ignoredoxygen>
</kdevdoctreeview>
<kdevfilecreate>
<filetypes/>
<useglobaltypes>
<type ext="cpp" />
<type ext="h" />
</useglobaltypes>
</kdevfilecreate>
<kdevfileview>
<groups>
<group pattern="*.h" name="Header files" />
<group pattern="*.cpp" name="Source files" />
<hidenonprojectfiles>false</hidenonprojectfiles>
<hidenonlocation>false</hidenonlocation>
</groups>
<tree>
<hidepatterns>*.o,*.lo,CVS</hidepatterns>
<hidenonprojectfiles>false</hidenonprojectfiles>
</tree>
</kdevfileview>
<kdevdocumentation>
<projectdoc>
<docsystem>Doxygen Documentation Collection</docsystem>
<docurl>nzbget.tag</docurl>
<usermanualurl/>
</projectdoc>
</kdevdocumentation>
<substmap>
<APPNAME>nzbget</APPNAME>
<APPNAMELC>nzbget</APPNAMELC>
<APPNAMESC>Nzbget</APPNAMESC>
<APPNAMEUC>NZBGET</APPNAMEUC>
<AUTHOR>gnu</AUTHOR>
<EMAIL/>
<LICENSE>GPL</LICENSE>
<LICENSEFILE>COPYING</LICENSEFILE>
<VERSION>0.3.0</VERSION>
<YEAR>2007</YEAR>
<dest>/home/user/nzbget-0.3.0/nzbget</dest>
</substmap>
<cppsupportpart>
<filetemplates>
<interfacesuffix>.h</interfacesuffix>
<implementationsuffix>.cpp</implementationsuffix>
</filetemplates>
</cppsupportpart>
<kdevcppsupport>
<qt>
<used>false</used>
<version>3</version>
<root></root>
<includestyle>3</includestyle>
<designerintegration>EmbeddedKDevDesigner</designerintegration>
<qmake></qmake>
<designer></designer>
<designerpluginpaths/>
</qt>
<codecompletion>
<includeGlobalFunctions>true</includeGlobalFunctions>
<includeTypes>true</includeTypes>
<includeEnums>true</includeEnums>
<includeTypedefs>false</includeTypedefs>
<automaticCodeCompletion>false</automaticCodeCompletion>
<automaticArgumentsHint>false</automaticArgumentsHint>
<automaticHeaderCompletion>true</automaticHeaderCompletion>
<codeCompletionDelay>250</codeCompletionDelay>
<argumentsHintDelay>400</argumentsHintDelay>
<headerCompletionDelay>250</headerCompletionDelay>
<showOnlyAccessibleItems>false</showOnlyAccessibleItems>
<completionBoxItemOrder>0</completionBoxItemOrder>
<howEvaluationContextMenu>true</howEvaluationContextMenu>
<showCommentWithArgumentHint>false</showCommentWithArgumentHint>
<statusBarTypeEvaluation>false</statusBarTypeEvaluation>
<namespaceAliases>std=_GLIBCXX_STD;__gnu_cxx=std</namespaceAliases>
<processPrimaryTypes>true</processPrimaryTypes>
<processFunctionArguments>false</processFunctionArguments>
<preProcessAllHeaders>false</preProcessAllHeaders>
<parseMissingHeaders>false</parseMissingHeaders>
<resolveIncludePaths>true</resolveIncludePaths>
<alwaysParseInBackground>true</alwaysParseInBackground>
<usePermanentCaching>true</usePermanentCaching>
<alwaysIncludeNamespaces>false</alwaysIncludeNamespaces>
<includePaths>.;</includePaths>
</codecompletion>
<creategettersetter>
<prefixGet/>
<prefixSet>set</prefixSet>
<prefixVariable>m_,_</prefixVariable>
<parameterName>theValue</parameterName>
<inlineGet>true</inlineGet>
<inlineSet>true</inlineSet>
</creategettersetter>
<references/>
<splitheadersource>
<enabled>false</enabled>
<synchronize>true</synchronize>
<orientation>Vertical</orientation>
</splitheadersource>
</kdevcppsupport>
<kdevdebugger>
<general>
<programargs>--standalone /home/user/.nzbget/nzb/t1.nzb</programargs>
<gdbpath/>
<dbgshell/>
<configGdbScript/>
<runShellScript/>
<runGdbScript/>
<breakonloadinglibs>true</breakonloadinglibs>
<separatetty>true</separatetty>
<floatingtoolbar>false</floatingtoolbar>
</general>
<display>
<staticmembers>false</staticmembers>
<demanglenames>true</demanglenames>
<outputradix>10</outputradix>
</display>
</kdevdebugger>
<dist>
<custom>false</custom>
<bzip>false</bzip>
<archname/>
<appname>nzbget</appname>
<version>0.2.4</version>
<release/>
<vendor/>
<licence/>
<summary/>
<group/>
<packager/>
<description/>
<changelog/>
<devpackage>false</devpackage>
<docspackage>false</docspackage>
<appicon>false</appicon>
<arch>0</arch>
<genHTML>false</genHTML>
<useRPM>false</useRPM>
<ftpkde>false</ftpkde>
<appskde>false</appskde>
<url/>
</dist>
</kdevelop>

View File

@@ -1,218 +0,0 @@
#!/usr/bin/env python
#
# E-Mail post-processing script for NZBGet
#
# Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Revision$
# $Date$
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Send E-Mail notification.
#
# This script sends E-Mail notification when the job is done.
#
# NOTE: This script requires Python to be installed on your system.
##############################################################################
### OPTIONS ###
# Email address you want this email to be sent from.
#From="NZBGet" <myaccount@gmail.com>
# Email address you want this email to be sent to.
#To=myaccount@gmail.com
# SMTP server host.
#Server=smtp.gmail.com
# SMTP server port (1-65535).
#Port=25
# Secure communication using TLS/SSL (yes, no).
#Encryption=yes
# SMTP server user name, if required.
#Username=myaccount
# SMTP server password, if required.
#Password=mypass
# Append list of files to the message (yes, no).
#
# Add the list of downloaded files (the content of destination directory).
#FileList=yes
# Append broken-log to the message (yes, no).
#
# Add the content of file _brokenlog.txt. This file contains the list of damaged
# files and the result of par-check/repair. For successful downloads the broken-log
# is usually deleted by cleanup-script and therefore is not sent.
#BrokenLog=yes
# Append post-processing log to the message (Always, Never, OnFailure).
#
# Add the post-processing log of active job.
#PostProcessLog=OnFailure
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import os
import sys
import datetime
import smtplib
from email.mime.text import MIMEText
try:
from xmlrpclib import ServerProxy # python 2
except ImportError:
from xmlrpc.client import ServerProxy # python 3
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_ERROR=94
# Check if the script is called from nzbget 11.0 or later
if not 'NZBOP_SCRIPTDIR' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (11.0 or later).')
sys.exit(POSTPROCESS_ERROR)
print('[DETAIL] Script successfully started')
sys.stdout.flush()
required_options = ('NZBPO_FROM', 'NZBPO_TO', 'NZBPO_SERVER', 'NZBPO_PORT', 'NZBPO_ENCRYPTION',
'NZBPO_USERNAME', 'NZBPO_PASSWORD', 'NZBPO_FILELIST', 'NZBPO_BROKENLOG', 'NZBPO_POSTPROCESSLOG')
for optname in required_options:
if (not optname in os.environ):
print('[ERROR] Option %s is missing in configuration file. Please check script settings' % optname[6:])
sys.exit(POSTPROCESS_ERROR)
# Check par and unpack status for errors.
success=False
if os.environ['NZBPP_PARSTATUS'] == '1' or os.environ['NZBPP_UNPACKSTATUS'] == '1':
subject = 'Failure for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" has failed.' % (os.environ['NZBPP_NZBNAME'])
elif os.environ['NZBPP_PARSTATUS'] == '4':
subject = 'Damaged for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" requires par-repair.' % (os.environ['NZBPP_NZBNAME'])
else:
subject = 'Success for "%s"' % (os.environ['NZBPP_NZBNAME'])
text = 'Download of "%s" has successfully completed.' % (os.environ['NZBPP_NZBNAME'])
success=True
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
# 1 = checked and failed to repair;
# 2 = checked and successfully repaired;
# 3 = checked and can be repaired but repair is disabled.
# 4 = par-check needed but skipped (option ParCheck=manual);
parStatus = { '0': 'skipped', '1': 'failed', '2': 'repaired', '3': 'repairable', '4': 'manual' }
text += '\nPar-Status: %s' % parStatus[os.environ['NZBPP_PARSTATUS']]
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful.
unpackStatus = { '0': 'skipped', '1': 'failed', '2': 'success' }
text += '\nUnpack-Status: %s' % unpackStatus[os.environ['NZBPP_UNPACKSTATUS']]
# add list of downloaded files
if os.environ['NZBPO_FILELIST'] == 'yes':
text += '\n\nFiles:'
for dirname, dirnames, filenames in os.walk(os.environ['NZBPP_DIRECTORY']):
for filename in filenames:
text += '\n' + os.path.join(dirname, filename)[len(os.environ['NZBPP_DIRECTORY']) + 1:]
# add _brokenlog.txt (if exists)
if os.environ['NZBPO_BROKENLOG'] == 'yes':
brokenlog = '%s/_brokenlog.txt' % os.environ['NZBPP_DIRECTORY']
if os.path.exists(brokenlog):
text += '\n\nBrokenlog:\n' + open(brokenlog, 'r').read().strip()
# add post-processing log
if os.environ['NZBPO_POSTPROCESSLOG'] == 'Always' or \
(os.environ['NZBPO_POSTPROCESSLOG'] == 'OnFailure' and not success):
# To get the post-processing log we connect to NZBGet via XML-RPC
# and call method "postqueue", which returns the list of post-processing job.
# The first item in the list is current job. This item has a field 'Log',
# containing an array of log-entries.
# For more info visit http://nzbget.sourceforge.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
# NZBGet passes all configuration options to post-processing script as
# environment variables.
host = os.environ['NZBOP_CONTROLIP'];
port = os.environ['NZBOP_CONTROLPORT'];
username = os.environ['NZBOP_CONTROLUSERNAME'];
password = os.environ['NZBOP_CONTROLPASSWORD'];
if host == '0.0.0.0': host = '127.0.0.1'
# Build an URL for XML-RPC requests
rpcUrl = 'http://%s:%s@%s:%s/xmlrpc' % (username, password, host, port);
# Create remote server object
server = ServerProxy(rpcUrl)
# Call remote method 'postqueue'. The only parameter tells how many log-entries to return as maximum.
postqueue = server.postqueue(10000)
# Get field 'Log' from the first post-processing job
log = postqueue[0]['Log']
# Now iterate through entries and save them to message text
if len(log) > 0:
text += '\n\nPost-processing log:';
for entry in log:
text += '\n%s\t%s\t%s' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])
# Create message
msg = MIMEText(text)
msg['Subject'] = subject
msg['From'] = os.environ['NZBPO_FROM']
msg['To'] = os.environ['NZBPO_TO']
# Send message
print('[DETAIL] Sending E-Mail')
sys.stdout.flush()
try:
smtp = smtplib.SMTP(os.environ['NZBPO_SERVER'], os.environ['NZBPO_PORT'])
if os.environ['NZBPO_ENCRYPTION'] == 'yes':
smtp.starttls()
if os.environ['NZBPO_USERNAME'] != '' and os.environ['NZBPO_PASSWORD'] != '':
smtp.login(os.environ['NZBPO_USERNAME'], os.environ['NZBPO_PASSWORD'])
smtp.sendmail(os.environ['NZBPO_FROM'], os.environ['NZBPO_TO'], msg.as_string())
smtp.quit()
except Exception as err:
print('[ERROR] %s' % err)
sys.exit(POSTPROCESS_ERROR)
# All OK, returning exit status 'POSTPROCESS_SUCCESS' (int <93>) to let NZBGet know
# that our script has successfully completed.
sys.exit(POSTPROCESS_SUCCESS)

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env python
#
# Logger post-processing script for NZBGet
#
# Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# $Revision$
# $Date$
#
##############################################################################
### NZBGET POST-PROCESSING SCRIPT ###
# Save post-processing log into a file.
#
# This script saves post-processing log of nzb-file into file
# _postprocesslog.txt in the destination directory.
#
# NOTE: This script requires Python to be installed on your system.
### NZBGET POST-PROCESSING SCRIPT ###
##############################################################################
import os
import sys
import datetime
try:
from xmlrpclib import ServerProxy # python 2
except ImportError:
from xmlrpc.client import ServerProxy # python 3
# Exit codes used by NZBGet
POSTPROCESS_SUCCESS=93
POSTPROCESS_NONE=95
POSTPROCESS_ERROR=94
# Check if the script is called from nzbget 11.0 or later
if not 'NZBOP_SCRIPTDIR' in os.environ:
print('*** NZBGet post-processing script ***')
print('This script is supposed to be called from nzbget (11.0 or later).')
sys.exit(POSTPROCESS_ERROR)
if not os.path.exists(os.environ['NZBPP_DIRECTORY']):
print('Destination directory doesn\'t exist, exiting')
sys.exit(POSTPROCESS_NONE)
# To get the post-processing log we connect to NZBGet via XML-RPC
# and call method "postqueue", which returns the list of post-processing job.
# The first item in the list is current job. This item has a field 'Log',
# containing an array of log-entries.
# For more info visit http://nzbget.sourceforge.net/RPC_API_reference
# First we need to know connection info: host, port and password of NZBGet server.
# NZBGet passes all configuration options to post-processing script as
# environment variables.
host = os.environ['NZBOP_CONTROLIP'];
port = os.environ['NZBOP_CONTROLPORT'];
username = os.environ['NZBOP_CONTROLUSERNAME'];
password = os.environ['NZBOP_CONTROLPASSWORD'];
if host == '0.0.0.0': host = '127.0.0.1'
# Build an URL for XML-RPC requests
rpcUrl = 'http://%s:%s@%s:%s/xmlrpc' % (username, password, host, port);
# Create remote server object
server = ServerProxy(rpcUrl)
# Call remote method 'postqueue'. The only parameter tells how many log-entries to return as maximum.
postqueue = server.postqueue(10000)
# Get field 'Log' from the first post-processing job
log = postqueue[0]['Log']
# Now iterate through entries and save them to the output file
if len(log) > 0:
f = open('%s/_postprocesslog.txt' % os.environ['NZBPP_DIRECTORY'], 'wb')
for entry in log:
f.write((u'%s\t%s\t%s\n' % (entry['Kind'], datetime.datetime.fromtimestamp(int(entry['Time'])), entry['Text'])).encode('utf8'))
f.close()
# All OK, returning exit status 'POSTPROCESS_SUCCESS' (int <93>) to let NZBGet know
# that our script has successfully completed.
sys.exit(POSTPROCESS_SUCCESS)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -416,14 +416,7 @@ var Downloads = (new function($)
notification = '#Notif_Downloads_Resumed';
RPC.call('editqueue', ['GroupResume', 0, '', checkedEditIDs], function()
{
if (Options.option('ParCheck') === 'force')
{
editCompleted();
}
else
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
}
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
});
}
@@ -480,9 +473,6 @@ var Downloads = (new function($)
}
};
Util.show('#DownloadsDeleteConfirmDialog_Cleanup', Options.option('DeleteCleanupDisk') === 'yes');
Util.show('#DownloadsDeleteConfirmDialog_Remain', Options.option('DeleteCleanupDisk') != 'yes');
ConfirmDialog.showModal('DownloadsDeleteConfirmDialog', deleteGroups);
}
@@ -663,8 +653,6 @@ var DownloadsUI = (new function($)
if (group.post.Log && group.post.Log.length > 0)
{
text = group.post.Log[group.post.Log.length-1].Text;
// remove "for <nzb-name>" from label text
text = text.replace(' for ' + group.NZBName, ' ');
}
else
{

View File

@@ -26,9 +26,7 @@
* In this module:
* 1) Download edit dialog;
* 2) Download multi edit dialog (edit multiple items);
* 3) Download merge dialog;
* 4) Download split dialog;
* 5) History edit dialog.
* 3) Download merge dialog.
*/
/*** DOWNLOAD EDIT DIALOG ************************************************************/
@@ -41,7 +39,6 @@ var DownloadsEditDialog = (new function($)
var $DownloadsEditDialog;
var $DownloadsLogTable;
var $DownloadsFileTable;
var $DownloadsEdit_ParamData;
// State
var curGroup;
@@ -55,7 +52,6 @@ var DownloadsEditDialog = (new function($)
this.init = function()
{
$DownloadsEditDialog = $('#DownloadsEditDialog');
$DownloadsEdit_ParamData = $('#DownloadsEdit_ParamData');
$('#DownloadsEdit_Save').click(saveChanges);
$('#DownloadsEdit_Pause').click(itemPause);
@@ -100,7 +96,6 @@ var DownloadsEditDialog = (new function($)
// cleanup
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
$DownloadsEdit_ParamData.empty();
// resume updates
Refresher.resume();
});
@@ -188,7 +183,8 @@ var DownloadsEditDialog = (new function($)
$DownloadsLogTable.fasttable('update', []);
$DownloadsFileTable.fasttable('update', []);
var postParamConfig = ParamTab.createPostParamConfig();
var postParamConfig = Options.postParamConfig;
defineBuiltinParams(postParamConfig);
Util.show('#DownloadsEdit_NZBNameReadonly', group.postprocess);
Util.show('#DownloadsEdit_CancelPPGroup', group.postprocess);
@@ -196,7 +192,7 @@ var DownloadsEditDialog = (new function($)
Util.show('#DownloadsEdit_PauseGroup', !group.postprocess);
Util.show('#DownloadsEdit_ResumeGroup', false);
Util.show('#DownloadsEdit_Save', !group.postprocess);
var postParam = postParamConfig[0].options.length > 0;
var postParam = postParamConfig && postParamConfig.length > 0;
var postLog = group.postprocess && group.post.Log.length > 0;
Util.show('#DownloadsEdit_Param', postParam);
Util.show('#DownloadsEdit_Log', postLog);
@@ -224,7 +220,12 @@ var DownloadsEditDialog = (new function($)
if (postParam)
{
postParams = ParamTab.buildPostParamTab($DownloadsEdit_ParamData, postParamConfig, curGroup.Parameters);
postParams = $.extend(true, [], postParamConfig);
Options.mergeValues(postParams, group.Parameters);
var content = Config.buildOptionsContent(postParams[0]);
var configData = $('#DownloadsEdit_ParamData');
configData.empty();
configData.append(content);
}
enableAllButtons();
@@ -397,14 +398,7 @@ var DownloadsEditDialog = (new function($)
notification = '#Notif_Downloads_Resumed';
RPC.call('editqueue', ['GroupResume', 0, '', [curGroup.LastID]], function()
{
if (Options.option('ParCheck') === 'force')
{
completed();
}
else
{
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', [curGroup.LastID]], completed);
}
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', [curGroup.LastID]], completed);
});
}
@@ -437,9 +431,53 @@ var DownloadsEditDialog = (new function($)
/*** TAB: POST-PROCESSING PARAMETERS **************************************************/
function defineBuiltinParams(postParamConfig)
{
if (Options.option('Unpack') !== 'yes')
{
return;
}
if (postParamConfig.length == 0)
{
postParamConfig.push({category: 'P', postparam: true, options: []});
}
if (!Options.findOption(postParamConfig[0].options, '*Unpack:'))
{
postParamConfig[0].options.unshift({name: '*Unpack:Password', value: '', defvalue: '', select: [], caption: 'Password', description: 'Unpack-password for encrypted posts.'});
postParamConfig[0].options.unshift({name: '*Unpack:', value: '', defvalue: 'yes', select: ['yes', 'no'], caption: 'Unpack', description: 'Set to "no" to disable unpack for this nzb-file.'});
}
}
function prepareParamRequest()
{
var request = [];
for (var i=0; i < postParams.length; i++)
{
var section = postParams[i];
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template && !section.hidden)
{
var oldValue = option.value;
var newValue = Config.getOptionValue(option);
if (oldValue != newValue && !(oldValue === '' && newValue === option.defvalue))
{
var opt = option.name + '=' + newValue;
request.push(opt);
}
}
}
}
return request;
}
function saveParam()
{
var paramList = ParamTab.prepareParamRequest(postParams);
var paramList = prepareParamRequest();
saveNextParam(paramList);
}
@@ -624,9 +662,6 @@ var DownloadsEditDialog = (new function($)
var file = files[i];
file.moved = false;
}
var editIDList = [];
var splitError = false;
for (var i = 0; i < files.length; i++)
{
@@ -640,8 +675,6 @@ var DownloadsEditDialog = (new function($)
if (checkedRows.indexOf(file.ID) > -1)
{
editIDList.push(file.ID);
switch (action)
{
case 'pause':
@@ -694,28 +727,10 @@ var DownloadsEditDialog = (new function($)
i--;
}
break;
case 'split':
if (file.ActiveDownloads > 0 || file.FileSizeLo !== file.RemainingSizeLo)
{
splitError = true;
}
break;
}
}
}
if (action === 'split')
{
if (splitError)
{
Notification.show('#Notif_Downloads_SplitNotPossible');
}
else
{
DownloadsSplitDialog.showModal(curGroup, editIDList);
}
}
filesLoaded(files);
}
@@ -792,89 +807,6 @@ var DownloadsEditDialog = (new function($)
}(jQuery));
/*** PARAM TAB FOR EDIT DIALOGS ************************************************************/
var ParamTab = (new function($)
{
'use strict'
this.buildPostParamTab = function(configData, postParamConfig, parameters)
{
var postParams = $.extend(true, [], postParamConfig);
Options.mergeValues(postParams, parameters);
var content = Config.buildOptionsContent(postParams[0]);
configData.empty();
configData.append(content);
configData.addClass('retain-margin');
var lastClass = '';
var lastDiv = null;
for (var i=0; i < configData.children().length; i++)
{
var div = $(configData.children()[i]);
var divClass = div.attr('class');
if (divClass != lastClass && lastClass != '')
{
lastDiv.addClass('wants-divider');
}
lastDiv = div;
lastClass = divClass;
}
return postParams;
}
this.createPostParamConfig = function()
{
var postParamConfig = Options.postParamConfig;
defineBuiltinParams(postParamConfig);
return postParamConfig;
}
function defineBuiltinParams(postParamConfig)
{
if (Options.option('Unpack') !== 'yes')
{
return;
}
if (postParamConfig.length == 0)
{
postParamConfig.push({category: 'P', postparam: true, options: []});
}
if (!Options.findOption(postParamConfig[0].options, '*Unpack:'))
{
postParamConfig[0].options.unshift({name: '*Unpack:Password', value: '', defvalue: '', select: [], caption: 'Password', sectionId: '_Unpack_', description: 'Unpack-password for encrypted archives.'});
postParamConfig[0].options.unshift({name: '*Unpack:', value: '', defvalue: 'yes', select: ['yes', 'no'], caption: 'Unpack', sectionId: '_Unpack_', description: 'Unpack rar and 7-zip archives.'});
}
}
this.prepareParamRequest = function(postParams)
{
var request = [];
for (var i=0; i < postParams.length; i++)
{
var section = postParams[i];
for (var j=0; j < section.options.length; j++)
{
var option = section.options[j];
if (!option.template && !section.hidden)
{
var oldValue = option.value;
var newValue = Config.getOptionValue(option);
if (oldValue != newValue && !(oldValue === '' && newValue === option.defvalue))
{
var opt = option.name + '=' + newValue;
request.push(opt);
}
}
}
}
return request;
}
}(jQuery));
/*** DOWNLOAD MULTI EDIT DIALOG ************************************************************/
var DownloadsMultiDialog = (new function($)
@@ -1137,281 +1069,3 @@ var DownloadsMergeDialog = (new function($)
Notification.show('#Notif_Downloads_Merged');
}
}(jQuery));
/*** DOWNLOAD SPLIT DIALOG ************************************************************/
var DownloadsSplitDialog = (new function($)
{
'use strict'
// Controls
var $DownloadsSplitDialog;
// State
var splitEditIDList;
this.init = function()
{
$DownloadsSplitDialog = $('#DownloadsSplitDialog');
$('#DownloadsSplit_Split').click(split);
$DownloadsSplitDialog.on('hidden', function ()
{
Refresher.resume();
});
if (UISettings.setFocus)
{
$DownloadsSplitDialog.on('shown', function ()
{
$('#DownloadsSplit_Merge').focus();
});
}
}
this.showModal = function(group, editIDList)
{
Refresher.pause();
splitEditIDList = editIDList;
var groupName = group.NZBName + ' (' + editIDList[0] + (editIDList.length > 1 ? '-' + editIDList[editIDList.length-1] : '') + ')';
$('#DownloadsSplit_NZBName').attr('value', groupName);
$DownloadsSplitDialog.modal({backdrop: 'static'});
}
function split()
{
var groupName = $('#DownloadsSplit_NZBName').val();
RPC.call('editqueue', ['FileSplit', 0, groupName, splitEditIDList], completed);
}
function completed(result)
{
$('#DownloadsEditDialog').modal('hide');
$DownloadsSplitDialog.modal('hide');
Refresher.update();
Notification.show(result ? '#Notif_Downloads_Splitted' : '#Notif_Downloads_SplitError');
}
}(jQuery));
/*** EDIT HISTORY DIALOG *************************************************************************/
var HistoryEditDialog = (new function()
{
'use strict'
// Controls
var $HistoryEditDialog;
var $HistoryEdit_ParamData;
// State
var curHist;
var notification = null;
var postParams = [];
var lastPage;
var lastFullscreen;
var saveParamCompleted;
this.init = function()
{
$HistoryEditDialog = $('#HistoryEditDialog');
$HistoryEdit_ParamData = $('#HistoryEdit_ParamData');
$('#HistoryEdit_Save').click(saveChanges);
$('#HistoryEdit_Delete').click(itemDelete);
$('#HistoryEdit_Return').click(itemReturn);
$('#HistoryEdit_Reprocess').click(itemReprocess);
$('#HistoryEdit_Param').click(tabClick);
$('#HistoryEdit_Back').click(backClick);
$HistoryEditDialog.on('hidden', function ()
{
$HistoryEdit_ParamData.empty();
// resume updates
Refresher.resume();
});
TabDialog.extend($HistoryEditDialog);
}
this.showModal = function(hist)
{
Refresher.pause();
curHist = hist;
var status;
if (hist.Kind === 'URL')
{
status = HistoryUI.buildStatus(hist.status, '');
}
else
{
status = HistoryUI.buildStatus(hist.ParStatus, 'Par: ') + ' ' +
(Options.option('Unpack') == 'yes' || hist.UnpackStatus != 'NONE' ? HistoryUI.buildStatus(hist.UnpackStatus, 'Unpack: ') + ' ' : '') +
(hist.MoveStatus === "FAILURE" ? HistoryUI.buildStatus(hist.MoveStatus, 'Move: ') + ' ' : "");
for (var i=0; i<hist.ScriptStatuses.length; i++)
{
var scriptStatus = hist.ScriptStatuses[i];
status += HistoryUI.buildStatus(scriptStatus.Status, Options.shortScriptName(scriptStatus.Name) + ': ') + ' ';
}
}
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
if (hist.Kind === 'URL')
{
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + '&nbsp;' + '<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');
var postParamConfig = ParamTab.createPostParamConfig();
var postParam = hist.Kind === 'NZB' && postParamConfig[0].options.length > 0;
Util.show('#HistoryEdit_Param', postParam);
Util.show('#HistoryEdit_Save', postParam);
$('#HistoryEdit_Close').toggleClass('btn-primary', !postParam);
if (postParam)
{
postParams = ParamTab.buildPostParamTab($HistoryEdit_ParamData, postParamConfig, curHist.Parameters);
}
enableAllButtons();
$('#HistoryEdit_GeneralTab').show();
$('#HistoryEdit_ParamTab').hide();
$('#HistoryEdit_Back').hide();
$('#HistoryEdit_BackSpace').show();
$HistoryEditDialog.restoreTab();
notification = null;
$HistoryEditDialog.modal({backdrop: 'static'});
}
function tabClick(e)
{
e.preventDefault();
$('#HistoryEdit_Back').fadeIn(500);
$('#HistoryEdit_BackSpace').hide();
var tab = '#' + $(this).attr('data-tab');
lastPage = $(tab);
lastFullscreen = ($(this).attr('data-fullscreen') === 'true') && !UISettings.miniTheme;
$HistoryEditDialog.switchTab($('#HistoryEdit_GeneralTab'), lastPage,
e.shiftKey || !UISettings.slideAnimation ? 0 : 500,
{fullscreen: lastFullscreen, mini: UISettings.miniTheme});
}
function backClick(e)
{
e.preventDefault();
$('#HistoryEdit_Back').fadeOut(500, function()
{
$('#HistoryEdit_BackSpace').show();
});
$HistoryEditDialog.switchTab(lastPage, $('#HistoryEdit_GeneralTab'),
e.shiftKey || !UISettings.slideAnimation ? 0 : 500,
{fullscreen: lastFullscreen, mini: UISettings.miniTheme, back: true});
}
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();
saveParam(function()
{
notification = '#Notif_History_Reproces';
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
});
}
function completed()
{
$HistoryEditDialog.modal('hide');
Refresher.update();
if (notification)
{
Notification.show(notification);
notification = null;
}
}
function saveChanges()
{
disableAllButtons();
notification = null;
saveParam(completed);
}
/*** TAB: POST-PROCESSING PARAMETERS **************************************************/
function saveParam(_saveParamCompleted)
{
saveParamCompleted = _saveParamCompleted;
var paramList = ParamTab.prepareParamRequest(postParams);
saveNextParam(paramList);
}
function saveNextParam(paramList)
{
if (paramList.length > 0)
{
RPC.call('editqueue', ['HistorySetParameter', 0, paramList[0], [curHist.ID]], function()
{
notification = '#Notif_History_Saved';
paramList.shift();
saveNextParam(paramList);
})
}
else
{
saveParamCompleted();
}
}
}(jQuery));

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -192,7 +192,7 @@
pageDots : Util.parseBool(config.pageDots),
curPage : 1,
checkedRows: [],
lastClickedRowID: null
lastClickedRowID: 0
});
}
});
@@ -227,11 +227,6 @@
return $(this).data('fasttable').checkedRows;
},
checkRow : function(id, checked)
{
checkRow($(this).data('fasttable'), id, checked);
},
itemCheckClick : itemCheckClick,
titleCheckClick : titleCheckClick
@@ -629,7 +624,7 @@
var id = row.fasttableID;
var doToggle = true;
if (event.shiftKey && data.lastClickedRowID != null)
if (event.shiftKey && data.lastClickedRowID > 0)
{
var checked = checkedRows.indexOf(id) > -1;
doToggle = !checkRange(data, id, data.lastClickedRowID, !checked);
@@ -661,7 +656,7 @@
}
}
data.lastClickedRowID = null;
data.lastClickedRowID = 0;
checkAll(data, !hasSelectedItems);
}

View File

@@ -25,15 +25,15 @@
/*
* In this module:
* 1) History tab;
* 2) Functions for html generation for history, also used from other modules (edit dialog).
* 2) History edit dialog.
*/
/*** HISTORY TAB AND EDIT HISTORY DIALOG **********************************************/
var History = (new function($)
{
'use strict';
// Controls
var $HistoryTable;
var $HistoryTabBadge;
@@ -53,6 +53,8 @@ var History = (new function($)
$HistoryTabBadge = $('#HistoryTabBadge');
$HistoryTabBadgeEmpty = $('#HistoryTabBadgeEmpty');
$HistoryRecordsPerPage = $('#HistoryRecordsPerPage');
historyEditDialog.init();
var recordsPerPage = UISettings.read('HistoryRecordsPerPage', 10);
$HistoryRecordsPerPage.val(recordsPerPage);
@@ -113,11 +115,7 @@ var History = (new function($)
{
if (hist.ParStatus == 'FAILURE' || hist.UnpackStatus == 'FAILURE' || hist.MoveStatus == 'FAILURE' || hist.ScriptStatus == 'FAILURE')
{
hist.status = 'failure';
}
else if (hist.ParStatus == 'MANUAL')
{
hist.status = 'damaged';
hist.status = 'failure';
}
else
{
@@ -192,7 +190,7 @@ var History = (new function($)
{
var hist = item.hist;
var status = HistoryUI.buildStatus(hist.status, '');
var status = buildStatus(hist.status, '');
var name = '<a href="#" histid="' + hist.ID + '">' + Util.textToHtml(Util.formatNZBName(hist.Name)) + '</a>';
var category = Util.textToHtml(hist.Category);
@@ -234,6 +232,30 @@ var History = (new function($)
}
}
function buildStatus(status, prefix)
{
switch (status)
{
case 'success':
case 'SUCCESS':
return '<span class="label label-status label-success">' + prefix + 'success</span>';
case 'failure':
case 'FAILURE':
return '<span class="label label-status label-important">' + prefix + 'failure</span>';
case 'unknown':
case 'UNKNOWN':
return '<span class="label label-status label-info">' + prefix + 'unknown</span>';
case 'repairable':
case 'REPAIR_POSSIBLE':
return '<span class="label label-status label-success">' + prefix + 'repairable</span>';
case 'none':
case 'NONE':
return '<span class="label label-status">' + prefix + 'none</span>';
default:
return '<span class="label label-status">' + prefix + status + '</span>';
}
}
this.recordsPerPageChange = function()
{
var val = $HistoryRecordsPerPage.val();
@@ -303,66 +325,138 @@ var History = (new function($)
notification = null;
}
}
function editClick()
{
var histid = $(this).attr('histid');
$(this).blur();
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;
}
HistoryEditDialog.showModal(hist);
}
}(jQuery));
/*** FUNCTIONS FOR HTML GENERATION (also used from other modules) *****************************/
var HistoryUI = (new function($)
{
'use strict';
this.buildStatus = function(status, prefix)
{
switch (status)
{
case 'success':
case 'SUCCESS':
return '<span class="label label-status label-success">' + prefix + 'success</span>';
case 'failure':
case 'FAILURE':
return '<span class="label label-status label-important">' + prefix + 'failure</span>';
case 'unknown':
case 'UNKNOWN':
return '<span class="label label-status label-info">' + prefix + 'unknown</span>';
case 'repairable':
case 'REPAIR_POSSIBLE':
return '<span class="label label-status label-success">' + prefix + 'repairable</span>';
case 'manual':
case 'MANUAL':
case 'damaged':
return '<span class="label label-status label-warning">' + prefix + status + '</span>';
case 'none':
case 'NONE':
return '<span class="label label-status">' + prefix + 'none</span>';
default:
return '<span class="label label-status">' + prefix + status + '</span>';
}
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;
if (hist.Kind === 'URL')
{
status = buildStatus(hist.status, '');
}
else
{
status = buildStatus(hist.ParStatus, 'Par: ') + ' ' +
(Options.option('Unpack') == 'yes' || hist.UnpackStatus != 'NONE' ? buildStatus(hist.UnpackStatus, 'Unpack: ') + ' ' : '') +
(hist.MoveStatus === "FAILURE" ? buildStatus(hist.MoveStatus, 'Move: ') + ' ' : "") +
buildStatus(hist.ScriptStatus, 'Script: ');
}
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
if (hist.Kind === 'URL')
{
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + '&nbsp;' + '<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));

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -127,7 +127,7 @@
<li class="menu-header">Automatic refresh</li>
<li data="0.1"><a href="#"><table><tr><td></td><td>0.1</td><td>Second</td></tr></table></a></li>
<li data="0.2"><a href="#"><table><tr><td></td><td>0.2</td><td>Second</td></tr></table></a></li>
<li data="0.5"><a href="#"><table><tr><td></td><td>0.5</td><td>Second</td></tr></table></a></li>
<li data="0.5"><a href="#"><table><tr><td></td></i><td>0.5</td><td>Second</td></tr></table></a></li>
<li data="1"><a href="#"><table><tr><td></td><td>1</td><td>Second</td></tr></table></a></li>
<li data="5"><a href="#"><table><tr><td></td><td>5</td><td>Seconds</td></tr></table></a></li>
<li data="15"><a href="#"><table><tr><td></td><td>15</td><td>Seconds</td></tr></table></a></li>
@@ -181,12 +181,8 @@
<p id="ErrorAlert-text"></p>
</div>
<!-- *** ERROR BLOCK ****************************************************************** -->
<div id="RefreshError" data-duration="1000" style="display:none" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Communication error, retrying...</strong>
</div>
<!-- *** INIT LOADING-ALERT ************************************************************ -->
<div class="alert alert-info alert-block" id="FirstUpdateInfo">
<h4 class="alert-heading">Loading...</h4>
Please wait.
@@ -404,16 +400,17 @@
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadServerTemplateError">
<h4 class="alert-heading">Loading configuration failed</h4>
Could not load template configuration file <em>nzbget.conf</em>. The file contains descriptions of options
and is needed to display settings page. The file comes with NZBGet distribution archive and is usually
automaically installed into the right location. This seems not to be a case with your installation though.<br><br>
<span id="ConfigLoadServerTemplateErrorEmpty">Please put the template configuration file <em>nzbget.conf</em> into the
directory with web-interface files (<em><span id="ConfigLoadServerTemplateErrorWebDir"></span></em>).</span>
<span id="ConfigLoadServerTemplateErrorNotFound">Please edit your configuration file
(<em><span id="ConfigLoadServerTemplateErrorConfigFile"></span></em>) in a text editor
and set the option <em>ConfigTemplate</em> to point to the template configuration file <em>nzbget.conf</em>.</span>
Could not load template configuration file <em>nzbget.conf</em>. NZBGet reads the description of options from this file.
Please put the file <em>nzbget.conf</em> which comes with the distribution archive of NZBGet into webui-directory
(option <em>WebDir</em>).
Do not put your actual configuration file but rather the unchanged original file.
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadPostTemplateError">
<h4 class="alert-heading">Loading configuration of post-processing script failed</h4>
Could not load template post-processing configuration file <em class="ConfigLoadPostTemplateErrorFilename"></em>. NZBGet reads the description of options from this file.
Please put the file <em class="ConfigLoadPostTemplateErrorFilename"></em> which comes with the post-processing script into webui-directory
(option <em>WebDir</em>).
Do not put your actual configuration file but rather the unchanged original file.
</div>
<div class="alert alert-error alert-block hide" id="ConfigLoadError">
<h4 class="alert-heading">Loading configuration failed</h4>
@@ -450,14 +447,22 @@
<div id="ConfigInfo">
<p>
On this page you can review and change settings. When you done with changes click
<em><strong>Save all changes</strong></em>, which saves the changes made in all sections.
It's not neccessary to save changes in each section individually.
<em><strong>Save all changes</strong></em>, which will save the changes made on all pages.
It's not neccessary to save the changes on each page individually.
</p>
<p>The configuration consists of two parts:
<ul>
<li>NZBGet settings</li>
<li>Post-processing script settings</li>
</ul>
</p>
<h4>NZBGet settings</h4>
<p>
When you configure NZBGet for the first time you need
With these settings you configure NZBGet. When you configure NZBGet for the first time you need
to check at least the option <a class="option" href="#" data-category="S" onclick="Config.scrollToOption(event, this)">MainDir</a> and configure one news server.
Any changes of NZBGet settings require a restart or reload (soft-restart) of NZBGet.
</p>
<p>
There are many configuration options affecting performance. If you use
@@ -466,18 +471,20 @@
performance - see <a href="http://nzbget.sourceforge.net/Performance_tips">Performance tips</a>.
</p>
<h4>Post-processing scripts settings</h4>
<h4>Post-processing script settings</h4>
<p>
When NZBGet finishes download of a nzb-file it can execute post-processing scripts for further processing (cleanup, etc.).
To configure scripts use options in section <em><strong>POST-PROCESSING SCRIPTS</strong></em>.
When NZBGet finishes download
of nzb-file it can start a post-processing script for further processing (cleanup, etc.).
You can configure what script must be executed using option <a class="option" href="#" data-category="S" onclick="Config.scrollToOption(event, this)">PostProcess</a>
in section <em><strong>POST-PROCESSING</strong></em>.
</p>
<p>
If your post-processing scripts define own options they are also shown here and can be configured like NZBGets built-in options.
</p>
<h4>Backup and restore settings</h4>
<p>
This can be done in section <em><strong>SYSTEM</strong></em>.
The post-processing script settings are shown on the navigation bar
only if you have setup a post-processing script and if it has a configuration file. For your convenience
NZBGet proivides you with a way to configure the post-processing script options via web-interface.
These settings however do not
have any effect on NZBGet but rather on the post-processing script only. The changes made here are in
effect after saving for the next post-processing job started by NZBGet. A restart of NZBGet is not required.
</p>
</div>
@@ -497,16 +504,14 @@
</div>
</div>
<!--
<div class="control-group config-static config-system">
<label class="control-label nowrap">Backup Settings</label>
<div class="controls">
<button type="button" class="btn" onclick="ConfigBackupRestore.backupSettings()">Backup</button>
<button type="button" class="btn" onclick="TODO()">Backup</button>
<p class="help-block">
<span class="help-option-title">Save settings to a local file.</span><br><br>
<span class="help-option-title">Save settings to local file.</span><br><br>
Export all settings for backup purpose.
<span id="ConfigBackupSafariNote"><br><br>
<span class="label label-warning">NOTE:</span>
This works with all major browsers except Safari (tested with 6.0.4; may work with newer versions).</span>
</p>
</div>
</div>
@@ -514,14 +519,14 @@
<div class="control-group config-static config-system">
<label class="control-label nowrap">Restore Settings</label>
<div class="controls">
<button type="button" class="btn" onclick="ConfigBackupRestore.restoreSettings()">Restore</button>
<input type="file" id="Config_RestoreInput" class="hidden-file-input">
<button type="button" class="btn" onclick="TODO()">Restore</button>
<p class="help-block">
<span class="help-option-title">Load settings from a local file.</span><br><br>
Import settings from a previously created backup file. You can restore the whole configuration file or choose individual sections.
<span class="help-option-title">Load settings from local file.</span><br><br>
Import all settings from a perviously created backup file.
</p>
</div>
</div>
-->
</fieldset>
</div>
@@ -568,7 +573,7 @@
<h2>Contact</h2>
<p>For more info please visit <a href="http://nzbget.sourceforge.net">NZBGet Home Page</a>. Among other things the developers of third-party apps find there complete docs about RPC interface.</p>
<p>Should you need help, have suggestions or want to share your improvements - <a href="http://nzbget.sourceforge.net/forum">NZBGet Forum</a> is a place to do that.</p>
<p>Should you need help, have suggestions or want to share your improvements - <a href="http://sourceforge.net/apps/phpbb/nzbget/">NZBGet Forum</a> is a place to do that.</p>
<p>If you use NZBGet on a media player, router, NAS or other non-PC device the best place to get help is probably forums specialized on your device.</p>
<h1>Information about included libraries</h1>
@@ -760,12 +765,10 @@
<button class="btn" onclick="DownloadsEditDialog.editActionClick('pause')" title="Pause selected files"><i class="icon-pause"></i><span class="btn-caption"> Pause</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('resume')" title="Resume selected files"><i class="icon-play"></i><span class="btn-caption"> Resume</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('delete')" title="Delete selected files"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button>
<button class="btn" onclick="DownloadsEditDialog.editActionClick('split')" title="Split selected files into new download"><i class="icon-split"></i><span class="btn-caption"> Split</span></button>
</div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('pause')" title="Pause selected files"><i class="icon-pause"></i><span class="btn-caption"> Pause</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('resume')" title="Resume selected files"><i class="icon-play"></i><span class="btn-caption"> Resume</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('delete')" title="Delete selected files"><i class="icon-trash"></i><span class="btn-caption"> Delete</span></button></div>
<div class="btn-group phone-only inline"><button class="btn" onclick="DownloadsEditDialog.editActionClick('split')" title="Split selected files into new download"><i class="icon-split"></i><span class="btn-caption"> Split</span></button></div>
<!-- MOVE BUTTONS -->
<div class="btn-group phone-hide">
@@ -963,40 +966,6 @@
</div>
</div>
<!-- *** SPLIT DOWNLOAD DIALOG ************************************************************ -->
<div class="modal modal-padded hide" id="DownloadsSplitDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Split Download</h3>
</div>
<div class="modal-body">
<div class="form-vertical">
<fieldset>
<p>New download will be created from selected files. Please choose the name for new download.</p>
<div class="control-group">
<div class="controls" id="DownloadsSplit_NZBNameGroup">
<input type="text" class="input-xxlarge" id="DownloadsSplit_NZBName" />
</div>
</div>
<p class="help-block" style="margin-top:10px;">The new download inherits
all other properties such as category, priority, etc. from the source item.
This action closes the download-edit-dialog. Any changes made in the dialog are not saved.</p>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div class="dialog-transmit hide" style="position:absolute;margin-left:260px;" id="DownloadsSplit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="DownloadsSplit_Split">Split</a>
</div>
</div>
<!-- *** QUICK HELP: Configure Categores ************************************************** -->
<div class="modal modal-mini modal-center hide" id="ConfigureCategoriesHelp">
@@ -1014,76 +983,51 @@
<div class="modal hide" id="HistoryEditDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<a class="back" id="HistoryEdit_Back" href="#" title="Back"><i class="icon-back"></i></a>
<a class="back back-hidden" id="HistoryEdit_BackSpace"><i class="icon-hidden"></i></a>
<h3 id="HistoryEdit_Title"></h3>
</div>
<div class="modal-body">
<div>
<div class="modal-tab" id="HistoryEdit_GeneralTab">
<div class="form-horizontal">
<fieldset>
<div class="form-horizontal">
<fieldset>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Status">Status</label>
<div class="controls" style="margin-top: 4px;">
<span id="HistoryEdit_Status"></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Category">Category</label>
<div class="controls">
<span class="input-xlarge uneditable-input" id="HistoryEdit_Category"></span>
</div>
</div>
<div class="control-group" id="HistoryEdit_PathGroup">
<label class="control-label" for="HistoryEdit_Path">Download Path</label>
<div class="controls">
<span class="uneditable-mulitline-input" id="HistoryEdit_Path"></span>
</div>
</div>
<div class="control-group control-group-last" id="HistoryEdit_StatisticsGroup">
<label class="control-label" for="HistoryEdit_Statistics">Statistics</label>
<div class="controls">
<table class="table table-striped table-bordered data-statistics">
<tbody id="HistoryEdit_Statistics">
</tbody>
</table>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="modal-bottom-toolbar">
<button class="btn" data-tab="HistoryEdit_ParamTab" id="HistoryEdit_Param" title="Post-processing parameters">PP-Parameters <i class="icon-forward" style="opacity:0.6;"></i></button>
</div>
</div>
</div>
</fieldset>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Status">Status</label>
<div class="controls" style="margin-top: 4px;">
<span id="HistoryEdit_Status"></span>
</div>
</div>
</div>
<div class="modal-tab hide" id="HistoryEdit_ParamTab">
<div class="form-horizontal">
<fieldset id="HistoryEdit_ParamData">
</fieldset>
<div class="control-group">
<label class="control-label" for="HistoryEdit_Category">Category</label>
<div class="controls">
<span class="input-xlarge uneditable-input" id="HistoryEdit_Category"></span>
</div>
</div>
</div>
<div class="control-group" id="HistoryEdit_PathGroup">
<label class="control-label" for="HistoryEdit_Path">Download Path</label>
<div class="controls">
<span class="uneditable-mulitline-input" id="HistoryEdit_Path"></span>
</div>
</div>
<div class="control-group" style="margin-top: 15px; margin-bottom:0;" id="HistoryEdit_StatisticsGroup">
<label class="control-label" for="HistoryEdit_Statistics">Statistics</label>
<div class="controls">
<table class="table table-striped table-bordered data-statistics">
<tbody id="HistoryEdit_Statistics">
</tbody>
</table>
</div>
</div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<div class="btn-group dropup pull-left" id="HistoryEdit_DeleteGroup">
<a class="btn dropdown-toggle btn-danger" data-toggle="dropdown"><i class="icon-trash-white"></i> Delete <span class="caret"></span></a>
<ul class="dropdown-menu confirm-menu">
<li class="menu-header">Confirm</li>
<li><a href="#" id="HistoryEdit_Delete">Yes, Delete</a></li>
</ul>
<div class="btn-group pull-left" id="HistoryEdit_DeleteGroup">
<a href="#" class="btn pull-left" id="HistoryEdit_Delete"><i class="icon-trash"></i> Delete</a>
</div>
<div class="btn-group dropup pull-left" id="HistoryEdit_ReprocessGroup">
@@ -1095,16 +1039,15 @@
</div>
<div class="btn-group dropup pull-left" id="HistoryEdit_ReturnGroup">
<a class="btn dropdown-toggle" data-toggle="dropdown">Return <span class="caret"></span></a>
<a class="btn dropdown-toggle" data-toggle="dropdown">Return to Queue <span class="caret"></span></a>
<ul class="dropdown-menu confirm-menu">
<li class="menu-header">Confirm</li>
<li><a href="#" id="HistoryEdit_Return">Yes, Return to Queue</a></li>
<li><a href="#" id="HistoryEdit_Return">Yes, Return</a></li>
</ul>
</div>
<div class="dialog-transmit" style="position:absolute;margin-left:320px;" id="HistoryEdit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn" data-dismiss="modal" id="HistoryEdit_Close">Close</a>
<a href="#" class="btn btn-primary" id="HistoryEdit_Save">Save changes</a>
<div class="dialog-transmit" style="position:absolute;margin-left:400px;" id="HistoryEdit_Transmit"><img src="img/transmit.gif"></div>
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
@@ -1134,13 +1077,8 @@
<p>
Delete selected downloads?
</p>
<p id="DownloadsDeleteConfirmDialog_Remain" class="confirm-help-block">
Selected items will be removed from queue. Already downloaded files remain on disk (this behavior can be changed via option
<strong><em>DeleteCleanupDisk</em></strong>).
</p>
<p id="DownloadsDeleteConfirmDialog_Cleanup" class="confirm-help-block">
Selected items will be removed from queue. Already downloaded files will be deleted from disk (this behavior can be changed via option
<strong><em>DeleteCleanupDisk</em></strong>).
<p class="confirm-help-block">
Selected items will be removed from the queue. Already downloaded files remain on disk.
</p>
</div>
<div id="DownloadsDeleteConfirmDialog_OK">Delete</div>
@@ -1227,7 +1165,7 @@
<label class="control-label" for="URL">Add local files</label>
<div class="controls">
<div style="margin-top:6px; margin-bottom:10px;"><a href="#" class="btn" style="margin-top: -6px;" id="AddDialog_Select">Select files</a> or drop files here.</div>
<input multiple="multiple" type="file" id="AddDialog_Input" class="hidden-file-input">
<input multiple="multiple" type="file" id="AddDialog_Input">
<p class="help-block" id="AddDialog_FilesHelp">You need a modern browser for this to work.</p>
<div class="dialog-add-files hide" id="AddDialog_Files"></div>
</div>
@@ -1303,91 +1241,6 @@
</div>
</div>
<!-- *** CHOOSE SCRIPT DIALOG ****************************************************** -->
<div class="modal modal-padded hide" id="ScriptListDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3 id="ScriptListDialog_Title">Choose scripts</h3>
</div>
<div class="modal-body" style="padding-bottom:0px">
<div class="form-vertical">
<fieldset>
<p id="ScriptListDialog_Instruction">Select scripts for option <strong>DefScript</strong></p>
<div>
<table class="table table-striped table-bordered table-check table-cancheck order-mode datatable" id="ScriptListDialog_ScriptTable" style="margin-bottom:15px;">
<thead><tr><th><div class="check img-check"/></th><th>Name</th></tr></thead>
<tbody></tbody>
</table>
</div>
<p id="ScriptListDialog_OrderInfo" class="hide" style="margin-top:-5px; margin-bottom:10px;">The script execution order is saved globally in the option <em><strong>ScriptOrder</strong></em> and affects all
categories as well as the order of scripts in the edit download dialog.</p>
<div class="hide" id="ScriptListDialog_ScriptTable_pager"></div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="ScriptListDialog_Save">Save</a>
</div>
</div>
<!-- *** RESTORE SETTINGS DIALOG ****************************************************** -->
<div class="modal modal-padded hide" id="RestoreSettingsDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Restore Settings</h3>
</div>
<div class="modal-body" style="padding-bottom:0px">
<div class="form-vertical">
<fieldset>
<p id="RestoreSettingsDialog_Instruction">Select sections to restore. Tip: click on the checkbox of the table header to select all sections.</p>
<div>
<table class="table table-striped table-bordered table-check table-cancheck datatable" id="RestoreSettingsDialog_SectionTable" style="margin-bottom:15px;">
<thead><tr><th><div class="check img-check"/></th><th>Section</th></tr></thead>
<tbody></tbody>
</table>
</div>
<div class="hide" id="RestoreSettingsDialog_SectionTable_pager"></div>
</fieldset>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<a href="#" class="btn btn-primary" id="RestoreSettingsDialog_Restore">Restore</a>
</div>
</div>
<!-- *** SETTINGS RESTORED DIALOG ************************************************** -->
<div class="modal modal-mini modal-center hide" id="SettingsRestoredDialog">
<div class="modal-header">
<a class="close" href='#' data-dismiss="modal"><i class="icon-close"></i></a>
<h3>Settings restored</h3>
</div>
<div class="modal-body">
<p>Settings have been loaded from backup file.</p>
<p>You can review the loaded settings and then save them by clicking on <em><strong>Save all changes</strong></em>.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
</div>
</div>
<!-- *** QUICK HELP: New Configuration Option ************************************************** -->
<div class="modal modal-mini modal-center hide" id="ConfigNewOptionHelp">
@@ -1487,18 +1340,6 @@
<strong>Merged</strong>
</div>
<div id="Notif_Downloads_Splitted" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Splitted</strong>
</div>
<div id="Notif_Downloads_SplitError" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Could not split. Check messages for errors.</strong>
</div>
<div id="Notif_Downloads_SplitNotPossible" data-duration="4000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Cannot split. Some of selected files are already (partially) downloaded.</strong>
</div>
<div id="Notif_Downloads_Select" data-duration="2000" class="alert alert-error alert-center alert-center-small hide">
<strong>Please select records first</strong>
</div>
@@ -1535,10 +1376,6 @@
<strong>Post-Processing</strong>
</div>
<div id="Notif_History_Saved" data-duration="1000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Saved</strong>
</div>
<div id="Notif_Config_Unchanged" data-duration="2000" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Nothing to save</strong><br>No changes have been made
</div>
@@ -1552,14 +1389,6 @@
Please wait few seconds, then refresh the page.
</div>
<div id="Notif_Config_RestoreSections" data-duration="2000" class="alert alert-error alert-center alert-center-medium hide">
<strong>Please select at least one section</strong>
</div>
<div id="Notif_Config_Restoring" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Restoring settings...</strong>
</div>
<div id="Notif_Debug" data-duration="400" class="alert alert-inverse alert-center alert-center-small hide">
<strong>Debug</strong>
</div>

View File

@@ -26,10 +26,11 @@
* In this module:
* 1) Web-interface intialization;
* 2) Web-interface settings;
* 3) Refresh handling;
* 4) Window resize handling including automatic theme switching (desktop/phone);
* 5) Confirmation dialog;
* 6) Popup notifications.
* 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!) ***********************************/
@@ -64,10 +65,6 @@ var UISettings = (new function($)
// The choosen interval is saved in web-browser and then restored.
// The default value sets the interval on first use only.
this.refreshInterval = 1;
// Number of refresh attempts if a communication error occurs.
// If all attempts fail, an error is displayed and the automatic refresh stops.
this.refreshRetries = 4;
// URL for communication with NZBGet via JSON-RPC
this.rpcUrl = './jsonrpc';
@@ -143,7 +140,7 @@ var Frontend = (new function($)
$('#FirstUpdateInfo').show();
UISettings.load();
Refresher.init();
RPCController.init();
initControls();
switchTheme();
@@ -152,26 +149,26 @@ var Frontend = (new function($)
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 });
ConfigBackupRestore.init();
ConfirmDialog.init();
ScriptListDialog.init();
RestoreSettingsDialog.init();
DownloadsEditDialog.init();
DownloadsMultiDialog.init();
DownloadsMergeDialog.init();
DownloadsSplitDialog.init();
HistoryEditDialog.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()
@@ -240,6 +237,8 @@ var Frontend = (new function($)
windowResized();
firstLoad = false;
}
Refresher.refreshCompleted();
}
function beforeTabShow(e)
@@ -460,23 +459,15 @@ var Frontend = (new function($)
}(jQuery));
/*** REFRESH CONTROL *********************************************************/
/*** RPC CONTROL *********************************************************/
var Refresher = (new function($)
var RPCController = (new function($)
{
'use strict';
// State
var loadQueue;
var firstLoad = true;
var secondsToUpdate = -1;
var refreshTimer = 0;
var indicatorTimer = 0;
var indicatorFrame=0;
var refreshPaused = 0;
var refreshing = false;
var refreshNeeded = false;
var refreshErrors = 0;
this.init = function()
{
@@ -484,17 +475,13 @@ var Refresher = (new function($)
RPC.connectErrorMessage = 'Cannot establish connection to NZBGet.'
RPC.defaultFailureCallback = rpcFailure;
RPC.next = loadNext;
$('#RefreshMenu li a').click(refreshIntervalClick);
$('#RefreshButton').click(refreshClick);
updateRefreshMenu();
}
function refresh()
this.refresh = function()
{
UISettings.connectionError = false;
$('#ErrorAlert').hide();
refreshStarted();
Refresher.refreshStarted();
loadQueue = new Array(
function() { Options.update(); },
@@ -524,44 +511,49 @@ var Refresher = (new function($)
{
firstLoad = false;
Frontend.loadCompleted();
refreshCompleted();
}
}
function rpcFailure(res, result)
function rpcFailure(res)
{
// If a communication error occurs during status refresh we retry:
// first attempt is made immediately, other attempts are made after defined refresh interval
if (refreshing && !(result && result.error))
{
refreshErrors = refreshErrors + 1;
if (refreshErrors === 1 && refreshErrors <= UISettings.refreshRetries)
{
refresh();
return;
}
else if (refreshErrors <= UISettings.refreshRetries)
{
$('#RefreshError').show();
scheduleNextRefresh();
return;
}
}
Refresher.pause();
UISettings.connectionError = true;
$('#FirstUpdateInfo').hide();
$('#ErrorAlert-text').html(res);
$('#ErrorAlert').show();
$('#RefreshError').hide();
if (Status.status)
{
// stop animations
Status.redraw();
}
};
}(jQuery));
function refreshStarted()
/*** 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;
@@ -570,11 +562,9 @@ var Refresher = (new function($)
refreshAnimationShow();
}
function refreshCompleted()
this.refreshCompleted = function()
{
refreshing = false;
refreshErrors = 0;
$('#RefreshError').hide();
scheduleNextRefresh();
}
@@ -611,8 +601,7 @@ var Refresher = (new function($)
// force animation restart
indicatorFrame = 0;
}
refreshErrors = 0;
refresh();
refreshCallback();
}
function scheduleNextRefresh()
@@ -636,7 +625,7 @@ var Refresher = (new function($)
secondsToUpdate -= 0.1;
if (secondsToUpdate <= 0)
{
refresh();
refreshCallback();
}
else
{
@@ -674,7 +663,7 @@ var Refresher = (new function($)
'transform': 'rotate(' + degree + 'deg)'
});
if ((!refreshing && indicatorFrame === 0 && (UISettings.refreshInterval === 0 || UISettings.refreshInterval > 1 || !UISettings.refreshAnimation)) || UISettings.connectionError)
if (!refreshing && indicatorFrame === 0 && (UISettings.refreshInterval === 0 || UISettings.refreshInterval > 1 || !UISettings.refreshAnimation) || UISettings.connectionError)
{
indicatorTimer = 0;
}

View File

@@ -1,7 +1,7 @@
/*!
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -554,10 +554,6 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
background-position: -400px -80px;
}
.icon-split {
background-position: -432px -80px;
}
.img-checkmark {
background-position: -432px -16px;
}
@@ -590,10 +586,6 @@ body.navfixed.scrolled .navbar-fixed-top .navbar-inner {
text-transform: uppercase;
}
.controls .label-status {
line-height: 22px;
}
/* links in black color */
.datatable a {
color: #000000;
@@ -674,8 +666,7 @@ form {
margin-bottom: 0px;
}
#DownloadsEdit_PostParamData,
#HistoryEdit_PostParamData {
#DownloadsEdit_PostParamData {
padding-bottom: 1px;
}
@@ -792,11 +783,6 @@ tr.checked td {
background-color: #FFFFCC;
}
table.table-hidecheck thead > tr > th:first-child,
table.table-hidecheck tbody > tr > td:first-child {
display: none;
}
.checked .progress {
background-color: #FFFFE1;
}
@@ -906,11 +892,7 @@ table.table-hidecheck tbody > tr > td:first-child {
}
.modal .form-horizontal .control-group:last-child {
margin-bottom: 0;
}
.modal .form-horizontal .retain-margin .control-group:last-child {
margin-bottom: 15px;
margin-bottom: 0;
}
.form-horizontal .control-group-last {
@@ -929,11 +911,6 @@ table.table-hidecheck tbody > tr > td:first-child {
.modal .input-xlarge {
width: 350px;
}
.modal.modal-padded .input-xxlarge {
width: 470px;
}
/* END: override bootstrap styles for modals */
.modal-bottom-toolbar .btn {
@@ -986,7 +963,7 @@ ul.help > li {
}
/* Make "select files" native control invisible */
.hidden-file-input {
#AddDialog_Input {
position: absolute;
left: 0;
top: 0;
@@ -1114,11 +1091,6 @@ ul.help > li {
font-size: 12px;
}
#ConfigNav.nav-list.long-list a {
padding-top: 3px;
padding-bottom: 3px;
}
#ConfigNav.nav-list > .active > a,
#ConfigNav.nav-list > .active > a:hover {
color: #ffffff;
@@ -1156,7 +1128,7 @@ ul.help > li {
margin-right: 15px;
}
span.help-option-title {
#ConfigContent span.help-option-title {
color: #8D1212;
}
@@ -1185,19 +1157,6 @@ span.help-option-title {
width: 150px;
}
#ConfigContent table.editor {
width: 97%;
}
#ConfigContent table.editor td:first-child {
width: 100%;
padding-right:15px;
}
#ConfigContent table.editor input {
width: 100%;
}
.ConfigFooter hr {
margin: 6px 0 15px;
}
@@ -1228,8 +1187,6 @@ div.ConfigFooter {
font-weight: normal;
}
#DownloadsEdit_ParamTab div.control-group.wants-divider,
#HistoryEdit_ParamTab div.control-group.wants-divider,
#ConfigContent div.control-group,
#ConfigContent.search div.control-group.multiset {
border-bottom: 1px solid #eeeeee;
@@ -1250,6 +1207,7 @@ div.ConfigFooter {
#ConfigContent .control-label {
width: 170px;
white-space: nowrap;
}
#ConfigContent .form-horizontal .controls {
@@ -1293,37 +1251,11 @@ div.ConfigFooter {
color: #005580;
}
#ScriptListDialog_ScriptTable td:nth-child(2) {
padding-right: 100px;
/* table "LIST OF ALL OPTIONS" */
#ConfigContent .datatable td:first-child {
font-weight: bold;
}
#ScriptListDialog_ScriptTable .btn-row-order-block {
float: right;
width: 100px;
margin-right: -115px;
display: block;
}
#ScriptListDialog_ScriptTable .btn-row-order {
float: none;
width: 20px;
display: none;
}
#ScriptListDialog_ScriptTable tr:hover .btn-row-order {
display: inline-block;
cursor: pointer;
}
#ScriptListDialog_ScriptTable tbody > tr:first-child div.btn-row-order:first-child,
#ScriptListDialog_ScriptTable tbody > tr:last-child div.btn-row-order:last-child,
#ScriptListDialog_ScriptTable tbody > tr:first-child div.btn-row-order:nth-child(2),
#ScriptListDialog_ScriptTable tbody > tr:last-child div.btn-row-order:nth-child(3) {
opacity: 0.4;
}
/*#ScriptListDialog_ScriptTable*/
/****************************************************************************/
/* SMARTPHONE THEME */
@@ -1404,10 +1336,6 @@ body.phone,
height: inherit;
}
.phone .controls .label-status {
line-height: 28px;
}
.phone .menu-header {
font-size: 18px;
line-height: 24px;
@@ -1789,8 +1717,7 @@ body.phone,
text-align: center;
}
.phone .modal-mini .modal-body,
.phone .modal-padded .modal-body {
.phone .modal-mini .modal-body {
padding-left: 15px;
padding-right: 15px;
}
@@ -1878,8 +1805,6 @@ body.phone,
}
.modal .input-xlarge ,
.modal .input-xxlarge,
.modal.modal-padded .input-xxlarge,
.uneditable-mulitline-input {
width: 95%;
}

View File

@@ -195,9 +195,8 @@ var Upload = (new function($)
for (var i = 0; i<selectedFiles.length; i++)
{
var file = selectedFiles[i];
var filename = file.name.replace(/\.queued$/g, '');
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(filename) + '</td></tr></table>';
Util.formatNZBName(file.name) + '</td></tr></table>';
$('#AddDialog_Files').append(html);
files.push(file);
}
@@ -327,8 +326,7 @@ var Upload = (new function($)
}
var category = $('#AddDialog_Category').val();
var priority = parseInt($('#AddDialog_Priority').val());
var filename = file.name.replace(/\.queued$/g, '');
RPC.call('append', [filename, category, priority, false, base64str], fileCompleted, fileFailure);
RPC.call('append', [file.name, category, priority, false, base64str], fileCompleted, fileFailure);
};
if (reader.readAsBinaryString)

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* 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
@@ -261,7 +261,7 @@ var TabDialog = (new function($)
var dialog = this;
var sign = options.back ? -1 : 1;
var fullscreen = options.fullscreen && !options.back;
var bodyPadding = 15;
var bodyPadding = 30;
var dialogMargin = options.mini ? 0 : 15;
var dialogBorder = 2;
@@ -292,8 +292,8 @@ var TabDialog = (new function($)
// 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*2 : body.height();
var newTabWidth = fullscreen ? windowWidth - dialogMargin*2 - dialogBorder - bodyPadding*2 : toTab.width();
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();
@@ -313,9 +313,9 @@ var TabDialog = (new function($)
body.css({position: '', height: oldBodyHeight});
dialog.css('overflow', 'hidden');
fromTab.css({position: 'absolute', left: leftPos, width: oldTabWidth, height: oldBodyHeight});
toTab.css({position: 'absolute', width: newTabWidth, height: oldBodyHeight,
left: sign * ((options.back ? newTabWidth : oldTabWidth) + bodyPadding*2)});
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
@@ -356,9 +356,8 @@ var TabDialog = (new function($)
body.animate({height: newBodyHeight}, duration);
}
fromTab.animate({left: sign * -((options.back ? newTabWidth : oldTabWidth) + bodyPadding*2),
height: newBodyHeight + bodyPadding}, duration);
toTab.animate({left: leftPos, height: newBodyHeight + bodyPadding}, duration, function()
fromTab.animate({left: sign * -((options.back ? newTabWidth : oldTabWidth) + bodyPadding)}, duration);
toTab.animate({left: leftPos}, duration, function()
{
fromTab.hide();
fromTab.css({position: '', width: '', height: '', left: ''});
@@ -405,7 +404,7 @@ var RPC = (new function($)
var _this = this;
var request = JSON.stringify({nocache: new Date().getTime(), method: method, params: params});
var xhr = new XMLHttpRequest();
var xhr = createXMLHttpRequest();
xhr.open('post', this.rpcUrl);
@@ -472,4 +471,40 @@ var RPC = (new function($)
};
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));

View File

@@ -74,7 +74,7 @@
/* Define to 1 if spinlocks are supported */
#define HAVE_SPINLOCK
#define VERSION "11.0-testing"
#define VERSION "10.2"
/* Suppress warnings */
#define _CRT_SECURE_NO_DEPRECATE