mirror of
https://github.com/nzbget/nzbget.git
synced 2025-12-24 06:37:44 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08b1b79ac2 |
25
.gitattributes
vendored
25
.gitattributes
vendored
@@ -1,25 +0,0 @@
|
||||
* text=auto
|
||||
|
||||
# Use CRLF for certain Windows files.
|
||||
*.vcproj eol=crlf
|
||||
*.sln eol=crlf
|
||||
*.bat eol=crlf
|
||||
README-WINDOWS.txt eol=crlf
|
||||
nzbget-setup.nsi eol=crlf
|
||||
windows/package-info.json eol=crlf
|
||||
windows/resources/resource.h eol=crlf
|
||||
windows/resources/nzbget.rc eol=crlf
|
||||
|
||||
# Configure GitHub's language detector
|
||||
lib/* linguist-vendored linguist-language=C++
|
||||
webui/lib/* linguist-vendored
|
||||
Makefile.in linguist-vendored
|
||||
configure linguist-vendored
|
||||
config.sub linguist-vendored
|
||||
aclocal.m4 linguist-vendored
|
||||
config.guess linguist-vendored
|
||||
depcomp linguist-vendored
|
||||
install-sh linguist-vendored
|
||||
missing linguist-vendored
|
||||
configure.ac linguist-vendored=false
|
||||
Makefile.am linguist-vendored=false
|
||||
74
.gitignore
vendored
74
.gitignore
vendored
@@ -1,74 +0,0 @@
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
|
||||
# GNU Autotools
|
||||
.deps/
|
||||
config.h
|
||||
config.h.in~
|
||||
config.log
|
||||
config.status
|
||||
Makefile
|
||||
stamp-h1
|
||||
autom4te.cache/
|
||||
.dirstamp
|
||||
*.o-*
|
||||
|
||||
# Visual Studio User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
.vs/
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.ilk
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
*.sln
|
||||
.vscode/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# NZBGet specific
|
||||
nzbget
|
||||
code_revision.cpp
|
||||
*.temp
|
||||
*.pyc
|
||||
pytest.ini
|
||||
.cache
|
||||
17
.lgtm.yml
17
.lgtm.yml
@@ -1,17 +0,0 @@
|
||||
# Configuration file for integration with http://lgtm.com
|
||||
|
||||
path_classifiers:
|
||||
library:
|
||||
# exclude these directories from default alerts report:
|
||||
- lib
|
||||
- webui/lib
|
||||
|
||||
extraction:
|
||||
cpp:
|
||||
configure:
|
||||
command:
|
||||
# compile with tests to activate scanning of C++ sources for tests
|
||||
- ./configure --enable-tests
|
||||
|
||||
queries:
|
||||
- exclude: js/incomplete-sanitization # this one gives false positives only and nothing useful
|
||||
67
.travis.yml
67
.travis.yml
@@ -1,67 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: cpp
|
||||
|
||||
matrix:
|
||||
include:
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-5
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-5
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.9
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-4.9
|
||||
|
||||
- compiler: gcc
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=g++-4.8
|
||||
- CXXFLAGS="-std=c++11 -O2 -s"
|
||||
- CONFIGUREOPTS="--disable-cpp-check"
|
||||
|
||||
- compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- llvm-toolchain-precise-3.6
|
||||
packages:
|
||||
- clang-3.6
|
||||
- unrar
|
||||
- p7zip-full
|
||||
- par2
|
||||
env:
|
||||
- COMPILER=clang++-3.6
|
||||
|
||||
install:
|
||||
- sudo pip install -U pytest
|
||||
|
||||
script:
|
||||
- $COMPILER --version
|
||||
- CXX=$COMPILER ./configure $CONFIGUREOPTS --enable-tests && make
|
||||
- ./nzbget --tests
|
||||
- cd tests/functional && pytest -v
|
||||
4
AUTHORS
Normal file
4
AUTHORS
Normal file
@@ -0,0 +1,4 @@
|
||||
nzbget:
|
||||
Sven Henkel <sidddy@users.sourceforge.net> (versions 0.1.0 - ?)
|
||||
Bo Cordes Petersen <placebodk@users.sourceforge.net> (versions ? - 0.2.3)
|
||||
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 - 0.3.*)
|
||||
977
ArticleDownloader.cpp
Normal file
977
ArticleDownloader.cpp
Normal file
@@ -0,0 +1,977 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern DownloadSpeedMeter* g_pDownloadSpeedMeter;
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
ArticleDownloader::ArticleDownloader()
|
||||
{
|
||||
debug("Creating ArticleDownloader");
|
||||
|
||||
m_szResultFilename = NULL;
|
||||
m_szTempFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szOutputFilename = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_eStatus = adUndefined;
|
||||
m_bDuplicate = false;
|
||||
m_eFormat = Decoder::efUnknown;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
ArticleDownloader::~ArticleDownloader()
|
||||
{
|
||||
debug("Destroying ArticleDownloader");
|
||||
|
||||
if (m_szTempFilename)
|
||||
{
|
||||
free(m_szTempFilename);
|
||||
}
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetTempFilename(const char* v)
|
||||
{
|
||||
m_szTempFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetOutputFilename(const char* v)
|
||||
{
|
||||
m_szOutputFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetInfoName(const char * v)
|
||||
{
|
||||
m_szInfoName = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ArticleDownloader::Run()
|
||||
{
|
||||
debug("Entering ArticleDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
m_szResultFilename = m_pArticleInfo->GetResultFilename();
|
||||
|
||||
if (g_pOptions->GetContinuePartial())
|
||||
{
|
||||
if (Util::FileExists(m_szResultFilename))
|
||||
{
|
||||
// file exists from previous program's start
|
||||
detail("Article %s already downloaded, skipping", m_szInfoName);
|
||||
SetStatus(adFinished);
|
||||
FreeConnection(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
detail("Downloading %s", m_szInfoName);
|
||||
|
||||
int retry = g_pOptions->GetRetries();
|
||||
|
||||
EStatus Status = adFailed;
|
||||
int iMaxLevel = g_pServerPool->GetMaxLevel();
|
||||
int* LevelStatus = (int*)malloc((iMaxLevel + 1) * sizeof(int));
|
||||
for (int i = 0; i <= iMaxLevel; i++)
|
||||
{
|
||||
LevelStatus[i] = 0;
|
||||
}
|
||||
int level = 0;
|
||||
|
||||
while (!IsStopped() && (retry > 0))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = adFailed;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = g_pServerPool->GetConnection(level, true);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
debug("m_pConnection is NULL");
|
||||
error("Serious error: Connection is NULL");
|
||||
}
|
||||
|
||||
// test connection
|
||||
bool bConnected = m_pConnection && m_pConnection->Connect() >= 0;
|
||||
if (bConnected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
Status = Download();
|
||||
}
|
||||
|
||||
if (bConnected)
|
||||
{
|
||||
if (Status == adConnectError)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
bConnected = false;
|
||||
Status = adFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// freeing connection allows other threads to start.
|
||||
// we doing this only if the problem was with article or group.
|
||||
// if the problem occurs by connecting or authorization we do not
|
||||
// free the connection, to prevent starting of thousands of threads
|
||||
// (cause each of them will also free it's connection after the
|
||||
// same connect-error).
|
||||
FreeConnection(Status == adFinished);
|
||||
}
|
||||
}
|
||||
|
||||
if (((Status == adFailed) || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
|
||||
(retry > 1 || !bConnected) && !IsStopped())
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Status == adFinished) || (Status == adFatalError) ||
|
||||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LevelStatus[level] = Status;
|
||||
|
||||
bool bAllLevelNotFound = true;
|
||||
for (int lev = 0; lev <= iMaxLevel; lev++)
|
||||
{
|
||||
if (LevelStatus[lev] != adNotFound)
|
||||
{
|
||||
bAllLevelNotFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bAllLevelNotFound)
|
||||
{
|
||||
if (iMaxLevel > 0)
|
||||
{
|
||||
warn("Article %s @ all servers failed: Article not found", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// do not count connect-errors, only article- and group-errors
|
||||
if (bConnected)
|
||||
{
|
||||
level++;
|
||||
if (level > iMaxLevel)
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
retry--;
|
||||
}
|
||||
}
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
|
||||
free(LevelStatus);
|
||||
|
||||
if (m_bDuplicate)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Download %s failed", m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting ArticleDownloader-loop");
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
const char* szResponse = NULL;
|
||||
EStatus Status = adRunning;
|
||||
|
||||
// at first, change group
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
szResponse = m_pConnection->JoinGroup(*it);
|
||||
if (szResponse && !strncmp(szResponse, "2", 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Status = CheckResponse(szResponse, "could not join group");
|
||||
if (Status != adFinished)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
// retrieve article
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "ARTICLE %s\r\n", m_pArticleInfo->GetMessageID());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
for (int retry = 3; retry > 0; retry--)
|
||||
{
|
||||
szResponse = m_pConnection->Request(tmp);
|
||||
if (szResponse && !strncmp(szResponse, "2", 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Status = CheckResponse(szResponse, "could not fetch article");
|
||||
if (Status != adFinished)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
// positive answer!
|
||||
|
||||
if (g_pOptions->GetDecode())
|
||||
{
|
||||
m_YDecoder.Clear();
|
||||
m_YDecoder.SetAutoSeek(g_pOptions->GetDirectWrite());
|
||||
m_YDecoder.SetCrcCheck(g_pOptions->GetCrcCheck());
|
||||
|
||||
m_UDecoder.Clear();
|
||||
}
|
||||
|
||||
m_pOutFile = NULL;
|
||||
bool bBody = false;
|
||||
bool bEnd = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
Status = adRunning;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
// Throttle the bandwidth
|
||||
while (!IsStopped() && (g_pOptions->GetDownloadRate() > 0.0f) &&
|
||||
(g_pDownloadSpeedMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
int iLen = 0;
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
|
||||
g_pDownloadSpeedMeter->AddSpeedReading(iLen);
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
//detect end of article
|
||||
if (!strcmp(line, ".\r\n") || !strcmp(line, ".\n"))
|
||||
{
|
||||
bEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
//detect lines starting with "." (marked as "..")
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
if (!bBody)
|
||||
{
|
||||
// detect body of article
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
bBody = true;
|
||||
}
|
||||
// check id of returned article
|
||||
else if (!strncmp(line, "Message-ID: ", 12))
|
||||
{
|
||||
char* p = line + 12;
|
||||
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
|
||||
{
|
||||
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
|
||||
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_eFormat == Decoder::efUnknown && g_pOptions->GetDecode())
|
||||
{
|
||||
m_eFormat = Decoder::DetectFormat(line, iLen);
|
||||
}
|
||||
|
||||
// write to output file
|
||||
if (((bBody && m_eFormat != Decoder::efUnknown) || !g_pOptions->GetDecode()) && !Write(line, iLen))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
|
||||
if (m_pOutFile)
|
||||
{
|
||||
fclose(m_pOutFile);
|
||||
}
|
||||
|
||||
if (!bEnd && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
FreeConnection(true);
|
||||
return DecodeCheck();
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(m_szTempFilename);
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szResponse, const char* szComment)
|
||||
{
|
||||
if (!szResponse)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(szResponse, "2", 1))
|
||||
{
|
||||
// OK
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Write(char* szLine, int iLen)
|
||||
{
|
||||
if (!m_pOutFile && !PrepareFile(szLine))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDecode())
|
||||
{
|
||||
if (m_eFormat == Decoder::efYenc)
|
||||
{
|
||||
return m_YDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
}
|
||||
else if (m_eFormat == Decoder::efUx)
|
||||
{
|
||||
return m_UDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return fwrite(szLine, 1, iLen, m_pOutFile) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
bool bOpen = false;
|
||||
|
||||
// prepare file for writing
|
||||
if (m_eFormat == Decoder::efYenc)
|
||||
{
|
||||
if (!strncmp(szLine, "=ybegin ", 8))
|
||||
{
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
char* pb = strstr(szLine, " name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (!m_szArticleFilename)
|
||||
{
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
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())
|
||||
{
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char* pb = strstr(szLine, " size=");
|
||||
if (pb)
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
long iArticleFilesize = atol(pb);
|
||||
if (!Util::SetFileSize(m_szOutputFilename, iArticleFilesize))
|
||||
{
|
||||
error("Could not create file %s!", m_szOutputFilename);
|
||||
return false;
|
||||
}
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
|
||||
if (bOpen)
|
||||
{
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
|
||||
const char* szFilename = bDirectWrite ? m_szOutputFilename : m_szTempFilename;
|
||||
m_pOutFile = fopen(szFilename, bDirectWrite ? "r+" : "w");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", bDirectWrite ? "open" : "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, m_pArticleInfo->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
{
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
|
||||
|
||||
if (g_pOptions->GetDecode())
|
||||
{
|
||||
SetStatus(adDecoding);
|
||||
|
||||
Decoder* pDecoder = NULL;
|
||||
|
||||
if (m_eFormat == Decoder::efYenc)
|
||||
{
|
||||
pDecoder = &m_YDecoder;
|
||||
}
|
||||
else if (m_eFormat == Decoder::efUx)
|
||||
{
|
||||
pDecoder = &m_UDecoder;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
Decoder::EStatus eStatus = pDecoder->Check();
|
||||
bool bOK = eStatus == Decoder::eFinished;
|
||||
|
||||
if (!bDirectWrite && bOK)
|
||||
{
|
||||
if (!Util::MoveFile(m_szTempFilename, m_szResultFilename))
|
||||
{
|
||||
error("Could not rename file %s to %s! Errcode: %i", m_szTempFilename, m_szResultFilename, errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_szArticleFilename && pDecoder->GetArticleFilename())
|
||||
{
|
||||
m_szArticleFilename = strdup(pDecoder->GetArticleFilename());
|
||||
}
|
||||
|
||||
remove(m_szTempFilename);
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
detail("Successfully downloaded %s", m_szInfoName);
|
||||
|
||||
if (bDirectWrite && g_pOptions->GetContinuePartial())
|
||||
{
|
||||
// create empty flag-file to indicate that the artcile was downloaded
|
||||
FILE* flagfile = fopen(m_szResultFilename, "w");
|
||||
if (!flagfile)
|
||||
{
|
||||
error("Could not create file %s", m_szResultFilename);
|
||||
// this error can be ignored
|
||||
}
|
||||
fclose(flagfile);
|
||||
}
|
||||
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(m_szResultFilename);
|
||||
if (eStatus == Decoder::eCrcError)
|
||||
{
|
||||
warn("Decoding %s failed: CRC-Error", m_szInfoName);
|
||||
return adCrcError;
|
||||
}
|
||||
else if (eStatus == Decoder::eArticleIncomplete)
|
||||
{
|
||||
warn("Decoding %s failed: article incomplete", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else if (eStatus == Decoder::eInvalidSize)
|
||||
{
|
||||
warn("Decoding %s failed: size mismatch", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else if (eStatus == Decoder::eNoBinaryData)
|
||||
{
|
||||
warn("Decoding %s failed: no binary data found", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// rawmode
|
||||
if (Util::MoveFile(m_szTempFilename, m_szResultFilename))
|
||||
{
|
||||
detail("Article %s successfully downloaded", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", m_szTempFilename, m_szResultFilename, errno);
|
||||
}
|
||||
return adFinished;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::LogDebugInfo()
|
||||
{
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&m_tLastUpdateTime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&m_tLastUpdateTime, szTime);
|
||||
#endif
|
||||
|
||||
debug(" Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(GetTempFilename()));
|
||||
}
|
||||
|
||||
void ArticleDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop ArticleDownloader");
|
||||
Thread::Stop();
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
debug("ArticleDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Terminate()
|
||||
{
|
||||
NNTPConnection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
g_pServerPool->FreeConnection(pConnection, true);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void ArticleDownloader::FreeConnection(bool bKeepConnected)
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
if (!bKeepConnected || m_pConnection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
g_pServerPool->FreeConnection(m_pConnection, true);
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::CompleteFileParts()
|
||||
{
|
||||
debug("Completing file parts");
|
||||
debug("ArticleFilename: %s", m_pFileInfo->GetFilename());
|
||||
|
||||
SetStatus(adJoining);
|
||||
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
m_pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
char InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
if (!g_pOptions->GetDecode())
|
||||
{
|
||||
detail("Moving articles for %s", InfoFilename);
|
||||
}
|
||||
else if (bDirectWrite)
|
||||
{
|
||||
detail("Checking articles for %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Joining articles for %s", InfoFilename);
|
||||
}
|
||||
|
||||
char ofn[1024];
|
||||
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
ofn[1024-1] = '\0';
|
||||
|
||||
// Ensure the DstDir is created
|
||||
if (!Util::CreateDirectory(m_pFileInfo->GetNZBInfo()->GetDestDir()))
|
||||
{
|
||||
error("Could not create directory %s! Errcode: %i", m_pFileInfo->GetNZBInfo()->GetDestDir(), errno);
|
||||
SetStatus(adJoined);
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent overwriting existing files
|
||||
int dupcount = 0;
|
||||
while (Util::FileExists(ofn))
|
||||
{
|
||||
dupcount++;
|
||||
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
|
||||
ofn[1024-1] = '\0';
|
||||
}
|
||||
|
||||
FILE* outfile = NULL;
|
||||
char tmpdestfile[1024];
|
||||
snprintf(tmpdestfile, 1024, "%s.tmp", ofn);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
|
||||
if (g_pOptions->GetDecode() && !bDirectWrite)
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
outfile = fopen(tmpdestfile, "w+");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s!", tmpdestfile);
|
||||
SetStatus(adJoined);
|
||||
return;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1 && (*m_pFileInfo->GetArticles())[0])
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, (*m_pFileInfo->GetArticles())[0]->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
else if (!g_pOptions->GetDecode())
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
if (!Util::CreateDirectory(ofn))
|
||||
{
|
||||
error("Could not create directory %s! Errcode: %i", ofn, errno);
|
||||
SetStatus(adJoined);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool complete = true;
|
||||
int iBrokenCount = 0;
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
char* buffer = NULL;
|
||||
|
||||
if (g_pOptions->GetDecode() && !bDirectWrite)
|
||||
{
|
||||
buffer = (char*)malloc(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetStatus() != ArticleInfo::aiFinished)
|
||||
{
|
||||
iBrokenCount++;
|
||||
complete = false;
|
||||
}
|
||||
else if (g_pOptions->GetDecode() && !bDirectWrite)
|
||||
{
|
||||
FILE* infile;
|
||||
const char* fn = pa->GetResultFilename();
|
||||
|
||||
infile = fopen(fn, "r");
|
||||
if (infile)
|
||||
{
|
||||
int cnt = BUFFER_SIZE;
|
||||
|
||||
while (cnt == BUFFER_SIZE)
|
||||
{
|
||||
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
|
||||
fwrite(buffer, 1, cnt, outfile);
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete = false;
|
||||
iBrokenCount++;
|
||||
detail("Could not find file %s. Status is broken", fn);
|
||||
}
|
||||
}
|
||||
else if (!g_pOptions->GetDecode())
|
||||
{
|
||||
const char* fn = pa->GetResultFilename();
|
||||
char dstFileName[1024];
|
||||
snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
|
||||
dstFileName[1024-1] = '\0';
|
||||
if (!Util::MoveFile(fn, dstFileName))
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", fn, dstFileName, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (outfile)
|
||||
{
|
||||
fclose(outfile);
|
||||
if (!Util::MoveFile(tmpdestfile, ofn))
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", tmpdestfile, ofn, errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (bDirectWrite)
|
||||
{
|
||||
if (!Util::MoveFile(m_szOutputFilename, ofn))
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bDirectWrite || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
|
||||
if (complete)
|
||||
{
|
||||
info("Successfully downloaded %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Not renaming broken file %s", ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
fprintf(file, "%s (%i/%i)\n", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size());
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
SetStatus(adJoined);
|
||||
}
|
||||
119
ArticleDownloader.h
Normal file
119
ArticleDownloader.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef ARTICLEDOWNLOADER_H
|
||||
#define ARTICLEDOWNLOADER_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Thread.h"
|
||||
#include "NNTPConnection.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
class ArticleDownloader : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adDecodeError,
|
||||
adCrcError,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
adJoined,
|
||||
adNotFound,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
private:
|
||||
FileInfo* m_pFileInfo;
|
||||
ArticleInfo* m_pArticleInfo;
|
||||
NNTPConnection* m_pConnection;
|
||||
EStatus m_eStatus;
|
||||
Mutex m_mutexConnection;
|
||||
const char* m_szResultFilename;
|
||||
char* m_szTempFilename;
|
||||
char* m_szArticleFilename;
|
||||
char* m_szInfoName;
|
||||
char* m_szOutputFilename;
|
||||
time_t m_tLastUpdateTime;
|
||||
Decoder::EFormat m_eFormat;
|
||||
YDecoder m_YDecoder;
|
||||
UDecoder m_UDecoder;
|
||||
FILE* m_pOutFile;
|
||||
bool m_bDuplicate;
|
||||
|
||||
EStatus Download();
|
||||
bool Write(char* szLine, int iLen);
|
||||
bool PrepareFile(char* szLine);
|
||||
EStatus DecodeCheck();
|
||||
void FreeConnection(bool bKeepConnected);
|
||||
EStatus CheckResponse(const char* szResponse, const char* szComment);
|
||||
|
||||
public:
|
||||
ArticleDownloader();
|
||||
~ArticleDownloader();
|
||||
void SetFileInfo(FileInfo* pFileInfo) { m_pFileInfo = pFileInfo; }
|
||||
FileInfo* GetFileInfo() { return m_pFileInfo; }
|
||||
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
|
||||
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
bool Terminate();
|
||||
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
|
||||
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
|
||||
const char* GetTempFilename() { return m_szTempFilename; }
|
||||
void SetTempFilename(const char* v);
|
||||
void SetOutputFilename(const char* v);
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void SetInfoName(const char* v);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void CompleteFileParts();
|
||||
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
virtual void AddSpeedReading(int iBytes) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
772
BinRpc.cpp
Normal file
772
BinRpc.cpp
Normal file
@@ -0,0 +1,772 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "BinRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequestNames[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug",
|
||||
"Edit queue", "Log", "Quit", "Version", "Post-queue" };
|
||||
|
||||
const unsigned int g_iMessageRequestSizes[] =
|
||||
{ 0,
|
||||
sizeof(SNZBDownloadRequest),
|
||||
sizeof(SNZBPauseUnpauseRequest),
|
||||
sizeof(SNZBListRequest),
|
||||
sizeof(SNZBSetDownloadRateRequest),
|
||||
sizeof(SNZBDumpDebugRequest),
|
||||
sizeof(SNZBEditQueueRequest),
|
||||
sizeof(SNZBLogRequest),
|
||||
sizeof(SNZBShutdownRequest),
|
||||
sizeof(SNZBVersionRequest),
|
||||
sizeof(SNZBPostQueueRequest),
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// BinProcessor
|
||||
|
||||
void BinRpcProcessor::Execute()
|
||||
{
|
||||
// Read the first package which needs to be a request
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_szClientIP);
|
||||
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void BinRpcProcessor::Dispatch()
|
||||
{
|
||||
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
|
||||
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestPostQueue &&
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
|
||||
{
|
||||
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
|
||||
return;
|
||||
}
|
||||
|
||||
BinCommand* command = NULL;
|
||||
|
||||
switch (ntohl(m_MessageBase.m_iType))
|
||||
{
|
||||
case eRemoteRequestDownload:
|
||||
{
|
||||
command = new DownloadBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestList:
|
||||
{
|
||||
command = new ListBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestLog:
|
||||
{
|
||||
command = new LogBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPauseUnpause:
|
||||
{
|
||||
command = new PauseUnpauseBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestEditQueue:
|
||||
{
|
||||
command = new EditQueueBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestSetDownloadRate:
|
||||
{
|
||||
command = new SetDownloadRateBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestDumpDebug:
|
||||
{
|
||||
command = new DumpDebugBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestShutdown:
|
||||
{
|
||||
command = new ShutdownBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestVersion:
|
||||
{
|
||||
command = new VersionBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPostQueue:
|
||||
{
|
||||
command = new PostQueueBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestWriteLog:
|
||||
{
|
||||
command = new WriteLogBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
|
||||
break;
|
||||
}
|
||||
|
||||
if (command)
|
||||
{
|
||||
command->SetSocket(m_iSocket);
|
||||
command->SetMessageBase(&m_MessageBase);
|
||||
command->Execute();
|
||||
delete command;
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// Commands
|
||||
|
||||
void BinCommand::SendBoolResponse(bool bSuccess, const char* szText)
|
||||
{
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
|
||||
BoolResponse.m_bSuccess = htonl(bSuccess);
|
||||
int iTextLen = strlen(szText) + 1;
|
||||
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
|
||||
send(m_iSocket, (char*)szText, iTextLen, 0);
|
||||
}
|
||||
|
||||
bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
|
||||
{
|
||||
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
|
||||
iSize -= sizeof(SNZBRequestBase);
|
||||
if (iSize > 0)
|
||||
{
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
|
||||
if (iBytesReceived != iSize)
|
||||
{
|
||||
error("invalid request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PauseUnpauseBinCommand::Execute()
|
||||
{
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
|
||||
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
|
||||
}
|
||||
|
||||
void SetDownloadRateBinCommand::Execute()
|
||||
{
|
||||
SNZBSetDownloadRateRequest SetDownloadRequest;
|
||||
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
|
||||
SendBoolResponse(true, "Rate-Command completed successfully");
|
||||
}
|
||||
|
||||
void DumpDebugBinCommand::Execute()
|
||||
{
|
||||
SNZBDumpDebugRequest DumpDebugRequest;
|
||||
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendBoolResponse(true, "Debug-Command completed successfully");
|
||||
}
|
||||
|
||||
void ShutdownBinCommand::Execute()
|
||||
{
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, "Stopping server");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
void VersionBinCommand::Execute()
|
||||
{
|
||||
SNZBVersionRequest VersionRequest;
|
||||
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, VERSION);
|
||||
}
|
||||
|
||||
void DownloadBinCommand::Execute()
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", Util::BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(true, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(false, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void ListBinCommand::Execute()
|
||||
{
|
||||
SNZBListRequest ListRequest;
|
||||
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBListResponse ListResponse;
|
||||
memset(&ListResponse, 0, sizeof(ListResponse));
|
||||
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
|
||||
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (ntohl(ListRequest.m_bFileList))
|
||||
{
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
int NrEntries = pDownloadQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bufsize += strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetSubject()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
unsigned long iSizeHi, iSizeLo;
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
|
||||
pListAnswer->m_iID = htonl(pFileInfo->GetID());
|
||||
Util::SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
|
||||
Util::SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
|
||||
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
|
||||
pListAnswer->m_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1);
|
||||
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
|
||||
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
|
||||
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 1);
|
||||
bufptr += sizeof(SNZBListResponseEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += ntohl(pListAnswer->m_iSubjectLen);
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetDestDir());
|
||||
bufptr += ntohl(pListAnswer->m_iDestDirLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pListAnswer->m_iDestDirLen = htonl(ntohl(pListAnswer->m_iDestDirLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
ListResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
}
|
||||
|
||||
if (htonl(ListRequest.m_bServerState))
|
||||
{
|
||||
unsigned long iSizeHi, iSizeLo;
|
||||
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
|
||||
Util::SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
|
||||
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
|
||||
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
ListResponse.m_iPostJobCount = htonl(pPostQueue->size());
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
|
||||
int iUpTimeSec, iDnTimeSec;
|
||||
long long iAllBytes;
|
||||
bool bStandBy;
|
||||
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
|
||||
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
|
||||
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
|
||||
ListResponse.m_bServerStandBy = htonl(bStandBy);
|
||||
Util::SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
|
||||
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void LogBinCommand::Execute()
|
||||
{
|
||||
SNZBLogRequest LogRequest;
|
||||
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = ntohl(LogRequest.m_iLines);
|
||||
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
|
||||
int iStart = pMessages->size();
|
||||
if (iNrEntries > 0)
|
||||
{
|
||||
if (iNrEntries > (int)pMessages->size())
|
||||
{
|
||||
iNrEntries = pMessages->size();
|
||||
}
|
||||
iStart = pMessages->size() - iNrEntries;
|
||||
}
|
||||
if (iIDFrom > 0 && !pMessages->empty())
|
||||
{
|
||||
iStart = iIDFrom - pMessages->front()->GetID();
|
||||
if (iStart < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
}
|
||||
iNrEntries = pMessages->size() - iStart;
|
||||
if (iNrEntries < 0)
|
||||
{
|
||||
iNrEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate required buffer size
|
||||
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
bufsize += strlen(pMessage->GetText()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
char* buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
|
||||
pLogAnswer->m_iID = htonl(pMessage->GetID());
|
||||
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
|
||||
pLogAnswer->m_tTime = htonl((int)pMessage->GetTime());
|
||||
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
|
||||
bufptr += sizeof(SNZBLogResponseEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += ntohl(pLogAnswer->m_iTextLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pLogAnswer->m_iTextLen = htonl(ntohl(pLogAnswer->m_iTextLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogResponse LogResponse;
|
||||
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
|
||||
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
|
||||
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
|
||||
LogResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void EditQueueBinCommand::Execute()
|
||||
{
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
|
||||
int iAction = ntohl(EditQueueRequest.m_iAction);
|
||||
int iOffset = ntohl(EditQueueRequest.m_iOffset);
|
||||
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
|
||||
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
|
||||
|
||||
if (iNrEntries * sizeof(int32_t) != iBufLength)
|
||||
{
|
||||
error("Invalid struct size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iNrEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iBufLength);
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
char* pBufPtr = (char*)pIDs;
|
||||
int NeedBytes = iBufLength;
|
||||
int iResult = 0;
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
QueueEditor::IDList cIDList;
|
||||
cIDList.reserve(iNrEntries);
|
||||
for (int i = 0; i < iNrEntries; i++)
|
||||
{
|
||||
cIDList.push_back(ntohl(pIDs[i]));
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
SendBoolResponse(true, "Edit-Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed");
|
||||
}
|
||||
}
|
||||
|
||||
void PostQueueBinCommand::Execute()
|
||||
{
|
||||
SNZBPostQueueRequest PostQueueRequest;
|
||||
if (!ReceiveRequest(&PostQueueRequest, sizeof(PostQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBPostQueueResponse PostQueueResponse;
|
||||
memset(&PostQueueResponse, 0, sizeof(PostQueueResponse));
|
||||
PostQueueResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
PostQueueResponse.m_MessageBase.m_iStructSize = htonl(sizeof(PostQueueResponse));
|
||||
PostQueueResponse.m_iEntrySize = htonl(sizeof(SNZBPostQueueResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
|
||||
int NrEntries = pPostQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBPostQueueResponseEntry);
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
bufsize += strlen(pPostInfo->GetNZBFilename()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetDestDir()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
time_t tCurTime = time(NULL);
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) bufptr;
|
||||
pPostQueueAnswer->m_iID = htonl(pPostInfo->GetID());
|
||||
pPostQueueAnswer->m_iStage = htonl(pPostInfo->GetStage());
|
||||
pPostQueueAnswer->m_iStageProgress = htonl(pPostInfo->GetStageProgress());
|
||||
pPostQueueAnswer->m_iFileProgress = htonl(pPostInfo->GetFileProgress());
|
||||
pPostQueueAnswer->m_iTotalTimeSec = htonl((int)(pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0));
|
||||
pPostQueueAnswer->m_iStageTimeSec = htonl((int)(pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0));
|
||||
pPostQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pPostInfo->GetNZBFilename()) + 1);
|
||||
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
|
||||
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
|
||||
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetDestDir()) + 1);
|
||||
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
|
||||
bufptr += sizeof(SNZBPostQueueResponseEntry);
|
||||
strcpy(bufptr, pPostInfo->GetNZBFilename());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pPostInfo->GetParFilename());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iParFilename);
|
||||
strcpy(bufptr, pPostInfo->GetInfoName());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen);
|
||||
strcpy(bufptr, pPostInfo->GetDestDir());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iDestDirLen);
|
||||
strcpy(bufptr, pPostInfo->GetProgressLabel());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iProgressLabelLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pPostQueueAnswer->m_iProgressLabelLen = htonl(ntohl(pPostQueueAnswer->m_iProgressLabelLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
|
||||
PostQueueResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteLogBinCommand::Execute()
|
||||
{
|
||||
SNZBWriteLogRequest WriteLogRequest;
|
||||
if (!ReceiveRequest(&WriteLogRequest, sizeof(WriteLogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(WriteLogRequest.m_iTrailingDataLength);
|
||||
pRecvBuffer[NeedBytes] = '\0';
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
bool OK = true;
|
||||
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
|
||||
{
|
||||
case Message::mkDetail:
|
||||
detail(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
info(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
warn(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkError:
|
||||
error(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkDebug:
|
||||
debug(pRecvBuffer);
|
||||
break;
|
||||
default:
|
||||
OK = false;
|
||||
}
|
||||
SendBoolResponse(OK, OK ? "Message added to log" : "Invalid message-kind");
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
131
BinRpc.h
Normal file
131
BinRpc.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BINRPC_H
|
||||
#define BINRPC_H
|
||||
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class BinRpcProcessor
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase m_MessageBase;
|
||||
const char* m_szClientIP;
|
||||
|
||||
void Dispatch();
|
||||
|
||||
public:
|
||||
void Execute();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetSignature(int iSignature) { m_MessageBase.m_iSignature = iSignature; }
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
class BinCommand
|
||||
{
|
||||
protected:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase* m_pMessageBase;
|
||||
|
||||
bool ReceiveRequest(void* pBuffer, int iSize);
|
||||
void SendBoolResponse(bool bSuccess, const char* szText);
|
||||
|
||||
public:
|
||||
virtual ~BinCommand() {}
|
||||
virtual void Execute() = 0;
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
|
||||
};
|
||||
|
||||
class DownloadBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseUnpauseBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
41
COPYING
41
COPYING
@@ -1,12 +1,12 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
@@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
@@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
@@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
@@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
@@ -225,7 +225,7 @@ impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
@@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
@@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
@@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found.
|
||||
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.
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
@@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
||||
192
ColoredFrontend.cpp
Normal file
192
ColoredFrontend.cpp
Normal file
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ColoredFrontend.h"
|
||||
#include "Util.h"
|
||||
|
||||
ColoredFrontend::ColoredFrontend()
|
||||
{
|
||||
m_bSummary = true;
|
||||
m_bNeedGoBack = false;
|
||||
#ifdef WIN32
|
||||
m_hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforePrint()
|
||||
{
|
||||
if (m_bNeedGoBack)
|
||||
{
|
||||
// go back one line
|
||||
#ifdef WIN32
|
||||
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
||||
GetConsoleScreenBufferInfo(m_hConsole, &BufInfo);
|
||||
BufInfo.dwCursorPosition.Y--;
|
||||
SetConsoleCursorPosition(m_hConsole, BufInfo.dwCursorPosition);
|
||||
#else
|
||||
printf("\r\033[1A");
|
||||
#endif
|
||||
m_bNeedGoBack = false;
|
||||
}
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintStatus()
|
||||
{
|
||||
char tmp[1024];
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
||||
|
||||
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
|
||||
int h = (int)(remain_sec / 3600);
|
||||
int m = (int)((remain_sec % 3600) / 60);
|
||||
int s = (int)(remain_sec % 60);
|
||||
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
}
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
{
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
szDownloadLimit[0] = 0;
|
||||
}
|
||||
|
||||
char szPostStatus[128];
|
||||
if (m_iPostJobCount > 0)
|
||||
{
|
||||
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
szPostStatus[0] = 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char* szControlSeq = "";
|
||||
#else
|
||||
printf("\033[s");
|
||||
char* szControlSeq = "\033[K";
|
||||
#endif
|
||||
|
||||
snprintf(tmp, 1024, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, fCurrentDownloadSpeed, (float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0),
|
||||
timeString, szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
#ifdef WIN32
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
SetConsoleTextAttribute(m_hConsole, 8);
|
||||
printf("[DEBUG]");
|
||||
break;
|
||||
case Message::mkError:
|
||||
SetConsoleTextAttribute(m_hConsole, 4);
|
||||
printf("[ERROR]");
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
SetConsoleTextAttribute(m_hConsole, 5);
|
||||
printf("[WARNING]");
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
SetConsoleTextAttribute(m_hConsole, 2);
|
||||
printf("[INFO]");
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
SetConsoleTextAttribute(m_hConsole, 2);
|
||||
printf("[DETAIL]");
|
||||
break;
|
||||
}
|
||||
SetConsoleTextAttribute(m_hConsole, 7);
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
CharToOem(msg, msg);
|
||||
printf(" %s\n", msg);
|
||||
free(msg);
|
||||
#else
|
||||
const char* msg = pMessage->GetText();
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("\033[31m[ERROR]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("\033[35m[WARNING]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("\033[32m[DETAIL]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintSkip()
|
||||
{
|
||||
#ifdef WIN32
|
||||
printf(".....\n");
|
||||
#else
|
||||
printf(".....\033[K\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void ColoredFrontend::BeforeExit()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,12 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -27,22 +32,22 @@
|
||||
|
||||
class ColoredFrontend : public LoggableFrontend
|
||||
{
|
||||
public:
|
||||
ColoredFrontend();
|
||||
|
||||
protected:
|
||||
virtual void BeforePrint();
|
||||
virtual void PrintMessage(Message& message);
|
||||
virtual void PrintStatus();
|
||||
virtual void PrintSkip();
|
||||
virtual void BeforeExit();
|
||||
|
||||
private:
|
||||
bool m_needGoBack = false;
|
||||
bool m_bNeedGoBack;
|
||||
|
||||
#ifdef WIN32
|
||||
HANDLE m_console;
|
||||
HANDLE m_hConsole;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual void BeforePrint();
|
||||
virtual void PrintMessage(Message* pMessage);
|
||||
virtual void PrintStatus();
|
||||
virtual void PrintSkip();
|
||||
virtual void BeforeExit();
|
||||
|
||||
public:
|
||||
ColoredFrontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
568
Connection.cpp
Normal file
568
Connection.cpp
Normal file
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Intiializing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
|
||||
if (err != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
return;
|
||||
}
|
||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE( wsaData.wVersion ) != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::Final()
|
||||
{
|
||||
debug("Finalizing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Connection::Connection(NetAddress* pNetAddress)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = pNetAddress;
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
m_bAutoClose = true;
|
||||
}
|
||||
|
||||
Connection::Connection(SOCKET iSocket, bool bAutoClose)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = NULL;
|
||||
m_eStatus = csConnected;
|
||||
m_iSocket = iSocket;
|
||||
m_iBufAvail = 0;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
m_bAutoClose = bAutoClose;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected && m_bAutoClose)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
free(m_szReadBuf);
|
||||
}
|
||||
|
||||
int Connection::Connect()
|
||||
{
|
||||
debug("Connecting");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoConnect();
|
||||
|
||||
if (iRes >= 0)
|
||||
m_eStatus = csConnected;
|
||||
else
|
||||
Connection::DoDisconnect();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Disconnect()
|
||||
{
|
||||
debug("Disconnecting");
|
||||
|
||||
if (m_eStatus == csDisconnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoDisconnect();
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Bind()
|
||||
{
|
||||
debug("Binding");
|
||||
|
||||
if (m_eStatus == csListening)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iRes = DoBind();
|
||||
|
||||
if (iRes == 0)
|
||||
{
|
||||
m_eStatus = csListening;
|
||||
}
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::WriteLine(const char* pBuffer)
|
||||
{
|
||||
//debug("Connection::write(char* line)");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = DoWriteLine(pBuffer);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Send(const char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Sending data");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = send(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SOCKET Connection::Accept()
|
||||
{
|
||||
debug("Accepting connection");
|
||||
|
||||
if (m_eStatus != csListening)
|
||||
{
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET iRes = DoAccept();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Recv(char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
int iReceived = recv(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
if (iReceived < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
}
|
||||
|
||||
return iReceived;
|
||||
}
|
||||
|
||||
bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data (full buffer)");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
char* pBufPtr = (char*)pBuffer;
|
||||
int NeedBytes = iSize;
|
||||
|
||||
if (m_iBufAvail > 0)
|
||||
{
|
||||
int len = iSize > m_iBufAvail ? m_iBufAvail : iSize;
|
||||
memcpy(pBufPtr, m_szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
m_szBufPtr += len;
|
||||
m_iBufAvail -= len;
|
||||
NeedBytes -= len;
|
||||
}
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
int iReceived = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iReceived <= 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
return false;
|
||||
}
|
||||
pBufPtr += iReceived;
|
||||
NeedBytes -= iReceived;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoConnect()
|
||||
{
|
||||
debug("Do connecting");
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
int MSecVal = m_iTimeout * 1000;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
|
||||
#else
|
||||
struct timeval TimeVal;
|
||||
TimeVal.tv_sec = m_iTimeout;
|
||||
TimeVal.tv_usec = 0;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeVal, sizeof(TimeVal));
|
||||
#endif
|
||||
if (err != 0)
|
||||
{
|
||||
ReportError("setsockopt failed", NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
{
|
||||
unsigned int uaddr = inet_addr(szHost);
|
||||
if (uaddr == (unsigned int)-1)
|
||||
{
|
||||
struct hostent* hinfo;
|
||||
bool err = false;
|
||||
int h_errnop = 0;
|
||||
#ifdef WIN32
|
||||
hinfo = gethostbyname(szHost);
|
||||
err = hinfo == NULL;
|
||||
h_errnop = WSAGetLastError();
|
||||
#else
|
||||
struct hostent hinfobuf;
|
||||
static const int strbuflen = 1024;
|
||||
char* strbuf = (char*)malloc(strbuflen);
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_6
|
||||
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
|
||||
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
|
||||
#else
|
||||
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
#endif
|
||||
if (err)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", szHost, h_errnop);
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
|
||||
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
}
|
||||
return uaddr;
|
||||
}
|
||||
|
||||
int Connection::DoDisconnect()
|
||||
{
|
||||
debug("Do disconnecting");
|
||||
|
||||
if (m_iSocket > 0)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, 0);
|
||||
break;
|
||||
}
|
||||
else if (iBufAvail == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
szBufPtr = m_szReadBuf;
|
||||
m_szReadBuf[iBufAvail] = '\0';
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
|
||||
if (p)
|
||||
{
|
||||
len = (int)(p - szBufPtr + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = iBufAvail;
|
||||
}
|
||||
|
||||
if (len > iSize)
|
||||
{
|
||||
len = iSize;
|
||||
}
|
||||
|
||||
memcpy(pBufPtr, szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
szBufPtr += len;
|
||||
iBufAvail -= len;
|
||||
iBytesRead += len;
|
||||
iSize -= len;
|
||||
|
||||
if (p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
*pBufPtr = '\0';
|
||||
|
||||
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
|
||||
m_szBufPtr = szBufPtr; // copy back to member
|
||||
|
||||
if (pBytesRead)
|
||||
{
|
||||
*pBytesRead = iBytesRead;
|
||||
}
|
||||
|
||||
if (pBufPtr == pBuffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
int Connection::DoBind()
|
||||
{
|
||||
debug("Do binding");
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
|
||||
if (bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress)) < 0)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(m_iSocket, 10) < 0)
|
||||
{
|
||||
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SOCKET Connection::DoAccept()
|
||||
{
|
||||
struct sockaddr_in ClientAddress;
|
||||
socklen_t SockLen;
|
||||
|
||||
SockLen = sizeof(ClientAddress);
|
||||
|
||||
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
|
||||
|
||||
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
|
||||
{
|
||||
ReportError("Could not accept connection", NULL, 0);
|
||||
}
|
||||
|
||||
return iSocket;
|
||||
}
|
||||
|
||||
void Connection::Cancel()
|
||||
{
|
||||
debug("Cancelling connection");
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
m_eStatus = csCancelled;
|
||||
int r = shutdown(m_iSocket, SHUT_RDWR);
|
||||
if (r == -1)
|
||||
{
|
||||
ReportError("Could not shutdown connection", NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode)
|
||||
{
|
||||
if (ErrCode == 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ErrCode = WSAGetLastError();
|
||||
#else
|
||||
ErrCode = errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
char szErrPrefix[1024];
|
||||
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
|
||||
szErrPrefix[1024-1] = '\0';
|
||||
#ifdef WIN32
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
#else
|
||||
const char* szErrMsg = hstrerror(ErrCode);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
87
Connection.h
Normal file
87
Connection.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled
|
||||
};
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
SOCKET m_iSocket;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
bool m_bAutoClose;
|
||||
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(const char* pBuffer);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
Connection(SOCKET iSocket, bool bAutoClose);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int Bind();
|
||||
int Send(const char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
int WriteLine(const char* pBuffer);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
};
|
||||
|
||||
#endif
|
||||
485
Decoder.cpp
Normal file
485
Decoder.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
|
||||
unsigned int YDecoder::crc_tab[256];
|
||||
|
||||
Decoder::Decoder()
|
||||
{
|
||||
debug("Creating Decoder");
|
||||
|
||||
m_szSrcFilename = NULL;
|
||||
m_szDestFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
Decoder::~ Decoder()
|
||||
{
|
||||
debug("Destroying Decoder");
|
||||
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void Decoder::Clear()
|
||||
{
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
|
||||
{
|
||||
if (!strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
return efYenc;
|
||||
}
|
||||
|
||||
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
{
|
||||
return efUx;
|
||||
}
|
||||
|
||||
if (!strncmp(buffer, "begin ", 6))
|
||||
{
|
||||
bool bOK = true;
|
||||
buffer += 6; //strlen("begin ")
|
||||
while (*buffer && *buffer != ' ')
|
||||
{
|
||||
char ch = *buffer++;
|
||||
if (ch < '0' || ch > '7')
|
||||
{
|
||||
bOK = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
return efUx;
|
||||
}
|
||||
}
|
||||
|
||||
return efUnknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* YDecoder: fast implementation of yEnc-Decoder
|
||||
*/
|
||||
|
||||
void YDecoder::Init()
|
||||
{
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
void YDecoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
|
||||
YDecoder::YDecoder()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void YDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
m_bBody = false;
|
||||
m_bBegin = false;
|
||||
m_bPart = false;
|
||||
m_bEnd = false;
|
||||
m_bCrc = false;
|
||||
m_lExpectedCRC = 0;
|
||||
m_lCalculatedCRC = 0xFFFFFFFF;
|
||||
m_iBegin = 0;
|
||||
m_iEnd = 0;
|
||||
m_iSize = 0;
|
||||
m_iEndSize = 0;
|
||||
m_bAutoSeek = false;
|
||||
m_bNeedSetPos = false;
|
||||
m_bCrcCheck = false;
|
||||
}
|
||||
|
||||
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
*
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
* Released under GPL (thanks)
|
||||
*
|
||||
* chksum_crc32gentab() -- to a global crc_tab[256], this one will
|
||||
* calculate the crcTable for crc32-checksums.
|
||||
* it is generated to the polynom [..]
|
||||
*/
|
||||
void YDecoder::crc32gentab()
|
||||
{
|
||||
unsigned long crc, poly;
|
||||
int i, j;
|
||||
|
||||
poly = 0xEDB88320L;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
crc = i;
|
||||
for (j = 8; j > 0; j--)
|
||||
{
|
||||
if (crc & 1)
|
||||
{
|
||||
crc = (crc >> 1) ^ poly;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
crc_tab[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is modified version of chksum_crc() from
|
||||
* crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
*
|
||||
* chksum_crc() -- to a given block, this one calculates the
|
||||
* crc32-checksum until the length is
|
||||
* reached. the crc32-checksum will be
|
||||
* the result.
|
||||
*/
|
||||
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
{
|
||||
register unsigned long crc = startCrc;
|
||||
for (unsigned long i = 0; i < length; i++)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
unsigned int YDecoder::DecodeBuffer(char* buffer)
|
||||
{
|
||||
if (m_bBody && !m_bEnd)
|
||||
{
|
||||
if (!strncmp(buffer, "=yend ", 6))
|
||||
{
|
||||
m_bEnd = true;
|
||||
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
|
||||
if (pb)
|
||||
{
|
||||
m_bCrc = true;
|
||||
pb += 7 + (int)m_bPart; //=strlen(" crc32=") or strlen(" pcrc32=")
|
||||
m_lExpectedCRC = strtoul(pb, NULL, 16);
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_iEndSize = (int)atoi(pb);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (true)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
if (m_bCrcCheck)
|
||||
{
|
||||
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, (unsigned int)(optr - buffer));
|
||||
}
|
||||
return (unsigned int)(optr - buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
m_bBegin = true;
|
||||
char* pb = strstr(buffer, " name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_iSize = (int)atoi(pb);
|
||||
}
|
||||
m_bPart = strstr(buffer, " part=");
|
||||
if (!m_bPart)
|
||||
{
|
||||
m_bBody = true;
|
||||
m_iBegin = 1;
|
||||
m_iEnd = m_iSize;
|
||||
}
|
||||
}
|
||||
else if (m_bPart && !strncmp(buffer, "=ypart ", 7))
|
||||
{
|
||||
m_bPart = true;
|
||||
m_bBody = true;
|
||||
char* pb = strstr(buffer, " begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 7; //=strlen(" begin=")
|
||||
m_iBegin = (int)atoi(pb);
|
||||
}
|
||||
pb = strstr(buffer, " end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen(" end=")
|
||||
m_iEnd = (int)atoi(pb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
if (m_bNeedSetPos)
|
||||
{
|
||||
if (m_iBegin == 0 || m_iEnd == 0 || !outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_bNeedSetPos = false;
|
||||
}
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::EStatus YDecoder::Check()
|
||||
{
|
||||
m_lCalculatedCRC ^= 0xFFFFFFFF;
|
||||
|
||||
debug("Expected crc32=%x", m_lExpectedCRC);
|
||||
debug("Calculated crc32=%x", m_lCalculatedCRC);
|
||||
|
||||
if (!m_bBegin)
|
||||
{
|
||||
return eNoBinaryData;
|
||||
}
|
||||
else if (!m_bEnd)
|
||||
{
|
||||
return eArticleIncomplete;
|
||||
}
|
||||
else if (!m_bPart && m_iSize != m_iEndSize)
|
||||
{
|
||||
return eInvalidSize;
|
||||
}
|
||||
else if (m_bCrcCheck && m_bCrc && (m_lExpectedCRC != m_lCalculatedCRC))
|
||||
{
|
||||
return eCrcError;
|
||||
}
|
||||
|
||||
return eFinished;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UDecoder: supports UU encoding formats
|
||||
*/
|
||||
|
||||
UDecoder::UDecoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
m_bBody = false;
|
||||
m_bEnd = false;
|
||||
}
|
||||
|
||||
/* DecodeBuffer-function uses portions of code from tool UUDECODE by Clem Dye
|
||||
* UUDECODE.c (http://www.bastet.com/uue.zip)
|
||||
* Copyright (C) 1998 Clem Dye
|
||||
*
|
||||
* Released under GPL (thanks)
|
||||
*/
|
||||
|
||||
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
|
||||
|
||||
unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
{
|
||||
if (!m_bBody)
|
||||
{
|
||||
if (!strncmp(buffer, "begin ", 6))
|
||||
{
|
||||
char* pb = buffer;
|
||||
pb += 6; //strlen("begin ")
|
||||
|
||||
// skip file-permissions
|
||||
for (; *pb != ' ' && *pb != '\0' && *pb != '\n' && *pb != '\r'; pb++) ;
|
||||
pb++;
|
||||
|
||||
// extracting filename
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
|
||||
m_bBody = true;
|
||||
return 0;
|
||||
}
|
||||
else if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
{
|
||||
m_bBody = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bBody && (!strncmp(buffer, "end ", 4) || *buffer == '`'))
|
||||
{
|
||||
m_bEnd = true;
|
||||
}
|
||||
|
||||
if (m_bBody && !m_bEnd)
|
||||
{
|
||||
int iEffLen = UU_DECODE_CHAR(buffer[0]);
|
||||
if (iEffLen > len)
|
||||
{
|
||||
// error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
for (++iptr; iEffLen > 0; iptr += 4, iEffLen -= 3)
|
||||
{
|
||||
if (iEffLen >= 3)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
|
||||
*optr++ = UU_DECODE_CHAR (iptr[2]) << 6 | UU_DECODE_CHAR (iptr[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iEffLen >= 1)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
}
|
||||
if (iEffLen >= 2)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (unsigned int)(optr - buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UDecoder::Write(char* buffer, int len, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer, len);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::EStatus UDecoder::Check()
|
||||
{
|
||||
if (!m_bBody)
|
||||
{
|
||||
return eNoBinaryData;
|
||||
}
|
||||
|
||||
return eFinished;
|
||||
}
|
||||
121
Decoder.h
Normal file
121
Decoder.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
eUnknownError,
|
||||
eFinished,
|
||||
eArticleIncomplete,
|
||||
eCrcError,
|
||||
eInvalidSize,
|
||||
eNoBinaryData
|
||||
};
|
||||
|
||||
enum EFormat
|
||||
{
|
||||
efUnknown,
|
||||
efYenc,
|
||||
efUx,
|
||||
};
|
||||
|
||||
static const char* FormatNames[];
|
||||
|
||||
protected:
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
virtual ~Decoder();
|
||||
virtual EStatus Check() = 0;
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
static EFormat DetectFormat(const char* buffer, int len);
|
||||
};
|
||||
|
||||
class YDecoder: public Decoder
|
||||
{
|
||||
protected:
|
||||
static unsigned int crc_tab[256];
|
||||
bool m_bBegin;
|
||||
bool m_bPart;
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
bool m_bCrc;
|
||||
unsigned long m_lExpectedCRC;
|
||||
unsigned long m_lCalculatedCRC;
|
||||
unsigned long m_iBegin;
|
||||
unsigned long m_iEnd;
|
||||
unsigned long m_iSize;
|
||||
unsigned long m_iEndSize;
|
||||
bool m_bAutoSeek;
|
||||
bool m_bNeedSetPos;
|
||||
bool m_bCrcCheck;
|
||||
|
||||
unsigned int DecodeBuffer(char* buffer);
|
||||
static void crc32gentab();
|
||||
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
|
||||
|
||||
public:
|
||||
YDecoder();
|
||||
virtual EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile);
|
||||
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
|
||||
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
};
|
||||
|
||||
class UDecoder: public Decoder
|
||||
{
|
||||
private:
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
|
||||
unsigned int DecodeBuffer(char* buffer, int len);
|
||||
|
||||
public:
|
||||
UDecoder();
|
||||
virtual EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile);
|
||||
};
|
||||
|
||||
#endif
|
||||
637
DiskState.cpp
Normal file
637
DiskState.cpp
Normal file
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version 3\n";
|
||||
|
||||
/* Save Download Queue to Disk.
|
||||
* The Disk State consists of file "queue", which contains the order of files
|
||||
* and of one diskstate-file for each file in download queue.
|
||||
* This function saves file "queue" and files with NZB-info. It does not
|
||||
* save file-infos.
|
||||
*/
|
||||
bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Saving queue to disk");
|
||||
|
||||
// prepare list of nzb-infos
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bool inlist = false;
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
if (pNZBInfo == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
inlist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inlist)
|
||||
{
|
||||
cNZBList.push_back(pFileInfo->GetNZBInfo());
|
||||
}
|
||||
}
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* outfile = fopen(fileName, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", fileName);
|
||||
perror(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, FORMATVERSION_SIGNATURE);
|
||||
|
||||
// save nzb-infos
|
||||
fprintf(outfile, "%i\n", cNZBList.size());
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
|
||||
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
|
||||
unsigned long High, Low;
|
||||
Util::SplitInt64(pNZBInfo->GetSize(), &High, &Low);
|
||||
fprintf(outfile, "%lu,%lu\n", High, Low);
|
||||
}
|
||||
|
||||
// save file-infos
|
||||
fprintf(outfile, "%i\n", pDownloadQueue->size());
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetDeleted())
|
||||
{
|
||||
// find index of nzb-info
|
||||
int iNZBIndex = 0;
|
||||
for (unsigned int i = 0; i < cNZBList.size(); i++)
|
||||
{
|
||||
iNZBIndex++;
|
||||
if (cNZBList[i] == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(outfile, "%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused());
|
||||
}
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (pDownloadQueue->empty())
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Loading queue from disk");
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
int size;
|
||||
char buf[1024];
|
||||
|
||||
// load nzb-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = new NZBInfo();
|
||||
cNZBList.push_back(pNZBInfo);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pNZBInfo->SetFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pNZBInfo->SetDestDir(buf);
|
||||
|
||||
int iFileCount;
|
||||
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
||||
pNZBInfo->SetFileCount(iFileCount);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
pNZBInfo->SetSize(Util::JoinInt64(High, Low));
|
||||
}
|
||||
|
||||
// load file-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
unsigned int id, iNZBIndex, paused;
|
||||
if (fscanf(infile, "%i,%i,%i\n", &id, &iNZBIndex, &paused) != 3) goto error;
|
||||
if (iNZBIndex < 0 || iNZBIndex > cNZBList.size()) goto error;
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pFileInfo->SetNZBInfo(cNZBList[iNZBIndex - 1]);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiskState::SaveFile(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return SaveFileInfo(pFileInfo, fileName);
|
||||
}
|
||||
|
||||
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
{
|
||||
debug("Saving FileInfo to disk");
|
||||
|
||||
FILE* outfile = fopen(szFilename, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
|
||||
unsigned long High, Low;
|
||||
Util::SplitInt64(pFileInfo->GetSize(), &High, &Low);
|
||||
fprintf(outfile, "%lu,%lu\n", High, Low);
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetGroups()->size());
|
||||
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
fprintf(outfile, "%s\n", *it);
|
||||
}
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetArticles()->size());
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = *it;
|
||||
fprintf(outfile, "%i,%i\n", pArticleInfo->GetPartNumber(), pArticleInfo->GetSize());
|
||||
fprintf(outfile, "%s\n", pArticleInfo->GetMessageID());
|
||||
}
|
||||
|
||||
fclose(outfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadArticles(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return LoadFileInfo(pFileInfo, fileName, false, true);
|
||||
}
|
||||
|
||||
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
|
||||
{
|
||||
debug("Loading FileInfo from disk");
|
||||
|
||||
FILE* infile = fopen(szFilename, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetFilename(buf);
|
||||
|
||||
int iFilenameConfirmed;
|
||||
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
||||
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
if (bFileSummary) pFileInfo->SetSize(Util::JoinInt64(High, Low));
|
||||
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
||||
|
||||
int size;
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
}
|
||||
|
||||
if (bArticles)
|
||||
{
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int PartNumber, PartSize;
|
||||
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
|
||||
ArticleInfo* pArticleInfo = new ArticleInfo();
|
||||
pArticleInfo->SetPartNumber(PartNumber);
|
||||
pArticleInfo->SetSize(PartSize);
|
||||
pArticleInfo->SetMessageID(buf);
|
||||
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiskState::SavePostQueue(PostQueue* pPostQueue, bool bCompleted)
|
||||
{
|
||||
debug("Saving post-queue to disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* outfile = fopen(fileName, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", fileName);
|
||||
perror(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, FORMATVERSION_SIGNATURE);
|
||||
|
||||
fprintf(outfile, "%i\n", pPostQueue->size());
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetParFilename());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetInfoName());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParCheck());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParStatus());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParFailed());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetStage());
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (pPostQueue->empty())
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadPostQueue(PostQueue* pPostQueue, bool bCompleted)
|
||||
{
|
||||
debug("Loading post-queue from disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
int size;
|
||||
char buf[1024];
|
||||
int iIntValue;
|
||||
|
||||
// load file-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
PostInfo* pPostInfo = new PostInfo();
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetParFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetInfoName(buf);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParCheck(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParStatus(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParFailed(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetStage((PostInfo::EStage)iIntValue);
|
||||
|
||||
pPostQueue->push_back(pPostInfo);
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::DiscardDownloadQueue()
|
||||
{
|
||||
debug("Discarding queue");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
// skip nzb-infos
|
||||
int size = 0;
|
||||
char buf[1024];
|
||||
fscanf(infile, "%i\n", &size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
}
|
||||
|
||||
fscanf(infile, "%i\n", &size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int id, group, paused;
|
||||
if (fscanf(infile, "%i,%i,%i\n", &id, &group, &paused) == 3)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not discard diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
if (res)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::DiscardPostQueue()
|
||||
{
|
||||
debug("Discarding post-queue");
|
||||
|
||||
char fileName[1024];
|
||||
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postc");
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::DownloadQueueExists()
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
return Util::FileExists(fileName);
|
||||
}
|
||||
|
||||
bool DiskState::PostQueueExists(bool bCompleted)
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
return Util::FileExists(fileName);
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
// delete diskstate-file for file-info
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return !pDownloadQueue || SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
// build array of IDs of files in queue for faster access
|
||||
int* ids = (int*)malloc(sizeof(int) * (pDownloadQueue->size() + 1));
|
||||
int* ptr = ids;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
*ptr++ = pFileInfo->GetID();
|
||||
}
|
||||
*ptr = 0;
|
||||
|
||||
// read directory
|
||||
DirBrowser dir(g_pOptions->GetTempDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int id, part;
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec") ||
|
||||
((sscanf(filename, "%i.out", &id) == 1) &&
|
||||
!(g_pOptions->GetContinuePartial() && g_pOptions->GetDirectWrite()));
|
||||
if (!del)
|
||||
{
|
||||
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
|
||||
(sscanf(filename, "%i.out", &id) == 1))
|
||||
{
|
||||
del = true;
|
||||
ptr = ids;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == id)
|
||||
{
|
||||
del = false;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (del)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetTempDir(), filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
remove(szFullFilename);
|
||||
}
|
||||
}
|
||||
|
||||
free(ids);
|
||||
}
|
||||
53
DiskState.h
Normal file
53
DiskState.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DISKSTATE_H
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
|
||||
|
||||
public:
|
||||
bool DownloadQueueExists();
|
||||
bool PostQueueExists(bool bCompleted);
|
||||
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool SaveFile(FileInfo* pFileInfo);
|
||||
bool LoadArticles(FileInfo* pFileInfo);
|
||||
bool SavePostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool LoadPostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool DiscardDownloadQueue();
|
||||
bool DiscardPostQueue();
|
||||
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
363
DownloadInfo.cpp
Normal file
363
DownloadInfo.cpp
Normal file
@@ -0,0 +1,363 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
|
||||
NZBInfo::NZBInfo()
|
||||
{
|
||||
debug("Creating NZBInfo");
|
||||
|
||||
m_szFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_iFileCount = 0;
|
||||
m_lSize = 0;
|
||||
m_iRefCount = 0;
|
||||
}
|
||||
|
||||
NZBInfo::~NZBInfo()
|
||||
{
|
||||
debug("Destroying NZBInfo");
|
||||
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::AddReference()
|
||||
{
|
||||
m_iRefCount++;
|
||||
}
|
||||
|
||||
void NZBInfo::Release()
|
||||
{
|
||||
m_iRefCount--;
|
||||
if (m_iRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void NZBInfo::SetFilename(const char * szFilename)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void NZBInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(m_szFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = Util::BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// OK, using stripped name
|
||||
}
|
||||
else
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
Util::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is empty, use basename without cleaning up "msgid_"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
Util::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is STILL empty, use "noname"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
strncpy(postname, "noname", 1024);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
ArticleInfo::ArticleInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
m_szMessageID = NULL;
|
||||
m_iSize = 0;
|
||||
m_eStatus = aiUndefined;
|
||||
m_szResultFilename = NULL;
|
||||
}
|
||||
|
||||
ArticleInfo::~ ArticleInfo()
|
||||
{
|
||||
//debug("Destroying ArticleInfo");
|
||||
|
||||
if (m_szMessageID)
|
||||
{
|
||||
free(m_szMessageID);
|
||||
}
|
||||
if (m_szResultFilename)
|
||||
{
|
||||
free(m_szResultFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleInfo::SetMessageID(const char * szMessageID)
|
||||
{
|
||||
m_szMessageID = strdup(szMessageID);
|
||||
}
|
||||
|
||||
void ArticleInfo::SetResultFilename(const char * v)
|
||||
{
|
||||
m_szResultFilename = strdup(v);
|
||||
}
|
||||
|
||||
|
||||
FileInfo::FileInfo()
|
||||
{
|
||||
debug("Creating FileInfo");
|
||||
|
||||
m_Articles.clear();
|
||||
m_Groups.clear();
|
||||
m_szSubject = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_bFilenameConfirmed = false;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_bOutputInitialized = false;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
FileInfo::~ FileInfo()
|
||||
{
|
||||
debug("Destroying FileInfo");
|
||||
|
||||
if (m_szSubject)
|
||||
{
|
||||
free(m_szSubject);
|
||||
}
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
|
||||
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_Groups.clear();
|
||||
|
||||
ClearArticles();
|
||||
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::ClearArticles()
|
||||
{
|
||||
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Articles.clear();
|
||||
}
|
||||
|
||||
void FileInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo = pNZBInfo;
|
||||
m_pNZBInfo->AddReference();
|
||||
}
|
||||
|
||||
void FileInfo::SetSubject(const char* szSubject)
|
||||
{
|
||||
m_szSubject = strdup(szSubject);
|
||||
}
|
||||
|
||||
void FileInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::MakeValidFilename()
|
||||
{
|
||||
Util::MakeValidFilename(m_szFilename, '_');
|
||||
}
|
||||
|
||||
void FileInfo::LockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Lock();
|
||||
}
|
||||
|
||||
void FileInfo::UnlockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Unlock();
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe(const char* szFilename)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
if (Util::FileExists(fileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
if (Util::FileExists(fileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
GroupInfo::GroupInfo()
|
||||
{
|
||||
m_iFirstID = 0;
|
||||
m_iLastID = 0;
|
||||
m_iRemainingFileCount = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_lPausedSize = 0;
|
||||
m_iRemainingParCount = 0;
|
||||
}
|
||||
|
||||
GroupInfo::~GroupInfo()
|
||||
{
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfo::BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
GroupInfo* pGroupInfo = NULL;
|
||||
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
|
||||
{
|
||||
GroupInfo* pGroupInfo1 = *itg;
|
||||
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupInfo = new GroupInfo();
|
||||
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
|
||||
pGroupInfo->m_pNZBInfo->AddReference();
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
pGroupQueue->push_back(pGroupInfo);
|
||||
}
|
||||
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
|
||||
{
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
}
|
||||
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
|
||||
{
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
}
|
||||
pGroupInfo->m_iRemainingFileCount++;
|
||||
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
if (pFileInfo->GetPaused())
|
||||
{
|
||||
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
if (strstr(szLoFileName, ".par2"))
|
||||
{
|
||||
pGroupInfo->m_iRemainingParCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
182
DownloadInfo.h
Normal file
182
DownloadInfo.h
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DOWNLOADINFO_H
|
||||
#define DOWNLOADINFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class NZBInfo
|
||||
{
|
||||
private:
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
int m_iFileCount;
|
||||
long long m_lSize;
|
||||
|
||||
public:
|
||||
NZBInfo();
|
||||
~NZBInfo();
|
||||
void AddReference();
|
||||
void Release();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long s) { m_lSize = s; }
|
||||
int GetFileCount() { return m_iFileCount; }
|
||||
void SetFileCount(int s) { m_iFileCount = s; }
|
||||
};
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iPartNumber;
|
||||
char* m_szMessageID;
|
||||
int m_iSize;
|
||||
EStatus m_eStatus;
|
||||
char* m_szResultFilename;
|
||||
|
||||
public:
|
||||
ArticleInfo();
|
||||
~ArticleInfo();
|
||||
void SetPartNumber(int s) { m_iPartNumber = s; }
|
||||
int GetPartNumber() { return m_iPartNumber; }
|
||||
const char* GetMessageID() { return m_szMessageID; }
|
||||
void SetMessageID(const char* szMessageID);
|
||||
void SetSize(int s) { m_iSize = s; }
|
||||
int GetSize() { return m_iSize; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
const char* GetResultFilename() { return m_szResultFilename; }
|
||||
void SetResultFilename(const char* v);
|
||||
};
|
||||
|
||||
class FileInfo
|
||||
{
|
||||
public:
|
||||
typedef std::vector<ArticleInfo*> Articles;
|
||||
typedef std::vector<char*> Groups;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
Articles m_Articles;
|
||||
Groups m_Groups;
|
||||
char* m_szSubject;
|
||||
char* m_szFilename;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void SetNZBInfo(NZBInfo* pNZBInfo);
|
||||
Articles* GetArticles() { return &m_Articles; }
|
||||
Groups* GetGroups() { return &m_Groups; }
|
||||
const char* GetSubject() { return m_szSubject; }
|
||||
void SetSubject(const char* szSubject);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void MakeValidFilename();
|
||||
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
|
||||
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
|
||||
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
|
||||
bool GetPaused() { return m_bPaused; }
|
||||
void SetPaused(bool Paused) { m_bPaused = Paused; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void ClearArticles();
|
||||
void LockOutputFile();
|
||||
void UnlockOutputFile();
|
||||
bool GetOutputInitialized() { return m_bOutputInitialized; }
|
||||
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
|
||||
bool IsDupe(const char* szFilename);
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
class GroupInfo;
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
private:
|
||||
NZBInfo* m_pNZBInfo;
|
||||
int m_iFirstID;
|
||||
int m_iLastID;
|
||||
int m_iRemainingFileCount;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iRemainingParCount;
|
||||
|
||||
public:
|
||||
GroupInfo();
|
||||
~GroupInfo();
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
int GetFirstID() { return m_iFirstID; }
|
||||
int GetLastID() { return m_iLastID; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
long long GetPausedSize() { return m_lPausedSize; }
|
||||
int GetRemainingFileCount() { return m_iRemainingFileCount; }
|
||||
int GetRemainingParCount() { return m_iRemainingParCount; }
|
||||
|
||||
static void BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
450
Frontend.cpp
Normal file
450
Frontend.cpp
Normal file
@@ -0,0 +1,450 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_iNeededLogFirstID = 0;
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
m_fCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPause = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iPostJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
m_iPostJobCount = pPostQueue->size();
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frontend::FreeData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
for (Log::Messages::iterator it = m_RemoteMessages.begin(); it != m_RemoteMessages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteMessages.clear();
|
||||
|
||||
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteQueue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages * Frontend::LockMessages()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteMessages;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pLog->LockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockMessages()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pLog->UnlockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue * Frontend::LockQueue()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteQueue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->LockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockQueue()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::IsRemoteMode()
|
||||
{
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPause(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(fRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetDownloadRate(fRate);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerDumpDebug()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestDumpDebug();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
}
|
||||
|
||||
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
|
||||
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
|
||||
m_RemoteMessages.push_back(pMessage);
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(m_bFileList);
|
||||
ListRequest.m_bServerState = htonl(m_bSummary);
|
||||
|
||||
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ntohl(ListResponse.m_bServerPaused);
|
||||
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
|
||||
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
|
||||
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
|
||||
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
|
||||
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
|
||||
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
|
||||
pFileInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
|
||||
pFileInfo->SetRemainingSize(Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
|
||||
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
|
||||
|
||||
// find nzb-info or create new
|
||||
NZBInfo* pNZBInfo = NULL;
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo2 = *it;
|
||||
if (!strcmp(pNZBInfo2->GetFilename(), szNZBFilename))
|
||||
{
|
||||
pNZBInfo = pNZBInfo2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pNZBInfo)
|
||||
{
|
||||
pNZBInfo = new NZBInfo();
|
||||
pNZBInfo->SetFilename(szNZBFilename);
|
||||
pNZBInfo->SetDestDir(szDestDir);
|
||||
cNZBList.push_back(pNZBInfo);
|
||||
}
|
||||
|
||||
pFileInfo->SetNZBInfo(pNZBInfo);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
}
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(float fRate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(fRate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestDumpDebug()
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
|
||||
}
|
||||
85
Frontend.h
Normal file
85
Frontend.h
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FRONTEND_H
|
||||
#define FRONTEND_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueEditor.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
private:
|
||||
Log::Messages m_RemoteMessages;
|
||||
DownloadQueue m_RemoteQueue;
|
||||
|
||||
bool RequestMessages();
|
||||
bool RequestFileList();
|
||||
|
||||
protected:
|
||||
bool m_bSummary;
|
||||
bool m_bFileList;
|
||||
unsigned int m_iNeededLogEntries;
|
||||
unsigned int m_iNeededLogFirstID;
|
||||
int m_iUpdateInterval;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
float m_fDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
int m_iPostJobCount;
|
||||
int m_iUpTimeSec;
|
||||
int m_iDnTimeSec;
|
||||
long long m_iAllBytes;
|
||||
bool m_bStandBy;
|
||||
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
Log::Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause);
|
||||
bool RequestPauseUnpause(bool bPause);
|
||||
void ServerSetDownloadRate(float fRate);
|
||||
bool RequestSetDownloadRate(float fRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
167
INSTALL
Normal file
167
INSTALL
Normal file
@@ -0,0 +1,167 @@
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, a file
|
||||
`config.cache' that saves the results of its tests to speed up
|
||||
reconfiguring, and a file `config.log' containing compiler output
|
||||
(useful mainly for debugging `configure').
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If at some point `config.cache'
|
||||
contains results you don't want to keep, you may remove or edit it.
|
||||
|
||||
The file `configure.in' is used to create `configure' by a program
|
||||
called `autoconf'. You only need `configure.in' if you want to change
|
||||
it or regenerate `configure' using a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes a while. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
4. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. You can give `configure'
|
||||
initial values for variables by setting them in the environment. Using
|
||||
a Bourne-compatible shell, you can do that on the command line like
|
||||
this:
|
||||
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
|
||||
|
||||
Or on systems that have the `env' program, you can do it like this:
|
||||
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not supports the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a time
|
||||
in the source code directory. After you have installed the package for
|
||||
one architecture, use `make distclean' before reconfiguring for another
|
||||
architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' can not figure out
|
||||
automatically, but needs to determine by the type of host the package
|
||||
will run on. Usually `configure' can figure that out, but if it prints
|
||||
a message saying it can not guess the host type, give it the
|
||||
`--host=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name with three fields:
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the host type.
|
||||
|
||||
If you are building compiler tools for cross-compiling, you can also
|
||||
use the `--target=TYPE' option to select the type of system they will
|
||||
produce code for and the `--build=TYPE' option to select the type of
|
||||
system on which you are compiling the package.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Operation Controls
|
||||
==================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Use and save the results of the tests in FILE instead of
|
||||
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
|
||||
debugging `configure'.
|
||||
|
||||
`--help'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made.
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--version'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options.
|
||||
|
||||
358
Log.cpp
Normal file
358
Log.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Log::Log()
|
||||
{
|
||||
m_Messages.clear();
|
||||
m_iIDGen = 0;
|
||||
m_szLogFilename = NULL;
|
||||
#ifdef DEBUG
|
||||
m_bExtraDebug = Util::FileExists("extradebug");
|
||||
#endif
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
free(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Filelog(const char* msg, ...)
|
||||
{
|
||||
if (
|
||||
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
#ifdef DEBUG
|
||||
|| (m_szLogFilename && m_bExtraDebug)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!m_szLogFilename)
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&rawtime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&rawtime, szTime);
|
||||
#endif
|
||||
szTime[50-1] = '\0';
|
||||
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
||||
|
||||
FILE* file = fopen(m_szLogFilename, "a+");
|
||||
if (file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
unsigned long iThreadId = GetCurrentThreadId();
|
||||
#else
|
||||
unsigned long iThreadId = (unsigned long)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
|
||||
#else
|
||||
fprintf(file, "%s\t%s\n", szTime, tmp2);
|
||||
#endif
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef debug
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
|
||||
#else
|
||||
void debug(const char* msg, ...)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char tmp1[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp1, 1024, msg, ap);
|
||||
tmp1[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
char tmp2[1024];
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
if (szFuncname)
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, Util::BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, Util::BaseFileName(szFilename), iLineNr);
|
||||
}
|
||||
#else
|
||||
snprintf(tmp2, 1024, "%s", tmp1);
|
||||
#endif
|
||||
tmp2[1024-1] = '\0';
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
if (!g_pOptions)
|
||||
{
|
||||
if (g_pLog->m_bExtraDebug)
|
||||
{
|
||||
printf("%s\n", tmp2);
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkDebug, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void error(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("ERROR\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkError, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void warn(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("WARNING\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkWarning, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void info(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("INFO\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkInfo, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void detail(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DETAIL\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkDetail, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void abort(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
printf("\n%s", tmp2);
|
||||
|
||||
g_pLog->Filelog(tmp2);
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//************************************************************
|
||||
// Message
|
||||
|
||||
Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText)
|
||||
{
|
||||
m_iID = iID;
|
||||
m_eKind = eKind;
|
||||
m_tTime = tTime;
|
||||
if (szText)
|
||||
{
|
||||
m_szText = strdup(szText);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szText = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Message::~ Message()
|
||||
{
|
||||
if (m_szText)
|
||||
{
|
||||
free(m_szText);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages* Log::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void Log::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::ResetLog()
|
||||
{
|
||||
remove(g_pOptions->GetLogFile());
|
||||
}
|
||||
121
Log.h
Normal file
121
Log.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LOG_H
|
||||
#define LOG_H
|
||||
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
void error(const char* msg, ...);
|
||||
void warn(const char* msg, ...);
|
||||
void info(const char* msg, ...);
|
||||
void detail(const char* msg, ...);
|
||||
void abort(const char* msg, ...);
|
||||
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
void debug(const char* msg, ...);
|
||||
#endif
|
||||
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
mkInfo,
|
||||
mkWarning,
|
||||
mkError,
|
||||
mkDebug,
|
||||
mkDetail
|
||||
};
|
||||
|
||||
private:
|
||||
unsigned int m_iID;
|
||||
EKind m_eKind;
|
||||
time_t m_tTime;
|
||||
char* m_szText;
|
||||
|
||||
public:
|
||||
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
|
||||
~Message();
|
||||
unsigned int GetID() { return m_iID; }
|
||||
EKind GetKind() { return m_eKind; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
const char* GetText() { return m_szText; }
|
||||
};
|
||||
|
||||
class Log
|
||||
{
|
||||
public:
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
char* m_szLogFilename;
|
||||
unsigned int m_iIDGen;
|
||||
#ifdef DEBUG
|
||||
bool m_bExtraDebug;
|
||||
#endif
|
||||
|
||||
void Filelog(const char* msg, ...);
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
friend void error(const char* msg, ...);
|
||||
friend void warn(const char* msg, ...);
|
||||
friend void info(const char* msg, ...);
|
||||
friend void abort(const char* msg, ...);
|
||||
friend void detail(const char* msg, ...);
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
friend void debug(const char* msg, ...);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Log();
|
||||
~Log();
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
void ResetLog();
|
||||
};
|
||||
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
#ifdef DEBUG
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
extern Log* g_pLog;
|
||||
|
||||
#endif
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,15 +15,43 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
#include "LoggableFrontend.h"
|
||||
#include "Log.h"
|
||||
|
||||
LoggableFrontend::LoggableFrontend()
|
||||
{
|
||||
debug("Creating LoggableFrontend");
|
||||
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
}
|
||||
|
||||
void LoggableFrontend::Run()
|
||||
{
|
||||
debug("Entering LoggableFrontend-loop");
|
||||
@@ -31,7 +59,7 @@ void LoggableFrontend::Run()
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
Wait(m_updateInterval);
|
||||
usleep(m_iUpdateInterval * 1000);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
@@ -51,24 +79,23 @@ void LoggableFrontend::Update()
|
||||
|
||||
BeforePrint();
|
||||
|
||||
Log::Messages* pMessages = LockMessages();
|
||||
if (!pMessages->empty())
|
||||
{
|
||||
GuardedMessageList messages = GuardMessages();
|
||||
if (!messages->empty())
|
||||
Message* pFirstMessage = pMessages->front();
|
||||
int iStart = m_iNeededLogFirstID - pFirstMessage->GetID() + 1;
|
||||
if (iStart < 0)
|
||||
{
|
||||
Message& firstMessage = messages->front();
|
||||
int start = m_neededLogFirstId - firstMessage.GetId() + 1;
|
||||
if (start < 0)
|
||||
{
|
||||
PrintSkip();
|
||||
start = 0;
|
||||
}
|
||||
for (uint32 i = (uint32)start; i < messages->size(); i++)
|
||||
{
|
||||
PrintMessage(messages->at(i));
|
||||
m_neededLogFirstId = messages->at(i).GetId();
|
||||
}
|
||||
PrintSkip();
|
||||
iStart = 0;
|
||||
}
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
PrintMessage((*pMessages)[i]);
|
||||
m_iNeededLogFirstID = (*pMessages)[i]->GetID();
|
||||
}
|
||||
}
|
||||
UnlockMessages();
|
||||
|
||||
PrintStatus();
|
||||
|
||||
@@ -77,16 +104,15 @@ void LoggableFrontend::Update()
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintMessage(Message& message)
|
||||
void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
#ifdef WIN32
|
||||
CString cmsg = message.GetText();
|
||||
CharToOem(cmsg, cmsg);
|
||||
const char* msg = cmsg;
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
CharToOem(msg, msg);
|
||||
#else
|
||||
const char* msg = message.GetText();
|
||||
const char* msg = pMessage->GetText();
|
||||
#endif
|
||||
switch (message.GetKind())
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", msg);
|
||||
@@ -104,6 +130,9 @@ void LoggableFrontend::PrintMessage(Message& message)
|
||||
printf("[DETAIL] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
#ifdef WIN32
|
||||
free(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintSkip()
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,12 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -28,13 +33,16 @@
|
||||
class LoggableFrontend : public Frontend
|
||||
{
|
||||
protected:
|
||||
virtual void Run();
|
||||
virtual void Update();
|
||||
virtual void BeforePrint() {};
|
||||
virtual void BeforeExit() {};
|
||||
virtual void PrintMessage(Message& message);
|
||||
virtual void PrintStatus() {};
|
||||
virtual void PrintSkip();
|
||||
|
||||
virtual void Run();
|
||||
virtual void Update();
|
||||
virtual void BeforePrint() {};
|
||||
virtual void BeforeExit() {};
|
||||
virtual void PrintMessage(Message* pMessage);
|
||||
virtual void PrintStatus() {};
|
||||
virtual void PrintSkip();
|
||||
public:
|
||||
LoggableFrontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
565
Makefile.am
565
Makefile.am
@@ -1,554 +1,19 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2019 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
bin_PROGRAMS = nzbget
|
||||
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
|
||||
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
|
||||
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
|
||||
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
|
||||
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
|
||||
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
|
||||
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
|
||||
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
|
||||
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
|
||||
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
|
||||
|
||||
nzbget_SOURCES = \
|
||||
daemon/connect/Connection.cpp \
|
||||
daemon/connect/Connection.h \
|
||||
daemon/connect/TlsSocket.cpp \
|
||||
daemon/connect/TlsSocket.h \
|
||||
daemon/connect/WebDownloader.cpp \
|
||||
daemon/connect/WebDownloader.h \
|
||||
daemon/extension/FeedScript.cpp \
|
||||
daemon/extension/FeedScript.h \
|
||||
daemon/extension/CommandScript.cpp \
|
||||
daemon/extension/CommandScript.h \
|
||||
daemon/extension/NzbScript.cpp \
|
||||
daemon/extension/NzbScript.h \
|
||||
daemon/extension/PostScript.cpp \
|
||||
daemon/extension/PostScript.h \
|
||||
daemon/extension/QueueScript.cpp \
|
||||
daemon/extension/QueueScript.h \
|
||||
daemon/extension/ScanScript.cpp \
|
||||
daemon/extension/ScanScript.h \
|
||||
daemon/extension/SchedulerScript.cpp \
|
||||
daemon/extension/SchedulerScript.h \
|
||||
daemon/extension/ScriptConfig.cpp \
|
||||
daemon/extension/ScriptConfig.h \
|
||||
daemon/feed/FeedCoordinator.cpp \
|
||||
daemon/feed/FeedCoordinator.h \
|
||||
daemon/feed/FeedFile.cpp \
|
||||
daemon/feed/FeedFile.h \
|
||||
daemon/feed/FeedFilter.cpp \
|
||||
daemon/feed/FeedFilter.h \
|
||||
daemon/feed/FeedInfo.cpp \
|
||||
daemon/feed/FeedInfo.h \
|
||||
daemon/frontend/ColoredFrontend.cpp \
|
||||
daemon/frontend/ColoredFrontend.h \
|
||||
daemon/frontend/Frontend.cpp \
|
||||
daemon/frontend/Frontend.h \
|
||||
daemon/frontend/LoggableFrontend.cpp \
|
||||
daemon/frontend/LoggableFrontend.h \
|
||||
daemon/frontend/NCursesFrontend.cpp \
|
||||
daemon/frontend/NCursesFrontend.h \
|
||||
daemon/main/CommandLineParser.cpp \
|
||||
daemon/main/CommandLineParser.h \
|
||||
daemon/main/DiskService.cpp \
|
||||
daemon/main/DiskService.h \
|
||||
daemon/main/Maintenance.cpp \
|
||||
daemon/main/Maintenance.h \
|
||||
daemon/main/nzbget.cpp \
|
||||
daemon/main/nzbget.h \
|
||||
daemon/main/Options.cpp \
|
||||
daemon/main/Options.h \
|
||||
daemon/main/WorkState.cpp \
|
||||
daemon/main/WorkState.h \
|
||||
daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h \
|
||||
daemon/main/StackTrace.cpp \
|
||||
daemon/main/StackTrace.h \
|
||||
daemon/nntp/ArticleDownloader.cpp \
|
||||
daemon/nntp/ArticleDownloader.h \
|
||||
daemon/nntp/ArticleWriter.cpp \
|
||||
daemon/nntp/ArticleWriter.h \
|
||||
daemon/nntp/Decoder.cpp \
|
||||
daemon/nntp/Decoder.h \
|
||||
daemon/nntp/NewsServer.cpp \
|
||||
daemon/nntp/NewsServer.h \
|
||||
daemon/nntp/NntpConnection.cpp \
|
||||
daemon/nntp/NntpConnection.h \
|
||||
daemon/nntp/ServerPool.cpp \
|
||||
daemon/nntp/ServerPool.h \
|
||||
daemon/nntp/StatMeter.cpp \
|
||||
daemon/nntp/StatMeter.h \
|
||||
daemon/postprocess/Cleanup.cpp \
|
||||
daemon/postprocess/Cleanup.h \
|
||||
daemon/postprocess/DupeMatcher.cpp \
|
||||
daemon/postprocess/DupeMatcher.h \
|
||||
daemon/postprocess/ParChecker.cpp \
|
||||
daemon/postprocess/ParChecker.h \
|
||||
daemon/postprocess/ParParser.cpp \
|
||||
daemon/postprocess/ParParser.h \
|
||||
daemon/postprocess/ParRenamer.cpp \
|
||||
daemon/postprocess/ParRenamer.h \
|
||||
daemon/postprocess/PrePostProcessor.cpp \
|
||||
daemon/postprocess/PrePostProcessor.h \
|
||||
daemon/postprocess/RarRenamer.cpp \
|
||||
daemon/postprocess/RarRenamer.h \
|
||||
daemon/postprocess/RarReader.cpp \
|
||||
daemon/postprocess/RarReader.h \
|
||||
daemon/postprocess/Rename.cpp \
|
||||
daemon/postprocess/Rename.h \
|
||||
daemon/postprocess/Repair.cpp \
|
||||
daemon/postprocess/Repair.h \
|
||||
daemon/postprocess/Unpack.cpp \
|
||||
daemon/postprocess/Unpack.h \
|
||||
daemon/postprocess/DirectUnpack.cpp \
|
||||
daemon/postprocess/DirectUnpack.h \
|
||||
daemon/queue/DirectRenamer.cpp \
|
||||
daemon/queue/DirectRenamer.h \
|
||||
daemon/queue/DiskState.cpp \
|
||||
daemon/queue/DiskState.h \
|
||||
daemon/queue/DownloadInfo.cpp \
|
||||
daemon/queue/DownloadInfo.h \
|
||||
daemon/queue/DupeCoordinator.cpp \
|
||||
daemon/queue/DupeCoordinator.h \
|
||||
daemon/queue/HistoryCoordinator.cpp \
|
||||
daemon/queue/HistoryCoordinator.h \
|
||||
daemon/queue/NzbFile.cpp \
|
||||
daemon/queue/NzbFile.h \
|
||||
daemon/queue/QueueCoordinator.cpp \
|
||||
daemon/queue/QueueCoordinator.h \
|
||||
daemon/queue/QueueEditor.cpp \
|
||||
daemon/queue/QueueEditor.h \
|
||||
daemon/queue/Scanner.cpp \
|
||||
daemon/queue/Scanner.h \
|
||||
daemon/queue/UrlCoordinator.cpp \
|
||||
daemon/queue/UrlCoordinator.h \
|
||||
daemon/remote/BinRpc.cpp \
|
||||
daemon/remote/BinRpc.h \
|
||||
daemon/remote/MessageBase.h \
|
||||
daemon/remote/RemoteClient.cpp \
|
||||
daemon/remote/RemoteClient.h \
|
||||
daemon/remote/RemoteServer.cpp \
|
||||
daemon/remote/RemoteServer.h \
|
||||
daemon/remote/WebServer.cpp \
|
||||
daemon/remote/WebServer.h \
|
||||
daemon/remote/XmlRpc.cpp \
|
||||
daemon/remote/XmlRpc.h \
|
||||
daemon/util/Log.cpp \
|
||||
daemon/util/Log.h \
|
||||
daemon/util/NString.cpp \
|
||||
daemon/util/NString.h \
|
||||
daemon/util/Container.h \
|
||||
daemon/util/Observer.cpp \
|
||||
daemon/util/Observer.h \
|
||||
daemon/util/Script.cpp \
|
||||
daemon/util/Script.h \
|
||||
daemon/util/Thread.cpp \
|
||||
daemon/util/Thread.h \
|
||||
daemon/util/Service.cpp \
|
||||
daemon/util/Service.h \
|
||||
daemon/util/FileSystem.cpp \
|
||||
daemon/util/FileSystem.h \
|
||||
daemon/util/Util.cpp \
|
||||
daemon/util/Util.h \
|
||||
daemon/nserv/NServMain.h \
|
||||
daemon/nserv/NServMain.cpp \
|
||||
daemon/nserv/NServFrontend.h \
|
||||
daemon/nserv/NServFrontend.cpp \
|
||||
daemon/nserv/NntpServer.h \
|
||||
daemon/nserv/NntpServer.cpp \
|
||||
daemon/nserv/NzbGenerator.h \
|
||||
daemon/nserv/NzbGenerator.cpp \
|
||||
daemon/nserv/YEncoder.h \
|
||||
daemon/nserv/YEncoder.cpp \
|
||||
code_revision.cpp
|
||||
|
||||
if WITH_PAR2
|
||||
nzbget_SOURCES += \
|
||||
lib/par2/commandline.cpp \
|
||||
lib/par2/commandline.h \
|
||||
lib/par2/crc.cpp \
|
||||
lib/par2/crc.h \
|
||||
lib/par2/creatorpacket.cpp \
|
||||
lib/par2/creatorpacket.h \
|
||||
lib/par2/criticalpacket.cpp \
|
||||
lib/par2/criticalpacket.h \
|
||||
lib/par2/datablock.cpp \
|
||||
lib/par2/datablock.h \
|
||||
lib/par2/descriptionpacket.cpp \
|
||||
lib/par2/descriptionpacket.h \
|
||||
lib/par2/diskfile.cpp \
|
||||
lib/par2/diskfile.h \
|
||||
lib/par2/filechecksummer.cpp \
|
||||
lib/par2/filechecksummer.h \
|
||||
lib/par2/galois.cpp \
|
||||
lib/par2/galois.h \
|
||||
lib/par2/letype.h \
|
||||
lib/par2/mainpacket.cpp \
|
||||
lib/par2/mainpacket.h \
|
||||
lib/par2/md5.cpp \
|
||||
lib/par2/md5.h \
|
||||
lib/par2/par2cmdline.h \
|
||||
lib/par2/par2fileformat.cpp \
|
||||
lib/par2/par2fileformat.h \
|
||||
lib/par2/par2repairer.cpp \
|
||||
lib/par2/par2repairer.h \
|
||||
lib/par2/par2repairersourcefile.cpp \
|
||||
lib/par2/par2repairersourcefile.h \
|
||||
lib/par2/parheaders.cpp \
|
||||
lib/par2/parheaders.h \
|
||||
lib/par2/recoverypacket.cpp \
|
||||
lib/par2/recoverypacket.h \
|
||||
lib/par2/reedsolomon.cpp \
|
||||
lib/par2/reedsolomon.h \
|
||||
lib/par2/verificationhashtable.cpp \
|
||||
lib/par2/verificationhashtable.h \
|
||||
lib/par2/verificationpacket.cpp \
|
||||
lib/par2/verificationpacket.h
|
||||
endif
|
||||
|
||||
# Simd decoder and Crc32
|
||||
nzbget_SOURCES += \
|
||||
lib/yencode/YEncode.h \
|
||||
lib/yencode/SimdInit.cpp \
|
||||
lib/yencode/SimdDecoder.cpp \
|
||||
lib/yencode/ScalarDecoder.cpp \
|
||||
lib/yencode/Sse2Decoder.cpp \
|
||||
lib/yencode/Ssse3Decoder.cpp \
|
||||
lib/yencode/PclmulCrc.cpp \
|
||||
lib/yencode/NeonDecoder.cpp \
|
||||
lib/yencode/AcleCrc.cpp \
|
||||
lib/yencode/SliceCrc.cpp
|
||||
|
||||
lib/yencode/Sse2Decoder.$(OBJEXT) : CXXFLAGS+=$(SSE2_CXXFLAGS)
|
||||
lib/yencode/Ssse3Decoder.$(OBJEXT) : CXXFLAGS+=$(SSSE3_CXXFLAGS)
|
||||
lib/yencode/PclmulCrc.$(OBJEXT) : CXXFLAGS+=$(PCLMUL_CXXFLAGS)
|
||||
lib/yencode/NeonDecoder.$(OBJEXT) : CXXFLAGS+=$(NEON_CXXFLAGS)
|
||||
lib/yencode/AcleCrc.$(OBJEXT) : CXXFLAGS+=$(ACLECRC_CXXFLAGS)
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(srcdir)/daemon/connect \
|
||||
-I$(srcdir)/daemon/extension \
|
||||
-I$(srcdir)/daemon/feed \
|
||||
-I$(srcdir)/daemon/frontend \
|
||||
-I$(srcdir)/daemon/main \
|
||||
-I$(srcdir)/daemon/nntp \
|
||||
-I$(srcdir)/daemon/postprocess \
|
||||
-I$(srcdir)/daemon/queue \
|
||||
-I$(srcdir)/daemon/remote \
|
||||
-I$(srcdir)/daemon/util \
|
||||
-I$(srcdir)/daemon/nserv \
|
||||
-I$(srcdir)/lib/par2 \
|
||||
-I$(srcdir)/lib/yencode
|
||||
|
||||
if WITH_TESTS
|
||||
nzbget_SOURCES += \
|
||||
lib/catch/catch.h \
|
||||
tests/suite/TestMain.cpp \
|
||||
tests/suite/TestMain.h \
|
||||
tests/suite/TestUtil.cpp \
|
||||
tests/suite/TestUtil.h \
|
||||
tests/main/CommandLineParserTest.cpp \
|
||||
tests/main/OptionsTest.cpp \
|
||||
tests/feed/FeedFilterTest.cpp \
|
||||
tests/postprocess/DupeMatcherTest.cpp \
|
||||
tests/postprocess/RarRenamerTest.cpp \
|
||||
tests/postprocess/RarReaderTest.cpp \
|
||||
tests/postprocess/DirectUnpackTest.cpp \
|
||||
tests/queue/NzbFileTest.cpp \
|
||||
tests/nntp/ServerPoolTest.cpp \
|
||||
tests/util/FileSystemTest.cpp \
|
||||
tests/util/NStringTest.cpp \
|
||||
tests/util/UtilTest.cpp
|
||||
|
||||
if WITH_PAR2
|
||||
nzbget_SOURCES += \
|
||||
tests/postprocess/ParCheckerTest.cpp \
|
||||
tests/postprocess/ParRenamerTest.cpp
|
||||
endif
|
||||
|
||||
AM_CPPFLAGS += \
|
||||
-I$(srcdir)/lib/catch \
|
||||
-I$(srcdir)/tests/suite
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(windows_FILES) \
|
||||
$(osx_FILES) \
|
||||
$(linux_FILES) \
|
||||
$(testdata_FILES) \
|
||||
$(par2doc_FILES)
|
||||
|
||||
windows_FILES = \
|
||||
daemon/windows/StdAfx.cpp \
|
||||
daemon/windows/WinService.cpp \
|
||||
daemon/windows/WinService.h \
|
||||
daemon/windows/WinConsole.cpp \
|
||||
daemon/windows/WinConsole.h \
|
||||
nzbget.vcxproj \
|
||||
windows/nzbget-command-shell.bat \
|
||||
windows/install-update.bat \
|
||||
windows/README-WINDOWS.txt \
|
||||
windows/package-info.json \
|
||||
windows/resources/mainicon.ico \
|
||||
windows/resources/nzbget.rc \
|
||||
windows/resources/resource.h \
|
||||
windows/resources/trayicon_idle.ico \
|
||||
windows/resources/trayicon_paused.ico \
|
||||
windows/resources/trayicon_working.ico \
|
||||
windows/resources/install.bmp \
|
||||
windows/resources/uninstall.bmp \
|
||||
windows/nzbget-setup.nsi
|
||||
|
||||
osx_FILES = \
|
||||
osx/App_Prefix.pch \
|
||||
osx/NZBGet-Info.plist \
|
||||
osx/DaemonController.h \
|
||||
osx/DaemonController.m \
|
||||
osx/MainApp.h \
|
||||
osx/MainApp.m \
|
||||
osx/MainApp.xib \
|
||||
osx/PFMoveApplication.h \
|
||||
osx/PFMoveApplication.m \
|
||||
osx/PreferencesDialog.h \
|
||||
osx/PreferencesDialog.m \
|
||||
osx/PreferencesDialog.xib \
|
||||
osx/RPC.h \
|
||||
osx/RPC.m \
|
||||
osx/WebClient.h \
|
||||
osx/WebClient.m \
|
||||
osx/WelcomeDialog.h \
|
||||
osx/WelcomeDialog.m \
|
||||
osx/WelcomeDialog.xib \
|
||||
osx/NZBGet.xcodeproj/project.pbxproj \
|
||||
osx/Resources/Images/mainicon.icns \
|
||||
osx/Resources/Images/statusicon.png \
|
||||
osx/Resources/Images/statusicon@2x.png \
|
||||
osx/Resources/licenses/license-bootstrap.txt \
|
||||
osx/Resources/licenses/license-jquery-GPL.txt \
|
||||
osx/Resources/licenses/license-jquery-MIT.txt \
|
||||
osx/Resources/Credits.rtf \
|
||||
osx/Resources/Localizable.strings \
|
||||
osx/Resources/Welcome.rtf
|
||||
|
||||
linux_FILES = \
|
||||
linux/installer.sh \
|
||||
linux/install-update.sh \
|
||||
linux/package-info.json \
|
||||
linux/build-info.txt \
|
||||
linux/build-nzbget \
|
||||
linux/build-unpack \
|
||||
linux/build-toolchain-android \
|
||||
linux/build-toolchain-freebsd
|
||||
|
||||
doc_FILES = \
|
||||
README \
|
||||
ChangeLog \
|
||||
COPYING
|
||||
|
||||
par2doc_FILES = \
|
||||
lib/par2/AUTHORS \
|
||||
lib/par2/README
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf
|
||||
|
||||
webui_FILES = \
|
||||
webui/index.html \
|
||||
webui/index.js \
|
||||
webui/downloads.js \
|
||||
webui/edit.js \
|
||||
webui/fasttable.js \
|
||||
webui/history.js \
|
||||
webui/messages.js \
|
||||
webui/status.js \
|
||||
webui/style.css \
|
||||
webui/upload.js \
|
||||
webui/util.js \
|
||||
webui/config.js \
|
||||
webui/feed.js \
|
||||
webui/lib/bootstrap.js \
|
||||
webui/lib/bootstrap.min.js \
|
||||
webui/lib/bootstrap.css \
|
||||
webui/lib/jquery.js \
|
||||
webui/lib/jquery.min.js \
|
||||
webui/lib/raphael.js \
|
||||
webui/lib/raphael.min.js \
|
||||
webui/lib/elycharts.js \
|
||||
webui/lib/elycharts.min.js \
|
||||
webui/img/icons.png \
|
||||
webui/img/icons-2x.png \
|
||||
webui/img/transmit.gif \
|
||||
webui/img/transmit-file.gif \
|
||||
webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png \
|
||||
webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif \
|
||||
webui/img/favicon-256x256-opaque.png \
|
||||
webui/img/favicon-256x256.png
|
||||
|
||||
scripts_FILES = \
|
||||
scripts/EMail.py \
|
||||
scripts/Logger.py
|
||||
|
||||
testdata_FILES = \
|
||||
tests/testdata/dupematcher1/testfile.part01.rar \
|
||||
tests/testdata/dupematcher1/testfile.part24.rar \
|
||||
tests/testdata/dupematcher2/testfile.part04.rar \
|
||||
tests/testdata/dupematcher2/testfile.part43.rar \
|
||||
tests/testdata/nzbfile/dotless.nzb \
|
||||
tests/testdata/nzbfile/dotless.txt \
|
||||
tests/testdata/nzbfile/plain.nzb \
|
||||
tests/testdata/nzbfile/plain.txt \
|
||||
tests/testdata/parchecker/crc.txt \
|
||||
tests/testdata/parchecker/testfile.dat \
|
||||
tests/testdata/parchecker/testfile.nfo \
|
||||
tests/testdata/parchecker/testfile.par2 \
|
||||
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol03+3.PAR2 \
|
||||
tests/testdata/parchecker2/crc.txt \
|
||||
tests/testdata/parchecker2/testfile.7z.001 \
|
||||
tests/testdata/parchecker2/testfile.7z.002 \
|
||||
tests/testdata/parchecker2/testfile.7z.003 \
|
||||
tests/testdata/parchecker2/testfile.7z.par2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol0+1.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol1+2.PAR2 \
|
||||
tests/testdata/parchecker2/testfile.7z.vol3+3.PAR2 \
|
||||
tests/testdata/rarrenamer/testfile3.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.rar \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.r00 \
|
||||
tests/testdata/rarrenamer/testfile3oldnam.r01 \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3encdata.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile3encnam.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5encdata.part03.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part01.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part02.rar \
|
||||
tests/testdata/rarrenamer/testfile5encnam.part03.rar
|
||||
|
||||
# Install
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
scriptsdir = $(datadir)/nzbget
|
||||
nobase_dist_scripts_SCRIPTS = $(scripts_FILES)
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
|
||||
# 1) copy the original file to original.temp (delete existing original.temp, if any);
|
||||
# 2) sed < original.temp > original
|
||||
# 3) delete original.temp
|
||||
# These steps ensure that the output file has the same permissions as the original file.
|
||||
|
||||
# Prepare example configuration file
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:typically installed to /usr/local/share/nzbget/scripts:installed to $(scriptsdir)/scripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
|
||||
# Install configuration files into /etc
|
||||
# (only if they do not exist there to prevent override by update)
|
||||
install-conf:
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
|
||||
|
||||
# Determining git revision:
|
||||
# 1) If directory ".git" exists we take revision from git log.
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".git" doesn't exists we keep and reuse file "code_revision.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".git" nor file "code_revision.cpp" are available
|
||||
# we create new file "code_revision.c" with empty revision number.
|
||||
code_revision.cpp: FORCE
|
||||
@ if test -d ./.git ; then \
|
||||
B=`git branch | sed -n -e 's/^\* \(.*\)/\1/p'`; \
|
||||
M=`git status --porcelain` ; \
|
||||
if test "$$M" != "" ; then \
|
||||
M="M" ; \
|
||||
fi ; \
|
||||
if test "$$B" = "master" ; then \
|
||||
V="$$M" ; \
|
||||
elif test "$$B" = "develop" ; then \
|
||||
V=`git rev-list HEAD | wc -l | xargs` ; \
|
||||
V="$${V}$$M" ; \
|
||||
else \
|
||||
V=`git rev-list HEAD | wc -l | xargs` ; \
|
||||
V="$${V}$$M ($$B)" ; \
|
||||
fi ; \
|
||||
H=`test -f ./code_revision.cpp && head -n 1 code_revision.cpp`; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "#include \"nzbget.h\"" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"$$V\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi \
|
||||
elif test -f ./code_revision.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "#include \"nzbget.h\"" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
# Ignore "code_revision.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
EXTRA_DIST = nzbget.conf.example \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
find $(distdir)/daemon -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/lib -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/tests -type f -print -exec chmod -x {} \;
|
||||
|
||||
|
||||
8
Makefile.cvs
Normal file
8
Makefile.cvs
Normal file
@@ -0,0 +1,8 @@
|
||||
default: all
|
||||
|
||||
all:
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
autoconf
|
||||
|
||||
2051
Makefile.in
2051
Makefile.in
File diff suppressed because it is too large
Load Diff
348
MessageBase.h
Normal file
348
MessageBase.h
Normal file
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
/**
|
||||
* NZBGet communication protocol uses only two basic data types: integer and char.
|
||||
* Integer values are passed using network byte order (Big-Endian).
|
||||
* Use function "htonl" and "ntohl" to convert integers to/from machine
|
||||
' (host) byte order.
|
||||
* All char-strings ends with NULL-char.
|
||||
*/
|
||||
|
||||
// Possible values for field "m_iType" of struct "SNZBRequestBase":
|
||||
enum eRemoteRequest
|
||||
{
|
||||
eRemoteRequestDownload = 1,
|
||||
eRemoteRequestPauseUnpause,
|
||||
eRemoteRequestList,
|
||||
eRemoteRequestSetDownloadRate,
|
||||
eRemoteRequestDumpDebug,
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestVersion,
|
||||
eRemoteRequestPostQueue,
|
||||
eRemoteRequestWriteLog
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
// File-Actions affect one file, Group-Actions affect all files in group.
|
||||
// Group is a list of files, added to queue from one NZB-File.
|
||||
enum eRemoteEditAction
|
||||
{
|
||||
eRemoteEditActionFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionFileMoveTop, // move to top of queue
|
||||
eRemoteEditActionFileMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionFilePause, // pause
|
||||
eRemoteEditActionFileResume, // resume (unpause)
|
||||
eRemoteEditActionFileDelete, // delete
|
||||
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionGroupMoveTop, // move to top of queue
|
||||
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionGroupPause, // pause
|
||||
eRemoteEditActionGroupResume, // resume (unpause)
|
||||
eRemoteEditActionGroupDelete, // delete
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionGroupPauseExtraPars // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
};
|
||||
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
struct SNZBRequestBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
|
||||
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
|
||||
};
|
||||
|
||||
// The basic SNZBResposneBase struct, used in all responses
|
||||
struct SNZBResponseBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
};
|
||||
|
||||
// A download request
|
||||
struct SNZBDownloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A download response
|
||||
struct SNZBDownloadResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A list and status request
|
||||
struct SNZBListRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList; // 1 - return file list
|
||||
int32_t m_bServerState; // 1 - return server state
|
||||
};
|
||||
|
||||
// A list response
|
||||
struct SNZBListResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
|
||||
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
|
||||
int32_t m_bServerPaused; // 1 - server is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
|
||||
int32_t m_iUpTimeSec; // Server up time in seconds
|
||||
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
|
||||
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
|
||||
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
|
||||
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
|
||||
int32_t m_iNrTrailingEntries; // Number of List-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list response entry
|
||||
struct SNZBListResponseEntry
|
||||
{
|
||||
int32_t m_iID; // Entry-ID
|
||||
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bPaused; // 1 - file is paused
|
||||
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szSubject[m_iSubjectLen]; // variable sized
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iIDFrom; // Only one of these two parameters
|
||||
int32_t m_iLines; // can be set. The another one must be set to "0".
|
||||
};
|
||||
|
||||
// A log response
|
||||
struct SNZBLogResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
|
||||
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A log response entry
|
||||
struct SNZBLogResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Log-entry
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
|
||||
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTextLen]; // variable sized
|
||||
};
|
||||
|
||||
// A Pause/Unpause request
|
||||
struct SNZBPauseUnpauseRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
|
||||
};
|
||||
|
||||
// A Pause/Unpause response
|
||||
struct SNZBPauseUnpauseResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request setting the download rate
|
||||
struct SNZBSetDownloadRateRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
|
||||
};
|
||||
|
||||
// A setting download rate response
|
||||
struct SNZBSetDownloadRateResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// An edit queue request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
|
||||
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
|
||||
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure
|
||||
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
struct SNZBEditQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request dumping of debug info
|
||||
struct SNZBDumpDebugRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Dumping of debug response
|
||||
struct SNZBDumpDebugResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Shutdown server request
|
||||
struct SNZBShutdownRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Shutdown server response
|
||||
struct SNZBShutdownResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Server version request
|
||||
struct SNZBVersionRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Server version response
|
||||
struct SNZBVersionResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// PostQueue request
|
||||
struct SNZBPostQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// A PostQueue response
|
||||
struct SNZBPostQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBPostQueueResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of PostQueue-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all PostQueue-entries, following to this structure
|
||||
// SNZBPostQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A PostQueue response entry
|
||||
struct SNZBPostQueueResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Post-entry
|
||||
int32_t m_iStage; // See PrePostProcessor::EPostJobStage
|
||||
int32_t m_iStageProgress; // Progress of current stage, value in range 0..1000
|
||||
int32_t m_iFileProgress; // Progress of current file, value in range 0..1000
|
||||
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
|
||||
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
|
||||
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szParFilename[m_iParFilename]; // variable sized
|
||||
//char m_szInfoName[m_iInfoNameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
|
||||
};
|
||||
|
||||
// Write log request
|
||||
struct SNZBWriteLogRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Write log response
|
||||
struct SNZBWriteLogResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
#endif
|
||||
1324
NCursesFrontend.cpp
Normal file
1324
NCursesFrontend.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
NCursesFrontend.h
Normal file
122
NCursesFrontend.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NCURSESFRONTEND_H
|
||||
#define NCURSESFRONTEND_H
|
||||
|
||||
#ifndef DISABLE_CURSES
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NCursesFrontend : public Frontend
|
||||
{
|
||||
private:
|
||||
|
||||
enum EInputMode
|
||||
{
|
||||
eNormal,
|
||||
eEditQueue,
|
||||
eDownloadRate
|
||||
};
|
||||
|
||||
bool m_bUseColor;
|
||||
int m_iDataUpdatePos;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
int m_iQueueWinHeight;
|
||||
int m_iQueueWinClientHeight;
|
||||
int m_iMessagesWinTop;
|
||||
int m_iMessagesWinHeight;
|
||||
int m_iMessagesWinClientHeight;
|
||||
int m_iSelectedQueueEntry;
|
||||
int m_iLastEditEntry;
|
||||
bool m_bLastPausePars;
|
||||
int m_iQueueScrollOffset;
|
||||
GroupQueue m_groupQueue;
|
||||
|
||||
// Inputting numbres
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
|
||||
#ifdef WIN32
|
||||
CHAR_INFO* m_pScreenBuffer;
|
||||
CHAR_INFO* m_pOldScreenBuffer;
|
||||
int m_iScreenBufferSize;
|
||||
std::vector<WORD> m_ColorAttr;
|
||||
#else
|
||||
void* m_pWindow; // WINDOW*
|
||||
#endif
|
||||
|
||||
EInputMode m_eInputMode;
|
||||
bool m_bShowNZBname;
|
||||
bool m_bShowTimestamp;
|
||||
bool m_bGroupFiles;
|
||||
float m_QueueWindowPercentage;
|
||||
|
||||
#ifdef WIN32
|
||||
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
|
||||
#endif
|
||||
void PlotLine(const char * szString, int iRow, int iPos, int iColorPair);
|
||||
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
|
||||
void PrintMessages();
|
||||
void PrintQueue();
|
||||
void PrintFileQueue();
|
||||
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
void PrintGroupQueue();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
|
||||
void PrepareGroupQueue();
|
||||
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
|
||||
void ClearGroupQueue();
|
||||
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
|
||||
void PrintKeyInputBar();
|
||||
void PrintStatus();
|
||||
void UpdateInput();
|
||||
void Update();
|
||||
void SetCurrentQueueEntry(int iEntry);
|
||||
void CalcWindowSizes();
|
||||
void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
void RefreshScreen();
|
||||
int ReadConsoleKey();
|
||||
int CalcQueueSize();
|
||||
void NeedUpdateData();
|
||||
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
|
||||
public:
|
||||
NCursesFrontend();
|
||||
virtual ~NCursesFrontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
270
NNTPConnection.cpp
Normal file
270
NNTPConnection.cpp
Normal file
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "NNTPConnection.h"
|
||||
#include "Connection.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
{
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
|
||||
m_bAuthError = false;
|
||||
}
|
||||
|
||||
NNTPConnection::~NNTPConnection()
|
||||
{
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
|
||||
const char* NNTPConnection::Request(char* req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_bAuthError = false;
|
||||
|
||||
WriteLine(req);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
|
||||
//authentication required!
|
||||
if (!Authenticate())
|
||||
{
|
||||
m_bAuthError = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//try again
|
||||
WriteLine(req);
|
||||
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
return answer;
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool NNTPConnection::Authenticate()
|
||||
{
|
||||
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
|
||||
!((NewsServer*)m_pNetAddress)->GetPassword())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return AuthInfoUser();
|
||||
}
|
||||
|
||||
bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
else if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
return AuthInfoUser(++iRecur);
|
||||
}
|
||||
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
{
|
||||
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
|
||||
{
|
||||
// already in group
|
||||
strcpy(m_szLineBuf, "211 ");
|
||||
return m_szLineBuf;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
const char* answer = Request(tmp);
|
||||
if (m_bAuthError)
|
||||
{
|
||||
return answer;
|
||||
}
|
||||
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
m_szActiveGroup = strdup(grp);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Error changing group on %s to %s: %s.",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetServer()->GetHost());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoDisconnect()
|
||||
{
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
}
|
||||
58
NNTPConnection.h
Normal file
58
NNTPConnection.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NNTPCONNECTION_H
|
||||
#define NNTPCONNECTION_H
|
||||
|
||||
#include "NewsServer.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
void Clear();
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
const char* Request(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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,19 +14,32 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "win32.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "WinService.h"
|
||||
#include "NTService.h"
|
||||
|
||||
extern void ExitProc();
|
||||
RunProc Run = nullptr;
|
||||
RunProc Run = NULL;
|
||||
|
||||
char* strServiceName = "NZBGet";
|
||||
SERVICE_STATUS_HANDLE nServiceStatusHandle;
|
||||
SERVICE_STATUS_HANDLE nServiceStatusHandle;
|
||||
DWORD nServiceCurrentStatus;
|
||||
|
||||
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
@@ -56,7 +69,7 @@ BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
void ServiceCtrlHandler(DWORD nControlCode)
|
||||
{
|
||||
switch(nControlCode)
|
||||
{
|
||||
{
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP:
|
||||
nServiceCurrentStatus = SERVICE_STOP_PENDING;
|
||||
@@ -89,7 +102,7 @@ void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Run();
|
||||
|
||||
UpdateServiceStatus(SERVICE_STOPPED, 0);
|
||||
@@ -102,7 +115,7 @@ void StartService(RunProc RunProcPtr)
|
||||
SERVICE_TABLE_ENTRY servicetable[]=
|
||||
{
|
||||
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{nullptr,nullptr}
|
||||
{NULL,NULL}
|
||||
};
|
||||
BOOL success = StartServiceCtrlDispatcher(servicetable);
|
||||
if(!success)
|
||||
@@ -119,18 +132,20 @@ void InstallService(int argc, char *argv[])
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char szExeName[1024];
|
||||
GetModuleFileName(NULL, szExeName, 1024);
|
||||
szExeName[1024-1] = '\0';
|
||||
|
||||
char exeName[1024];
|
||||
GetModuleFileName(nullptr, exeName, 1024);
|
||||
exeName[1024-1] = '\0';
|
||||
|
||||
BString<1024> cmdLine("%s -D", exeName);
|
||||
char szCmdLine[1024];
|
||||
snprintf(szCmdLine, 1024, "%s -D", szExeName);
|
||||
szCmdLine[1024-1] = '\0';
|
||||
|
||||
SC_HANDLE hService = CreateService(scm, strServiceName,
|
||||
strServiceName,
|
||||
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
cmdLine,
|
||||
szCmdLine,
|
||||
0,0,0,0,0);
|
||||
if(!hService)
|
||||
{
|
||||
@@ -152,7 +167,7 @@ void UnInstallService()
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
|
||||
if(!hService)
|
||||
{
|
||||
@@ -185,24 +200,3 @@ void InstallUninstallServiceCheck(int argc, char *argv[])
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsServiceRunning()
|
||||
{
|
||||
SC_HANDLE scm = OpenSCManager(0, 0, 0);
|
||||
if (!scm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, "NZBGet", SERVICE_QUERY_STATUS);
|
||||
SERVICE_STATUS ServiceStatus;
|
||||
bool running = false;
|
||||
if (hService && QueryServiceStatus(hService, &ServiceStatus))
|
||||
{
|
||||
running = ServiceStatus.dwCurrentState != SERVICE_STOPPED;
|
||||
}
|
||||
|
||||
CloseServiceHandle(scm);
|
||||
|
||||
return running;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -14,17 +14,21 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WINSERVICE_H
|
||||
#define WINSERVICE_H
|
||||
#ifndef NTSERVICE_H
|
||||
#define NTSERVICE_H
|
||||
|
||||
typedef void (*RunProc)(void);
|
||||
|
||||
void InstallUninstallServiceCheck(int argc, char *argv[]);
|
||||
void StartService(RunProc RunProcPtr);
|
||||
bool IsServiceRunning();
|
||||
|
||||
#endif
|
||||
592
NZBFile.cpp
Normal file
592
NZBFile.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
m_pNZBInfo = new NZBInfo();
|
||||
m_pNZBInfo->AddReference();
|
||||
m_pNZBInfo->SetFilename(szFileName);
|
||||
BuildDestDirName();
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile::~NZBFile()
|
||||
{
|
||||
debug("Destroying NZBFile");
|
||||
|
||||
// Cleanup
|
||||
if (m_szFileName)
|
||||
{
|
||||
free(m_szFileName);
|
||||
}
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileInfos.clear();
|
||||
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
{
|
||||
debug(" NZBFile %s", m_szFileName);
|
||||
}
|
||||
|
||||
void NZBFile::DetachFileInfos()
|
||||
{
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
|
||||
{
|
||||
return Create(szFileName, szBuffer, iSize, true);
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
|
||||
{
|
||||
return Create(szFileName, NULL, 0, false);
|
||||
}
|
||||
|
||||
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
{
|
||||
// make Article-List big enough
|
||||
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
|
||||
pFileInfo->GetArticles()->push_back(NULL);
|
||||
|
||||
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
|
||||
}
|
||||
|
||||
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
|
||||
{
|
||||
// deleting empty articles
|
||||
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
|
||||
int i = 0;
|
||||
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
|
||||
{
|
||||
if (*it == NULL)
|
||||
{
|
||||
pArticles->erase(it);
|
||||
it = pArticles->begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
{
|
||||
ParseSubject(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
pFileInfo->SetNZBInfo(m_pNZBInfo);
|
||||
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
{
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
|
||||
typedef std::list<char*> TokenList;
|
||||
TokenList tokens;
|
||||
tokens.clear();
|
||||
|
||||
// tokenizing
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
char* start = p;
|
||||
bool quot = false;
|
||||
while (true)
|
||||
{
|
||||
char ch = *p;
|
||||
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
|
||||
if (sep)
|
||||
{
|
||||
// end of token
|
||||
int len = (int)(p - start);
|
||||
if (len > 0)
|
||||
{
|
||||
char* token = (char*)malloc(len + 1);
|
||||
strncpy(token, start, len);
|
||||
token[len] = '\0';
|
||||
tokens.push_back(token);
|
||||
}
|
||||
start = p;
|
||||
if (ch != '\"' || quot)
|
||||
{
|
||||
start++;
|
||||
}
|
||||
quot = *start == '\"';
|
||||
if (quot)
|
||||
{
|
||||
start++;
|
||||
char* q = strchr(start, '\"');
|
||||
if (q)
|
||||
{
|
||||
p = q - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quot = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!tokens.empty())
|
||||
{
|
||||
// finding the best candidate for being a filename
|
||||
char* besttoken = tokens.back();
|
||||
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
|
||||
{
|
||||
char* s = *it;
|
||||
char* p = strchr(s, '.');
|
||||
if (p && (p[1] != '\0'))
|
||||
{
|
||||
besttoken = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pFileInfo->SetFilename(besttoken);
|
||||
|
||||
// free mem
|
||||
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// subject is empty or contains only separators?
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
|
||||
pFileInfo->MakeValidFilename();
|
||||
}
|
||||
|
||||
void NZBFile::BuildDestDirName()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
m_pNZBInfo->GetNiceNZBName(szNiceNZBName, 1024);
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
m_pNZBInfo->SetDestDir(szBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::CheckFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 0;
|
||||
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
|
||||
{
|
||||
iDupe++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than two files have the same parsed filename but different subjects,
|
||||
// this means, that the parsing was not correct.
|
||||
// in this case we take subjects as filenames to prevent
|
||||
// false "duplicate files"-alarm.
|
||||
// It's Ok for just two files to have the same filename, this is
|
||||
// an often case by posting-errors to repost bad files
|
||||
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
|
||||
{
|
||||
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
|
||||
pFileInfo2->MakeValidFilename();
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo2);
|
||||
g_pDiskState->SaveFile(pFileInfo2);
|
||||
pFileInfo2->ClearArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MSXML::IXMLDOMDocumentPtr doc;
|
||||
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Load the XML document file...
|
||||
doc->put_resolveExternals(VARIANT_FALSE);
|
||||
doc->put_validateOnParse(VARIANT_FALSE);
|
||||
doc->put_async(VARIANT_FALSE);
|
||||
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);
|
||||
const char* szErrMsg = r;
|
||||
error("Error parsing nzb-file: %s", szErrMsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
|
||||
{
|
||||
while (char ch = *szFilename++)
|
||||
{
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z') )
|
||||
{
|
||||
*szURL++ = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
*szURL++ = '%';
|
||||
int a = ch >> 4;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
}
|
||||
}
|
||||
*szURL = NULL;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
{
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
|
||||
for (int i = 0; i < fileList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
|
||||
if (!attribute) return false;
|
||||
_bstr_t subject(attribute->Gettext());
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetSubject(subject);
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = groupList->Getitem(g);
|
||||
_bstr_t group = node->Gettext();
|
||||
pFileInfo->GetGroups()->push_back(strdup((const char*)group));
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr segmentList = node->selectNodes("segments/segment");
|
||||
for (int g = 0; g < segmentList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = segmentList->Getitem(g);
|
||||
_bstr_t id = node->Gettext();
|
||||
char szId[2048];
|
||||
snprintf(szId, 2048, "<%s>", (const char*)id);
|
||||
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("number");
|
||||
if (!attribute) return false;
|
||||
_bstr_t number(attribute->Gettext());
|
||||
|
||||
attribute = node->Getattributes()->getNamedItem("bytes");
|
||||
if (!attribute) return false;
|
||||
_bstr_t bytes(attribute->Gettext());
|
||||
|
||||
int partNumber = atoi(number);
|
||||
int lsize = atoi(bytes);
|
||||
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(szId);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
}
|
||||
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
xmlTextReaderPtr doc;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
doc = xmlReaderForFile(szFileName, NULL, 0);
|
||||
}
|
||||
if (!doc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
xmlFreeTextReader(doc);
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(void* nzb)
|
||||
{
|
||||
FileInfo* pFileInfo = NULL;
|
||||
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
|
||||
// walk through whole doc and search for segments-tags
|
||||
int ret = xmlTextReaderRead(node);
|
||||
while (ret == 1)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
xmlChar *name, *value;
|
||||
|
||||
name = xmlTextReaderName(node);
|
||||
if (name == NULL)
|
||||
{
|
||||
name = xmlStrdup(BAD_CAST "--");
|
||||
}
|
||||
value = xmlTextReaderValue(node);
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 1)
|
||||
{
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetFilename(m_szFileName);
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
if (!strcmp("subject",(char*)name))
|
||||
{
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment",(char*)name))
|
||||
{
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
if (!strcmp("bytes",(char*)name))
|
||||
{
|
||||
lsize = atol((char*)value);
|
||||
}
|
||||
if (!strcmp("number",(char*)name))
|
||||
{
|
||||
partNumber = atol((char*)value);
|
||||
}
|
||||
}
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
|
||||
/* Get the #text part */
|
||||
ret = xmlTextReaderRead(node);
|
||||
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
char tmp[2048];
|
||||
snprintf(tmp, 2048, "<%s>", (char*)value);
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(tmp);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
}
|
||||
}
|
||||
else if (!strcmp("group",(char*)name))
|
||||
{
|
||||
ret = xmlTextReaderRead(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->GetGroups()->push_back(strdup((char*)value));
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 15)
|
||||
{
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
69
NZBFile.h
Normal file
69
NZBFile.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBFILE_H
|
||||
#define NZBFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NZBFile
|
||||
{
|
||||
public:
|
||||
typedef std::vector<FileInfo*> FileInfos;
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo);
|
||||
void BuildDestDirName();
|
||||
void CheckFilenames();
|
||||
#ifdef WIN32
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool ParseNZB(void* nzb);
|
||||
#endif
|
||||
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
|
||||
public:
|
||||
virtual ~NZBFile();
|
||||
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
|
||||
static NZBFile* CreateFromFile(const char* szFileName);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
54
NetAddress.cpp
Normal file
54
NetAddress.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NetAddress.h"
|
||||
|
||||
NetAddress::NetAddress(const char* szHost, int iPort)
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
if (szHost)
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
|
||||
NetAddress::~NetAddress()
|
||||
{
|
||||
if (m_szHost)
|
||||
free(m_szHost);
|
||||
m_szHost = NULL;
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,28 +15,30 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BINRPC_H
|
||||
#define BINRPC_H
|
||||
#ifndef NETADDRESS_H
|
||||
#define NETADDRESS_H
|
||||
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class BinRpcProcessor
|
||||
class NetAddress
|
||||
{
|
||||
public:
|
||||
BinRpcProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* connection) { m_connection = connection; }
|
||||
|
||||
private:
|
||||
SNzbRequestBase m_messageBase;
|
||||
Connection* m_connection;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
|
||||
void Dispatch();
|
||||
public:
|
||||
NetAddress(const char* szHost, int iPort);
|
||||
virtual ~NetAddress();
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
65
NewsServer.cpp
Normal file
65
NewsServer.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NewsServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
NewsServer::NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level) : NetAddress(host, port)
|
||||
{
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = level;
|
||||
m_iMaxConnections = maxConnections;
|
||||
|
||||
if (pass)
|
||||
{
|
||||
m_szPassword = strdup(pass);
|
||||
}
|
||||
if (user)
|
||||
{
|
||||
m_szUser = strdup(user);
|
||||
}
|
||||
}
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
}
|
||||
49
NewsServer.h
Normal file
49
NewsServer.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
{
|
||||
private:
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
|
||||
public:
|
||||
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
|
||||
virtual ~NewsServer();
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
int GetLevel() { return m_iLevel; }
|
||||
};
|
||||
|
||||
#endif
|
||||
62
Observer.cpp
Normal file
62
Observer.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "Log.h"
|
||||
|
||||
Subject::Subject()
|
||||
{
|
||||
m_Observers.clear();
|
||||
}
|
||||
|
||||
void Subject::Attach(Observer* Observer)
|
||||
{
|
||||
m_Observers.push_back(Observer);
|
||||
}
|
||||
|
||||
void Subject::Detach(Observer* Observer)
|
||||
{
|
||||
m_Observers.remove(Observer);
|
||||
}
|
||||
|
||||
void Subject::Notify(void* Aspect)
|
||||
{
|
||||
debug("Notifying observers");
|
||||
|
||||
for (std::list<Observer*>::iterator it = m_Observers.begin(); it != m_Observers.end(); it++)
|
||||
{
|
||||
Observer* Observer = *it;
|
||||
Observer->Update(this, Aspect);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2016 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,31 +15,39 @@
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OBSERVER_H
|
||||
#define OBSERVER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
class Observer;
|
||||
|
||||
class Subject
|
||||
{
|
||||
public:
|
||||
void Attach(Observer* observer);
|
||||
void Detach(Observer* observer);
|
||||
void Notify(void* aspect);
|
||||
|
||||
private:
|
||||
std::vector<Observer*> m_observers;
|
||||
std::list<Observer*> m_Observers;
|
||||
|
||||
public:
|
||||
Subject();
|
||||
void Attach(Observer* Observer);
|
||||
void Detach(Observer* Observer);
|
||||
void Notify(void* Aspect);
|
||||
};
|
||||
|
||||
class Observer
|
||||
{
|
||||
protected:
|
||||
virtual void Update(Subject* caller, void* aspect) = 0;
|
||||
friend class Subject;
|
||||
public:
|
||||
virtual ~Observer() {};
|
||||
virtual void Update(Subject* Caller, void* Aspect) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
1273
Options.cpp
Normal file
1273
Options.cpp
Normal file
File diff suppressed because it is too large
Load Diff
269
Options.h
Normal file
269
Options.h
Normal file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
{
|
||||
lpNone,
|
||||
lpOne,
|
||||
lpAll
|
||||
};
|
||||
|
||||
enum EPostLogKind
|
||||
{
|
||||
plNone,
|
||||
plDetail,
|
||||
plInfo,
|
||||
plWarning,
|
||||
plError,
|
||||
plDebug
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
bool m_bConfigInitialized;
|
||||
|
||||
// Options
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
EMessageTarget m_eDebugTarget;
|
||||
EMessageTarget m_eDetailTarget;
|
||||
bool m_bDecode;
|
||||
bool m_bCreateBrokenLog;
|
||||
bool m_bResetLog;
|
||||
int m_iConnectionTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendNZBDir;
|
||||
bool m_bContinuePartial;
|
||||
bool m_bRenameBroken;
|
||||
int m_iRetries;
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szServerIP;
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
bool m_bReloadPostQueue;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
ELoadPars m_eLoadPars;
|
||||
bool m_bParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
bool m_bStrictParName;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
bool m_bCursesNZBName;
|
||||
bool m_bCursesTime;
|
||||
bool m_bCursesGroup;
|
||||
bool m_bCrcCheck;
|
||||
bool m_bRetryOnCrcError;
|
||||
int m_iThreadLimit;
|
||||
bool m_bDirectWrite;
|
||||
int m_iWriteBufferSize;
|
||||
int m_iNzbDirInterval;
|
||||
int m_iNzbDirFileAge;
|
||||
bool m_bParCleanupQueue;
|
||||
int m_iDiskSpace;
|
||||
EPostLogKind m_ePostLogKind;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
bool m_bDaemonMode;
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
char* m_szArgFilename;
|
||||
char* m_szLastArg;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
int m_iWriteLogKind;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
float m_fDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
const char* GetOption(const char* optname);
|
||||
void DelOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
// Options
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
|
||||
bool GetResetLog() const { return m_bResetLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
|
||||
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
|
||||
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
|
||||
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
bool GetDecode() { return m_bDecode; };
|
||||
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
|
||||
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; }
|
||||
char* GetServerIP() { return m_szServerIP; }
|
||||
char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
char* GetLockFile() { return m_szLockFile; }
|
||||
char* GetDaemonUserName() { return m_szDaemonUserName; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
bool GetCreateLog() { return m_bCreateLog; }
|
||||
char* GetLogFile() { return m_szLogFile; }
|
||||
ELoadPars GetLoadPars() { return m_eLoadPars; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
bool GetStrictParName() { return m_bStrictParName; }
|
||||
int GetUMask() { return m_iUMask; }
|
||||
int GetUpdateInterval() {return m_iUpdateInterval; }
|
||||
bool GetCursesNZBName() { return m_bCursesNZBName; }
|
||||
bool GetCursesTime() { return m_bCursesTime; }
|
||||
bool GetCursesGroup() { return m_bCursesGroup; }
|
||||
bool GetCrcCheck() { return m_bCrcCheck; }
|
||||
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
|
||||
int GetThreadLimit() { return m_iThreadLimit; }
|
||||
bool GetDirectWrite() { return m_bDirectWrite; }
|
||||
int GetWriteBufferSize() { return m_iWriteBufferSize; }
|
||||
int GetNzbDirInterval() { return m_iNzbDirInterval; }
|
||||
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
|
||||
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
|
||||
int GetDiskSpace() { return m_iDiskSpace; }
|
||||
EPostLogKind GetPostLogKind() { return m_ePostLogKind; }
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
bool GetDaemonMode() { return m_bDaemonMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
int GetEditQueueAction() { return m_iEditQueueAction; }
|
||||
int GetEditQueueOffset() { return m_iEditQueueOffset; }
|
||||
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
|
||||
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
const char* GetLastArg() { return m_szLastArg; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
int GetWriteLogKind() { return m_iWriteLogKind; }
|
||||
|
||||
// Current state
|
||||
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
|
||||
bool GetPause() const { return m_bPause; }
|
||||
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
|
||||
float GetDownloadRate() const { return m_fDownloadRate; }
|
||||
};
|
||||
|
||||
#endif
|
||||
543
ParChecker.cpp
Normal file
543
ParChecker.cpp
Normal file
@@ -0,0 +1,543 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#else
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ParChecker.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"data files are damaged and there is enough recovery data available to repair them",
|
||||
"data files are damaged and there is insufficient recovery data available to be able to repair them",
|
||||
"there was something wrong with the command line arguments",
|
||||
"the PAR2 files did not contain sufficient information about the data files to be able to verify them",
|
||||
"repair completed but the data files still appear to be damaged",
|
||||
"an error occured when accessing files",
|
||||
"internal error occurred",
|
||||
"out of memory" };
|
||||
|
||||
|
||||
class Repairer : public Par2Repairer
|
||||
{
|
||||
friend class ParChecker;
|
||||
};
|
||||
|
||||
|
||||
ParChecker::ParChecker()
|
||||
{
|
||||
debug("Creating ParChecker");
|
||||
|
||||
m_eStatus = psUndefined;
|
||||
m_szParFilename = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szErrMsg = NULL;
|
||||
m_szProgressLabel = (char*)malloc(1024);
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_iExtraFiles = 0;
|
||||
m_bVerifyingExtraFiles = false;
|
||||
m_eStage = ptLoadingPars;
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
ParChecker::~ParChecker()
|
||||
{
|
||||
debug("Destroying ParChecker");
|
||||
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
free(m_szProgressLabel);
|
||||
|
||||
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
void ParChecker::SetParFilename(const char * szParFilename)
|
||||
{
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetInfoName(const char * szInfoName)
|
||||
{
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void ParChecker::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ParChecker::Run()
|
||||
{
|
||||
m_bRepairNotNeeded = false;
|
||||
m_eStage = ptLoadingPars;
|
||||
m_iProcessedFiles = 0;
|
||||
m_iExtraFiles = 0;
|
||||
m_bVerifyingExtraFiles = false;
|
||||
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
UpdateProgress();
|
||||
|
||||
debug("par: %s", m_szParFilename);
|
||||
CommandLine commandLine;
|
||||
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
|
||||
if (!commandLine.Parse(5, (char**)argv))
|
||||
{
|
||||
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
|
||||
SetStatus(psFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
Result res;
|
||||
|
||||
Repairer* pRepairer = new Repairer();
|
||||
m_pRepairer = pRepairer;
|
||||
|
||||
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
|
||||
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
|
||||
|
||||
res = pRepairer->PreProcess(commandLine);
|
||||
debug("ParChecker: PreProcess-result=%i", res);
|
||||
|
||||
if (res != eSuccess || IsStopped())
|
||||
{
|
||||
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
|
||||
m_szErrMsg = strdup("par2-file could not be processed");
|
||||
SetStatus(psFailed);
|
||||
delete pRepairer;
|
||||
return;
|
||||
}
|
||||
|
||||
char BufReason[1024];
|
||||
BufReason[0] = '\0';
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
m_szErrMsg = NULL;
|
||||
|
||||
m_eStage = ptVerifyingSources;
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
|
||||
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
|
||||
{
|
||||
pRepairer->UpdateVerificationResults();
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
|
||||
bool bMoreFilesLoaded = true;
|
||||
while (!IsStopped() && res == eRepairNotPossible)
|
||||
{
|
||||
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
|
||||
if (bMoreFilesLoaded)
|
||||
{
|
||||
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
|
||||
}
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
bool hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
int iBlockFound = 0;
|
||||
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
|
||||
if (requested)
|
||||
{
|
||||
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!requested && !hasMorePars)
|
||||
{
|
||||
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
|
||||
BufReason[1024-1] = '\0';
|
||||
m_szErrMsg = strdup(BufReason);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
m_semNeedMoreFiles.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
bMoreFilesLoaded = LoadMorePars();
|
||||
if (bMoreFilesLoaded)
|
||||
{
|
||||
pRepairer->UpdateVerificationResults();
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
SetStatus(psFailed);
|
||||
delete pRepairer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Repair not needed for %s", m_szInfoName);
|
||||
m_bRepairNotNeeded = true;
|
||||
}
|
||||
else if (res == eRepairPossible)
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
info("Repairing %s", m_szInfoName);
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_iProcessedFiles = 0;
|
||||
m_eStage = ptRepairing;
|
||||
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
|
||||
UpdateProgress();
|
||||
|
||||
res = pRepairer->Process(commandLine, true);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Successfully repaired %s", m_szInfoName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Repair possible for %s", m_szInfoName);
|
||||
res = eSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
SetStatus(psFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
|
||||
{
|
||||
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
|
||||
}
|
||||
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
|
||||
SetStatus(psFailed);
|
||||
}
|
||||
|
||||
delete pRepairer;
|
||||
}
|
||||
|
||||
bool ParChecker::LoadMorePars()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
QueuedParFiles moreFiles;
|
||||
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
|
||||
m_QueuedParFiles.clear();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
|
||||
if (loadedOK)
|
||||
{
|
||||
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
free(szParFilename);
|
||||
}
|
||||
|
||||
return !moreFiles.empty();
|
||||
}
|
||||
|
||||
void ParChecker::AddParFile(const char * szParFilename)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_QueuedParFiles.push_back(strdup(szParFilename));
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::QueueChanged()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
bool ParChecker::CheckSplittedFragments()
|
||||
{
|
||||
bool bFragmentsAdded = false;
|
||||
|
||||
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
|
||||
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
|
||||
{
|
||||
Par2RepairerSourceFile *sourcefile = *it;
|
||||
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
|
||||
{
|
||||
bFragmentsAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bFragmentsAdded;
|
||||
}
|
||||
|
||||
bool ParChecker::AddSplittedFragments(const char* szFilename)
|
||||
{
|
||||
char szDirectory[1024];
|
||||
strncpy(szDirectory, szFilename, 1024);
|
||||
szDirectory[1024-1] = '\0';
|
||||
|
||||
char* szBasename = Util::BaseFileName(szDirectory);
|
||||
if (szBasename == szDirectory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
szBasename[-1] = '\0';
|
||||
int iBaseLen = strlen(szBasename);
|
||||
|
||||
list<CommandLine::ExtraFile> extrafiles;
|
||||
|
||||
DirBrowser dir(szDirectory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (!strncasecmp(filename, szBasename, iBaseLen))
|
||||
{
|
||||
const char* p = filename + iBaseLen;
|
||||
if (*p == '.')
|
||||
{
|
||||
for (p++; *p && strchr("0123456789", *p); p++) ;
|
||||
if (!*p)
|
||||
{
|
||||
debug("Found splitted fragment %s", filename);
|
||||
|
||||
char fullfilename[1024];
|
||||
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
|
||||
fullfilename[1024-1] = '\0';
|
||||
|
||||
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
|
||||
extrafiles.push_back(extrafile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bFragmentsAdded = false;
|
||||
|
||||
if (!extrafiles.empty())
|
||||
{
|
||||
m_iExtraFiles = extrafiles.size();
|
||||
m_bVerifyingExtraFiles = true;
|
||||
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
|
||||
m_bVerifyingExtraFiles = false;
|
||||
}
|
||||
|
||||
return bFragmentsAdded;
|
||||
}
|
||||
|
||||
void ParChecker::signal_filename(std::string str)
|
||||
{
|
||||
char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
|
||||
|
||||
if (m_eStage == ptRepairing)
|
||||
{
|
||||
m_eStage = ptVerifyingRepaired;
|
||||
}
|
||||
|
||||
info("%s %s", szStageMessage[m_eStage], str.c_str());
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void ParChecker::signal_progress(double progress)
|
||||
{
|
||||
m_iFileProgress = (int)progress;
|
||||
|
||||
if (m_eStage == ptRepairing)
|
||||
{
|
||||
// calculating repair-data for all files
|
||||
m_iStageProgress = m_iFileProgress;
|
||||
}
|
||||
else
|
||||
{
|
||||
// processing individual files
|
||||
|
||||
int iTotalFiles = 0;
|
||||
if (m_eStage == ptVerifyingRepaired)
|
||||
{
|
||||
// repairing individual files
|
||||
iTotalFiles = m_iFilesToRepair;
|
||||
}
|
||||
else
|
||||
{
|
||||
// verifying individual files
|
||||
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
|
||||
}
|
||||
|
||||
if (iTotalFiles > 0)
|
||||
{
|
||||
if (m_iFileProgress < 1000)
|
||||
{
|
||||
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iStageProgress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
|
||||
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void ParChecker::signal_done(std::string str, int available, int total)
|
||||
{
|
||||
m_iProcessedFiles++;
|
||||
|
||||
if (m_eStage == ptVerifyingSources)
|
||||
{
|
||||
if (available < total && !m_bVerifyingExtraFiles)
|
||||
{
|
||||
bool bFileExists = true;
|
||||
|
||||
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
|
||||
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
|
||||
{
|
||||
Par2RepairerSourceFile *sourcefile = *it;
|
||||
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
|
||||
!sourcefile->GetTargetExists())
|
||||
{
|
||||
bFileExists = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFileExists)
|
||||
{
|
||||
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("File %s with %i block(s) is missing", str.c_str(), total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
118
ParChecker.h
Normal file
118
ParChecker.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PARCHECKER_H
|
||||
#define PARCHECKER_H
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class ParChecker : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
psUndefined,
|
||||
psWorking,
|
||||
psFailed,
|
||||
psFinished
|
||||
};
|
||||
|
||||
enum EStage
|
||||
{
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
};
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
EStage m_eStage;
|
||||
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
int m_iProcessedFiles;
|
||||
int m_iFilesToRepair;
|
||||
int m_iExtraFiles;
|
||||
bool m_bVerifyingExtraFiles;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
|
||||
bool LoadMorePars();
|
||||
bool CheckSplittedFragments();
|
||||
bool AddSplittedFragments(const char* szFilename);
|
||||
void signal_filename(std::string str);
|
||||
void signal_progress(double progress);
|
||||
void signal_done(std::string str, int available, int total);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Unpause par2-files
|
||||
* returns true, if the files with required number of blocks were unpaused,
|
||||
* or false if there are no more files in queue for this collection or not enough blocks
|
||||
*/
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
|
||||
virtual void UpdateProgress() {}
|
||||
EStage GetStage() { return m_eStage; }
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
|
||||
public:
|
||||
ParChecker();
|
||||
virtual ~ParChecker();
|
||||
virtual void Run();
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
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();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
154
PostInfo.cpp
Normal file
154
PostInfo.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PostInfo.h"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int PostInfo::m_iIDGen = 0;
|
||||
|
||||
PostInfo::PostInfo()
|
||||
{
|
||||
debug("Creating PostInfo");
|
||||
|
||||
m_szNZBFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szParFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bWorking = false;
|
||||
m_bParCheck = false;
|
||||
m_iParStatus = 0;
|
||||
m_bParFailed = false;
|
||||
m_szProgressLabel = strdup("");
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_tStartTime = 0;
|
||||
m_tStageTime = 0;
|
||||
m_eStage = ptQueued;
|
||||
m_pScriptThread = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
PostInfo::~ PostInfo()
|
||||
{
|
||||
debug("Destroying PostInfo");
|
||||
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
}
|
||||
|
||||
void PostInfo::SetNZBFilename(const char* szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void PostInfo::SetParFilename(const char* szParFilename)
|
||||
{
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetInfoName(const char* szInfoName)
|
||||
{
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void PostInfo::SetProgressLabel(const char* szProgressLabel)
|
||||
{
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
m_szProgressLabel = strdup(szProgressLabel);
|
||||
}
|
||||
|
||||
PostInfo::Messages* PostInfo::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void PostInfo::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
|
||||
m_mutexLog.Lock();
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
113
PostInfo.h
Normal file
113
PostInfo.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef POSTINFO_H
|
||||
#define POSTINFO_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class PostInfo
|
||||
{
|
||||
public:
|
||||
enum EStage
|
||||
{
|
||||
ptQueued,
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
ptExecutingScript,
|
||||
ptFinished
|
||||
};
|
||||
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
bool m_bWorking;
|
||||
bool m_bParCheck;
|
||||
int m_iParStatus;
|
||||
bool m_bParFailed;
|
||||
EStage m_eStage;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
time_t m_tStartTime;
|
||||
time_t m_tStageTime;
|
||||
Thread* m_pScriptThread;
|
||||
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
PostInfo();
|
||||
~PostInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
EStage GetStage() { return m_eStage; }
|
||||
void SetStage(EStage eStage) { m_eStage = eStage; }
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
|
||||
time_t GetStartTime() { return m_tStartTime; }
|
||||
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
|
||||
time_t GetStageTime() { return m_tStageTime; }
|
||||
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
|
||||
bool GetWorking() { return m_bWorking; }
|
||||
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
|
||||
int GetParStatus() { return m_iParStatus; }
|
||||
void SetParStatus(int iParStatus) { m_iParStatus = iParStatus; }
|
||||
bool GetParFailed() { return m_bParFailed; }
|
||||
void SetParFailed(bool bParFailed) { m_bParFailed = bParFailed; }
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
Thread* GetScriptThread() { return m_pScriptThread; }
|
||||
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
#endif
|
||||
1159
PrePostProcessor.cpp
Normal file
1159
PrePostProcessor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
134
PrePostProcessor.h
Normal file
134
PrePostProcessor.h
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PREPOSTPROCESSOR_H
|
||||
#define PREPOSTPROCESSOR_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
#endif
|
||||
|
||||
class PrePostProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
class QueueCoordinatorObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
class ParCheckerObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
class PostParChecker: public ParChecker
|
||||
{
|
||||
private:
|
||||
PrePostProcessor* m_Owner;
|
||||
protected:
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
virtual void UpdateProgress();
|
||||
|
||||
friend class PrePostProcessor;
|
||||
};
|
||||
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
#endif
|
||||
|
||||
private:
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
bool m_bPostScript;
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const char* szNZBFilename,
|
||||
bool bIgnoreFirstInPostQueue, bool bIgnorePaused, bool bCheckPostQueue);
|
||||
bool CheckScript(FileInfo* pFileInfo);
|
||||
bool JobExists(PostQueue* pPostQueue, const char* szNZBFilename);
|
||||
bool ClearCompletedJobs(const char* szNZBFilename);
|
||||
void CheckPostQueue();
|
||||
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void SavePostQueue();
|
||||
void SanitisePostQueue();
|
||||
void CheckDiskSpace();
|
||||
|
||||
Mutex m_mutexQueue;
|
||||
PostQueue m_PostQueue;
|
||||
PostQueue m_CompletedJobs;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
PostParChecker m_ParChecker;
|
||||
ParCheckerObserver m_ParCheckerObserver;
|
||||
|
||||
void ParCheckerUpdate(Subject* Caller, void* Aspect);
|
||||
bool CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
|
||||
bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
int GetParCleanupQueueGroup(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
bool HasFailedParJobs(const char* szNZBFilename);
|
||||
bool ParJobExists(PostQueue* pPostQueue, const char* szParFilename);
|
||||
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
bool RequestMorePars(const char* szNZBFilename, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, const char* szNZBFilename, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
|
||||
void UpdateParProgress();
|
||||
void StartParJob(PostInfo* pPostInfo);
|
||||
#endif
|
||||
|
||||
public:
|
||||
PrePostProcessor();
|
||||
virtual ~PrePostProcessor();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
PostQueue* LockPostQueue();
|
||||
void UnlockPostQueue();
|
||||
};
|
||||
|
||||
#endif
|
||||
791
QueueCoordinator.cpp
Normal file
791
QueueCoordinator.cpp
Normal file
@@ -0,0 +1,791 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
{
|
||||
debug("Creating QueueCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
m_DownloadQueue.clear();
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
m_iSpeedBytes[i] = 0;
|
||||
}
|
||||
m_iSpeedBytesIndex = 0;
|
||||
|
||||
m_iAllBytes = 0;
|
||||
m_tStartServer = 0;
|
||||
m_tStartDownload = 0;
|
||||
m_tPausedFrom = 0;
|
||||
m_bStandBy = true;
|
||||
|
||||
YDecoder::Init();
|
||||
}
|
||||
|
||||
QueueCoordinator::~QueueCoordinator()
|
||||
{
|
||||
debug("Destroying QueueCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting DownloadQueue");
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_DownloadQueue.clear();
|
||||
|
||||
debug("Deleting ArticleDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
YDecoder::Final();
|
||||
|
||||
debug("QueueCoordinator destroyed");
|
||||
}
|
||||
|
||||
void QueueCoordinator::Run()
|
||||
{
|
||||
debug("Entering QueueCoordinator-loop");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
|
||||
{
|
||||
if (g_pOptions->GetReloadQueue())
|
||||
{
|
||||
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pDiskState->DiscardDownloadQueue();
|
||||
}
|
||||
}
|
||||
|
||||
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
m_tStartServer = time(NULL);
|
||||
bool bWasStandBy = true;
|
||||
bool bArticeDownloadsRunning = false;
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!g_pOptions->GetPause())
|
||||
{
|
||||
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
|
||||
if (pConnection)
|
||||
{
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
|
||||
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
|
||||
bArticeDownloadsRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pServerPool->FreeConnection(pConnection, false);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool bStandBy = !bArticeDownloadsRunning;
|
||||
if (bStandBy ^ bWasStandBy)
|
||||
{
|
||||
EnterLeaveStandBy(bStandBy);
|
||||
bWasStandBy = bStandBy;
|
||||
}
|
||||
|
||||
// sleep longer in StandBy
|
||||
int iSleepInterval = bStandBy ? 100 : 5;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
AddSpeedReading(0);
|
||||
|
||||
iResetCounter+= iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
g_pServerPool->CloseUnusedConnections();
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("QueueCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("QueueCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting QueueCoordinator-loop");
|
||||
}
|
||||
|
||||
void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
|
||||
{
|
||||
debug("Adding NZBFile to queue");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
DownloadQueue tmpDownloadQueue;
|
||||
tmpDownloadQueue.clear();
|
||||
DownloadQueue DupeList;
|
||||
DupeList.clear();
|
||||
|
||||
int index1 = 0;
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
index1++;
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
bool dupe = false;
|
||||
if (IsDupe(pFileInfo))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
}
|
||||
int index2 = 0;
|
||||
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
|
||||
{
|
||||
index2++;
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (pFileInfo != pFileInfo2 &&
|
||||
!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
(pFileInfo->GetSize() < pFileInfo2->GetSize() ||
|
||||
(pFileInfo->GetSize() == pFileInfo2->GetSize() && index2 < index1)))
|
||||
{
|
||||
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dupe)
|
||||
{
|
||||
DupeList.push_back(pFileInfo);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpDownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
{
|
||||
if (bAddFirst)
|
||||
{
|
||||
m_DownloadQueue.push_front(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(NULL, pFileInfo);
|
||||
}
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
pNZBFile->DetachFileInfos();
|
||||
|
||||
Aspect aspect = { eaNZBFileAdded, NULL, &m_DownloadQueue, pNZBFile->GetFileName() };
|
||||
Notify(&aspect);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool QueueCoordinator::AddFileToQueue(const char* szFileName)
|
||||
{
|
||||
// Parse the buffer and make it into a NZBFile
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFileName);
|
||||
|
||||
// Did file parse correctly?
|
||||
if (!pNZBFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add NZBFile to Queue
|
||||
AddNZBFileToQueue(pNZBFile, false);
|
||||
|
||||
delete pNZBFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: see note to "AddSpeedReading"
|
||||
*/
|
||||
float QueueCoordinator::CalcCurrentDownloadSpeed()
|
||||
{
|
||||
int iTotal = 0;
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
iTotal += m_iSpeedBytes[i];
|
||||
}
|
||||
|
||||
float fSpeed = iTotal / 1024.0f / SPEEDMETER_SECONDS;
|
||||
|
||||
return fSpeed;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
|
||||
* but this would results in a big performance loss (the function
|
||||
* "AddSpeedReading" is called extremly often), so we better agree with calculation
|
||||
* errors possible because of simultaneuos access from several threads.
|
||||
* The used algorithm is able to recover after few seconds.
|
||||
* In any case the calculation errors can not result in fatal system
|
||||
* errors (segmentation faults).
|
||||
*/
|
||||
void QueueCoordinator::AddSpeedReading(int iBytes)
|
||||
{
|
||||
int iIndex = (int)time(NULL);
|
||||
|
||||
if (iIndex - m_iSpeedBytesIndex > SPEEDMETER_SECONDS)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex - SPEEDMETER_SECONDS - 1;
|
||||
}
|
||||
|
||||
for (int i = m_iSpeedBytesIndex + 1; i < iIndex; i++)
|
||||
{
|
||||
m_iSpeedBytes[i % SPEEDMETER_SECONDS] = 0;
|
||||
}
|
||||
|
||||
if (iIndex > m_iSpeedBytesIndex)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex;
|
||||
m_iSpeedBytes[iIndex % SPEEDMETER_SECONDS] = iBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iSpeedBytes[m_iSpeedBytesIndex % SPEEDMETER_SECONDS] += iBytes;
|
||||
}
|
||||
|
||||
m_iAllBytes += iBytes;
|
||||
}
|
||||
|
||||
long long QueueCoordinator::CalcRemainingSize()
|
||||
{
|
||||
long long lRemainingSize = 0;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
return lRemainingSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: DownloadQueue must be locked prior to call of this function
|
||||
* Returns True if Entry was deleted from Queue or False if it was scheduled for Deletion.
|
||||
* NOTE: "False" does not mean unsuccess; the entry is (or will be) deleted in any case.
|
||||
*/
|
||||
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
pFileInfo->SetDeleted(true);
|
||||
bool hasDownloads = false;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (pArticleDownloader->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
hasDownloads = true;
|
||||
pArticleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
if (!hasDownloads)
|
||||
{
|
||||
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, false);
|
||||
}
|
||||
return hasDownloads;
|
||||
}
|
||||
|
||||
void QueueCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping ArticleDownloads");
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
debug("ArticleDownloads are notified");
|
||||
}
|
||||
|
||||
bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo)
|
||||
{
|
||||
//debug("QueueCoordinator::GetNextArticle()");
|
||||
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo);
|
||||
}
|
||||
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
|
||||
{
|
||||
pArticleInfo = *at;
|
||||
if (pArticleInfo->GetStatus() == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
|
||||
{
|
||||
debug("Starting new ArticleDownloader");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = new ArticleDownloader();
|
||||
pArticleDownloader->SetAutoDestroy(true);
|
||||
pArticleDownloader->Attach(this);
|
||||
pArticleDownloader->SetFileInfo(pFileInfo);
|
||||
pArticleDownloader->SetArticleInfo(pArticleInfo);
|
||||
pArticleDownloader->SetConnection(pConnection);
|
||||
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
|
||||
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pArticleDownloader);
|
||||
pArticleDownloader->Start();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetInfoName(name);
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetOutputFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue* QueueCoordinator::LockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
return &m_DownloadQueue;
|
||||
}
|
||||
|
||||
void QueueCoordinator::UnlockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::Update(Subject* Caller, void* Aspect)
|
||||
{
|
||||
debug("Notification from ArticleDownloader received");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = (ArticleDownloader*) Caller;
|
||||
if ((pArticleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
|
||||
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed))
|
||||
{
|
||||
ArticleCompleted(pArticleDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
{
|
||||
debug("Article downloaded");
|
||||
|
||||
FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFinished);
|
||||
}
|
||||
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
|
||||
}
|
||||
|
||||
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
|
||||
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
|
||||
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
|
||||
|
||||
if (!pFileInfo->GetFilenameConfirmed() &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
|
||||
pArticleDownloader->GetArticleFilename())
|
||||
{
|
||||
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
|
||||
pFileInfo->SetFilenameConfirmed(true);
|
||||
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
|
||||
fileCompleted = false;
|
||||
DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
bool deleteFileObj = false;
|
||||
|
||||
if (pFileInfo->GetDeleted())
|
||||
{
|
||||
int cnt = 0;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
if ((*it)->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt == 1)
|
||||
{
|
||||
// this was the last Download for a file deleted from queue
|
||||
deleteFileObj = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
m_mutexDownloadQueue.Lock();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pa = *it;
|
||||
if (pa == pArticleDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deleteFileObj)
|
||||
{
|
||||
bool fileDeleted = pFileInfo->GetDeleted();
|
||||
// delete File from Queue
|
||||
pFileInfo->SetDeleted(true);
|
||||
|
||||
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, fileCompleted);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pa = *it;
|
||||
if (pa == pFileInfo)
|
||||
{
|
||||
m_DownloadQueue.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(&m_DownloadQueue, pFileInfo);
|
||||
}
|
||||
|
||||
if (!bCompleted)
|
||||
{
|
||||
// deleting temporary files
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetResultFilename())
|
||||
{
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char name[1024];
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
{
|
||||
debug("Checking if the file is already queued");
|
||||
|
||||
// checking on disk
|
||||
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// checking in queue
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pQueueEntry = *it;
|
||||
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
|
||||
pFileInfo != pQueueEntry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug("--------------------------------------------");
|
||||
debug("Dumping debug info to log");
|
||||
debug("--------------------------------------------");
|
||||
|
||||
debug(" QueueCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
pArticleDownloader->LogDebugInfo();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
debug("");
|
||||
|
||||
g_pServerPool->LogDebugInfo();
|
||||
}
|
||||
|
||||
void QueueCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
debug("Terminating hanging download %s", pArticleDownloader->GetInfoName());
|
||||
if (pArticleDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pArticleDownloader->GetInfoName());
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", Util::BaseFileName(pArticleInfo->GetResultFilename()));
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
|
||||
delete pArticleDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
m_bStandBy = bEnter;
|
||||
if (bEnter)
|
||||
{
|
||||
m_tPausedFrom = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tStartDownload == 0)
|
||||
{
|
||||
m_tStartDownload = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tStartDownload += time(NULL) - m_tPausedFrom;
|
||||
}
|
||||
m_tPausedFrom = 0;
|
||||
}
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
if (m_tStartServer > 0)
|
||||
{
|
||||
*iUpTimeSec = (int)(time(NULL) - m_tStartServer);
|
||||
}
|
||||
else
|
||||
{
|
||||
*iUpTimeSec = 0;
|
||||
}
|
||||
*bStandBy = m_bStandBy;
|
||||
if (m_bStandBy)
|
||||
{
|
||||
*iDnTimeSec = (int)(m_tPausedFrom - m_tStartDownload);
|
||||
}
|
||||
else
|
||||
{
|
||||
*iDnTimeSec = (int)(time(NULL) - m_tStartDownload);
|
||||
}
|
||||
*iAllBytes = m_iAllBytes;
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
116
QueueCoordinator.h
Normal file
116
QueueCoordinator.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUECOORDINATOR_H
|
||||
#define QUEUECOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
{
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
DownloadQueue* pDownloadQueue;
|
||||
const char* szNZBFilename;
|
||||
};
|
||||
|
||||
private:
|
||||
DownloadQueue m_DownloadQueue;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
QueueEditor m_QueueEditor;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
// statistics
|
||||
static const int SPEEDMETER_SECONDS = 5;
|
||||
int m_iSpeedBytes[SPEEDMETER_SECONDS];
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
time_t m_tStartDownload;
|
||||
time_t m_tPausedFrom;
|
||||
void EnterLeaveStandBy(bool bEnter);
|
||||
bool m_bStandBy;
|
||||
Mutex m_mutexStat;
|
||||
|
||||
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
|
||||
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
|
||||
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
bool IsDupe(FileInfo* pFileInfo);
|
||||
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
|
||||
void ResetHangingDownloads();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// statistics
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
virtual void AddSpeedReading(int iBytes);
|
||||
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
|
||||
|
||||
// Editing the queue
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue() ;
|
||||
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
|
||||
bool AddFileToQueue(const char* szFileName);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
bool GetStandBy() { return m_bStandBy; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
655
QueueEditor.cpp
Normal file
655
QueueEditor.cpp
Normal file
@@ -0,0 +1,655 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
const int MAX_ID = 100000000;
|
||||
|
||||
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
m_pFileInfo = pFileInfo;
|
||||
m_iOffset = iOffset;
|
||||
}
|
||||
|
||||
QueueEditor::QueueEditor()
|
||||
{
|
||||
debug("Creating QueueEditor");
|
||||
}
|
||||
|
||||
QueueEditor::~QueueEditor()
|
||||
{
|
||||
debug("Destroying QueueEditor");
|
||||
}
|
||||
|
||||
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 == pFileInfo)
|
||||
{
|
||||
return iEntry;
|
||||
}
|
||||
iEntry ++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the pause flag of the specific entry in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
|
||||
if (iEntry > -1)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
|
||||
{
|
||||
FileInfo* fi = (*pDownloadQueue)[iEntry];
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
|
||||
}
|
||||
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
|
||||
case eaFileResume:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, false);
|
||||
break;
|
||||
|
||||
case eaFileMoveOffset:
|
||||
case eaFileMoveTop:
|
||||
case eaFileMoveBottom:
|
||||
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
|
||||
break;
|
||||
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
|
||||
break;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
return cItemList.size() > 0;
|
||||
}
|
||||
|
||||
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
|
||||
EEditAction EEditAction, int iOffset)
|
||||
{
|
||||
if (EEditAction == eaFileMoveTop)
|
||||
{
|
||||
iOffset = -MAX_ID;
|
||||
}
|
||||
else if (EEditAction == eaFileMoveBottom)
|
||||
{
|
||||
iOffset = MAX_ID;
|
||||
}
|
||||
|
||||
pItemList->reserve(pIDList->size());
|
||||
if (bSmartOrder && iOffset != 0 &&
|
||||
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
|
||||
{
|
||||
//add IDs to list in order they currently have in download queue
|
||||
int iLastDestPos = -1;
|
||||
int iStart, iEnd, iStep;
|
||||
if (iOffset < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
iEnd = pDownloadQueue->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
|
||||
int iID = pFileInfo->GetID();
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
if (iID == *it)
|
||||
{
|
||||
int iWorkOffset = iOffset;
|
||||
int iDestPos = iIndex + iWorkOffset;
|
||||
if (iLastDestPos == -1)
|
||||
{
|
||||
if (iDestPos < 0)
|
||||
{
|
||||
iWorkOffset = -iIndex;
|
||||
}
|
||||
else if (iDestPos > int(pDownloadQueue->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex + 1;
|
||||
}
|
||||
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex - 1;
|
||||
}
|
||||
}
|
||||
iLastDestPos = iIndex + iWorkOffset;
|
||||
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check ID range
|
||||
int iMaxID = 0;
|
||||
int iMinID = MAX_ID;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int ID = pFileInfo->GetID();
|
||||
if (ID > iMaxID)
|
||||
{
|
||||
iMaxID = ID;
|
||||
}
|
||||
if (ID < iMinID)
|
||||
{
|
||||
iMinID = ID;
|
||||
}
|
||||
}
|
||||
|
||||
//add IDs to list in order they were transmitted in command
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
int iID = *it;
|
||||
if (iMinID <= iID && iID <= iMaxID)
|
||||
{
|
||||
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
pItemList->push_back(new EditItem(pFileInfo, iOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
|
||||
// collecting files belonging to group
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
cIDList.push_back(pFileInfo2->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
// calculating offset in terms of files
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pGroupInfo = *it;
|
||||
if (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
int iFileOffset = 0;
|
||||
if (iOffset > 0)
|
||||
{
|
||||
if (iNum + iOffset >= cGroupList.size() - 1)
|
||||
{
|
||||
eAction = eaGroupMoveBottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum + 2; i < cGroupList.size() && iOffset > 0; i++, iOffset--)
|
||||
{
|
||||
iFileOffset += FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iNum + iOffset <= 0)
|
||||
{
|
||||
eAction = eaGroupMoveTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum; i > 0 && iOffset < 0; i--, iOffset++)
|
||||
{
|
||||
iFileOffset -= FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
iOffset = iFileOffset;
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
|
||||
{
|
||||
FileInfo* pGroupInfo1 = *itg;
|
||||
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupList->push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::ItemExists(FileList* pFileList, FileInfo* pFileInfo)
|
||||
{
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
if (*it == pFileInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset)
|
||||
{
|
||||
// Build list of all groups; List contains first file of each group
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
|
||||
// Find affected groups. It includes groups being moved and groups directly
|
||||
// above or under of these groups (those order is also changed)
|
||||
FileList cAffectedGroupList;
|
||||
cAffectedGroupList.clear();
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eaFileMoveOffset, iOffset);
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, pFileInfo))
|
||||
{
|
||||
cAffectedGroupList.push_back(pFileInfo);
|
||||
}
|
||||
if (iOffset < 0)
|
||||
{
|
||||
for (int i = iNum - 1; i >= -iOffset-1; i--)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iOffset > 0)
|
||||
{
|
||||
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (iNum + 1 < cGroupList.size())
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[iNum + 1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
cGroupList.clear();
|
||||
|
||||
// Aligning groups
|
||||
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
|
||||
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
|
||||
iLastNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
iLastNum = iNum;
|
||||
}
|
||||
pLastFileInfo = pFileInfo;
|
||||
}
|
||||
iNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
FileList GroupFileList;
|
||||
GroupFileList.clear();
|
||||
FileInfo* pFirstFileInfo = NULL;
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (!pFirstFileInfo ||
|
||||
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
|
||||
{
|
||||
GroupFileList.push_back(pItem->m_pFileInfo);
|
||||
if (!pFirstFileInfo)
|
||||
{
|
||||
pFirstFileInfo = pItem->m_pFileInfo;
|
||||
}
|
||||
delete pItem;
|
||||
pItemList->erase(it);
|
||||
it = pItemList->begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (!GroupFileList.empty())
|
||||
{
|
||||
PausePars(&GroupFileList, bExtraParsOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files.
|
||||
* If the parameter "bExtraParsOnly" is set to "true", we use the following strategy:
|
||||
* At first we find all par-files, which do not have "vol" in their names, then we pause
|
||||
* all vols and do not affect all just-pars.
|
||||
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
|
||||
* and do not affect it, but pause all other pars.
|
||||
*/
|
||||
void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
|
||||
{
|
||||
debug("QueueEditor: Pausing pars");
|
||||
|
||||
FileList Pars, Vols;
|
||||
Pars.clear();
|
||||
Vols.clear();
|
||||
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
if (strstr(szLoFileName, ".par2"))
|
||||
{
|
||||
if (!bExtraParsOnly)
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(szLoFileName, ".vol"))
|
||||
{
|
||||
Vols.push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pars.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bExtraParsOnly)
|
||||
{
|
||||
if (!Pars.empty())
|
||||
{
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pausing all Vol-files except the smallest one
|
||||
FileInfo* pSmallest = NULL;
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pSmallest)
|
||||
{
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else if (pSmallest->GetSize() > pFileInfo->GetSize())
|
||||
{
|
||||
pSmallest->SetPaused(true);
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
QueueEditor.h
Normal file
98
QueueEditor.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUEEDITOR_H
|
||||
#define QUEUEEDITOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eaFileMoveTop,
|
||||
eaFileMoveBottom,
|
||||
eaFilePause,
|
||||
eaFileResume,
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars
|
||||
};
|
||||
|
||||
private:
|
||||
class EditItem
|
||||
{
|
||||
public:
|
||||
int m_iOffset;
|
||||
FileInfo* m_pFileInfo;
|
||||
|
||||
EditItem(FileInfo* pFileInfo, int iOffset);
|
||||
};
|
||||
|
||||
typedef std::vector<EditItem*> ItemList;
|
||||
typedef std::vector<FileInfo*> FileList;
|
||||
|
||||
private:
|
||||
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
|
||||
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
#endif
|
||||
325
README
325
README
@@ -2,10 +2,6 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.net
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. About NZBGet
|
||||
@@ -33,9 +29,6 @@ In server/client mode NZBGet runs as server in background.
|
||||
Then you use client to send requests to server. The sample requests
|
||||
are: download nzb-file, list files in queue, etc.
|
||||
|
||||
There is also a built-in web-interface. The server has RPC-support
|
||||
and can be controlled from third party applications too.
|
||||
|
||||
Standalone-tool, server and client are all contained in only one
|
||||
executable file "nzbget". The mode in which the program works
|
||||
depends on command-line parameters passed to the program.
|
||||
@@ -44,16 +37,29 @@ depends on command-line parameters passed to the program.
|
||||
2. Supported OS
|
||||
=====================================
|
||||
|
||||
NZBGet is written in C++ and works on Windows, OS X, Linux and
|
||||
most POSIX-conform OS'es.
|
||||
NZBGet is written in C++ and was initialy developed on Linux.
|
||||
It was ported to Windows later and tested for compatibility with
|
||||
several POSIX-OS'es.
|
||||
|
||||
The current version (0.4.0) should run at least on:
|
||||
- Linux Debian 4.0 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
|
||||
The previous version (0.3.0) was also tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Linux Debian 3.1 on SPARC (QEmu).
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
control your NZBGet-server running on Linux.
|
||||
|
||||
The download-section of NZBGet web-site provides binary files
|
||||
for Windows, OS X and Linux. For most POSIX-systems you need to compile
|
||||
the program yourself.
|
||||
for Windows. The binary packages for many routers and NAS devices are
|
||||
also available in OPTWARE repository (http://www.nslu2-linux.org),
|
||||
but for most POSIX-systems you need to compile the program yourself.
|
||||
|
||||
If you have downloaded binaries you can just jump to section
|
||||
"Configuration".
|
||||
@@ -62,8 +68,8 @@ If you have downloaded binaries you can just jump to section
|
||||
3. Prerequisites on POSIX
|
||||
=====================================
|
||||
|
||||
NZBGet is developed on a linux-system, but it runs on other
|
||||
POSIX platforms.
|
||||
NZBGet is developed on a linux-system, but it should run on other
|
||||
POSIX platforms (see the list of tested platforms above).
|
||||
|
||||
NZBGet absolutely needs the following libraries:
|
||||
|
||||
@@ -76,60 +82,40 @@ And the following libraries are optional:
|
||||
- libcurses (usually part of commercial systems)
|
||||
or (better)
|
||||
- libncurses (http://invisible-island.net/ncurses)
|
||||
|
||||
- for encrypted connections (TLS/SSL):
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
- for par-check and -repair (enabled by default):
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
- for gzip support in web-server and web-client (enabled by default):
|
||||
- zlib (http://www.zlib.net)
|
||||
|
||||
All these libraries are included in modern POSIX distributions and
|
||||
All these libraries are included in modern Linux distributions and
|
||||
should be available as installable packages. Please note that you also
|
||||
need the developer packages for these libraries too, they package names
|
||||
have often suffix "dev" or "devel". On other systems you may need to
|
||||
download the libraries at the given URLs and compile them (see hints below).
|
||||
|
||||
|
||||
=====================================
|
||||
4. Installation on POSIX
|
||||
=====================================
|
||||
|
||||
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
|
||||
Well, the usual stuff:
|
||||
|
||||
- untar the nzbget-source via
|
||||
tar -zxf nzbget-VERSION.tar.gz
|
||||
|
||||
- change into nzbget-directory via
|
||||
cd nzbget-VERSION
|
||||
|
||||
- configure it via
|
||||
./configure
|
||||
|
||||
(maybe you have to tell configure, where to find some libraries.
|
||||
./configure --help is your friend!
|
||||
also see "Configure-options" later)
|
||||
|
||||
- in a case you don't have root access or want to install the program
|
||||
in your home directory use the configure parameter --prefix, e. g.:
|
||||
|
||||
./configure --prefix ~/usr
|
||||
|
||||
./configure --help is your friend!)
|
||||
also see "Configure-options" later.)
|
||||
- compile it via
|
||||
make
|
||||
|
||||
- to install system wide become root via:
|
||||
- become root via
|
||||
su
|
||||
|
||||
- install it via:
|
||||
- install it via
|
||||
make install
|
||||
|
||||
- install configuration files into <prefix>/etc via:
|
||||
make install-conf
|
||||
|
||||
(you can skip this step if you intend to store configuration
|
||||
files in a non-standard location)
|
||||
|
||||
Configure-options
|
||||
-----------------
|
||||
You may run configure with additional arguments:
|
||||
@@ -138,16 +124,7 @@ You may run configure with additional arguments:
|
||||
if you can not use curses/ncurses.
|
||||
|
||||
--disable-parcheck - to make without parcheck-support. Use this option
|
||||
if you have troubles when compiling par2-module.
|
||||
|
||||
--with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
|
||||
should be used for encrypted server connections.
|
||||
|
||||
--disable-tls - to make without TLS/SSL support. Use this option if
|
||||
you can not neither OpenSSL nor GnuTLS.
|
||||
|
||||
--disable-gzip - to make without gzip support. Use this option
|
||||
if you can not use zlib.
|
||||
if you can not use libpar2 or libsigc++.
|
||||
|
||||
--enable-debug - to build in debug-mode, if you want to see and log
|
||||
debug-messages.
|
||||
@@ -155,95 +132,80 @@ You may run configure with additional arguments:
|
||||
Optional package: par-check
|
||||
---------------------------
|
||||
NZBGet can check and repair downloaded files for you. For this purpose
|
||||
it uses library par2.
|
||||
it uses library par2 (libpar2), which needs sigc++ on its part.
|
||||
|
||||
For your convenience the source code of libpar2 is integrated into
|
||||
NZBGet’s source tree and is compiled automatically when you make NZBGet.
|
||||
The libpar2 and libsigc++ (version 2 or later) must be installed on your
|
||||
system. On most linux distributions these libraries are available as packages.
|
||||
So you only need to install theme and run configure with parameter
|
||||
"--enable-parcheck".
|
||||
If you do not have these packages you can compile them yourself.
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
In a case errors occur during this process the inclusion of par2-module
|
||||
can be disabled using configure option "--disable-parcheck":
|
||||
--with-libpar2-includes
|
||||
--with-libpar2-libraries
|
||||
--with-libsigc-includes
|
||||
--with-libsigc-libraries
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
|
||||
If you are not able to use libpar2 or libsigc++ or do not want them you can
|
||||
make the program without support for par-check using option
|
||||
"--disable-parcheck":
|
||||
|
||||
./configure --disable-parcheck
|
||||
|
||||
|
||||
Optional package: curses
|
||||
-------------------------
|
||||
For curses-outputmode you need ncurses or curses on your system.
|
||||
If you do not have one of them you can download and compile ncurses yourself.
|
||||
Following configure-parameters may be useful:
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
--with-libcurses-includes=/path/to/curses/includes
|
||||
--with-libcurses-libraries=/path/to/curses/libraries
|
||||
--with-libcurses-includes
|
||||
--with-libcurses-libraries
|
||||
|
||||
If you are not able to use curses or ncurses or do not want them you can
|
||||
make the program without support for curses using option "--disable-curses":
|
||||
|
||||
./configure --disable-curses
|
||||
|
||||
Optional package: TLS
|
||||
-------------------------
|
||||
To enable encrypted server connections (TLS/SSL) you need to build the program
|
||||
with TLS/SSL support. NZBGet can use two libraries: OpenSSL or GnuTLS.
|
||||
Configure-script checks which library is installed and use it. If both are
|
||||
available it gives the precedence to OpenSSL. You may override that with
|
||||
the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
|
||||
|
||||
./configure --with-tlslib= GnuTLS
|
||||
|
||||
Following configure-parameters may be useful:
|
||||
|
||||
--with-libtls-includess=/path/to/gnutls/includes
|
||||
--with-libtls-libraries=/path/to/gnutls/libraries
|
||||
|
||||
--with-openssl-includess=/path/to/openssl/includes
|
||||
--with-openssl-libraries=/path/to/openssl/libraries
|
||||
|
||||
If none of these libraries is available you can make the program without
|
||||
TLS/SSL support using option "--disable-tls":
|
||||
|
||||
./configure --disable-tls
|
||||
|
||||
=====================================
|
||||
5. Compiling on Windows
|
||||
=====================================
|
||||
|
||||
NZBGet is developed using MS Visual Studio 2015 (Community Edition). The project
|
||||
file is provided.
|
||||
NZBGet is developed using MS Visual C++ 2005. The project file and solution
|
||||
are provided. If you use MS Visual C++ 2005 Express you need to download
|
||||
and install Platform SDK.
|
||||
|
||||
To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS:
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
To compile the program with par-check-support you also need the following
|
||||
libraries:
|
||||
|
||||
Also required are:
|
||||
- Regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
|
||||
- Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
|
||||
Download these libaries, then use patch-files provided with NZBGet to create
|
||||
preconfigured project files and solutions for each library.
|
||||
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
|
||||
to use patch-files, if you do not familiar with this technique.
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib)
|
||||
and include- and libraries-paths are configured in MS Visual C++ 2005 you
|
||||
should be able to compile NZBGet.
|
||||
|
||||
=====================================
|
||||
6. Configuration
|
||||
=====================================
|
||||
|
||||
NZBGet needs a configuration file.
|
||||
NZBGet needs a configuration-file to work properly.
|
||||
|
||||
An example configuration file is provided in "nzbget.conf", which
|
||||
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
|
||||
system configuration and configure options - typically "/usr/local",
|
||||
"/usr" or "/opt"). The installer adjusts the file according to your
|
||||
system paths. If you have performed the installation step
|
||||
"make install-conf" this file is already copied to "<prefix>/etc" and
|
||||
NZBGet finds it automatically. If you install the program manually
|
||||
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
|
||||
to one of the locations listed below.
|
||||
|
||||
Open the file in a text editor and modify it accodring to your needs.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one news server in
|
||||
configuration file. The file has comments on how to use each option.
|
||||
You need to set at least the option "MAINDIR" and one newsserver in
|
||||
configuration file. Have a look at the example in nzbget.conf.example,
|
||||
it has comments on how to use each option.
|
||||
|
||||
The program looks for configuration file in following standard
|
||||
locations (in this order):
|
||||
|
||||
On POSIX systems:
|
||||
<EXE-DIR>/nzbget.conf
|
||||
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -267,12 +229,6 @@ options via command-line.
|
||||
NZBGet can be used in either standalone mode which downloads a single file
|
||||
or as a server which is able to queue up numerous download requests.
|
||||
|
||||
TIP for Windows users: NZBGet is controlled via various command line
|
||||
parameters. For easier using there is a simple shell script included
|
||||
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
|
||||
be running a command shell with PATH adjusted to find NZBGet executable.
|
||||
Then you can type all commands without full path to nzbget.exe.
|
||||
|
||||
Standalone mode:
|
||||
----------------
|
||||
|
||||
@@ -300,16 +256,10 @@ To stop server use:
|
||||
|
||||
nzbget -Q
|
||||
|
||||
TIP for POSIX users: with included script "nzbgetd" you can use standard
|
||||
commands to control daemon:
|
||||
|
||||
nzbgetd start
|
||||
nzbgetd stop
|
||||
etc.
|
||||
|
||||
When NZBGet is started in console server mode it displays a message that
|
||||
it is ready to receive download requests. In daemon mode it doesn't print any
|
||||
messages to console since it runs in background.
|
||||
Depending on which frontend has been selected in the nzbget.conf file
|
||||
(option "outputmode") the server should display a message that
|
||||
it is ready to receive download requests (this applies only to console
|
||||
mode, not to daemon mode).
|
||||
|
||||
When the server is running it is possible to queue up downloads. This can be
|
||||
done either in terminal with "nzbget -A <nzb-file>" or by uploading
|
||||
@@ -336,18 +286,9 @@ It prints something like:
|
||||
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.00 MB)
|
||||
[3] another-nzb\filename3.r01 (100.00 MB)
|
||||
[4] another-nzb\filename3.r02 (100.00 MB)
|
||||
|
||||
This is the list of individual files listed within nzb-file. To print
|
||||
the list of nzb-files (without content) add G-modifier to the list command:
|
||||
|
||||
[1] nzbname (4.56 GB)
|
||||
[2] another-nzb (4.20 GB)
|
||||
|
||||
The numbers in square braces are ID's of files or groups in queue.
|
||||
They can be used in edit-command. For example to move file with
|
||||
ID 2 to the top of queue:
|
||||
The numbers in square braces are ID's of files in queue. They can be used
|
||||
in edit-command. For example to move file with ID 2 to the top of queue:
|
||||
|
||||
nzbget -E T 2
|
||||
|
||||
@@ -360,8 +301,8 @@ or to delete files from queue:
|
||||
nzbget -E D 3 10-15 20-21 16
|
||||
|
||||
The edit-command has also a group-mode which affects all files from the
|
||||
same nzb-file. You need to pass an ID of the group. For example to delete
|
||||
the whole group 1:
|
||||
same nzb-request. You need to pass one ID of any file in the group. For
|
||||
example to delete all files from the first nzb-request:
|
||||
|
||||
nzbget -E G D 1
|
||||
|
||||
@@ -380,7 +321,7 @@ Running client & server on seperate machines:
|
||||
Since nzbget communicates via TCP/IP it's possible to have a server running on
|
||||
one computer and adding downloads via a client on another computer.
|
||||
|
||||
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
|
||||
Do this by setting the "serverip" option in the nzbget.conf file to point to the
|
||||
IP of the server (default is localhost which means client and server runs on
|
||||
same computer)
|
||||
|
||||
@@ -396,112 +337,36 @@ 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.
|
||||
|
||||
Post processing scripts
|
||||
-----------------------
|
||||
|
||||
After the download of nzb-file is completed nzbget can call post-processing
|
||||
scripts, defined in configuration file.
|
||||
|
||||
Example post-processing scripts are provided in directory "scripts".
|
||||
|
||||
To use the scripts copy them into your local directory and set options
|
||||
<ScriptDir>, <PostScript> and <ScriptOrder>.
|
||||
|
||||
For information on writing your own post-processing scripts please
|
||||
visit NZBGet web site.
|
||||
|
||||
Web-interface
|
||||
-------------
|
||||
|
||||
NZBGet has a built-in web-server providing the access to the program
|
||||
functions via web-interface.
|
||||
|
||||
To activate web-interface set the option "WebDir" to the path with
|
||||
web-interface files. If you install using "make install-conf" as
|
||||
described above the option is set automatically. If you install using
|
||||
binary files you should check if the option is set correctly.
|
||||
|
||||
To access web-interface from your web-browser use the server address
|
||||
and port defined in NZBGet configuration file in options "ControlIP" and
|
||||
"ControlPort". For example:
|
||||
|
||||
http://localhost:6789/
|
||||
|
||||
For login credentials type username and the password defined by
|
||||
options "ControlUsername" (default "nzbget") and "ControlPassword"
|
||||
(default "tegbzn6789").
|
||||
|
||||
In a case your browser forget credentials, to prevent typing them each
|
||||
time, there is a workaround - use URL in the form:
|
||||
|
||||
http://localhost:6789/username:password/
|
||||
|
||||
Please note, that in this case the password is saved in a bookmark or in
|
||||
browser history in plain text and is easy to find by persons having
|
||||
access to your computer.
|
||||
|
||||
=====================================
|
||||
8. Authors
|
||||
=====================================
|
||||
|
||||
NZBGet is developed and maintained by Andrey Prygunkov
|
||||
(hugbug@users.sourceforge.net).
|
||||
|
||||
The original project was initially created by Sven Henkel
|
||||
(sidddy@users.sourceforge.net) in 2004 and later developed by
|
||||
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
|
||||
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
|
||||
Since then the program has been completely rewritten.
|
||||
|
||||
NZBGet distribution archive includes additional components
|
||||
written by other authors:
|
||||
|
||||
Par2:
|
||||
Peter Brian Clements <peterbclements@users.sourceforge.net>
|
||||
|
||||
Par2 library API:
|
||||
Francois Lesueur <flesueur@users.sourceforge.net>
|
||||
|
||||
Catch:
|
||||
Two Blue Cubes Ltd <https://github.com/philsquared/Catch>
|
||||
|
||||
jQuery:
|
||||
John Resig <http://jquery.com>
|
||||
The Dojo Foundation <http://sizzlejs.com>
|
||||
|
||||
Bootstrap:
|
||||
Twitter, Inc <http://twitter.github.com/bootstrap>
|
||||
|
||||
Raphaël:
|
||||
Dmitry Baranovskiy <http://raphaeljs.com>
|
||||
Sencha Labs <http://sencha.com>
|
||||
|
||||
Elycharts:
|
||||
Void Labs s.n.c. <http://void.it>
|
||||
|
||||
iconSweets:
|
||||
Yummygum <http://yummygum.com>
|
||||
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
|
||||
Up to version 0.2.3 it was been developed and maintained by Bo Cordes Petersen
|
||||
(placebodk@users.sourceforge.net). Beginning at version 0.3.0 the program is
|
||||
being developed by Andrei Prygounkov (hugbug@users.sourceforge.net).
|
||||
|
||||
=====================================
|
||||
9. Copyright
|
||||
=====================================
|
||||
|
||||
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.
|
||||
|
||||
NZBGet is distributed under GNU General Pubic License Version 2.
|
||||
The complete content of license is provided in file COPYING.
|
||||
|
||||
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
|
||||
Binary distribution for Windows contains code from the following libraries:
|
||||
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
libpar2 is distributed under GPL and libsigc++ under LGPL.
|
||||
|
||||
=====================================
|
||||
10. Contact
|
||||
=====================================
|
||||
|
||||
If you encounter any problem, feel free to use the forum
|
||||
If you encounter any problem, feel free to use tracker/forums on
|
||||
|
||||
nzbget.net/forum
|
||||
sourceforge.net/projects/nzbget
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -1,19 +0,0 @@
|
||||
# NZBGet #
|
||||
[](http://www.gnu.org/licenses/)
|
||||
[](https://travis-ci.org/nzbget/nzbget)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:cpp)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/context:javascript)
|
||||
[](https://lgtm.com/projects/g/nzbget/nzbget/alerts)
|
||||
|
||||
[](https://github.com/nzbget/nzbget/releases)
|
||||
[](https://github.com/nzbget/nzbget/releases/latest)
|
||||
|
||||
NZBGet is a binary downloader, which downloads files from Usenet
|
||||
based on information given in nzb-files.
|
||||
|
||||
NZBGet is written in C++ and is known for its performance and efficiency.
|
||||
|
||||
NZBGet can run on almost any device - classic PC, NAS, media player, SAT-receiver, WLAN-router, etc.
|
||||
The download area provides precompiled binaries for Windows, macOS, Linux (compatible with
|
||||
many CPUs and platform variants), FreeBSD and Android. For other platforms
|
||||
the program can be compiled from sources.
|
||||
729
RemoteClient.cpp
Normal file
729
RemoteClient.cpp
Normal file
@@ -0,0 +1,729 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
RemoteClient::RemoteClient()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_pNetAddress = NULL;
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
|
||||
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
|
||||
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
|
||||
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
|
||||
printf("sizeof(SNZBListResponseEntry)=%i\n", sizeof(SNZBListResponseEntry));
|
||||
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
|
||||
printf("sizeof(SNZBLogResponse)=%i\n", sizeof(SNZBLogResponse));
|
||||
printf("sizeof(SNZBLogResponseEntry)=%i\n", sizeof(SNZBLogResponseEntry));
|
||||
printf("sizeof(SNZBPauseUnpauseRequest)=%i\n", sizeof(SNZBPauseUnpauseRequest));
|
||||
printf("sizeof(SNZBSetDownloadRateRequest)=%i\n", sizeof(SNZBSetDownloadRateRequest));
|
||||
printf("sizeof(SNZBEditQueueRequest)=%i\n", sizeof(SNZBEditQueueRequest));
|
||||
printf("sizeof(SNZBDumpDebugRequest)=%i\n", sizeof(SNZBDumpDebugRequest));
|
||||
*/
|
||||
}
|
||||
|
||||
RemoteClient::~RemoteClient()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
if (m_pNetAddress)
|
||||
{
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::printf(char * msg,...)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
::vprintf(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::perror(char * msg)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
::perror(msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteClient::InitConnection()
|
||||
{
|
||||
// Create a connection to the server
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
|
||||
bool OK = m_pConnection->Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool RemoteClient::ReceiveBoolResponse()
|
||||
{
|
||||
printf("Request sent\n");
|
||||
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
|
||||
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
|
||||
if (iResponseLen != sizeof(BoolResponse) ||
|
||||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
|
||||
char* buf = (char*)malloc(iTextLen);
|
||||
iResponseLen = m_pConnection->Recv(buf, iTextLen);
|
||||
if (iResponseLen != iTextLen)
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("server returned: %s\n", buf);
|
||||
free(buf);
|
||||
return ntohl(BoolResponse.m_bSuccess);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a message to the running nzbget process.
|
||||
*/
|
||||
bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
{
|
||||
// Read the file into the buffer
|
||||
char* szBuffer = NULL;
|
||||
int iLength = 0;
|
||||
if (!Util::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
|
||||
{
|
||||
printf("Could not load file %s\n", szName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = InitConnection();
|
||||
if (OK)
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
|
||||
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
strncpy(DownloadRequest.m_szFilename, szName, NZBREQUESTFILENAMESIZE - 1);
|
||||
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
|
||||
|
||||
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
OK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send(szBuffer, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (szBuffer)
|
||||
{
|
||||
free(szBuffer);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerList()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(true);
|
||||
ListRequest.m_bServerState = htonl(true);
|
||||
|
||||
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for download\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Queue List\n");
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
long long lFileSize = Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
|
||||
long long lRemainingSize = Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
|
||||
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (lRemainingSize < lFileSize)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(lRemainingSize) * 100.0 / Util::Int64ToFloat(lFileSize)), "%");
|
||||
}
|
||||
char szStatus[100];
|
||||
if (ntohl(pListAnswer->m_bPaused))
|
||||
{
|
||||
sprintf(szStatus, " (paused)");
|
||||
lPaused += lRemainingSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus[0] = '\0';
|
||||
lRemaining += lRemainingSize;
|
||||
}
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename,
|
||||
(float)(Util::Int64ToFloat(lFileSize) / 1024.0 / 1024.0), szCompleted, szStatus);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Files: %i\n", ntohl(ListResponse.m_iNrTrailingEntries));
|
||||
if (lPaused > 0)
|
||||
{
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0),
|
||||
(float)(Util::Int64ToFloat(lPaused) / 1024.0 / 1024.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
|
||||
}
|
||||
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
long long iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
float fAverageSpeed = Util::Int64ToFloat(ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0);
|
||||
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
|
||||
|
||||
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
|
||||
{
|
||||
printf("Speed limit: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadLimit) / 1024.0));
|
||||
}
|
||||
|
||||
int sec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
int h = sec / 3600;
|
||||
int m = (sec % 3600) / 60;
|
||||
int s = sec % 60;
|
||||
printf("Up time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
sec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
h = sec / 3600;
|
||||
m = (sec % 3600) / 60;
|
||||
s = sec % 60;
|
||||
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
printf("Downloaded: %.2f MB\n", (float)(Util::Int64ToFloat(iAllBytes) / 1024.0 / 1024.0));
|
||||
|
||||
printf("Threads running: %i\n", ntohl(ListResponse.m_iThreadCount));
|
||||
|
||||
if (ntohl(ListResponse.m_iPostJobCount) > 0)
|
||||
{
|
||||
printf("Post-jobs: %i\n", (int)ntohl(ListResponse.m_iPostJobCount));
|
||||
}
|
||||
|
||||
if (ntohl(ListResponse.m_bServerStandBy))
|
||||
{
|
||||
printf("Server state: Stand-By\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerLog(int iLines)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(iLines);
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (LogResponse.m_iTrailingDataLength == 0)
|
||||
{
|
||||
printf("Log is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
switch (ntohl(pLogAnswer->m_iKind))
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", szText);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("[ERROR] %s\n", szText);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("[WARNING] %s\n", szText);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", szText);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("[DETAIL] %s\n", szText);
|
||||
break;
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = htonl(bPause);
|
||||
|
||||
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBSetDownloadRateRequest SetDownloadRateRequest;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
|
||||
|
||||
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerDumpDebug()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBDumpDebugRequest DumpDebugInfo;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
|
||||
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder)
|
||||
{
|
||||
if (iIDCount <= 0 || pIDList == NULL)
|
||||
{
|
||||
printf("File(s) not specified\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
int iLength = sizeof(int32_t) * iIDCount;
|
||||
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = htonl(iAction);
|
||||
EditQueueRequest.m_iOffset = htonl((int)iOffset);
|
||||
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
|
||||
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iLength);
|
||||
|
||||
for (int i = 0; i < iIDCount; i++)
|
||||
{
|
||||
pIDs[i] = htonl(pIDList[i]);
|
||||
}
|
||||
|
||||
bool OK = false;
|
||||
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send((char*)pIDs, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerShutdown()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerVersion()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBVersionRequest VersionRequest;
|
||||
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestPostQueue()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPostQueueRequest PostQueueRequest;
|
||||
InitMessageBase(&PostQueueRequest.m_MessageBase, eRemoteRequestPostQueue, sizeof(PostQueueRequest));
|
||||
|
||||
if (m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBPostQueueResponse PostQueueResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
|
||||
if (iResponseLen != sizeof(PostQueueResponse) ||
|
||||
(int)ntohl(PostQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(PostQueueResponse.m_MessageBase.m_iStructSize) != sizeof(PostQueueResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(PostQueueResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(PostQueueResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ntohl(PostQueueResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for post-processing\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Post-Processing List\n");
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(PostQueueResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) pBufPtr;
|
||||
|
||||
int iStageProgress = ntohl(pPostQueueAnswer->m_iStageProgress);
|
||||
|
||||
static const int EXECUTING_SCRIPT = 5;
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
|
||||
}
|
||||
|
||||
char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
|
||||
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
|
||||
|
||||
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
|
||||
|
||||
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
|
||||
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBWriteLogRequest WriteLogRequest;
|
||||
InitMessageBase(&WriteLogRequest.m_MessageBase, eRemoteRequestWriteLog, sizeof(WriteLogRequest));
|
||||
WriteLogRequest.m_iKind = htonl(iKind);
|
||||
int iLength = strlen(szText) + 1;
|
||||
WriteLogRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
if (m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pConnection->Send(szText, iLength);
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
64
RemoteClient.h
Normal file
64
RemoteClient.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTECLIENT_H
|
||||
#define REMOTECLIENT_H
|
||||
|
||||
#include "Options.h"
|
||||
#include "MessageBase.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
NetAddress* m_pNetAddress;
|
||||
bool m_bVerbose;
|
||||
|
||||
bool InitConnection();
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
bool ReceiveBoolResponse();
|
||||
void printf(char* msg, ...);
|
||||
void perror(char* msg);
|
||||
|
||||
public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
|
||||
bool RequestServerDownload(const char* szName, bool bAddFirst);
|
||||
bool RequestServerList();
|
||||
bool RequestServerPauseUnpause(bool bPause);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerVersion();
|
||||
bool RequestPostQueue();
|
||||
bool RequestWriteLog(int iKind, const char* szText);
|
||||
};
|
||||
|
||||
#endif
|
||||
226
RemoteServer.cpp
Normal file
226
RemoteServer.cpp
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "BinRpc.h"
|
||||
#include "XmlRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
|
||||
RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
bool bBind = true;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
}
|
||||
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = INVALID_SOCKET;
|
||||
if (bBind)
|
||||
{
|
||||
iSocket = m_pConnection->Accept();
|
||||
}
|
||||
if (!bBind || iSocket == INVALID_SOCKET)
|
||||
{
|
||||
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
usleep(500 * 1000);
|
||||
delete m_pConnection;
|
||||
m_pConnection = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
RequestProcessor* commandThread = new RequestProcessor();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->Start();
|
||||
}
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
debug("Exiting RemoteServer-loop");
|
||||
}
|
||||
|
||||
void RemoteServer::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
#ifdef WIN32
|
||||
m_pConnection->Disconnect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// RequestProcessor
|
||||
|
||||
void RequestProcessor::Run()
|
||||
{
|
||||
// Read the first 4 bytes to determine request type
|
||||
bool bOK = false;
|
||||
int iSignature = 0;
|
||||
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Info - connection received
|
||||
#ifdef WIN32
|
||||
char* ip = NULL;
|
||||
#else
|
||||
char ip[20];
|
||||
#endif
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ip = inet_ntoa(PeerName.sin_addr);
|
||||
#else
|
||||
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
// binary request received
|
||||
bOK = true;
|
||||
BinRpcProcessor processor;
|
||||
processor.SetSocket(m_iSocket);
|
||||
processor.SetSignature(iSignature);
|
||||
processor.SetClientIP(ip);
|
||||
processor.Execute();
|
||||
}
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
|
||||
{
|
||||
// XML-RPC or JSON-RPC request received
|
||||
Connection con(m_iSocket, false);
|
||||
char szBuffer[1024];
|
||||
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
|
||||
char* szUrl = szBuffer;
|
||||
if (!strncmp((char*)&iSignature, "POST", 4))
|
||||
{
|
||||
eHttpMethod = XmlRpcProcessor::hmPost;
|
||||
szUrl++;
|
||||
}
|
||||
if (char* p = strchr(szUrl, ' '))
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
|
||||
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpXmlRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonRpc;
|
||||
}
|
||||
|
||||
if (eProtocol != XmlRpcProcessor::rpUndefined)
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetProtocol(eProtocol);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOK)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
|
||||
}
|
||||
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
57
RemoteServer.h
Normal file
57
RemoteServer.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTESERVER_H
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
RemoteServer();
|
||||
~RemoteServer();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
};
|
||||
|
||||
class RequestProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
};
|
||||
|
||||
#endif
|
||||
390
ScriptController.cpp
Normal file
390
ScriptController.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ScriptController.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
void ScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
|
||||
{
|
||||
if (!Util::FileExists(szScript))
|
||||
{
|
||||
error("Could not start post-process-script: could not find file %s", szScript);
|
||||
pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
|
||||
|
||||
ScriptController* pScriptController = new ScriptController();
|
||||
pScriptController->m_pPostInfo = pPostInfo;
|
||||
pScriptController->m_szScript = szScript;
|
||||
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
|
||||
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
|
||||
pScriptController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetScriptThread(pScriptController);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void ScriptController::Run()
|
||||
{
|
||||
char szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
|
||||
szParStatus[10-1] = '\0';
|
||||
|
||||
char szCollectionCompleted[10];
|
||||
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
|
||||
szCollectionCompleted[10-1] = '\0';
|
||||
|
||||
char szHasFailedParJobs[10];
|
||||
snprintf(szHasFailedParJobs, 10, "%i", (int)m_bHasFailedParJobs);
|
||||
szHasFailedParJobs[10-1] = '\0';
|
||||
|
||||
int pipein;
|
||||
|
||||
#ifdef WIN32
|
||||
char szCmdLine[2048];
|
||||
snprintf(szCmdLine, 2048, "\"%s\" \"%s\" \"%s\" \"%s\" %s %s %s", m_szScript, m_pPostInfo->GetDestDir(),
|
||||
m_pPostInfo->GetNZBFilename(), m_pPostInfo->GetParFilename(), szParStatus, szCollectionCompleted, szHasFailedParJobs);
|
||||
szCmdLine[2048-1] = '\0';
|
||||
|
||||
// create pipes to write and read data
|
||||
HANDLE hReadPipe, hWritePipe;
|
||||
SECURITY_ATTRIBUTES SecurityAttributes;
|
||||
memset(&SecurityAttributes, 0, sizeof(SecurityAttributes));
|
||||
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
||||
SecurityAttributes.bInheritHandle = TRUE;
|
||||
|
||||
CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0);
|
||||
|
||||
STARTUPINFO StartupInfo;
|
||||
memset(&StartupInfo, 0, sizeof(StartupInfo));
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
StartupInfo.hStdInput = 0;
|
||||
StartupInfo.hStdOutput = hWritePipe;
|
||||
StartupInfo.hStdError = hWritePipe;
|
||||
|
||||
PROCESS_INFORMATION ProcessInfo;
|
||||
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
|
||||
|
||||
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, m_pPostInfo->GetDestDir(), &StartupInfo, &ProcessInfo);
|
||||
if (!bOK)
|
||||
{
|
||||
DWORD dwErrCode = GetLastError();
|
||||
char szErrMsg[255];
|
||||
szErrMsg[255-1] = '\0';
|
||||
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, dwErrCode, 0, szErrMsg, 255, NULL))
|
||||
{
|
||||
error("Could not start post-process-script: %s", szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not start post-process-script: error %i", dwErrCode);
|
||||
}
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Child Process-ID: %i", (int)ProcessInfo.dwProcessId);
|
||||
|
||||
m_hProcess = ProcessInfo.hProcess;
|
||||
|
||||
// close unused "write" end
|
||||
CloseHandle(hWritePipe);
|
||||
|
||||
pipein = _open_osfhandle((intptr_t)hReadPipe, _O_RDONLY);
|
||||
|
||||
#else
|
||||
|
||||
char szDestDir[1024];
|
||||
strncpy(szDestDir, m_pPostInfo->GetDestDir(), 1024);
|
||||
szDestDir[1024-1] = '\0';
|
||||
|
||||
char szNZBFilename[1024];
|
||||
strncpy(szNZBFilename, m_pPostInfo->GetNZBFilename(), 1024);
|
||||
szNZBFilename[1024-1] = '\0';
|
||||
|
||||
char szParFilename[1024];
|
||||
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
|
||||
szParFilename[1024-1] = '\0';
|
||||
|
||||
int p[2];
|
||||
int pipeout;
|
||||
|
||||
// create the pipe
|
||||
if (pipe(p))
|
||||
{
|
||||
error("Could not open pipe: errno %i", errno);
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pipein = p[0];
|
||||
pipeout = p[1];
|
||||
|
||||
debug("forking");
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
error("Could not start post-process-script: errno %i", errno);
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
// here goes the second instance
|
||||
|
||||
// close up the "read" end
|
||||
close(pipein);
|
||||
|
||||
// make the pipeout to be the same as stdout and stderr
|
||||
dup2(pipeout, 1);
|
||||
dup2(pipeout, 2);
|
||||
|
||||
close(pipeout);
|
||||
|
||||
execlp(m_szScript, m_szScript, szDestDir, szNZBFilename, szParFilename,
|
||||
szParStatus, szCollectionCompleted, szHasFailedParJobs, NULL);
|
||||
fprintf(stdout, "[ERROR] Could not start post-process-script: %s", strerror(errno));
|
||||
fflush(stdout);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
// continue the first instance
|
||||
debug("forked");
|
||||
debug("Child Process-ID: %i", (int)pid);
|
||||
|
||||
m_hProcess = pid;
|
||||
|
||||
// close unused "write" end
|
||||
close(pipeout);
|
||||
#endif
|
||||
|
||||
// open the read end
|
||||
FILE* readpipe = fdopen(pipein, "r");
|
||||
if (!readpipe)
|
||||
{
|
||||
error("Could not open pipe to post-process-script");
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
char* buf = (char*)malloc(10240);
|
||||
|
||||
debug("Entering pipe-loop");
|
||||
while (!feof(readpipe) && !IsStopped())
|
||||
{
|
||||
if (fgets(buf, 10240, readpipe))
|
||||
{
|
||||
AddMessage(buf);
|
||||
}
|
||||
}
|
||||
debug("Exited pipe-loop");
|
||||
|
||||
free(buf);
|
||||
fclose(readpipe);
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
warn("Interrupted post-process-script for %s", m_pPostInfo->GetInfoName());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
WaitForSingleObject(m_hProcess, INFINITE);
|
||||
#else
|
||||
waitpid(m_hProcess, NULL, 0);
|
||||
#endif
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
info("Completed post-process-script for %s", m_pPostInfo->GetInfoName());
|
||||
}
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void ScriptController::AddMessage(char* szText)
|
||||
{
|
||||
debug("Adding message received from post-process-script");
|
||||
|
||||
for (char* pend = szText + strlen(szText) - 1; pend >= szText && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
|
||||
if (strlen(szText) == 0)
|
||||
{
|
||||
// skip empty lines
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(szText, "[INFO] ", 7))
|
||||
{
|
||||
info(szText + 7);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkInfo, szText + 7);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[WARNING] ", 10))
|
||||
{
|
||||
warn(szText + 10);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkWarning, szText + 10);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[ERROR] ", 8))
|
||||
{
|
||||
error(szText + 8);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkError, szText + 8);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[DETAIL] ", 9))
|
||||
{
|
||||
detail(szText + 9);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkDetail, szText + 9);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[DEBUG] ", 8))
|
||||
{
|
||||
debug(szText + 8);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkDebug, szText + 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Options::EMessageTarget eMessageTarget = Options::mtNone;
|
||||
Message::EKind eKind = Message::mkDebug;
|
||||
switch (g_pOptions->GetPostLogKind())
|
||||
{
|
||||
case Options::plNone:
|
||||
break;
|
||||
|
||||
case Options::plDetail:
|
||||
detail("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
eKind = Message::mkDetail;
|
||||
break;
|
||||
|
||||
case Options::plInfo:
|
||||
info("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
eKind = Message::mkInfo;
|
||||
break;
|
||||
|
||||
case Options::plWarning:
|
||||
warn("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
eKind = Message::mkWarning;
|
||||
break;
|
||||
|
||||
case Options::plError:
|
||||
error("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
eKind = Message::mkError;
|
||||
break;
|
||||
|
||||
case Options::plDebug:
|
||||
debug("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
eKind = Message::mkDebug;
|
||||
break;
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
|
||||
debug("Adding message received from post-process-script - completed");
|
||||
}
|
||||
|
||||
void ScriptController::Stop()
|
||||
{
|
||||
debug("Stopping post-process-script");
|
||||
Thread::Stop();
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL bOK = TerminateProcess(m_hProcess, -1);
|
||||
#else
|
||||
bool bOK = kill(m_hProcess, 9) == 0;
|
||||
#endif
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
debug("Terminated post-process-script");
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate post-process-script");
|
||||
}
|
||||
|
||||
debug("Post-process-script stopped");
|
||||
}
|
||||
54
ScriptController.h
Normal file
54
ScriptController.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCRIPTCONTROLLER_H
|
||||
#define SCRIPTCONTROLLER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
class ScriptController : public Thread
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
const char* m_szScript;
|
||||
bool m_bNZBFileCompleted;
|
||||
bool m_bHasFailedParJobs;
|
||||
#ifdef WIN32
|
||||
HANDLE m_hProcess;
|
||||
#else
|
||||
pid_t m_hProcess;
|
||||
#endif
|
||||
|
||||
void AddMessage(char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
|
||||
bool bNZBFileCompleted, bool bHasFailedParJobs);
|
||||
};
|
||||
|
||||
#endif
|
||||
251
ServerPool.cpp
Normal file
251
ServerPool.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* The queue of mutexes that was used did not work for every
|
||||
* implementation of POSIX threads. Now a m_Semaphore is used.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_HOLD_SECODNS = 5;
|
||||
|
||||
ServerPool::PooledConnection::PooledConnection(NewsServer* server) : NNTPConnection(server)
|
||||
{
|
||||
m_bInUse = false;
|
||||
m_tFreeTime = 0;
|
||||
}
|
||||
|
||||
ServerPool::ServerPool()
|
||||
{
|
||||
debug("Creating ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_Connections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Semaphores.clear();
|
||||
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Servers.clear();
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Connections.clear();
|
||||
}
|
||||
|
||||
void ServerPool::AddServer(NewsServer* pNewsServer)
|
||||
{
|
||||
debug("Adding server to ServerPool");
|
||||
|
||||
m_Servers.push_back(pNewsServer);
|
||||
}
|
||||
|
||||
void ServerPool::InitConnections()
|
||||
{
|
||||
debug("Initializing connections in ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (m_iMaxLevel < pNewsServer->GetLevel())
|
||||
{
|
||||
m_iMaxLevel = pNewsServer->GetLevel();
|
||||
}
|
||||
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
|
||||
{
|
||||
PooledConnection* pConnection = new PooledConnection(pNewsServer);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_Connections.push_back(pConnection);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
int iMaxConnectionsForLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (iLevel == pNewsServer->GetLevel())
|
||||
{
|
||||
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
|
||||
m_Semaphores.push_back(sem);
|
||||
}
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
|
||||
{
|
||||
bool bWaitVal = false;
|
||||
if (bWait)
|
||||
{
|
||||
debug("Getting connection (wait)");
|
||||
bWaitVal = m_Semaphores[iLevel]->Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitVal = m_Semaphores[iLevel]->TryWait();
|
||||
}
|
||||
|
||||
if (!bWaitVal)
|
||||
{
|
||||
// signal received or wait timeout
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
PooledConnection* pConnection = NULL;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
|
||||
{
|
||||
if (bUsed)
|
||||
{
|
||||
debug("Freeing used connection");
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
((PooledConnection*)pConnection)->SetInUse(false);
|
||||
if (bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::CloseUnusedConnections()
|
||||
{
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
time_t curtime = ::time(NULL);
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection = *it;
|
||||
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
|
||||
{
|
||||
int tdiff = (int)(curtime - pConnection->GetFreeTime());
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
{
|
||||
debug(" ServerPool");
|
||||
debug(" ----------------");
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
debug(" Connections: %i", m_Connections.size());
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
|
||||
}
|
||||
/*
|
||||
debug(" Semaphores: %i", m_Semaphores.size());
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
sem_t* sem = m_Semaphores[iLevel];
|
||||
int iSemValue;
|
||||
sem_getvalue(sem, &iSemValue);
|
||||
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
|
||||
}
|
||||
*/
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
79
ServerPool.h
Normal file
79
ServerPool.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SERVERPOOL_H
|
||||
#define SERVERPOOL_H
|
||||
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NewsServer.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class ServerPool
|
||||
{
|
||||
private:
|
||||
class PooledConnection : public NNTPConnection
|
||||
{
|
||||
private:
|
||||
bool m_bInUse;
|
||||
time_t m_tFreeTime;
|
||||
public:
|
||||
PooledConnection(NewsServer* server);
|
||||
bool GetInUse() { return m_bInUse; }
|
||||
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
|
||||
time_t GetFreeTime() { return m_tFreeTime; }
|
||||
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
typedef std::vector<PooledConnection*> Connections;
|
||||
|
||||
Servers m_Servers;
|
||||
Connections m_Connections;
|
||||
Semaphores m_Semaphores;
|
||||
int m_iMaxLevel;
|
||||
Mutex m_mutexConnections;
|
||||
int m_iTimeout;
|
||||
|
||||
public:
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
void AddServer(NewsServer *s);
|
||||
void InitConnections();
|
||||
int GetMaxLevel() { return m_iMaxLevel; }
|
||||
NNTPConnection* GetConnection(int iLevel, bool bWait);
|
||||
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
|
||||
void CloseUnusedConnections();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
301
Thread.cpp
Normal file
301
Thread.cpp
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
int Thread::m_iThreadCount = 1; // take the main program thread into account
|
||||
Mutex Thread::m_mutexThread;
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
InitializeCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_init(&m_mutexObj, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~ Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
DeleteCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection(&m_mutexObj);
|
||||
#ifdef DEBUG
|
||||
// CriticalSections on Windows can be locked many times from the same thread,
|
||||
// but we do not want this and must treat such situations as errors and detect them.
|
||||
if (m_mutexObj.RecursionCount > 1)
|
||||
{
|
||||
error("Internal program error: inconsistent thread-lock detected");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
pthread_mutex_lock(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
LeaveCriticalSection(&m_mutexObj);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_mutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Semaphore::Semaphore()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(int iValue)
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, iValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::~ Semaphore()
|
||||
{
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_semObj);
|
||||
#else
|
||||
sem_destroy(&m_semObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Semaphore::Post()
|
||||
{
|
||||
#ifdef WIN32
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
#else
|
||||
sem_post(&m_semObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::Wait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, INFINITE) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_wait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TryWait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_trywait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TimedWait(int iMSec)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, iMSec) == WAIT_OBJECT_0;
|
||||
#else
|
||||
struct timespec alarm;
|
||||
alarm.tv_sec = ::time(NULL) + iMSec / 1000;
|
||||
alarm.tv_nsec = (iMSec % 1000) * 1000;
|
||||
return sem_timedwait(&m_semObj, &alarm) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::IsLocked()
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool bCanLock = WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
if (bCanLock)
|
||||
{
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
}
|
||||
return !bCanLock;
|
||||
#else
|
||||
int iSemValue;
|
||||
sem_getvalue(&m_semObj, &iSemValue);
|
||||
return iSemValue <= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Thread::Init()
|
||||
{
|
||||
debug("Initializing global thread data");
|
||||
}
|
||||
|
||||
void Thread::Final()
|
||||
{
|
||||
debug("Finalizing global thread data");
|
||||
}
|
||||
|
||||
Thread::Thread()
|
||||
{
|
||||
debug("Creating Thread");
|
||||
|
||||
m_Thread = 0;
|
||||
m_bRunning = false;
|
||||
m_bStopped = false;
|
||||
m_bAutoDestroy = false;
|
||||
}
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
debug("Destroying Thread");
|
||||
}
|
||||
|
||||
void Thread::Start()
|
||||
{
|
||||
debug("Starting Thread");
|
||||
|
||||
m_bRunning = true;
|
||||
|
||||
// NOTE: we must garantee, that in a time we setting m_bRunning
|
||||
// to value returned from pthread_create, the thread-object still exists.
|
||||
// This is not obviously!
|
||||
// pthread_create could wait long enough before returning result
|
||||
// back to allow the started thread to complete it job
|
||||
// and terminate.
|
||||
// We lock mutex m_mutexThread on calling pthread_create; the started thread
|
||||
// then also try to lock the mutex (see thread_handler) and therefore
|
||||
// must wait until we unlock it
|
||||
m_mutexThread.Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_Thread != NULL;
|
||||
#else
|
||||
pthread_attr_t m_Attr;
|
||||
pthread_attr_init(&m_Attr);
|
||||
pthread_attr_setdetachstate(&m_Attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setinheritsched(&m_Attr , PTHREAD_INHERIT_SCHED);
|
||||
m_bRunning = !pthread_create(&m_Thread, &m_Attr, Thread::thread_handler, (void *) this);
|
||||
pthread_attr_destroy(&m_Attr);
|
||||
#endif
|
||||
|
||||
m_mutexThread.Unlock();
|
||||
}
|
||||
|
||||
void Thread::Stop()
|
||||
{
|
||||
debug("Stopping Thread");
|
||||
|
||||
m_bStopped = true;
|
||||
}
|
||||
|
||||
bool Thread::Kill()
|
||||
{
|
||||
debug("Killing Thread");
|
||||
|
||||
m_mutexThread.Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
bool terminated = TerminateThread(m_Thread, 0) != 0;
|
||||
#else
|
||||
bool terminated = pthread_cancel(m_Thread) == 0;
|
||||
#endif
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
m_iThreadCount--;
|
||||
}
|
||||
m_mutexThread.Unlock();
|
||||
return terminated;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void __cdecl Thread::thread_handler(void* pObject)
|
||||
#else
|
||||
void* Thread::thread_handler(void* pObject)
|
||||
#endif
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_iThreadCount++;
|
||||
m_mutexThread.Unlock();
|
||||
|
||||
debug("Entering Thread-func");
|
||||
|
||||
Thread* pThread = (Thread*)pObject;
|
||||
|
||||
pThread->Run();
|
||||
|
||||
debug("Thread-func exited");
|
||||
|
||||
pThread->m_bRunning = false;
|
||||
|
||||
if (pThread->m_bAutoDestroy)
|
||||
{
|
||||
debug("Autodestroying Thread-object");
|
||||
delete pThread;
|
||||
}
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_iThreadCount--;
|
||||
m_mutexThread.Unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
int iThreadCount = m_iThreadCount;
|
||||
m_mutexThread.Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
115
Thread.h
Normal file
115
Thread.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
class Mutex
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
CRITICAL_SECTION m_mutexObj;
|
||||
#else
|
||||
pthread_mutex_t m_mutexObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
~Mutex();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
|
||||
class Semaphore
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
HANDLE m_semObj;
|
||||
#else
|
||||
sem_t m_semObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Semaphore();
|
||||
Semaphore(int iValue);
|
||||
~Semaphore();
|
||||
void Post();
|
||||
bool Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(int iMSec);
|
||||
bool IsLocked();
|
||||
};
|
||||
|
||||
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexThread;
|
||||
static int m_iThreadCount;
|
||||
#ifdef WIN32
|
||||
HANDLE m_Thread;
|
||||
#else
|
||||
pthread_t m_Thread;
|
||||
#endif
|
||||
bool m_bRunning;
|
||||
bool m_bStopped;
|
||||
bool m_bAutoDestroy;
|
||||
|
||||
#ifdef WIN32
|
||||
static void __cdecl thread_handler(void* pObject);
|
||||
#else
|
||||
static void *thread_handler(void* pObject);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
bool Kill();
|
||||
|
||||
bool IsStopped() { return m_bStopped; };
|
||||
bool IsRunning() const { return m_bRunning; }
|
||||
void SetRunning(bool bOnOff) { m_bRunning = bOnOff; }
|
||||
bool GetAutoDestroy() { return m_bAutoDestroy; }
|
||||
void SetAutoDestroy(bool bAutoDestroy) { m_bAutoDestroy = bAutoDestroy; }
|
||||
static int GetThreadCount();
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
|
||||
protected:
|
||||
virtual void Run() {}; // Virtual function - override in derivatives
|
||||
};
|
||||
|
||||
#endif
|
||||
142
Util.h
Normal file
142
Util.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
extern int optind, opterr;
|
||||
extern char *optarg;
|
||||
int getopt(int argc, char *argv[], char *optstring);
|
||||
#endif
|
||||
|
||||
class DirBrowser
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
struct _finddata_t m_FindData;
|
||||
intptr_t m_hFile;
|
||||
bool m_bFirst;
|
||||
#else
|
||||
DIR* m_pDir;
|
||||
struct dirent* m_pFindData;
|
||||
#endif
|
||||
|
||||
public:
|
||||
DirBrowser(const char* szPath);
|
||||
~DirBrowser();
|
||||
const char* Next();
|
||||
};
|
||||
|
||||
class Util
|
||||
{
|
||||
public:
|
||||
|
||||
static char* BaseFileName(const char* filename);
|
||||
static void NormalizePathSeparators(char* szPath);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
static bool SetFileSize(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar);
|
||||
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
|
||||
static bool FileExists(const char* szFilename);
|
||||
static bool DirectoryExists(const char* szDirFilename);
|
||||
static bool CreateDirectory(const char* szDirFilename);
|
||||
static bool ForceDirectories(const char* szPath);
|
||||
static long long FileSize(const char* szFilename);
|
||||
static long long FreeDiskSize(const char* szPath);
|
||||
|
||||
static long long JoinInt64(unsigned long Hi, unsigned long Lo);
|
||||
static void SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo);
|
||||
|
||||
/**
|
||||
* Int64ToFloat converts Int64 to float.
|
||||
* Simple (float)Int64 does not work on all compilers,
|
||||
* for example on ARM for NSLU2 (unslung).
|
||||
*/
|
||||
static float Int64ToFloat(long long Int64);
|
||||
|
||||
static float EqualTime(_timeval* t1, _timeval* t2);
|
||||
static bool EmptyTime(_timeval* t);
|
||||
static float DiffTime(_timeval* t1, _timeval* t2);
|
||||
|
||||
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
|
||||
|
||||
/*
|
||||
* Encodes string to be used as content of xml-tag.
|
||||
* Returns new string allocated with malloc, it need to be freed by caller.
|
||||
*/
|
||||
static char* XmlEncode(const char* raw);
|
||||
|
||||
/*
|
||||
* Decodes string from xml.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void XmlDecode(char* raw);
|
||||
|
||||
/*
|
||||
* Returns pointer to tag-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* XmlFindTag(const char* szXml, const char* szTag, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Parses tag-content into szValueBuf.
|
||||
*/
|
||||
static bool XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd);
|
||||
|
||||
/*
|
||||
* Creates JSON-string by replace the certain characters with escape-sequences.
|
||||
* Returns new string allocated with malloc, it need to be freed by caller.
|
||||
*/
|
||||
static char* JsonEncode(const char* raw);
|
||||
|
||||
/*
|
||||
* Decodes JSON-string.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void JsonDecode(char* raw);
|
||||
|
||||
/*
|
||||
* Returns pointer to field-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Returns pointer to field-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
|
||||
};
|
||||
|
||||
#endif
|
||||
1442
XmlRpc.cpp
Normal file
1442
XmlRpc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
215
XmlRpc.h
Normal file
215
XmlRpc.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef XMLRPC_H
|
||||
#define XMLRPC_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
char* m_szBuffer;
|
||||
int m_iBufferSize;
|
||||
int m_iUsedSize;
|
||||
public:
|
||||
StringBuilder();
|
||||
~StringBuilder();
|
||||
void Append(const char* szStr);
|
||||
const char* GetBuffer() { return m_szBuffer; }
|
||||
};
|
||||
|
||||
class XmlCommand;
|
||||
|
||||
class XmlRpcProcessor
|
||||
{
|
||||
public:
|
||||
enum ERpcProtocol
|
||||
{
|
||||
rpUndefined,
|
||||
rpXmlRpc,
|
||||
rpJsonRpc
|
||||
};
|
||||
|
||||
enum EHttpMethod
|
||||
{
|
||||
hmPost,
|
||||
hmGet
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
ERpcProtocol m_eProtocol;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
char* m_szUrl;
|
||||
|
||||
void Dispatch();
|
||||
void SendResponse(const char* szResponse, bool bFault);
|
||||
XmlCommand* CreateCommand(const char* szMethodName);
|
||||
void MutliCall();
|
||||
|
||||
public:
|
||||
XmlRpcProcessor();
|
||||
~XmlRpcProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetProtocol(ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
class XmlCommand
|
||||
{
|
||||
protected:
|
||||
char* m_szRequest;
|
||||
char* m_szRequestPtr;
|
||||
StringBuilder m_StringBuilder;
|
||||
bool m_bFault;
|
||||
XmlRpcProcessor::ERpcProtocol m_eProtocol;
|
||||
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
|
||||
|
||||
void BuildErrorResponse(int iErrCode, const char* szErrText);
|
||||
void BuildBoolResponse(bool bOK);
|
||||
void AppendResponse(const char* szPart);
|
||||
bool IsJson() { return m_eProtocol == XmlRpcProcessor::rpJsonRpc; }
|
||||
bool NextParamAsInt(int* iValue);
|
||||
bool NextParamAsBool(bool* bValue);
|
||||
bool NextParamAsStr(char** szValueBuf);
|
||||
const char* BoolToStr(bool bValue);
|
||||
char* EncodeStr(const char* szStr);
|
||||
|
||||
public:
|
||||
XmlCommand();
|
||||
virtual ~XmlCommand() {}
|
||||
virtual void Execute() = 0;
|
||||
void PrepareParams();
|
||||
void SetRequest(char* szRequest) { m_szRequest = szRequest; m_szRequestPtr = m_szRequest; }
|
||||
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
|
||||
bool GetFault() { return m_bFault; }
|
||||
};
|
||||
|
||||
class ErrorXmlCommand: public XmlCommand
|
||||
{
|
||||
private:
|
||||
int m_iErrCode;
|
||||
const char* m_szErrText;
|
||||
|
||||
public:
|
||||
ErrorXmlCommand(int iErrCode, const char* szErrText);
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UnPauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class StatusXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListFilesXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListGroupsXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
1142
aclocal.m4
vendored
1142
aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
815
posix/config.guess → config.guess
vendored
815
posix/config.guess → config.guess
vendored
File diff suppressed because it is too large
Load Diff
107
config.h.in
107
config.h.in
@@ -6,28 +6,13 @@
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to not use libxml2, only for development purposes */
|
||||
#undef DISABLE_LIBXML2
|
||||
|
||||
/* Define to 1 to disable par-verification and repair */
|
||||
/* Define to 1 to disable smart par-verification and restoration */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
/* Define to 1 to not use TLS/SSL */
|
||||
#undef DISABLE_TLS
|
||||
|
||||
/* Define to 1 to enable unit and integration tests */
|
||||
#undef ENABLE_TESTS
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
|
||||
/* Define to 1 to create stacktrace on segmentation faults */
|
||||
#undef HAVE_BACKTRACE
|
||||
|
||||
/* Define to 1 if ctime_r takes 2 arguments */
|
||||
#undef HAVE_CTIME_R_2
|
||||
|
||||
@@ -37,54 +22,18 @@
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* define if the compiler supports basic C++14 syntax */
|
||||
#undef HAVE_CXX14
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#undef HAVE_ENDIAN_H
|
||||
|
||||
/* Define to 1 if fdatasync is supported */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#undef HAVE_FSEEKO
|
||||
|
||||
/* Define to 1 if F_FULLFSYNC is supported */
|
||||
#undef HAVE_FULLFSYNC
|
||||
|
||||
/* Define to 1 if getaddrinfo is supported */
|
||||
#undef HAVE_GETADDRINFO
|
||||
|
||||
/* Define to 1 if gethostbyname_r is supported */
|
||||
#undef HAVE_GETHOSTBYNAME_R
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 3 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_3
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 5 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_5
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 6 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_6
|
||||
|
||||
/* Define to 1 if you have the `getopt' function. */
|
||||
#undef HAVE_GETOPT
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define to 1 if getopt_long is supported */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
|
||||
#undef HAVE_LIBGNUTLS
|
||||
|
||||
/* Define to 1 if lockf is supported */
|
||||
#undef HAVE_LOCKF
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -94,20 +43,8 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 to use Nettle library for decryption. */
|
||||
#undef HAVE_NETTLE
|
||||
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support and decryption. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if pthread_cancel is supported */
|
||||
#undef HAVE_PTHREAD_CANCEL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
|
||||
#undef HAVE_SC_NPROCESSORS_ONLN
|
||||
/* 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
|
||||
@@ -115,18 +52,12 @@
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `stricmp' function. */
|
||||
#undef HAVE_STRICMP
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/prctl.h> header file. */
|
||||
#undef HAVE_SYS_PRCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
@@ -139,12 +70,6 @@
|
||||
/* Define to 1 if variadic macros are supported */
|
||||
#undef HAVE_VARIADIC_MACROS
|
||||
|
||||
/* Define to 1 if OpenSSL supports function "X509_check_host". */
|
||||
#undef HAVE_X509_CHECK_HOST
|
||||
|
||||
/* Define to 1 to exclude debug-code */
|
||||
#undef NDEBUG
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
@@ -160,37 +85,11 @@
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to 1 to install an empty signal handler for SIGCHLD */
|
||||
#undef SIGCHLD_HANDLER
|
||||
|
||||
/* Determine what socket length (socklen_t) data type is */
|
||||
#undef SOCKLEN_T
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
|
||||
#undef _LARGEFILE_SOURCE
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
403
posix/config.sub → config.sub
vendored
403
posix/config.sub → config.sub
vendored
@@ -1,40 +1,44 @@
|
||||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
|
||||
# Inc.
|
||||
|
||||
timestamp='2014-12-03'
|
||||
timestamp='2006-09-20'
|
||||
|
||||
# This file 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 3 of the License, or
|
||||
# This file is (in principle) common to ALL GNU software.
|
||||
# The presence of a machine in this file suggests that SOME GNU software
|
||||
# can handle that machine. It does not imply ALL GNU software can.
|
||||
#
|
||||
# This file 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.
|
||||
# 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, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
|
||||
# 02110-1301, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that
|
||||
# program. This Exception is an additional permission under section 7
|
||||
# of the GNU General Public License, version 3 ("GPLv3").
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
# Please send patches to <config-patches@gnu.org>. Submit a context
|
||||
# diff and a properly formatted ChangeLog entry.
|
||||
#
|
||||
# Configuration subroutine to validate and canonicalize a configuration type.
|
||||
# Supply the specified configuration type as an argument.
|
||||
# If it is invalid, we print an error message on stderr and exit with code 1.
|
||||
# Otherwise, we print the canonical config type on stdout and succeed.
|
||||
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
|
||||
|
||||
# This file is supposed to be the same for all GNU packages
|
||||
# and recognize all the CPU types, system types and aliases
|
||||
# that are meaningful with *any* GNU software.
|
||||
@@ -68,7 +72,8 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@@ -115,18 +120,12 @@ esac
|
||||
# Here we must recognize all the valid KERNEL-OS combinations.
|
||||
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||
case $maybe_os in
|
||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | \
|
||||
kopensolaris*-gnu* | \
|
||||
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
|
||||
uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
|
||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||
os=-$maybe_os
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
|
||||
;;
|
||||
android-linux)
|
||||
os=-linux-android
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
|
||||
;;
|
||||
*)
|
||||
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
|
||||
if [ $basic_machine != $1 ]
|
||||
@@ -149,13 +148,10 @@ case $os in
|
||||
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
|
||||
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
|
||||
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
|
||||
-apple | -axis | -knuth | -cray | -microblaze*)
|
||||
-apple | -axis | -knuth | -cray)
|
||||
os=
|
||||
basic_machine=$1
|
||||
;;
|
||||
-bluegene*)
|
||||
os=-cnk
|
||||
;;
|
||||
-sim | -cisco | -oki | -wec | -winbond)
|
||||
os=
|
||||
basic_machine=$1
|
||||
@@ -170,10 +166,10 @@ case $os in
|
||||
os=-chorusos
|
||||
basic_machine=$1
|
||||
;;
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
-chorusrdb)
|
||||
os=-chorusrdb
|
||||
basic_machine=$1
|
||||
;;
|
||||
;;
|
||||
-hiux*)
|
||||
os=-hiuxwe2
|
||||
;;
|
||||
@@ -218,12 +214,6 @@ case $os in
|
||||
-isc*)
|
||||
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
|
||||
;;
|
||||
-lynx*178)
|
||||
os=-lynxos178
|
||||
;;
|
||||
-lynx*5)
|
||||
os=-lynxos5
|
||||
;;
|
||||
-lynx*)
|
||||
os=-lynxos
|
||||
;;
|
||||
@@ -248,90 +238,59 @@ case $basic_machine in
|
||||
# Some are omitted here because they have special meanings below.
|
||||
1750a | 580 \
|
||||
| a29k \
|
||||
| aarch64 | aarch64_be \
|
||||
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
||||
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
||||
| am33_2.0 \
|
||||
| arc | arceb \
|
||||
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||
| avr | avr32 \
|
||||
| be32 | be64 \
|
||||
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
|
||||
| bfin \
|
||||
| c4x | c8051 | clipper \
|
||||
| c4x | clipper \
|
||||
| d10v | d30v | dlx | dsp16xx \
|
||||
| epiphany \
|
||||
| fido | fr30 | frv \
|
||||
| fr30 | frv \
|
||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||
| hexagon \
|
||||
| i370 | i860 | i960 | ia64 \
|
||||
| ip2k | iq2000 \
|
||||
| k1om \
|
||||
| le32 | le64 \
|
||||
| lm32 \
|
||||
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
|
||||
| maxq | mb | microblaze | mcore \
|
||||
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||
| mips16 \
|
||||
| mips64 | mips64el \
|
||||
| mips64octeon | mips64octeonel \
|
||||
| mips64orion | mips64orionel \
|
||||
| mips64r5900 | mips64r5900el \
|
||||
| mips64vr | mips64vrel \
|
||||
| mips64orion | mips64orionel \
|
||||
| mips64vr4100 | mips64vr4100el \
|
||||
| mips64vr4300 | mips64vr4300el \
|
||||
| mips64vr5000 | mips64vr5000el \
|
||||
| mips64vr5900 | mips64vr5900el \
|
||||
| mipsisa32 | mipsisa32el \
|
||||
| mipsisa32r2 | mipsisa32r2el \
|
||||
| mipsisa32r6 | mipsisa32r6el \
|
||||
| mipsisa64 | mipsisa64el \
|
||||
| mipsisa64r2 | mipsisa64r2el \
|
||||
| mipsisa64r6 | mipsisa64r6el \
|
||||
| mipsisa64sb1 | mipsisa64sb1el \
|
||||
| mipsisa64sr71k | mipsisa64sr71kel \
|
||||
| mipsr5900 | mipsr5900el \
|
||||
| mipstx39 | mipstx39el \
|
||||
| mn10200 | mn10300 \
|
||||
| moxie \
|
||||
| mt \
|
||||
| msp430 \
|
||||
| nds32 | nds32le | nds32be \
|
||||
| nios | nios2 | nios2eb | nios2el \
|
||||
| nios | nios2 \
|
||||
| ns16k | ns32k \
|
||||
| open8 | or1k | or1knd | or32 \
|
||||
| or32 \
|
||||
| pdp10 | pdp11 | pj | pjl \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
|
||||
| pyramid \
|
||||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
| score \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh64 | sh64le \
|
||||
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
|
||||
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
|
||||
| spu \
|
||||
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
|
||||
| ubicom32 \
|
||||
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
|
||||
| visium \
|
||||
| spu | strongarm \
|
||||
| tahoe | thumb | tic4x | tic80 | tron \
|
||||
| v850 | v850e \
|
||||
| we32k \
|
||||
| x86 | xc16x | xstormy16 | xtensa \
|
||||
| z8k | z80)
|
||||
| x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
|
||||
| z8k)
|
||||
basic_machine=$basic_machine-unknown
|
||||
;;
|
||||
c54x)
|
||||
basic_machine=tic54x-unknown
|
||||
;;
|
||||
c55x)
|
||||
basic_machine=tic55x-unknown
|
||||
;;
|
||||
c6x)
|
||||
basic_machine=tic6x-unknown
|
||||
;;
|
||||
leon|leon[3-9])
|
||||
basic_machine=sparc-$basic_machine
|
||||
;;
|
||||
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
|
||||
m6811 | m68hc11 | m6812 | m68hc12)
|
||||
# Motorola 68HC11/12.
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
@@ -341,21 +300,6 @@ case $basic_machine in
|
||||
basic_machine=mt-unknown
|
||||
;;
|
||||
|
||||
strongarm | thumb | xscale)
|
||||
basic_machine=arm-unknown
|
||||
;;
|
||||
xgate)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
;;
|
||||
xscaleeb)
|
||||
basic_machine=armeb-unknown
|
||||
;;
|
||||
|
||||
xscaleel)
|
||||
basic_machine=armel-unknown
|
||||
;;
|
||||
|
||||
# We use `pc' rather than `unknown'
|
||||
# because (1) that's what they normally are, and
|
||||
# (2) the word "unknown" tends to confuse beginning users.
|
||||
@@ -370,87 +314,64 @@ case $basic_machine in
|
||||
# Recognize the basic CPU types with company name.
|
||||
580-* \
|
||||
| a29k-* \
|
||||
| aarch64-* | aarch64_be-* \
|
||||
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
||||
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
|
||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||
| avr-* | avr32-* \
|
||||
| be32-* | be64-* \
|
||||
| bfin-* | bs2000-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||
| c8051-* | clipper-* | craynv-* | cydra-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
|
||||
| clipper-* | craynv-* | cydra-* \
|
||||
| d10v-* | d30v-* | dlx-* \
|
||||
| elxsi-* \
|
||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||
| f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
|
||||
| h8300-* | h8500-* \
|
||||
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
|
||||
| hexagon-* \
|
||||
| i*86-* | i860-* | i960-* | ia64-* \
|
||||
| ip2k-* | iq2000-* \
|
||||
| k1om-* \
|
||||
| le32-* | le64-* \
|
||||
| lm32-* \
|
||||
| m32c-* | m32r-* | m32rle-* \
|
||||
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
|
||||
| microblaze-* | microblazeel-* \
|
||||
| m88110-* | m88k-* | maxq-* | mcore-* \
|
||||
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
||||
| mips16-* \
|
||||
| mips64-* | mips64el-* \
|
||||
| mips64octeon-* | mips64octeonel-* \
|
||||
| mips64orion-* | mips64orionel-* \
|
||||
| mips64r5900-* | mips64r5900el-* \
|
||||
| mips64vr-* | mips64vrel-* \
|
||||
| mips64orion-* | mips64orionel-* \
|
||||
| mips64vr4100-* | mips64vr4100el-* \
|
||||
| mips64vr4300-* | mips64vr4300el-* \
|
||||
| mips64vr5000-* | mips64vr5000el-* \
|
||||
| mips64vr5900-* | mips64vr5900el-* \
|
||||
| mipsisa32-* | mipsisa32el-* \
|
||||
| mipsisa32r2-* | mipsisa32r2el-* \
|
||||
| mipsisa32r6-* | mipsisa32r6el-* \
|
||||
| mipsisa64-* | mipsisa64el-* \
|
||||
| mipsisa64r2-* | mipsisa64r2el-* \
|
||||
| mipsisa64r6-* | mipsisa64r6el-* \
|
||||
| mipsisa64sb1-* | mipsisa64sb1el-* \
|
||||
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
|
||||
| mipsr5900-* | mipsr5900el-* \
|
||||
| mipstx39-* | mipstx39el-* \
|
||||
| mmix-* \
|
||||
| mt-* \
|
||||
| msp430-* \
|
||||
| nds32-* | nds32le-* | nds32be-* \
|
||||
| nios-* | nios2-* | nios2eb-* | nios2el-* \
|
||||
| nios-* | nios2-* \
|
||||
| none-* | np1-* | ns16k-* | ns32k-* \
|
||||
| open8-* \
|
||||
| or1k*-* \
|
||||
| orion-* \
|
||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
|
||||
| pyramid-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| romp-* | rs6000-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
|
||||
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
|
||||
| sparclite-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
|
||||
| tahoe-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
|
||||
| tahoe-* | thumb-* \
|
||||
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
|
||||
| tile*-* \
|
||||
| tron-* \
|
||||
| ubicom32-* \
|
||||
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
|
||||
| vax-* \
|
||||
| visium-* \
|
||||
| v850-* | v850e-* | vax-* \
|
||||
| we32k-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* \
|
||||
| xstormy16-* | xtensa*-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
|
||||
| xstormy16-* | xtensa-* \
|
||||
| ymp-* \
|
||||
| z8k-* | z80-*)
|
||||
;;
|
||||
# Recognize the basic CPU types without company name, with glob match.
|
||||
xtensa*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
| z8k-*)
|
||||
;;
|
||||
# Recognize the various machine names and aliases which stand
|
||||
# for a CPU type and a company and sometimes even an OS.
|
||||
@@ -468,7 +389,7 @@ case $basic_machine in
|
||||
basic_machine=a29k-amd
|
||||
os=-udi
|
||||
;;
|
||||
abacus)
|
||||
abacus)
|
||||
basic_machine=abacus-unknown
|
||||
;;
|
||||
adobe68k)
|
||||
@@ -514,10 +435,6 @@ case $basic_machine in
|
||||
basic_machine=m68k-apollo
|
||||
os=-bsd
|
||||
;;
|
||||
aros)
|
||||
basic_machine=i386-pc
|
||||
os=-aros
|
||||
;;
|
||||
aux)
|
||||
basic_machine=m68k-apple
|
||||
os=-aux
|
||||
@@ -526,35 +443,10 @@ case $basic_machine in
|
||||
basic_machine=ns32k-sequent
|
||||
os=-dynix
|
||||
;;
|
||||
blackfin)
|
||||
basic_machine=bfin-unknown
|
||||
os=-linux
|
||||
;;
|
||||
blackfin-*)
|
||||
basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
bluegene*)
|
||||
basic_machine=powerpc-ibm
|
||||
os=-cnk
|
||||
;;
|
||||
c54x-*)
|
||||
basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c55x-*)
|
||||
basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c6x-*)
|
||||
basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
c90)
|
||||
basic_machine=c90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
cegcc)
|
||||
basic_machine=arm-unknown
|
||||
os=-cegcc
|
||||
;;
|
||||
convex-c1)
|
||||
basic_machine=c1-convex
|
||||
os=-bsd
|
||||
@@ -583,8 +475,8 @@ case $basic_machine in
|
||||
basic_machine=craynv-cray
|
||||
os=-unicosmp
|
||||
;;
|
||||
cr16 | cr16-*)
|
||||
basic_machine=cr16-unknown
|
||||
cr16c)
|
||||
basic_machine=cr16c-unknown
|
||||
os=-elf
|
||||
;;
|
||||
crds | unos)
|
||||
@@ -622,10 +514,6 @@ case $basic_machine in
|
||||
basic_machine=m88k-motorola
|
||||
os=-sysv3
|
||||
;;
|
||||
dicos)
|
||||
basic_machine=i686-pc
|
||||
os=-dicos
|
||||
;;
|
||||
djgpp)
|
||||
basic_machine=i586-pc
|
||||
os=-msdosdjgpp
|
||||
@@ -741,6 +629,7 @@ case $basic_machine in
|
||||
i370-ibm* | ibm*)
|
||||
basic_machine=i370-ibm
|
||||
;;
|
||||
# I'm not sure what "Sysv32" means. Should this be sysv3.2?
|
||||
i*86v32)
|
||||
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
|
||||
os=-sysv32
|
||||
@@ -779,17 +668,6 @@ case $basic_machine in
|
||||
basic_machine=m68k-isi
|
||||
os=-sysv
|
||||
;;
|
||||
leon-*|leon[3-9]-*)
|
||||
basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
|
||||
;;
|
||||
m68knommu)
|
||||
basic_machine=m68k-unknown
|
||||
os=-linux
|
||||
;;
|
||||
m68knommu-*)
|
||||
basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
m88k-omron*)
|
||||
basic_machine=m88k-omron
|
||||
;;
|
||||
@@ -801,21 +679,10 @@ case $basic_machine in
|
||||
basic_machine=ns32k-utek
|
||||
os=-sysv
|
||||
;;
|
||||
microblaze*)
|
||||
basic_machine=microblaze-xilinx
|
||||
;;
|
||||
mingw64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-mingw64
|
||||
;;
|
||||
mingw32)
|
||||
basic_machine=i686-pc
|
||||
basic_machine=i386-pc
|
||||
os=-mingw32
|
||||
;;
|
||||
mingw32ce)
|
||||
basic_machine=arm-unknown
|
||||
os=-mingw32ce
|
||||
;;
|
||||
miniframe)
|
||||
basic_machine=m68000-convergent
|
||||
;;
|
||||
@@ -837,10 +704,6 @@ case $basic_machine in
|
||||
basic_machine=powerpc-unknown
|
||||
os=-morphos
|
||||
;;
|
||||
moxiebox)
|
||||
basic_machine=moxie-unknown
|
||||
os=-moxiebox
|
||||
;;
|
||||
msdos)
|
||||
basic_machine=i386-pc
|
||||
os=-msdos
|
||||
@@ -848,18 +711,10 @@ case $basic_machine in
|
||||
ms1-*)
|
||||
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
|
||||
;;
|
||||
msys)
|
||||
basic_machine=i686-pc
|
||||
os=-msys
|
||||
;;
|
||||
mvs)
|
||||
basic_machine=i370-ibm
|
||||
os=-mvs
|
||||
;;
|
||||
nacl)
|
||||
basic_machine=le32-unknown
|
||||
os=-nacl
|
||||
;;
|
||||
ncr3000)
|
||||
basic_machine=i486-ncr
|
||||
os=-sysv4
|
||||
@@ -924,12 +779,6 @@ case $basic_machine in
|
||||
np1)
|
||||
basic_machine=np1-gould
|
||||
;;
|
||||
neo-tandem)
|
||||
basic_machine=neo-tandem
|
||||
;;
|
||||
nse-tandem)
|
||||
basic_machine=nse-tandem
|
||||
;;
|
||||
nsr-tandem)
|
||||
basic_machine=nsr-tandem
|
||||
;;
|
||||
@@ -960,14 +809,6 @@ case $basic_machine in
|
||||
basic_machine=i860-intel
|
||||
os=-osf
|
||||
;;
|
||||
parisc)
|
||||
basic_machine=hppa-unknown
|
||||
os=-linux
|
||||
;;
|
||||
parisc-*)
|
||||
basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=-linux
|
||||
;;
|
||||
pbd)
|
||||
basic_machine=sparc-tti
|
||||
;;
|
||||
@@ -1012,10 +853,9 @@ case $basic_machine in
|
||||
;;
|
||||
power) basic_machine=power-ibm
|
||||
;;
|
||||
ppc | ppcbe) basic_machine=powerpc-unknown
|
||||
ppc) basic_machine=powerpc-unknown
|
||||
;;
|
||||
ppc-* | ppcbe-*)
|
||||
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
ppcle | powerpclittle | ppc-le | powerpc-little)
|
||||
basic_machine=powerpcle-unknown
|
||||
@@ -1040,11 +880,7 @@ case $basic_machine in
|
||||
basic_machine=i586-unknown
|
||||
os=-pw32
|
||||
;;
|
||||
rdos | rdos64)
|
||||
basic_machine=x86_64-pc
|
||||
os=-rdos
|
||||
;;
|
||||
rdos32)
|
||||
rdos)
|
||||
basic_machine=i386-pc
|
||||
os=-rdos
|
||||
;;
|
||||
@@ -1089,9 +925,6 @@ case $basic_machine in
|
||||
basic_machine=sh-hitachi
|
||||
os=-hms
|
||||
;;
|
||||
sh5el)
|
||||
basic_machine=sh5le-unknown
|
||||
;;
|
||||
sh64)
|
||||
basic_machine=sh64-unknown
|
||||
;;
|
||||
@@ -1113,9 +946,6 @@ case $basic_machine in
|
||||
basic_machine=i860-stratus
|
||||
os=-sysv4
|
||||
;;
|
||||
strongarm-* | thumb-*)
|
||||
basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
sun2)
|
||||
basic_machine=m68000-sun
|
||||
;;
|
||||
@@ -1172,9 +1002,17 @@ case $basic_machine in
|
||||
basic_machine=t90-cray
|
||||
os=-unicos
|
||||
;;
|
||||
tile*)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-linux-gnu
|
||||
tic54x | c54x*)
|
||||
basic_machine=tic54x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic55x | c55x*)
|
||||
basic_machine=tic55x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tic6x | c6x*)
|
||||
basic_machine=tic6x-unknown
|
||||
os=-coff
|
||||
;;
|
||||
tx39)
|
||||
basic_machine=mipstx39-unknown
|
||||
@@ -1243,9 +1081,6 @@ case $basic_machine in
|
||||
xps | xps100)
|
||||
basic_machine=xps100-honeywell
|
||||
;;
|
||||
xscale-* | xscalee[bl]-*)
|
||||
basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
|
||||
;;
|
||||
ymp)
|
||||
basic_machine=ymp-cray
|
||||
os=-unicos
|
||||
@@ -1254,10 +1089,6 @@ case $basic_machine in
|
||||
basic_machine=z8k-unknown
|
||||
os=-sim
|
||||
;;
|
||||
z80-*-coff)
|
||||
basic_machine=z80-unknown
|
||||
os=-sim
|
||||
;;
|
||||
none)
|
||||
basic_machine=none-none
|
||||
os=-none
|
||||
@@ -1296,7 +1127,7 @@ case $basic_machine in
|
||||
we32k)
|
||||
basic_machine=we32k-att
|
||||
;;
|
||||
sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
|
||||
basic_machine=sh-unknown
|
||||
;;
|
||||
sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
|
||||
@@ -1343,12 +1174,9 @@ esac
|
||||
if [ x"$os" != x"" ]
|
||||
then
|
||||
case $os in
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# First match some system type aliases
|
||||
# that might get confused with valid system types.
|
||||
# -solaris* is a basic system type, with this one exception.
|
||||
-auroraux)
|
||||
os=-auroraux
|
||||
;;
|
||||
-solaris1 | -solaris1.*)
|
||||
os=`echo $os | sed -e 's|solaris1|sunos4|'`
|
||||
;;
|
||||
@@ -1369,31 +1197,29 @@ case $os in
|
||||
# Each alternative MUST END IN A *, to match a version number.
|
||||
# -sysv* is not here because it comes later, after sysvr4.
|
||||
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||
| -sym* | -kopensolaris* | -plan9* \
|
||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
|
||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||
| -aos* | -aros* \
|
||||
| -aos* \
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
| -bitrig* | -openbsd* | -solidbsd* \
|
||||
| -openbsd* | -solidbsd* \
|
||||
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
|
||||
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
|
||||
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
|
||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -chorusos* | -chorusrdb* \
|
||||
| -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
|
||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
|
||||
| -skyos* | -haiku* | -rdos* | -toppers*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
@@ -1432,7 +1258,7 @@ case $os in
|
||||
-opened*)
|
||||
os=-openedition
|
||||
;;
|
||||
-os400*)
|
||||
-os400*)
|
||||
os=-os400
|
||||
;;
|
||||
-wince*)
|
||||
@@ -1481,7 +1307,7 @@ case $os in
|
||||
-sinix*)
|
||||
os=-sysv4
|
||||
;;
|
||||
-tpf*)
|
||||
-tpf*)
|
||||
os=-tpf
|
||||
;;
|
||||
-triton*)
|
||||
@@ -1517,14 +1343,12 @@ case $os in
|
||||
-aros*)
|
||||
os=-aros
|
||||
;;
|
||||
-kaos*)
|
||||
os=-kaos
|
||||
;;
|
||||
-zvmoe)
|
||||
os=-zvmoe
|
||||
;;
|
||||
-dicos*)
|
||||
os=-dicos
|
||||
;;
|
||||
-nacl*)
|
||||
;;
|
||||
-none)
|
||||
;;
|
||||
*)
|
||||
@@ -1547,10 +1371,10 @@ else
|
||||
# system, and we'll never get to this point.
|
||||
|
||||
case $basic_machine in
|
||||
score-*)
|
||||
score-*)
|
||||
os=-elf
|
||||
;;
|
||||
spu-*)
|
||||
spu-*)
|
||||
os=-elf
|
||||
;;
|
||||
*-acorn)
|
||||
@@ -1562,23 +1386,8 @@ case $basic_machine in
|
||||
arm*-semi)
|
||||
os=-aout
|
||||
;;
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
;;
|
||||
c8051-*)
|
||||
os=-elf
|
||||
;;
|
||||
hexagon-*)
|
||||
os=-elf
|
||||
;;
|
||||
tic54x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic55x-*)
|
||||
os=-coff
|
||||
;;
|
||||
tic6x-*)
|
||||
os=-coff
|
||||
c4x-* | tic4x-*)
|
||||
os=-coff
|
||||
;;
|
||||
# This must come before the *-dec entry.
|
||||
pdp10-*)
|
||||
@@ -1598,13 +1407,13 @@ case $basic_machine in
|
||||
;;
|
||||
m68000-sun)
|
||||
os=-sunos3
|
||||
# This also exists in the configure program, but was not the
|
||||
# default.
|
||||
# os=-sunos4
|
||||
;;
|
||||
m68*-cisco)
|
||||
os=-aout
|
||||
;;
|
||||
mep-*)
|
||||
os=-elf
|
||||
;;
|
||||
mips*-cisco)
|
||||
os=-elf
|
||||
;;
|
||||
@@ -1629,7 +1438,7 @@ case $basic_machine in
|
||||
*-ibm)
|
||||
os=-aix
|
||||
;;
|
||||
*-knuth)
|
||||
*-knuth)
|
||||
os=-mmixware
|
||||
;;
|
||||
*-wec)
|
||||
@@ -1734,7 +1543,7 @@ case $basic_machine in
|
||||
-sunos*)
|
||||
vendor=sun
|
||||
;;
|
||||
-cnk*|-aix*)
|
||||
-aix*)
|
||||
vendor=ibm
|
||||
;;
|
||||
-beos*)
|
||||
767
configure.ac
767
configure.ac
@@ -1,72 +1,73 @@
|
||||
#
|
||||
# This file is part of nzbget. See <http://nzbget.net>.
|
||||
#
|
||||
# Copyright (C) 2008-2021 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, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.65)
|
||||
AC_INIT(nzbget, 21.2-testing, hugbug@users.sourceforge.net)
|
||||
AC_CONFIG_AUX_DIR(posix)
|
||||
AC_CANONICAL_TARGET
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AC_CONFIG_SRCDIR([daemon/main/nzbget.cpp])
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.4.0, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.4.0)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
m4_include([posix/ax_cxx_compile_stdcxx.m4])
|
||||
|
||||
dnl Architecture check
|
||||
AC_CANONICAL_HOST
|
||||
case "$host" in
|
||||
*86-*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CFLAGS1="${CFLAGS} -m486"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-freebsd*)
|
||||
LIBPREF1="/usr/local"
|
||||
;;
|
||||
*-solaris*)
|
||||
LIBPREF1="/usr"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
if test "$LIBPREF" = ""; then
|
||||
LIBPREF="$LIBPREF1"
|
||||
fi
|
||||
if test "$CFLAGS" = ""; then
|
||||
CFLAGS="$CFLAGS1"
|
||||
fi
|
||||
if test "$CPPFLAGS" = ""; then
|
||||
CPPFLAGS="$CPPFLAGS1"
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Check for programs.
|
||||
dnl
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PATH_PROG(FALSE, false, /usr/bin/false)
|
||||
AC_PATH_PROG(TRUE, true, /usr/bin/true)
|
||||
AC_PATH_PROG(RM, rm, $FALSE)
|
||||
AC_PATH_PROG(LN, ln, $FALSE)
|
||||
AC_PATH_PROG(TAR, tar, $FALSE)
|
||||
AC_PATH_PROG(AR, ar, $FALSE)
|
||||
AC_PATH_PROG(MAKE, make, $FALSE)
|
||||
AC_PATH_PROG(CXXCPP, cpp, $FALSE)
|
||||
AC_PATH_PROG(MV, mv, $FALSE)
|
||||
AC_PATH_PROG(MKDIR, mkdir, $FALSE)
|
||||
AC_PATH_PROG(CP, cp, $FALSE)
|
||||
AC_PROG_INSTALL
|
||||
|
||||
|
||||
dnl
|
||||
dnl Do all tests with c++ compiler.
|
||||
dnl
|
||||
AC_LANG(C++)
|
||||
|
||||
dnl
|
||||
dnl Determine compiler switches to support C++14 standard.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to test compiler features)
|
||||
AC_ARG_ENABLE(cpp-check,
|
||||
[AS_HELP_STRING([--disable-cpp-check], [disable check for C++14 compiler features])],
|
||||
[ ENABLECPPCHECK=$enableval ],
|
||||
[ ENABLECPPCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLECPPCHECK)
|
||||
if test "$ENABLECPPCHECK" = "yes"; then
|
||||
AX_CXX_COMPILE_STDCXX(14,,[optional])
|
||||
if test "$HAVE_CXX14" != "1"; then
|
||||
AC_MSG_ERROR("A compiler with support for C++14 language features is required. For details visit http://nzbget.net/cpp14")
|
||||
fi
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl Checks for header files.
|
||||
dnl
|
||||
AC_CHECK_HEADERS(sys/prctl.h regex.h endian.h getopt.h)
|
||||
|
||||
dnl AC_CHECK_HEADERS(stdarg.h time.h stdlib.h stdio.h unistd.h errno.h string.h sys/stat.h sys/time.h)
|
||||
dnl AC_CHECK_HEADERS(libgen.h pwd.h getopt.h dirent.h fcntl.h pthread.h semaphore.h)
|
||||
dnl AC_CHECK_HEADERS(sys/socket.h sys/types.h netinet/in.h arpa/inet.h netdb.h)
|
||||
|
||||
dnl
|
||||
dnl Check for libs
|
||||
@@ -74,41 +75,24 @@ dnl
|
||||
AC_SEARCH_LIBS([pthread_create], [pthread])
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([inet_addr], [nsl])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
AC_SEARCH_LIBS([hstrerror], [resolv])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Android NDK restrictions
|
||||
dnl
|
||||
AC_CHECK_FUNC(lockf,
|
||||
[AC_CHECK_DECL(lockf,
|
||||
[AC_DEFINE([HAVE_LOCKF], 1, [Define to 1 if lockf is supported])],,
|
||||
[#include <unistd.h>])])
|
||||
AC_CHECK_FUNC(pthread_cancel,
|
||||
[AC_CHECK_DECL(pthread_cancel,
|
||||
[AC_DEFINE([HAVE_PTHREAD_CANCEL], 1, [Define to 1 if pthread_cancel is supported])],,
|
||||
[#include <pthread.h>])])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Getopt
|
||||
dnl
|
||||
AC_CHECK_FUNC(getopt_long,
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
|
||||
|
||||
dnl
|
||||
dnl fsync
|
||||
dnl stat64
|
||||
dnl
|
||||
AC_CHECK_FUNC(fdatasync,
|
||||
[AC_DEFINE([HAVE_FDATASYNC], 1, [Define to 1 if fdatasync is supported])],)
|
||||
AC_CHECK_DECL(F_FULLFSYNC,
|
||||
[AC_DEFINE([HAVE_FULLFSYNC], 1, [Define to 1 if F_FULLFSYNC is supported])],,[#include <fcntl.h>])
|
||||
|
||||
dnl
|
||||
dnl use 64-Bits for file sizes
|
||||
dnl
|
||||
AC_SYS_LARGEFILE
|
||||
AC_CHECK_FUNC(stat64,
|
||||
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],
|
||||
[AC_LIBOBJ(stat64)])
|
||||
|
||||
|
||||
dnl
|
||||
@@ -119,7 +103,6 @@ AC_TRY_COMPILE(
|
||||
[#include <time.h>],
|
||||
[ time_t clock; char buf[26]; ctime_r(&clock, buf, 26); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_CTIME_R_3], 1, [Define to 1 if ctime_r takes 3 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
@@ -133,141 +116,82 @@ AC_TRY_COMPILE(
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function ctime_r not found")
|
||||
AC_MSG_ERROR("function ctime_r not found.")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check getaddrinfo
|
||||
dnl check gethostbyname_r
|
||||
dnl
|
||||
AC_CHECK_FUNC(getaddrinfo,
|
||||
FOUND="yes"
|
||||
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
|
||||
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
|
||||
FOUND="no")
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r, if getaddrinfo is not available
|
||||
dnl
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
|
||||
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Determine what socket length (socklen_t) data type is
|
||||
dnl
|
||||
AC_MSG_CHECKING([for type of socket length (socklen_t)])
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL)],[
|
||||
AC_MSG_RESULT(socklen_t)
|
||||
SOCKLEN_T=socklen_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
|
||||
AC_MSG_RESULT(size_t)
|
||||
SOCKLEN_T=size_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
|
||||
AC_MSG_RESULT(int)
|
||||
SOCKLEN_T=int],[
|
||||
AC_MSG_WARN(could not determine)
|
||||
SOCKLEN_T=int])])])
|
||||
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
|
||||
|
||||
|
||||
dnl
|
||||
dnl check cpu cores via sysconf
|
||||
dnl
|
||||
AC_MSG_CHECKING(for cpu cores via sysconf)
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <unistd.h>],
|
||||
[ int a = _SC_NPROCESSORS_ONLN; ],
|
||||
FOUND="yes"
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_SC_NPROCESSORS_ONLN], 1, [Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h]),
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
FOUND="no")
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function gethostbyname_r not found.")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl
|
||||
AC_MSG_CHECKING(for __FUNCTION__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_MSG_CHECKING(for __func__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
fi
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use libxml2)
|
||||
AC_ARG_ENABLE(libxml2,
|
||||
[AS_HELP_STRING([--disable-libxml2], [do not use libxml2 (removes dependency from libxml2-library, only for development purposes)])],
|
||||
[USELIBXML2=$enableval],
|
||||
[USELIBXML2=yes] )
|
||||
AC_MSG_RESULT($USELIBXML2)
|
||||
if test "$USELIBXML2" = "yes"; then
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[AS_HELP_STRING([--with-libxml2-libraries=DIR], [libxml2 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libxml2, libxml-2.0,
|
||||
[LIBS="${LIBS} $libxml2_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"],
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_LIBXML2],1,[Define to 1 to not use libxml2, only for development purposes])
|
||||
fi
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/libxml2"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[ --with-libxml2-includes=DIR libxml2 include directory],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files were not found."))
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[ --with-libxml2-libraries=DIR libxml2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found in $LIBVAL."))
|
||||
|
||||
|
||||
dnl
|
||||
@@ -275,28 +199,23 @@ dnl Use curses. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use curses)
|
||||
AC_ARG_ENABLE(curses,
|
||||
[AS_HELP_STRING([--disable-curses], [do not use curses (removes dependency from curses-library)])],
|
||||
[USECURSES=$enableval],
|
||||
[USECURSES=yes] )
|
||||
[ --disable-curses do not use curses (removes dependency from curses-library and makes executable smaller)],
|
||||
[ USECURSES=$enableval ],
|
||||
[ USECURSES=yes] )
|
||||
AC_MSG_RESULT($USECURSES)
|
||||
if test "$USECURSES" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libcurses_includes,
|
||||
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
[ --with-libcurses-includes=DIR libcurses include directory],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(libcurses_libraries,
|
||||
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(ncurses, ncurses,
|
||||
[LIBS="${LIBS} $ncurses_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $ncurses_CFLAGS"],
|
||||
AC_MSG_ERROR("ncurses library not found"))
|
||||
fi
|
||||
|
||||
[ --with-libcurses-libraries=DIR libcurses library directory],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_CHECK_HEADER(ncurses.h,
|
||||
FOUND=yes
|
||||
AC_DEFINE([HAVE_NCURSES_H],1,[Define to 1 if you have the <ncurses.h> header file.]),
|
||||
@@ -314,284 +233,78 @@ if test "$USECURSES" = "yes"; then
|
||||
FOUND=no)
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h)])
|
||||
AC_MSG_ERROR([Couldn't find curses headers (ncurses.h or curses.h).])
|
||||
fi
|
||||
AC_SEARCH_LIBS([refresh], [ncurses curses],,
|
||||
AC_ERROR([Couldn't find curses library]))
|
||||
AC_SEARCH_LIBS([nodelay], [ncurses curses tinfo],,
|
||||
AC_ERROR([Couldn't find curses library]))
|
||||
else
|
||||
AC_DEFINE([DISABLE_CURSES],1,[Define to 1 to not use curses])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use par-checking. Deafult: yes.
|
||||
dnl Use lib2par for par-checking. Deafult: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include code for par-checking)
|
||||
AC_ARG_ENABLE(parcheck,
|
||||
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support])],
|
||||
[ --enable-parcheck include code for par-checking],
|
||||
[ ENABLEPARCHECK=$enableval ],
|
||||
[ ENABLEPARCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
if test "$ENABLEPARCHECK" = "yes"; then
|
||||
dnl PAR2 checks.
|
||||
|
||||
dnl
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_FUNC_FSEEKO
|
||||
dnl Checks for library functions.
|
||||
AC_CHECK_FUNCS([stricmp])
|
||||
AC_CHECK_FUNCS([getopt])
|
||||
AM_CONDITIONAL(WITH_PAR2, true)
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable par-verification and repair])
|
||||
AM_CONDITIONAL(WITH_PAR2, false)
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use TLS/SSL. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use TLS/SSL)
|
||||
AC_ARG_ENABLE(tls,
|
||||
[AS_HELP_STRING([--disable-tls], [do not use TLS/SSL (removes dependency from TLS/SSL-libraries)])],
|
||||
[ USETLS=$enableval ],
|
||||
[ USETLS=yes] )
|
||||
AC_MSG_RESULT($USETLS)
|
||||
if test "$USETLS" = "yes"; then
|
||||
AC_ARG_WITH(tlslib,
|
||||
[AS_HELP_STRING([--with-tlslib=(OpenSSL, GnuTLS)], [TLS/SSL library to use])],
|
||||
[TLSLIB="$withval"])
|
||||
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
|
||||
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
|
||||
AC_ARG_WITH(openssl_includes,
|
||||
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(openssl_libraries,
|
||||
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([openssl], [openssl],
|
||||
[LIBS="${LIBS} $openssl_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $openssl_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(openssl/ssl.h,
|
||||
FOUND=yes
|
||||
TLSHEADERS=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
|
||||
AC_MSG_ERROR([Couldn't find OpenSSL headers (ssl.h)])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_SEARCH_LIBS([ASN1_OBJECT_free], [crypto],
|
||||
AC_SEARCH_LIBS([SSL_CTX_new], [ssl],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
|
||||
AC_MSG_ERROR([Couldn't find OpenSSL library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="OpenSSL"
|
||||
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support and decryption.])
|
||||
AC_SEARCH_LIBS([X509_check_host], [crypto],
|
||||
AC_DEFINE([HAVE_X509_CHECK_HOST],1,[Define to 1 if OpenSSL supports function "X509_check_host".]))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "GnuTLS" -o "$TLSLIB" = ""; then
|
||||
AC_ARG_WITH(libgnutls_includes,
|
||||
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libgnutls_libraries,
|
||||
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([gnutls], [gnutls],
|
||||
[LIBS="${LIBS} $gnutls_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $gnutls_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(gnutls/gnutls.h,
|
||||
FOUND=yes
|
||||
TLSHEADERS=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS headers (gnutls.h)])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "yes"; then
|
||||
dnl gcrypt is optional
|
||||
AC_MSG_CHECKING([whether gcrypt is needed])
|
||||
AC_TRY_COMPILE(
|
||||
[#include <gnutls/gnutls.h>]
|
||||
[#if GNUTLS_VERSION_NUMBER <= 0x020b00]
|
||||
[compile error]
|
||||
[#endif],
|
||||
[int a;],
|
||||
AC_MSG_RESULT([no])
|
||||
GCRYPT=no,
|
||||
AC_MSG_RESULT([yes])
|
||||
GCRYPT=yes)
|
||||
if test "$GCRYPT" = "yes"; then
|
||||
AC_CHECK_HEADER([gcrypt.h],
|
||||
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=yes)
|
||||
fi
|
||||
fi
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="GnuTLS"
|
||||
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "GnuTLS"; then
|
||||
AC_ARG_WITH(libnettle_includes,
|
||||
[AS_HELP_STRING([--with-libnettle-includes=DIR], [Nettle include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libnettle_libraries,
|
||||
[AS_HELP_STRING([--with-libnettle-libraries=DIR], [Nettle library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([nettle], [nettle],
|
||||
[LIBS="${LIBS} $nettle_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $nettle_CFLAGS"])
|
||||
fi
|
||||
AC_CHECK_HEADER(nettle/sha.h,
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find Nettle headers (sha.h)])
|
||||
fi
|
||||
AC_SEARCH_LIBS([nettle_pbkdf2_hmac_sha256], [nettle],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_ERROR([Couldn't find Nettle library, required when using GnuTLS])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_NETTLE],1,[Define to 1 to use Nettle library for decryption.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = ""; then
|
||||
if test "$TLSHEADERS" = ""; then
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS headers (ssl.h or gnutls.h)])
|
||||
else
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS library])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
AC_DEFINE([DISABLE_TLS],1,[Define to 1 to not use TLS/SSL])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for zlib includes and libraries.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use gzip)
|
||||
AC_ARG_ENABLE(gzip,
|
||||
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
|
||||
[USEZLIB=$enableval],
|
||||
[USEZLIB=yes] )
|
||||
AC_MSG_RESULT($USEZLIB)
|
||||
if test "$USEZLIB" = "yes"; then
|
||||
AC_ARG_WITH(zlib_includes,
|
||||
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(zlib_libraries,
|
||||
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES([zlib], [zlib],
|
||||
[LIBS="${LIBS} $zlib_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $zlib_CFLAGS"])
|
||||
fi
|
||||
dnl checks for libsigc++ includes and libraries (required for libpar2).
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/sigc++-2.0"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libsigc_includes,
|
||||
[ --with-libsigc-includes=DIR libsigc++-2.0 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
AC_CHECK_HEADER(zlib.h,,
|
||||
AC_MSG_ERROR("zlib header files not found"))
|
||||
AC_SEARCH_LIBS([deflateBound], [z], ,
|
||||
AC_MSG_ERROR("zlib library not found"))
|
||||
AC_ARG_WITH(libsigc_libraries,
|
||||
[ --with-libsigc-libraries=DIR libsigc++-2.0 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL} -I${LIBVAL}/sigc++-2.0/include"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(sigc++/type_traits.h,,
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
dnl
|
||||
dnl checks for libpar2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libpar2_includes,
|
||||
[ --with-libpar2-includes=DIR libpar2 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(libpar2/libpar2.h,,
|
||||
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
AC_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
dnl Q: How to check for c++-class in library?
|
||||
LIBS="${LIBS} -lpar2"
|
||||
dnl AC_CHECK_LIB(par2, GenerateCRC32Table, , FOUND=no)
|
||||
dnl if test "$FOUND" = "no"; then
|
||||
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
|
||||
dnl fi
|
||||
|
||||
else
|
||||
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Determine if CPU supports SIMD instructions
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use SIMD-optimized routines)
|
||||
USE_SIMD=no
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
SSE2_CXXFLAGS="-msse2"
|
||||
SSSE3_CXXFLAGS="-mssse3"
|
||||
PCLMUL_CXXFLAGS="-msse4.1 -mpclmul"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
arm*)
|
||||
NEON_CXXFLAGS="-mfpu=neon"
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
aarch64)
|
||||
ACLECRC_CXXFLAGS="-march=armv8-a+crc -fpermissive"
|
||||
USE_SIMD=yes
|
||||
;;
|
||||
esac
|
||||
AC_MSG_RESULT($USE_SIMD)
|
||||
AC_SUBST([SSE2_CXXFLAGS])
|
||||
AC_SUBST([SSSE3_CXXFLAGS])
|
||||
AC_SUBST([PCLMUL_CXXFLAGS])
|
||||
AC_SUBST([NEON_CXXFLAGS])
|
||||
AC_SUBST([ACLECRC_CXXFLAGS])
|
||||
|
||||
|
||||
dnl
|
||||
dnl Some Linux systems require an empty signal handler for SIGCHLD
|
||||
dnl in order for exit codes to be correctly delivered to parent process.
|
||||
dnl Some 32-Bit BSD systems however may not function properly if the handler is installed.
|
||||
dnl The default behavior is to install the handler.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use an empty SIGCHLD handler)
|
||||
AC_ARG_ENABLE(sigchld-handler,
|
||||
[AS_HELP_STRING([--disable-sigchld-handler], [do not use sigchld-handler (the disabling may be neccessary on 32-Bit BSD)])],
|
||||
[SIGCHLDHANDLER=$enableval],
|
||||
[SIGCHLDHANDLER=yes])
|
||||
AC_MSG_RESULT($SIGCHLDHANDLER)
|
||||
if test "$SIGCHLDHANDLER" = "yes"; then
|
||||
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
fi
|
||||
|
||||
|
||||
@@ -600,103 +313,43 @@ dnl Debugging. Default: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include all debugging code)
|
||||
AC_ARG_ENABLE(debug,
|
||||
[AS_HELP_STRING([--enable-debug], [enable debugging])],
|
||||
[ --enable-debug enable debugging],
|
||||
[ ENABLEDEBUG=$enableval ],
|
||||
[ ENABLEDEBUG=no] )
|
||||
AC_MSG_RESULT($ENABLEDEBUG)
|
||||
|
||||
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
|
||||
dnl
|
||||
dnl Begin of debugging code
|
||||
dnl
|
||||
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl
|
||||
AC_MSG_CHECKING(for macro returning current function name)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT(__FUNCTION__)
|
||||
FUNCTION_MACRO_NAME=__FUNCTION__,
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT(__func__)
|
||||
FUNCTION_MACRO_NAME=__func__,
|
||||
AC_MSG_RESULT(none)))
|
||||
if test "$FUNCTION_MACRO_NAME" != ""; then
|
||||
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($ENABLEDEBUG)
|
||||
|
||||
|
||||
dnl
|
||||
dnl variadic macros
|
||||
dnl
|
||||
AC_MSG_CHECKING(for variadic macros)
|
||||
AC_TRY_COMPILE(
|
||||
[ #define macro(...) macrofunc(__VA_ARGS__) ]
|
||||
[ int macrofunc(int a, int b) { return a + b; } ],
|
||||
[ int a=macro(1, 2); ],
|
||||
AC_COMPILE_IFELSE([
|
||||
#define macro(...) macrofunc(__VA_ARGS__)
|
||||
int macrofunc(int a, int b) { return a + b; }
|
||||
int test() { return macro(1, 2); }
|
||||
],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_VARIADIC_MACROS], 1, Define to 1 if variadic macros are supported),
|
||||
AC_MSG_RESULT([no]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl Backtracing on segmentation faults
|
||||
dnl
|
||||
AC_MSG_CHECKING(for backtrace)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <execinfo.h>]
|
||||
[#include <stdio.h>]
|
||||
[#include <stdlib.h>],
|
||||
[ void *array[100]; size_t size; char **strings; ]
|
||||
[ size = backtrace(array, 100); ]
|
||||
[ strings = backtrace_symbols(array, size); ],
|
||||
FOUND=yes
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
|
||||
FOUND=no
|
||||
AC_MSG_RESULT([[no]]))
|
||||
dnl Substitute flags.
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
AC_SUBST(LDFLAGS)
|
||||
AC_SUBST(CXXFLAGS)
|
||||
AC_SUBST(TAR)
|
||||
AC_SUBST(AR)
|
||||
AC_SUBST(ADDSRCS)
|
||||
|
||||
|
||||
dnl
|
||||
dnl "rdynamic" linker flag
|
||||
dnl
|
||||
AC_MSG_CHECKING(for rdynamic linker flag)
|
||||
old_LDFLAGS="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS -rdynamic"
|
||||
AC_TRY_LINK([], [],
|
||||
AC_MSG_RESULT([[yes]]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
[LDFLAGS="$old_LDFLAGS"])
|
||||
|
||||
dnl
|
||||
dnl End of debugging code
|
||||
dnl
|
||||
else
|
||||
AC_DEFINE([NDEBUG],1,Define to 1 to exclude debug-code)
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Enable test suite. Deafult: no.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to enable unit and integration tests)
|
||||
AC_ARG_ENABLE(tests,
|
||||
[AS_HELP_STRING([--enable-tests], [enable unit and integration tests])],
|
||||
[ ENABLETESTS=$enableval ],
|
||||
[ ENABLETESTS=no] )
|
||||
AC_MSG_RESULT($ENABLETESTS)
|
||||
if test "$ENABLETESTS" = "yes"; then
|
||||
AC_DEFINE([ENABLE_TESTS],1,[Define to 1 to enable unit and integration tests])
|
||||
AM_CONDITIONAL(WITH_TESTS, true)
|
||||
else
|
||||
AM_CONDITIONAL(WITH_TESTS, false)
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,157 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NString.h"
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
#include "TlsSocket.h"
|
||||
#endif
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled,
|
||||
csBroken
|
||||
};
|
||||
|
||||
enum EIPVersion
|
||||
{
|
||||
ipAuto,
|
||||
ipV4,
|
||||
ipV6
|
||||
};
|
||||
|
||||
Connection(const char* host, int port, bool tls);
|
||||
Connection(SOCKET socket, bool tls);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
virtual bool Connect();
|
||||
virtual bool Disconnect();
|
||||
bool Bind();
|
||||
bool Send(const char* buffer, int size);
|
||||
bool Recv(char* buffer, int size);
|
||||
int TryRecv(char* buffer, int size);
|
||||
char* ReadLine(char* buffer, int size, int* bytesRead);
|
||||
void ReadBuffer(char** buffer, int *bufLen);
|
||||
int WriteLine(const char* buffer);
|
||||
std::unique_ptr<Connection> Accept();
|
||||
void Cancel();
|
||||
const char* GetHost() { return m_host; }
|
||||
int GetPort() { return m_port; }
|
||||
bool GetTls() { return m_tls; }
|
||||
const char* GetCipher() { return m_cipher; }
|
||||
void SetCipher(const char* cipher) { m_cipher = cipher; }
|
||||
void SetTimeout(int timeout) { m_timeout = timeout; }
|
||||
void SetIPVersion(EIPVersion ipVersion) { m_ipVersion = ipVersion; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetSuppressErrors(bool suppressErrors);
|
||||
bool GetSuppressErrors() { return m_suppressErrors; }
|
||||
const char* GetRemoteAddr();
|
||||
bool GetGracefull() { return m_gracefull; }
|
||||
void SetGracefull(bool gracefull) { m_gracefull = gracefull; }
|
||||
void SetForceClose(bool forceClose) { m_forceClose = forceClose; }
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTls(bool isClient, const char* certFile, const char* keyFile);
|
||||
#endif
|
||||
int FetchTotalBytesRead();
|
||||
|
||||
protected:
|
||||
CString m_host;
|
||||
int m_port;
|
||||
bool m_tls;
|
||||
EIPVersion m_ipVersion = ipAuto;
|
||||
SOCKET m_socket = INVALID_SOCKET;
|
||||
CString m_cipher;
|
||||
CharBuffer m_readBuf;
|
||||
int m_bufAvail = 0;
|
||||
char* m_bufPtr = nullptr;
|
||||
EStatus m_status = csDisconnected;
|
||||
int m_timeout = 60;
|
||||
bool m_suppressErrors = true;
|
||||
BString<100> m_remoteAddr;
|
||||
int m_totalBytesRead = 0;
|
||||
bool m_gracefull = false;
|
||||
bool m_forceClose = false;
|
||||
|
||||
struct SockAddr
|
||||
{
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
bool operator==(const SockAddr& rhs) const
|
||||
{ return memcmp(this, &rhs, sizeof(SockAddr)) == 0; }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
class ConTlsSocket: public TlsSocket
|
||||
{
|
||||
public:
|
||||
ConTlsSocket(SOCKET socket, bool isClient, const char* host,
|
||||
const char* certFile, const char* keyFile, const char* cipher, Connection* owner) :
|
||||
TlsSocket(socket, isClient, host, certFile, keyFile, cipher), m_owner(owner) {}
|
||||
protected:
|
||||
virtual void PrintError(const char* errMsg) { m_owner->PrintError(errMsg); }
|
||||
private:
|
||||
Connection* m_owner;
|
||||
};
|
||||
|
||||
std::unique_ptr<ConTlsSocket> m_tlsSocket;
|
||||
bool m_tlsError = false;
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
static std::unique_ptr<Mutex> m_getHostByNameMutex;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ReportError(const char* msgPrefix, const char* msgArg, bool printErrCode, int errCode = 0,
|
||||
const char* errMsg = nullptr);
|
||||
virtual void PrintError(const char* errMsg);
|
||||
int GetLastNetworkError();
|
||||
bool DoConnect();
|
||||
bool DoDisconnect();
|
||||
bool InitSocketOpts(SOCKET socket);
|
||||
bool ConnectWithTimeout(void* address, int address_len);
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
in_addr_t ResolveHostAddr(const char* host);
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
int recv(SOCKET s, char* buf, int len, int flags);
|
||||
int send(SOCKET s, const char* buf, int len, int flags);
|
||||
void CloseTls();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,696 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
#include "TlsSocket.h"
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
CString TlsSocket::m_certStore;
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
|
||||
/**
|
||||
* Mutexes for gcryptlib
|
||||
*/
|
||||
|
||||
std::vector<std::unique_ptr<Mutex>> g_GCryptLibMutexes;
|
||||
|
||||
static int gcry_mutex_init(void **priv)
|
||||
{
|
||||
g_GCryptLibMutexes.emplace_back(std::make_unique<Mutex>());
|
||||
*priv = g_GCryptLibMutexes.back().get();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_destroy(void **lock)
|
||||
{
|
||||
Mutex* mutex = ((Mutex*)*lock);
|
||||
g_GCryptLibMutexes.erase(std::find_if(g_GCryptLibMutexes.begin(), g_GCryptLibMutexes.end(),
|
||||
[mutex](std::unique_ptr<Mutex>& itMutex)
|
||||
{
|
||||
return itMutex.get() == mutex;
|
||||
}));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_lock(void **lock)
|
||||
{
|
||||
((Mutex*)*lock)->Lock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_unlock(void **lock)
|
||||
{
|
||||
((Mutex*)*lock)->Unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gcry_thread_cbs gcry_threads_Mutex =
|
||||
{ GCRY_THREAD_OPTION_USER, nullptr,
|
||||
gcry_mutex_init, gcry_mutex_destroy,
|
||||
gcry_mutex_lock, gcry_mutex_unlock,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||
};
|
||||
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#ifndef CRYPTO_set_locking_callback
|
||||
#define NEED_CRYPTO_LOCKING
|
||||
#endif
|
||||
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
|
||||
/**
|
||||
* Mutexes for OpenSSL
|
||||
*/
|
||||
|
||||
std::vector<std::unique_ptr<Mutex>> g_OpenSSLMutexes;
|
||||
|
||||
static void openssl_locking(int mode, int n, const char* file, int line)
|
||||
{
|
||||
Mutex* mutex = g_OpenSSLMutexes[n].get();
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
mutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
static struct CRYPTO_dynlock_value* openssl_dynlock_create(const char *file, int line)
|
||||
{
|
||||
return (CRYPTO_dynlock_value*)new Mutex();
|
||||
}
|
||||
|
||||
static void openssl_dynlock_destroy(struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
Mutex* mutex = (Mutex*)l;
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
static void openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line)
|
||||
{
|
||||
Mutex* mutex = (Mutex*)l;
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
mutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* NEED_CRYPTO_LOCKING */
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
|
||||
void TlsSocket::Init()
|
||||
{
|
||||
debug("Initializing TLS library");
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
int error_code;
|
||||
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
error_code = gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_Mutex);
|
||||
if (error_code != 0)
|
||||
{
|
||||
error("Could not initialize libcrypt");
|
||||
return;
|
||||
}
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
|
||||
error_code = gnutls_global_init();
|
||||
if (error_code != 0)
|
||||
{
|
||||
error("Could not initialize libgnutls");
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
for (int i = 0, num = CRYPTO_num_locks(); i < num; i++)
|
||||
{
|
||||
g_OpenSSLMutexes.emplace_back(std::make_unique<Mutex>());
|
||||
}
|
||||
|
||||
CRYPTO_set_locking_callback(openssl_locking);
|
||||
CRYPTO_set_dynlock_create_callback(openssl_dynlock_create);
|
||||
CRYPTO_set_dynlock_destroy_callback(openssl_dynlock_destroy);
|
||||
CRYPTO_set_dynlock_lock_callback(openssl_dynlock_lock);
|
||||
#endif /* NEED_CRYPTO_LOCKING */
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::Final()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_global_deinit();
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#ifndef LIBRESSL_VERSION_NUMBER
|
||||
FIPS_mode_set(0);
|
||||
#endif
|
||||
#ifdef NEED_CRYPTO_LOCKING
|
||||
CRYPTO_set_locking_callback(nullptr);
|
||||
CRYPTO_set_id_callback(nullptr);
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && ! defined (LIBRESSL_VERSION_NUMBER)
|
||||
SSL_COMP_free_compression_methods();
|
||||
#endif
|
||||
//ENGINE_cleanup();
|
||||
CONF_modules_free();
|
||||
CONF_modules_unload(1);
|
||||
#ifndef OPENSSL_NO_COMP
|
||||
COMP_zlib_cleanup();
|
||||
#endif
|
||||
ERR_free_strings();
|
||||
EVP_cleanup();
|
||||
CRYPTO_cleanup_all_ex_data();
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
TlsSocket::~TlsSocket()
|
||||
{
|
||||
Close();
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
ERR_remove_state(0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void TlsSocket::ReportError(const char* errMsg, bool suppressable)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
const char* errstr = gnutls_strerror(m_retCode);
|
||||
if (suppressable && m_suppressErrors)
|
||||
{
|
||||
debug("%s: %s", errMsg, errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(BString<1024>("%s: %s", errMsg, errstr));
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
int errcode = ERR_get_error();
|
||||
do
|
||||
{
|
||||
char errstr[1024];
|
||||
ERR_error_string_n(errcode, errstr, sizeof(errstr));
|
||||
errstr[1024-1] = '\0';
|
||||
|
||||
if (suppressable && m_suppressErrors)
|
||||
{
|
||||
debug("%s: %s", errMsg, errstr);
|
||||
}
|
||||
else if (errcode != 0)
|
||||
{
|
||||
PrintError(BString<1024>("%s: %s", errMsg, errstr));
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(errMsg);
|
||||
}
|
||||
|
||||
errcode = ERR_get_error();
|
||||
} while (errcode);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::PrintError(const char* errMsg)
|
||||
{
|
||||
error("%s", errMsg);
|
||||
}
|
||||
|
||||
bool TlsSocket::Start()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_credentials_t cred;
|
||||
m_retCode = gnutls_certificate_allocate_credentials(&cred);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS context", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_context = cred;
|
||||
|
||||
if (m_certFile && m_keyFile)
|
||||
{
|
||||
m_retCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_context,
|
||||
m_certFile, m_keyFile, GNUTLS_X509_FMT_PEM);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not load certificate or key file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_session_t sess;
|
||||
m_retCode = gnutls_init(&sess, m_isClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_session = sess;
|
||||
|
||||
m_initialized = true;
|
||||
|
||||
const char* priority = !m_cipher.Empty() ? m_cipher.Str() :
|
||||
(m_certFile && m_keyFile ? "NORMAL:!VERS-SSL3.0" : "NORMAL");
|
||||
|
||||
m_retCode = gnutls_priority_set_direct((gnutls_session_t)m_session, priority, nullptr);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not select cipher for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_host)
|
||||
{
|
||||
m_retCode = gnutls_server_name_set((gnutls_session_t)m_session, GNUTLS_NAME_DNS, m_host, m_host.Length());
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not set hostname for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_retCode = gnutls_credentials_set((gnutls_session_t)m_session, GNUTLS_CRD_CERTIFICATE,
|
||||
(gnutls_certificate_credentials_t*)m_context);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError("Could not initialize TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr((gnutls_session_t)m_session, (gnutls_transport_ptr_t)(size_t)m_socket);
|
||||
|
||||
m_retCode = gnutls_handshake((gnutls_session_t)m_session);
|
||||
if (m_retCode != 0)
|
||||
{
|
||||
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
return true;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_context = SSL_CTX_new(SSLv23_method());
|
||||
|
||||
if (!m_context)
|
||||
{
|
||||
ReportError("Could not create TLS context", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_certFile && m_keyFile)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_chain_file((SSL_CTX*)m_context, m_certFile) != 1)
|
||||
{
|
||||
ReportError("Could not load certificate file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_context, m_keyFile, SSL_FILETYPE_PEM) != 1)
|
||||
{
|
||||
ReportError("Could not load key file", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (!SSL_CTX_set_options((SSL_CTX*)m_context, SSL_OP_NO_SSLv3))
|
||||
{
|
||||
ReportError("Could not select minimum protocol version for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// For ECC certificates
|
||||
EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
||||
if (!ecdh)
|
||||
{
|
||||
ReportError("Could not generate ecdh parameters for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (!SSL_CTX_set_tmp_ecdh((SSL_CTX*)m_context, ecdh))
|
||||
{
|
||||
ReportError("Could not set ecdh parameters for TLS", false);
|
||||
EC_KEY_free(ecdh);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
EC_KEY_free(ecdh);
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty())
|
||||
{
|
||||
// Enable certificate validation
|
||||
if (SSL_CTX_load_verify_locations((SSL_CTX*)m_context, m_certStore, nullptr) != 1)
|
||||
{
|
||||
ReportError("Could not set certificate store location", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
SSL_CTX_set_verify((SSL_CTX*)m_context, SSL_VERIFY_PEER, nullptr);
|
||||
}
|
||||
|
||||
m_session = SSL_new((SSL_CTX*)m_context);
|
||||
if (!m_session)
|
||||
{
|
||||
ReportError("Could not create TLS session", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_cipher.Empty() && !SSL_set_cipher_list((SSL*)m_session, m_cipher))
|
||||
{
|
||||
ReportError("Could not select cipher for TLS", false);
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && m_host && !SSL_set_tlsext_host_name((SSL*)m_session, m_host))
|
||||
{
|
||||
ReportError("Could not set host name for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SSL_set_fd((SSL*)m_session, (int)m_socket))
|
||||
{
|
||||
ReportError("Could not set the file descriptor for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int error_code = m_isClient ? SSL_connect((SSL*)m_session) : SSL_accept((SSL*)m_session);
|
||||
if (error_code < 1)
|
||||
{
|
||||
long verifyRes = SSL_get_verify_result((SSL*)m_session);
|
||||
if (verifyRes != X509_V_OK)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification",
|
||||
*m_host, X509_verify_cert_error_string(verifyRes)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportError(BString<1024>("TLS handshake failed for %s", *m_host));
|
||||
}
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_isClient && !m_certStore.Empty() && !ValidateCert())
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connected = true;
|
||||
return true;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
bool TlsSocket::ValidateCert()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030104
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030306
|
||||
if (FileSystem::DirectoryExists(m_certStore))
|
||||
{
|
||||
if (gnutls_certificate_set_x509_trust_dir((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
|
||||
{
|
||||
ReportError("Could not set certificate store location");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (gnutls_certificate_set_x509_trust_file((gnutls_certificate_credentials_t)m_context, m_certStore, GNUTLS_X509_FMT_PEM) < 0)
|
||||
{
|
||||
ReportError("Could not set certificate store location");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int status = 0;
|
||||
if (gnutls_certificate_verify_peers3((gnutls_session_t)m_session, m_host, &status) != 0 ||
|
||||
gnutls_certificate_type_get((gnutls_session_t)m_session) != GNUTLS_CRT_X509)
|
||||
{
|
||||
ReportError("Could not verify TLS certificate");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (status != 0)
|
||||
{
|
||||
if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
|
||||
{
|
||||
// Extracting hostname from the certificate
|
||||
unsigned int cert_list_size = 0;
|
||||
const gnutls_datum_t* cert_list = gnutls_certificate_get_peers((gnutls_session_t)m_session, &cert_list_size);
|
||||
if (cert_list_size > 0)
|
||||
{
|
||||
gnutls_x509_crt_t cert;
|
||||
gnutls_x509_crt_init(&cert);
|
||||
gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
|
||||
char dn[256];
|
||||
size_t size = sizeof(dn);
|
||||
if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &size) == 0)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, dn));
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
return false;
|
||||
}
|
||||
gnutls_x509_crt_deinit(cert);
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_datum_t msgdata;
|
||||
if (gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, &msgdata, 0) == 0)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, msgdata.data));
|
||||
gnutls_free(&msgdata);
|
||||
}
|
||||
else
|
||||
{
|
||||
ReportError(BString<1024>("TLS certificate verification failed for %s."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
// verify a server certificate was presented during the negotiation
|
||||
X509* cert = SSL_get_peer_certificate((SSL*)m_session);
|
||||
if (!cert)
|
||||
{
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: no certificate provided by server."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_X509_CHECK_HOST
|
||||
// hostname verification
|
||||
if (!m_host.Empty() && X509_check_host(cert, m_host, m_host.Length(), 0, nullptr) != 1)
|
||||
{
|
||||
const unsigned char* certHost = nullptr;
|
||||
// Find the position of the CN field in the Subject field of the certificate
|
||||
int common_name_loc = X509_NAME_get_index_by_NID(X509_get_subject_name(cert), NID_commonName, -1);
|
||||
if (common_name_loc >= 0)
|
||||
{
|
||||
// Extract the CN field
|
||||
X509_NAME_ENTRY* common_name_entry = X509_NAME_get_entry(X509_get_subject_name(cert), common_name_loc);
|
||||
if (common_name_entry != nullptr)
|
||||
{
|
||||
// Convert the CN field to a C string
|
||||
ASN1_STRING* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
|
||||
if (common_name_asn1 != nullptr)
|
||||
{
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
certHost = ASN1_STRING_get0_data(common_name_asn1);
|
||||
#else
|
||||
certHost = ASN1_STRING_data(common_name_asn1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrintError(BString<1024>("TLS certificate verification failed for %s: certificate hostname mismatch (%s)."
|
||||
" For more info visit http://nzbget.net/certificate-verification", *m_host, certHost));
|
||||
X509_free(cert);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
X509_free(cert);
|
||||
return true;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TlsSocket::Close()
|
||||
{
|
||||
if (m_session)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
if (m_connected)
|
||||
{
|
||||
gnutls_bye((gnutls_session_t)m_session, GNUTLS_SHUT_WR);
|
||||
}
|
||||
if (m_initialized)
|
||||
{
|
||||
gnutls_deinit((gnutls_session_t)m_session);
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (m_connected)
|
||||
{
|
||||
SSL_shutdown((SSL*)m_session);
|
||||
}
|
||||
SSL_free((SSL*)m_session);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_session = nullptr;
|
||||
}
|
||||
|
||||
if (m_context)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_context);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
SSL_CTX_free((SSL_CTX*)m_context);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int TlsSocket::Send(const char* buffer, int size)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
m_retCode = gnutls_record_send((gnutls_session_t)m_session, buffer, size);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_retCode = SSL_write((SSL*)m_session, buffer, size);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (m_retCode < 0)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (ERR_peek_error() == 0)
|
||||
{
|
||||
ReportError("Could not write to TLS-Socket: Connection closed by remote host");
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_OPENSSL */
|
||||
ReportError("Could not write to TLS-Socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return m_retCode;
|
||||
}
|
||||
|
||||
int TlsSocket::Recv(char* buffer, int size)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
m_retCode = gnutls_record_recv((gnutls_session_t)m_session, buffer, size);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_retCode = SSL_read((SSL*)m_session, buffer, size);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (m_retCode < 0)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (ERR_peek_error() == 0)
|
||||
{
|
||||
ReportError("Could not read from TLS-Socket: Connection closed by remote host");
|
||||
}
|
||||
else
|
||||
#endif /* HAVE_OPENSSL */
|
||||
{
|
||||
ReportError("Could not read from TLS-Socket");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return m_retCode;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2008-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TLSSOCKET_H
|
||||
#define TLSSOCKET_H
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
#include "NString.h"
|
||||
|
||||
class TlsSocket
|
||||
{
|
||||
public:
|
||||
TlsSocket(SOCKET socket, bool isClient, const char* host,
|
||||
const char* certFile, const char* keyFile, const char* cipher) :
|
||||
m_socket(socket), m_isClient(isClient), m_host(host),
|
||||
m_certFile(certFile), m_keyFile(keyFile), m_cipher(cipher) {}
|
||||
virtual ~TlsSocket();
|
||||
static void Init();
|
||||
static void InitOptions(const char* certStore) { m_certStore = certStore; }
|
||||
static void Final();
|
||||
bool Start();
|
||||
void Close();
|
||||
int Send(const char* buffer, int size);
|
||||
int Recv(char* buffer, int size);
|
||||
void SetSuppressErrors(bool suppressErrors) { m_suppressErrors = suppressErrors; }
|
||||
|
||||
protected:
|
||||
virtual void PrintError(const char* errMsg);
|
||||
|
||||
private:
|
||||
SOCKET m_socket;
|
||||
bool m_isClient;
|
||||
CString m_host;
|
||||
CString m_certFile;
|
||||
CString m_keyFile;
|
||||
CString m_cipher;
|
||||
bool m_suppressErrors = false;
|
||||
bool m_initialized = false;
|
||||
bool m_connected = false;
|
||||
int m_retCode;
|
||||
static CString m_certStore;
|
||||
|
||||
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TlsSocket.h
|
||||
void* m_context = nullptr;
|
||||
void* m_session = nullptr;
|
||||
|
||||
void ReportError(const char* errMsg, bool suppressable = true);
|
||||
bool ValidateCert();
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -1,666 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2019 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
WebDownloader::WebDownloader()
|
||||
{
|
||||
debug("Creating WebDownloader");
|
||||
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
void WebDownloader::SetUrl(const char* url)
|
||||
{
|
||||
m_url = WebUtil::UrlEncode(url);
|
||||
}
|
||||
|
||||
void WebDownloader::SetStatus(EStatus status)
|
||||
{
|
||||
m_status = status;
|
||||
Notify(nullptr);
|
||||
}
|
||||
|
||||
void WebDownloader::SetLastUpdateTimeNow()
|
||||
{
|
||||
m_lastUpdateTime = Util::CurrentTime();
|
||||
}
|
||||
|
||||
void WebDownloader::Run()
|
||||
{
|
||||
debug("Entering WebDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
|
||||
int remainedDownloadRetries = g_Options->GetUrlRetries() > 0 ? g_Options->GetUrlRetries() : 1;
|
||||
int remainedConnectRetries = remainedDownloadRetries > 10 ? remainedDownloadRetries : 10;
|
||||
if (!m_retry)
|
||||
{
|
||||
remainedDownloadRetries = 1;
|
||||
remainedConnectRetries = 1;
|
||||
}
|
||||
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && remainedDownloadRetries > 0 && remainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = DownloadWithRedirects(5);
|
||||
|
||||
if ((((Status == adFailed) && (remainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (remainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_Options->GetUrlInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_Options->GetUrlInterval() * 1000) &&
|
||||
!(!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
Util::Sleep(100);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || (!m_force && g_WorkState->GetPauseDownload()))
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status != adConnectError)
|
||||
{
|
||||
remainedDownloadRetries--;
|
||||
}
|
||||
else
|
||||
{
|
||||
remainedConnectRetries--;
|
||||
}
|
||||
}
|
||||
|
||||
if (Status != adFinished && Status != adRetry)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", *m_infoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Download %s failed", *m_infoName);
|
||||
}
|
||||
}
|
||||
|
||||
if (Status == adFinished)
|
||||
{
|
||||
detail("Download %s completed", *m_infoName);
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting WebDownloader-loop");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::Download()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
URL url(m_url);
|
||||
|
||||
Status = CreateConnection(&url);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
m_connection->SetTimeout(g_Options->GetUrlTimeout());
|
||||
m_connection->SetSuppressErrors(false);
|
||||
|
||||
// connection
|
||||
bool connected = m_connection->Connect();
|
||||
if (!connected || IsStopped())
|
||||
{
|
||||
FreeConnection();
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
detail("Downloading %s", *m_infoName);
|
||||
|
||||
SendHeaders(&url);
|
||||
|
||||
Status = DownloadHeaders();
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
Status = DownloadBody();
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
// Download failed, delete broken output file
|
||||
FileSystem::DeleteFile(m_outputFilename);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadWithRedirects(int maxRedirects)
|
||||
{
|
||||
// do sync download, following redirects
|
||||
EStatus status = adRedirect;
|
||||
while (status == adRedirect && maxRedirects >= 0)
|
||||
{
|
||||
maxRedirects--;
|
||||
status = Download();
|
||||
}
|
||||
|
||||
if (status == adRedirect && maxRedirects < 0)
|
||||
{
|
||||
warn("Too many redirects for %s", *m_infoName);
|
||||
status = adFailed;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CreateConnection(URL *url)
|
||||
{
|
||||
if (!url->IsValid())
|
||||
{
|
||||
error("URL is not valid: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
int port = url->GetPort();
|
||||
if (port == 0 && !strcasecmp(url->GetProtocol(), "http"))
|
||||
{
|
||||
port = 80;
|
||||
}
|
||||
if (port == 0 && !strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
port = 443;
|
||||
}
|
||||
|
||||
if (strcasecmp(url->GetProtocol(), "http") && strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
error("Unsupported protocol in URL: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
#ifdef DISABLE_TLS
|
||||
if (!strcasecmp(url->GetProtocol(), "https"))
|
||||
{
|
||||
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", url->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool tls = !strcasecmp(url->GetProtocol(), "https");
|
||||
|
||||
m_connection = std::make_unique<Connection>(url->GetHost(), port, tls);
|
||||
|
||||
return adRunning;
|
||||
}
|
||||
|
||||
void WebDownloader::SendHeaders(URL *url)
|
||||
{
|
||||
// retrieve file
|
||||
m_connection->WriteLine(BString<1024>("GET %s HTTP/1.0\r\n", url->GetResource()));
|
||||
m_connection->WriteLine(BString<1024>("User-Agent: nzbget/%s\r\n", Util::VersionRevision()));
|
||||
|
||||
if ((!strcasecmp(url->GetProtocol(), "http") && (url->GetPort() == 80 || url->GetPort() == 0)) ||
|
||||
(!strcasecmp(url->GetProtocol(), "https") && (url->GetPort() == 443 || url->GetPort() == 0)))
|
||||
{
|
||||
m_connection->WriteLine(BString<1024>("Host: %s\r\n", url->GetHost()));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_connection->WriteLine(BString<1024>("Host: %s:%i\r\n", url->GetHost(), url->GetPort()));
|
||||
}
|
||||
|
||||
m_connection->WriteLine("Accept: */*\r\n");
|
||||
#ifndef DISABLE_GZIP
|
||||
m_connection->WriteLine("Accept-Encoding: gzip\r\n");
|
||||
#endif
|
||||
m_connection->WriteLine("Connection: close\r\n");
|
||||
m_connection->WriteLine("\r\n");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadHeaders()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_confirmedLength = false;
|
||||
CharBuffer lineBuf(1024*10);
|
||||
m_contentLen = -1;
|
||||
bool firstLine = true;
|
||||
m_gzip = false;
|
||||
m_redirecting = false;
|
||||
m_redirected = false;
|
||||
|
||||
// Headers
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
int len = 0;
|
||||
char* line = m_connection->ReadLine(lineBuf, lineBuf.Size(), &len);
|
||||
|
||||
if (firstLine)
|
||||
{
|
||||
Status = CheckResponse(lineBuf);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
firstLine = false;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", *m_infoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
debug("Header: %s", line);
|
||||
|
||||
// detect body of response
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Util::TrimRight(line);
|
||||
ProcessHeader(line);
|
||||
|
||||
if (m_redirected)
|
||||
{
|
||||
Status = adRedirect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadBody()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_outFile.Close();
|
||||
bool end = false;
|
||||
CharBuffer lineBuf(1024*10);
|
||||
int writtenLen = 0;
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
m_gUnzipStream.reset();
|
||||
if (m_gzip)
|
||||
{
|
||||
m_gUnzipStream = std::make_unique<GUnzipStream>(1024*10);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Body
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
char* buffer;
|
||||
int len;
|
||||
m_connection->ReadBuffer(&buffer, &len);
|
||||
if (len == 0)
|
||||
{
|
||||
len = m_connection->TryRecv(lineBuf, lineBuf.Size());
|
||||
buffer = lineBuf;
|
||||
}
|
||||
|
||||
// Connection closed or timeout?
|
||||
if (len <= 0)
|
||||
{
|
||||
if (len == 0 && m_contentLen == -1 && writtenLen > 0)
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", *m_infoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
// write to output file
|
||||
if (!Write(buffer, len))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
writtenLen += len;
|
||||
|
||||
//detect end of file
|
||||
if (writtenLen == m_contentLen || (m_contentLen == -1 && m_gzip && m_confirmedLength))
|
||||
{
|
||||
end = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
m_gUnzipStream.reset();
|
||||
#endif
|
||||
|
||||
m_outFile.Close();
|
||||
|
||||
if (!end && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("URL %s failed: file incomplete", *m_infoName);
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (end)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CheckResponse(const char* response)
|
||||
{
|
||||
if (!response)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s: Connection closed by remote host", *m_infoName);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
const char* hTTPResponse = strchr(response, ' ');
|
||||
if (strncmp(response, "HTTP", 4) || !hTTPResponse)
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, response);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
hTTPResponse++;
|
||||
|
||||
if (!strncmp(hTTPResponse, "400", 3) || !strncmp(hTTPResponse, "499", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "404", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", *m_infoName, hTTPResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "301", 3) || !strncmp(hTTPResponse, "302", 3) ||
|
||||
!strncmp(hTTPResponse, "303", 3) || !strncmp(hTTPResponse, "307", 3) ||
|
||||
!strncmp(hTTPResponse, "308", 3))
|
||||
{
|
||||
m_redirecting = true;
|
||||
return adRunning;
|
||||
}
|
||||
else if (!strncmp(hTTPResponse, "200", 3))
|
||||
{
|
||||
// OK
|
||||
return adRunning;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("URL %s failed: %s", *m_infoName, response);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ProcessHeader(const char* line)
|
||||
{
|
||||
if (!strncasecmp(line, "Content-Length: ", 16))
|
||||
{
|
||||
m_contentLen = atoi(line + 16);
|
||||
m_confirmedLength = true;
|
||||
}
|
||||
else if (!strncasecmp(line, "Content-Encoding: gzip", 22))
|
||||
{
|
||||
m_gzip = true;
|
||||
}
|
||||
else if (!strncasecmp(line, "Content-Disposition: ", 21))
|
||||
{
|
||||
ParseFilename(line);
|
||||
}
|
||||
else if (m_redirecting && !strncasecmp(line, "Location: ", 10))
|
||||
{
|
||||
ParseRedirect(line + 10);
|
||||
m_redirected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ParseFilename(const char* contentDisposition)
|
||||
{
|
||||
// Examples:
|
||||
// Content-Disposition: attachment; filename="fname.ext"
|
||||
// Content-Disposition: attachement;filename=fname.ext
|
||||
// Content-Disposition: attachement;filename=fname.ext;
|
||||
const char *p = strstr(contentDisposition, "filename");
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p = strchr(p, '=');
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
while (*p == ' ') p++;
|
||||
|
||||
BString<1024> fname = p;
|
||||
|
||||
char *pe = fname + strlen(fname) - 1;
|
||||
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
|
||||
*pe = '\0';
|
||||
pe--;
|
||||
}
|
||||
|
||||
WebUtil::HttpUnquote(fname);
|
||||
|
||||
m_originalFilename = FileSystem::BaseFileName(fname);
|
||||
|
||||
debug("OriginalFilename: %s", *m_originalFilename);
|
||||
}
|
||||
|
||||
void WebDownloader::ParseRedirect(const char* location)
|
||||
{
|
||||
const char* newLocation = location;
|
||||
BString<1024> urlBuf;
|
||||
URL newUrl(newLocation);
|
||||
if (!newUrl.IsValid())
|
||||
{
|
||||
// redirect within host
|
||||
|
||||
BString<1024> resource;
|
||||
URL oldUrl(m_url);
|
||||
|
||||
if (*location == '/')
|
||||
{
|
||||
// absolute path within host
|
||||
resource = location;
|
||||
}
|
||||
else
|
||||
{
|
||||
// relative path within host
|
||||
resource = oldUrl.GetResource();
|
||||
|
||||
char* p = strchr(resource, '?');
|
||||
if (p)
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
p = strrchr(resource, '/');
|
||||
if (p)
|
||||
{
|
||||
p[1] = '\0';
|
||||
}
|
||||
|
||||
resource.Append(location);
|
||||
}
|
||||
|
||||
if (oldUrl.GetPort() > 0)
|
||||
{
|
||||
urlBuf.Format("%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), *resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
urlBuf.Format("%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), *resource);
|
||||
}
|
||||
newLocation = urlBuf;
|
||||
}
|
||||
detail("URL %s redirected to %s", *m_url, newLocation);
|
||||
SetUrl(newLocation);
|
||||
}
|
||||
|
||||
bool WebDownloader::Write(void* buffer, int len)
|
||||
{
|
||||
if (!m_outFile.Active() && !PrepareFile())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
if (m_gzip)
|
||||
{
|
||||
m_gUnzipStream->Write(buffer, len);
|
||||
const void *outBuf;
|
||||
int outLen = 1;
|
||||
while (outLen > 0)
|
||||
{
|
||||
GUnzipStream::EStatus gZStatus = m_gUnzipStream->Read(&outBuf, &outLen);
|
||||
|
||||
if (gZStatus == GUnzipStream::zlError)
|
||||
{
|
||||
error("URL %s: GUnzip failed", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (outLen > 0 && m_outFile.Write(outBuf, outLen) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gZStatus == GUnzipStream::zlFinished)
|
||||
{
|
||||
m_confirmedLength = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
return m_outFile.Write(buffer, len) > 0;
|
||||
}
|
||||
|
||||
bool WebDownloader::PrepareFile()
|
||||
{
|
||||
// prepare file for writing
|
||||
|
||||
const char* filename = m_outputFilename;
|
||||
if (!m_outFile.Open(filename, DiskFile::omWrite))
|
||||
{
|
||||
error("Could not %s file %s", "create", filename);
|
||||
return false;
|
||||
}
|
||||
if (g_Options->GetWriteBuffer() > 0)
|
||||
{
|
||||
m_outFile.SetWriteBuffer(g_Options->GetWriteBuffer() * 1024);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebDownloader::LogDebugInfo()
|
||||
{
|
||||
info(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_status,
|
||||
*Util::FormatTime(m_lastUpdateTime), FileSystem::BaseFileName(m_outputFilename));
|
||||
}
|
||||
|
||||
void WebDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop WebDownloader");
|
||||
Thread::Stop();
|
||||
Guard guard(m_connectionMutex);
|
||||
if (m_connection)
|
||||
{
|
||||
m_connection->SetSuppressErrors(true);
|
||||
m_connection->Cancel();
|
||||
}
|
||||
debug("WebDownloader stopped successfully");
|
||||
}
|
||||
|
||||
void WebDownloader::FreeConnection()
|
||||
{
|
||||
if (m_connection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
Guard guard(m_connectionMutex);
|
||||
if (m_connection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_connection->Disconnect();
|
||||
}
|
||||
m_connection.reset();
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2012-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBDOWNLOADER_H
|
||||
#define WEBDOWNLOADER_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Observer.h"
|
||||
#include "Thread.h"
|
||||
#include "Connection.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Util.h"
|
||||
|
||||
class WebDownloader : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adRetry,
|
||||
adNotFound,
|
||||
adRedirect,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
WebDownloader();
|
||||
EStatus GetStatus() { return m_status; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
EStatus Download();
|
||||
EStatus DownloadWithRedirects(int maxRedirects);
|
||||
void SetInfoName(const char* infoName) { m_infoName = infoName; }
|
||||
const char* GetInfoName() { return m_infoName; }
|
||||
void SetUrl(const char* url);
|
||||
const char* GetOutputFilename() { return m_outputFilename; }
|
||||
void SetOutputFilename(const char* outputFilename) { m_outputFilename = outputFilename; }
|
||||
time_t GetLastUpdateTime() { return m_lastUpdateTime; }
|
||||
void SetLastUpdateTimeNow();
|
||||
bool GetConfirmedLength() { return m_confirmedLength; }
|
||||
const char* GetOriginalFilename() { return m_originalFilename; }
|
||||
void SetForce(bool force) { m_force = force; }
|
||||
void SetRetry(bool retry) { m_retry = retry; }
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* line);
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
CString m_outputFilename;
|
||||
std::unique_ptr<Connection> m_connection;
|
||||
Mutex m_connectionMutex;
|
||||
EStatus m_status = adUndefined;
|
||||
time_t m_lastUpdateTime;
|
||||
CString m_infoName;
|
||||
DiskFile m_outFile;
|
||||
int m_contentLen;
|
||||
bool m_confirmedLength = false;
|
||||
CString m_originalFilename;
|
||||
bool m_force = false;
|
||||
bool m_redirecting;
|
||||
bool m_redirected;
|
||||
bool m_gzip;
|
||||
bool m_retry = true;
|
||||
#ifndef DISABLE_GZIP
|
||||
std::unique_ptr<GUnzipStream> m_gUnzipStream;
|
||||
#endif
|
||||
|
||||
void SetStatus(EStatus status);
|
||||
bool Write(void* buffer, int len);
|
||||
bool PrepareFile();
|
||||
void FreeConnection();
|
||||
EStatus CheckResponse(const char* response);
|
||||
EStatus CreateConnection(URL *url);
|
||||
void ParseFilename(const char* contentDisposition);
|
||||
void SendHeaders(URL *url);
|
||||
EStatus DownloadHeaders();
|
||||
EStatus DownloadBody();
|
||||
void ParseRedirect(const char* location);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "CommandScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const int COMMANDPROCESS_SUCCESS = 93;
|
||||
static const int COMMANDPROCESS_ERROR = 94;
|
||||
|
||||
bool CommandScriptController::StartScript(const char* scriptName, const char* command,
|
||||
std::unique_ptr<Options::OptEntries> modifiedOptions)
|
||||
{
|
||||
CommandScriptController* scriptController = new CommandScriptController();
|
||||
scriptController->m_script = scriptName;
|
||||
scriptController->m_command = command;
|
||||
scriptController->m_logId = g_CommandScriptLog->Reset();
|
||||
scriptController->m_modifiedOptions = std::move(modifiedOptions);
|
||||
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommandScriptController::Run()
|
||||
{
|
||||
ExecuteScriptList(m_script);
|
||||
}
|
||||
|
||||
void CommandScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Executing script %s with command %s", script->GetName(), *m_command);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("script %s with command %s", script->GetName(), *m_command);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
infoName[0] = 'S'; // uppercase
|
||||
SetLogPrefix(nullptr);
|
||||
|
||||
switch (exitCode)
|
||||
{
|
||||
case COMMANDPROCESS_SUCCESS:
|
||||
PrintMessage(Message::mkInfo, "%s successful", *infoName);
|
||||
break;
|
||||
|
||||
case COMMANDPROCESS_ERROR:
|
||||
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
|
||||
PrintMessage(Message::mkError, "%s failed", *infoName);
|
||||
break;
|
||||
|
||||
default:
|
||||
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", *infoName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CommandScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBCP_COMMAND", m_command);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
|
||||
const char* CommandScriptController::GetOptValue(const char* name, const char* value)
|
||||
{
|
||||
Options::OptEntry* entry = m_modifiedOptions->FindOption(name);
|
||||
return entry ? entry->GetValue() : value;
|
||||
}
|
||||
|
||||
void CommandScriptController::AddMessage(Message::EKind kind, const char * text)
|
||||
{
|
||||
NzbScriptController::AddMessage(kind, text);
|
||||
g_CommandScriptLog->AddMessage(m_logId, kind, text);
|
||||
}
|
||||
|
||||
|
||||
int CommandScriptLog::Reset()
|
||||
{
|
||||
Guard guard(m_logMutex);
|
||||
m_messages.clear();
|
||||
return ++m_idScriptGen;
|
||||
}
|
||||
|
||||
void CommandScriptLog::AddMessage(int scriptId, Message::EKind kind, const char * text)
|
||||
{
|
||||
Guard guard(m_logMutex);
|
||||
|
||||
// save only messages from the last started script
|
||||
if (scriptId == m_idScriptGen)
|
||||
{
|
||||
m_messages.emplace_back(++m_idMessageGen, kind, Util::CurrentTime(), text);
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef COMMANDSCRIPT_H
|
||||
#define COMMANDSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
#include "Log.h"
|
||||
|
||||
class CommandScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static bool StartScript(const char* scriptName, const char* command, std::unique_ptr<Options::OptEntries> modifiedOptions);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
virtual const char* GetOptValue(const char* name, const char* value);
|
||||
|
||||
private:
|
||||
CString m_script;
|
||||
CString m_command;
|
||||
int m_logId;
|
||||
std::unique_ptr<Options::OptEntries> m_modifiedOptions;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
class CommandScriptLog
|
||||
{
|
||||
public:
|
||||
GuardedMessageList GuardMessages() { return GuardedMessageList(&m_messages, &m_logMutex); }
|
||||
int Reset();
|
||||
void AddMessage(int scriptId, Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
MessageList m_messages;
|
||||
Mutex m_logMutex;
|
||||
int m_idMessageGen;
|
||||
int m_idScriptGen;
|
||||
};
|
||||
|
||||
extern CommandScriptLog* g_CommandScriptLog;
|
||||
|
||||
#endif
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
static const int FEED_SUCCESS = 93;
|
||||
|
||||
void FeedScriptController::ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success)
|
||||
{
|
||||
FeedScriptController scriptController;
|
||||
scriptController.m_feedFile = feedFile;
|
||||
scriptController.m_feedId = feedId;
|
||||
|
||||
scriptController.ExecuteScriptList(feedScript);
|
||||
|
||||
if (success)
|
||||
{
|
||||
*success = scriptController.m_success;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetFeedScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing feed-script %s for Feed%i", script->GetName(), m_feedId);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("feed-script %s for Feed%i", script->GetName(), m_feedId);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
if (exitCode != FEED_SUCCESS)
|
||||
{
|
||||
infoName[0] = 'F'; // uppercase
|
||||
PrintMessage(Message::mkError, "%s failed", *infoName);
|
||||
m_success = false;
|
||||
}
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void FeedScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBFP_FILENAME", m_feedFile);
|
||||
SetIntEnvVar("NZBFP_FEEDID", m_feedId);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2015-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDSCRIPT_H
|
||||
#define FEEDSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class FeedScriptController : public NzbScriptController
|
||||
{
|
||||
public:
|
||||
static void ExecuteScripts(const char* feedScript, const char* feedFile, int feedId, bool* success);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
|
||||
private:
|
||||
const char* m_feedFile;
|
||||
int m_feedId;
|
||||
bool m_success = true;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
/**
|
||||
* If szStripPrefix is not nullptr, only pp-parameters, whose names start with the prefix
|
||||
* are processed. The prefix is then stripped from the names.
|
||||
* If szStripPrefix is nullptr, all pp-parameters are processed; without stripping.
|
||||
*/
|
||||
void NzbScriptController::PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix)
|
||||
{
|
||||
int prefixLen = stripPrefix ? strlen(stripPrefix) : 0;
|
||||
|
||||
for (NzbParameter& parameter : parameters)
|
||||
{
|
||||
const char* value = parameter.GetValue();
|
||||
|
||||
if (stripPrefix && !strncmp(parameter.GetName(), stripPrefix, prefixLen) && (int)strlen(parameter.GetName()) > prefixLen)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", parameter.GetName() + prefixLen, value);
|
||||
}
|
||||
else if (!stripPrefix)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", parameter.GetName(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NzbScriptController::PrepareEnvScript(NzbParameterList* parameters, const char* scriptName)
|
||||
{
|
||||
if (parameters)
|
||||
{
|
||||
PrepareEnvParameters(parameters, nullptr);
|
||||
}
|
||||
|
||||
BString<1024> paramPrefix("%s:", scriptName);
|
||||
|
||||
if (parameters)
|
||||
{
|
||||
PrepareEnvParameters(parameters, paramPrefix);
|
||||
}
|
||||
|
||||
PrepareEnvOptions(paramPrefix);
|
||||
}
|
||||
|
||||
void NzbScriptController::ExecuteScriptList(const char* scriptList)
|
||||
{
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (scriptList && *scriptList)
|
||||
{
|
||||
// split szScriptList into tokens
|
||||
Tokenizer tok(scriptList, ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
ExecuteScript(&script);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBSCRIPT_H
|
||||
#define NZBSCRIPT_H
|
||||
|
||||
#include "Script.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class NzbScriptController : public ScriptController
|
||||
{
|
||||
protected:
|
||||
void PrepareEnvParameters(NzbParameterList* parameters, const char* stripPrefix);
|
||||
void PrepareEnvScript(NzbParameterList* parameters, const char* scriptName);
|
||||
void ExecuteScriptList(const char* scriptList);
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,309 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2019 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PostScript.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
|
||||
static const int POSTPROCESS_PARCHECK = 92;
|
||||
static const int POSTPROCESS_SUCCESS = 93;
|
||||
static const int POSTPROCESS_ERROR = 94;
|
||||
static const int POSTPROCESS_NONE = 95;
|
||||
|
||||
void PostScriptController::StartJob(PostInfo* postInfo)
|
||||
{
|
||||
PostScriptController* scriptController = new PostScriptController();
|
||||
scriptController->m_postInfo = postInfo;
|
||||
scriptController->SetAutoDestroy(false);
|
||||
scriptController->m_prefixLen = 0;
|
||||
|
||||
postInfo->SetPostThread(scriptController);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void PostScriptController::Run()
|
||||
{
|
||||
StringBuilder scriptCommaList;
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
for (NzbParameter& parameter : m_postInfo->GetNzbInfo()->GetParameters())
|
||||
{
|
||||
const char* varname = parameter.GetName();
|
||||
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname) - 1] == ':' &&
|
||||
(!strcasecmp(parameter.GetValue(), "yes") || !strcasecmp(parameter.GetValue(), "on") || !strcasecmp(parameter.GetValue(), "1")))
|
||||
{
|
||||
CString scriptName(varname);
|
||||
scriptName[strlen(scriptName) - 1] = '\0'; // remove trailing ':'
|
||||
scriptCommaList.Append(scriptName);
|
||||
scriptCommaList.Append(",");
|
||||
}
|
||||
}
|
||||
m_postInfo->GetNzbInfo()->GetScriptStatuses()->clear();
|
||||
}
|
||||
|
||||
ExecuteScriptList(scriptCommaList);
|
||||
|
||||
m_postInfo->SetStage(PostInfo::ptFinished);
|
||||
m_postInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void PostScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
// if any script has requested par-check, do not execute other scripts
|
||||
if (!script->GetPostScript() || m_postInfo->GetRequestParCheck())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
|
||||
|
||||
BString<1024> progressLabel("Executing post-process-script %s", script->GetName());
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->SetProgressLabel(progressLabel);
|
||||
}
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("post-process-script %s for %s", script->GetName(), m_postInfo->GetNzbInfo()->GetName());
|
||||
SetInfoName(infoName);
|
||||
|
||||
m_script = script;
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
int exitCode = Execute();
|
||||
|
||||
infoName[0] = 'P'; // uppercase
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
ScriptStatus::EStatus status = AnalyseExitCode(exitCode, infoName);
|
||||
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->GetScriptStatuses()->emplace_back(script->GetName(), status);
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBPP_NZBID", m_postInfo->GetNzbInfo()->GetId());
|
||||
SetEnvVar("NZBPP_NZBNAME", m_postInfo->GetNzbInfo()->GetName());
|
||||
SetEnvVar("NZBPP_DIRECTORY", m_postInfo->GetNzbInfo()->GetDestDir());
|
||||
SetEnvVar("NZBPP_NZBFILENAME", m_postInfo->GetNzbInfo()->GetFilename());
|
||||
SetEnvVar("NZBPP_QUEUEDFILE", m_postInfo->GetNzbInfo()->GetQueuedFilename());
|
||||
SetEnvVar("NZBPP_URL", m_postInfo->GetNzbInfo()->GetUrl());
|
||||
SetEnvVar("NZBPP_FINALDIR", m_postInfo->GetNzbInfo()->GetFinalDir());
|
||||
SetEnvVar("NZBPP_CATEGORY", m_postInfo->GetNzbInfo()->GetCategory());
|
||||
SetIntEnvVar("NZBPP_HEALTH", m_postInfo->GetNzbInfo()->CalcHealth());
|
||||
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_postInfo->GetNzbInfo()->CalcCriticalHealth(false));
|
||||
|
||||
SetEnvVar("NZBPP_DUPEKEY", m_postInfo->GetNzbInfo()->GetDupeKey());
|
||||
SetIntEnvVar("NZBPP_DUPESCORE", m_postInfo->GetNzbInfo()->GetDupeScore());
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBPP_DUPEMODE", dupeModeName[m_postInfo->GetNzbInfo()->GetDupeMode()]);
|
||||
|
||||
BString<1024> status = m_postInfo->GetNzbInfo()->MakeTextStatus(true);
|
||||
SetEnvVar("NZBPP_STATUS", status);
|
||||
|
||||
char* detail = strchr(status, '/');
|
||||
if (detail) *detail = '\0';
|
||||
SetEnvVar("NZBPP_TOTALSTATUS", status);
|
||||
|
||||
const char* scriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
|
||||
SetEnvVar("NZBPP_SCRIPTSTATUS", scriptStatusName[m_postInfo->GetNzbInfo()->GetScriptStatuses()->CalcTotalStatus()]);
|
||||
|
||||
// deprecated
|
||||
int parStatusCodes[] = { 0, 0, 1, 2, 3, 4 };
|
||||
NzbInfo::EParStatus parStatus = m_postInfo->GetNzbInfo()->GetParStatus();
|
||||
// for downloads marked as bad and for deleted downloads pass par status "Failure"
|
||||
// for compatibility with older scripts which don't check "NZBPP_TOTALSTATUS"
|
||||
if (m_postInfo->GetNzbInfo()->GetDeleteStatus() != NzbInfo::dsNone ||
|
||||
m_postInfo->GetNzbInfo()->GetMarkStatus() == NzbInfo::ksBad)
|
||||
{
|
||||
parStatus = NzbInfo::psFailure;
|
||||
}
|
||||
SetIntEnvVar("NZBPP_PARSTATUS", parStatusCodes[parStatus]);
|
||||
|
||||
// deprecated
|
||||
int unpackStatus[] = { 0, 0, 1, 2, 3, 4 };
|
||||
SetIntEnvVar("NZBPP_UNPACKSTATUS", unpackStatus[m_postInfo->GetNzbInfo()->GetUnpackStatus()]);
|
||||
|
||||
// deprecated
|
||||
SetIntEnvVar("NZBPP_HEALTHDELETED", (int)m_postInfo->GetNzbInfo()->GetDeleteStatus() == NzbInfo::dsHealth);
|
||||
|
||||
SetIntEnvVar("NZBPP_TOTALARTICLES", (int)m_postInfo->GetNzbInfo()->GetTotalArticles());
|
||||
SetIntEnvVar("NZBPP_SUCCESSARTICLES", (int)m_postInfo->GetNzbInfo()->GetSuccessArticles());
|
||||
SetIntEnvVar("NZBPP_FAILEDARTICLES", (int)m_postInfo->GetNzbInfo()->GetFailedArticles());
|
||||
|
||||
for (ServerStat& serverStat : m_postInfo->GetNzbInfo()->GetServerStats())
|
||||
{
|
||||
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_SUCCESSARTICLES", serverStat.GetServerId()),
|
||||
serverStat.GetSuccessArticles());
|
||||
|
||||
SetIntEnvVar(BString<1024>("NZBPP_SERVER%i_FAILEDARTICLES", serverStat.GetServerId()),
|
||||
serverStat.GetFailedArticles());
|
||||
}
|
||||
|
||||
PrepareEnvScript(m_postInfo->GetNzbInfo()->GetParameters(), scriptName);
|
||||
}
|
||||
|
||||
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int exitCode, const char* upInfoName)
|
||||
{
|
||||
// The ScriptStatus is accumulated for all scripts:
|
||||
// If any script has failed the status is "failure", etc.
|
||||
|
||||
switch (exitCode)
|
||||
{
|
||||
case POSTPROCESS_SUCCESS:
|
||||
PrintMessage(Message::mkInfo, "%s successful", upInfoName);
|
||||
return ScriptStatus::srSuccess;
|
||||
|
||||
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", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
|
||||
case POSTPROCESS_NONE:
|
||||
PrintMessage(Message::mkInfo, "%s skipped", upInfoName);
|
||||
return ScriptStatus::srNone;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
case POSTPROCESS_PARCHECK:
|
||||
if (m_postInfo->GetNzbInfo()->GetParStatus() > NzbInfo::psSkipped)
|
||||
{
|
||||
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "%s requested par-check/repair", upInfoName);
|
||||
m_postInfo->SetRequestParCheck(true);
|
||||
m_postInfo->SetForceRepair(true);
|
||||
return ScriptStatus::srSuccess;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", upInfoName);
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "FINALDIR=", 9))
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->SetFinalDir(msgText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DIRECTORY=", 10))
|
||||
{
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->SetDestDir(msgText + 6 + 10);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->GetNzbInfo()->GetParameters()->SetParameter(param, value + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
SetLogPrefix(nullptr);
|
||||
PrintMessage(Message::mkWarning, "Marking %s as bad", m_postInfo->GetNzbInfo()->GetName());
|
||||
SetLogPrefix(m_script->GetDisplayName());
|
||||
m_postInfo->GetNzbInfo()->SetMarkStatus(NzbInfo::ksBad);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_postInfo->GetNzbInfo()->AddMessage(kind, text);
|
||||
GuardedDownloadQueue guard = DownloadQueue::Guard();
|
||||
m_postInfo->SetProgressLabel(text);
|
||||
}
|
||||
|
||||
if (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority())
|
||||
{
|
||||
time_t stageTime = m_postInfo->GetStageTime();
|
||||
time_t startTime = m_postInfo->GetStartTime();
|
||||
time_t waitTime = Util::CurrentTime();
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_WorkState->GetPausePostProcess() && !m_postInfo->GetNzbInfo()->GetForcePriority() && !IsStopped())
|
||||
{
|
||||
Util::Sleep(100);
|
||||
|
||||
// update time stamps
|
||||
|
||||
time_t delta = Util::CurrentTime() - waitTime;
|
||||
|
||||
if (stageTime > 0)
|
||||
{
|
||||
m_postInfo->SetStageTime(stageTime + delta);
|
||||
}
|
||||
|
||||
if (startTime > 0)
|
||||
{
|
||||
m_postInfo->SetStartTime(startTime + delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::Stop()
|
||||
{
|
||||
debug("Stopping post-process-script");
|
||||
Thread::Stop();
|
||||
Terminate();
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef POSTSCRIPT_H
|
||||
#define POSTSCRIPT_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NzbScript.h"
|
||||
|
||||
class PostScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartJob(PostInfo* postInfo);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
PostInfo* m_postInfo;
|
||||
int m_prefixLen;
|
||||
ScriptConfig::Script* m_script;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
ScriptStatus::EStatus AnalyseExitCode(int exitCode, const char* upInfoName);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,495 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NString.h"
|
||||
#include "QueueScript.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
static const char* QUEUE_EVENT_NAMES[] = {
|
||||
"FILE_DOWNLOADED",
|
||||
"URL_COMPLETED",
|
||||
"NZB_MARKED",
|
||||
"NZB_ADDED",
|
||||
"NZB_NAMED",
|
||||
"NZB_DOWNLOADED",
|
||||
"NZB_DELETED" };
|
||||
|
||||
class QueueScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
CString m_nzbName;
|
||||
CString m_nzbFilename;
|
||||
CString m_url;
|
||||
CString m_category;
|
||||
CString m_destDir;
|
||||
CString m_queuedFilename;
|
||||
int m_id;
|
||||
int m_priority;
|
||||
CString m_dupeKey;
|
||||
EDupeMode m_dupeMode;
|
||||
int m_dupeScore;
|
||||
NzbParameterList m_parameters;
|
||||
int m_prefixLen;
|
||||
ScriptConfig::Script* m_script;
|
||||
QueueScriptCoordinator::EEvent m_event;
|
||||
bool m_markBad;
|
||||
NzbInfo::EDeleteStatus m_deleteStatus;
|
||||
NzbInfo::EUrlStatus m_urlStatus;
|
||||
NzbInfo::EMarkStatus m_markStatus;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
|
||||
void QueueScriptController::StartScript(NzbInfo* nzbInfo, ScriptConfig::Script* script, QueueScriptCoordinator::EEvent event)
|
||||
{
|
||||
QueueScriptController* scriptController = new QueueScriptController();
|
||||
|
||||
scriptController->m_nzbName = nzbInfo->GetName();
|
||||
scriptController->m_nzbFilename = nzbInfo->GetFilename();
|
||||
scriptController->m_url = nzbInfo->GetUrl();
|
||||
scriptController->m_category = nzbInfo->GetCategory();
|
||||
scriptController->m_destDir = nzbInfo->GetDestDir();
|
||||
scriptController->m_queuedFilename = nzbInfo->GetQueuedFilename();
|
||||
scriptController->m_id = nzbInfo->GetId();
|
||||
scriptController->m_priority = nzbInfo->GetPriority();
|
||||
scriptController->m_dupeKey = nzbInfo->GetDupeKey();
|
||||
scriptController->m_dupeMode = nzbInfo->GetDupeMode();
|
||||
scriptController->m_dupeScore = nzbInfo->GetDupeScore();
|
||||
scriptController->m_parameters.CopyFrom(nzbInfo->GetParameters());
|
||||
scriptController->m_script = script;
|
||||
scriptController->m_event = event;
|
||||
scriptController->m_prefixLen = 0;
|
||||
scriptController->m_markBad = false;
|
||||
scriptController->m_deleteStatus = nzbInfo->GetDeleteStatus();
|
||||
scriptController->m_urlStatus = nzbInfo->GetUrlStatus();
|
||||
scriptController->m_markStatus = nzbInfo->GetMarkStatus();
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void QueueScriptController::Run()
|
||||
{
|
||||
ExecuteScript(m_script);
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
|
||||
if (m_markBad)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName);
|
||||
nzbInfo->SetDeleteStatus(NzbInfo::dsBad);
|
||||
downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
g_QueueScriptCoordinator->CheckQueue();
|
||||
}
|
||||
|
||||
void QueueScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
PrintMessage(m_event == QueueScriptCoordinator::qeFileDownloaded ? Message::mkDetail : Message::mkInfo,
|
||||
"Executing queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("queue-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbName));
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void QueueScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNA_NZBNAME", m_nzbName);
|
||||
SetIntEnvVar("NZBNA_NZBID", m_id);
|
||||
SetEnvVar("NZBNA_FILENAME", m_nzbFilename);
|
||||
SetEnvVar("NZBNA_DIRECTORY", m_destDir);
|
||||
SetEnvVar("NZBNA_QUEUEDFILE", m_queuedFilename);
|
||||
SetEnvVar("NZBNA_URL", m_url);
|
||||
SetEnvVar("NZBNA_CATEGORY", m_category);
|
||||
SetIntEnvVar("NZBNA_PRIORITY", m_priority);
|
||||
SetIntEnvVar("NZBNA_LASTID", m_id); // deprecated
|
||||
|
||||
SetEnvVar("NZBNA_DUPEKEY", m_dupeKey);
|
||||
SetIntEnvVar("NZBNA_DUPESCORE", m_dupeScore);
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNA_DUPEMODE", dupeModeName[m_dupeMode]);
|
||||
|
||||
SetEnvVar("NZBNA_EVENT", QUEUE_EVENT_NAMES[m_event]);
|
||||
|
||||
const char* deleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD", "GOOD", "COPY", "SCAN" };
|
||||
SetEnvVar("NZBNA_DELETESTATUS", deleteStatusName[m_deleteStatus]);
|
||||
|
||||
const char* urlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
|
||||
SetEnvVar("NZBNA_URLSTATUS", urlStatusName[m_urlStatus]);
|
||||
|
||||
const char* markStatusName[] = { "NONE", "BAD", "GOOD", "SUCCESS" };
|
||||
SetEnvVar("NZBNA_MARKSTATUS", markStatusName[m_markStatus]);
|
||||
|
||||
PrepareEnvScript(&m_parameters, scriptName);
|
||||
}
|
||||
|
||||
void QueueScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->GetParameters()->SetParameter(param, value + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DIRECTORY=", 10) &&
|
||||
m_event == QueueScriptCoordinator::qeNzbDownloaded)
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->SetFinalDir(msgText + 6 + 10);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
m_markBad = true;
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName);
|
||||
nzbInfo->SetMarkStatus(NzbInfo::ksBad);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NzbInfo* nzbInfo = nullptr;
|
||||
{
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id);
|
||||
if (nzbInfo)
|
||||
{
|
||||
nzbInfo->AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nzbInfo)
|
||||
{
|
||||
ScriptController::AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QueueScriptCoordinator::InitOptions()
|
||||
{
|
||||
m_hasQueueScripts = false;
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (script.GetQueueScript())
|
||||
{
|
||||
m_hasQueueScripts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::EnqueueScript(NzbInfo* nzbInfo, EEvent event)
|
||||
{
|
||||
if (!m_hasQueueScripts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
if (event == qeNzbDownloaded)
|
||||
{
|
||||
// delete all other queued scripts for this nzb
|
||||
m_queue.erase(std::remove_if(m_queue.begin(), m_queue.end(),
|
||||
[nzbInfo](std::unique_ptr<QueueItem>& queueItem)
|
||||
{
|
||||
return queueItem->GetNzbId() == nzbInfo->GetId();
|
||||
}),
|
||||
m_queue.end());
|
||||
}
|
||||
|
||||
// respect option "EventInterval"
|
||||
time_t curTime = Util::CurrentTime();
|
||||
if (event == qeFileDownloaded &&
|
||||
(g_Options->GetEventInterval() == -1 ||
|
||||
(g_Options->GetEventInterval() > 0 && curTime - nzbInfo->GetQueueScriptTime() > 0 &&
|
||||
(int)(curTime - nzbInfo->GetQueueScriptTime()) < g_Options->GetEventInterval())))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (ScriptConfig::Script& script : g_ScriptConfig->GetScripts())
|
||||
{
|
||||
if (UsableScript(script, nzbInfo, event))
|
||||
{
|
||||
bool alreadyQueued = false;
|
||||
if (event == qeFileDownloaded)
|
||||
{
|
||||
// check if this script is already queued for this nzb
|
||||
for (QueueItem* queueItem : &m_queue)
|
||||
{
|
||||
if (queueItem->GetNzbId() == nzbInfo->GetId() && queueItem->GetScript() == &script)
|
||||
{
|
||||
alreadyQueued = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!alreadyQueued)
|
||||
{
|
||||
std::unique_ptr<QueueItem> queueItem = std::make_unique<QueueItem>(nzbInfo->GetId(), &script, event);
|
||||
if (m_curItem)
|
||||
{
|
||||
m_queue.push_back(std::move(queueItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_curItem = std::move(queueItem);
|
||||
QueueScriptController::StartScript(nzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
|
||||
}
|
||||
}
|
||||
|
||||
nzbInfo->SetQueueScriptTime(Util::CurrentTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueScriptCoordinator::UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event)
|
||||
{
|
||||
if (!script.GetQueueScript())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(script.GetQueueEvents()) && !strstr(script.GetQueueEvents(), QUEUE_EVENT_NAMES[event]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check extension scripts assigned for that nzb
|
||||
for (NzbParameter& parameter : nzbInfo->GetParameters())
|
||||
{
|
||||
const char* varname = parameter.GetName();
|
||||
if (strlen(varname) > 0 && varname[0] != '*' && varname[strlen(varname)-1] == ':' &&
|
||||
(!strcasecmp(parameter.GetValue(), "yes") ||
|
||||
!strcasecmp(parameter.GetValue(), "on") ||
|
||||
!strcasecmp(parameter.GetValue(), "1")))
|
||||
{
|
||||
BString<1024> scriptName = varname;
|
||||
scriptName[strlen(scriptName)-1] = '\0'; // remove trailing ':'
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// for URL-events the extension scripts are not assigned yet;
|
||||
// instead we take the default extension scripts for the category (or global)
|
||||
if (event == qeUrlCompleted)
|
||||
{
|
||||
const char* postScript = g_Options->GetExtensions();
|
||||
if (!Util::EmptyStr(nzbInfo->GetCategory()))
|
||||
{
|
||||
Options::Category* categoryObj = g_Options->FindCategory(nzbInfo->GetCategory(), false);
|
||||
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
|
||||
{
|
||||
postScript = categoryObj->GetExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(postScript))
|
||||
{
|
||||
Tokenizer tok(postScript, ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NzbInfo* QueueScriptCoordinator::FindNzbInfo(DownloadQueue* downloadQueue, int nzbId)
|
||||
{
|
||||
NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(nzbId);
|
||||
if (nzbInfo)
|
||||
{
|
||||
return nzbInfo;
|
||||
}
|
||||
|
||||
HistoryInfo* historyInfo = downloadQueue->GetHistory()->Find(nzbId);
|
||||
if (historyInfo)
|
||||
{
|
||||
return historyInfo->GetNzbInfo();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::CheckQueue()
|
||||
{
|
||||
if (m_stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GuardedDownloadQueue downloadQueue = DownloadQueue::Guard();
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
m_curItem.reset();
|
||||
NzbInfo* curNzbInfo = nullptr;
|
||||
Queue::iterator itCurItem;
|
||||
|
||||
for (Queue::iterator it = m_queue.begin(); it != m_queue.end(); )
|
||||
{
|
||||
std::unique_ptr<QueueItem>& queueItem = *it;
|
||||
|
||||
NzbInfo* nzbInfo = FindNzbInfo(downloadQueue, queueItem->GetNzbId());
|
||||
|
||||
// in a case this nzb must not be processed further - delete queue script from queue
|
||||
EEvent event = queueItem->GetEvent();
|
||||
bool ignoreEvent = !nzbInfo ||
|
||||
(nzbInfo->GetDeleteStatus() != NzbInfo::dsNone && event != qeNzbDeleted && event != qeNzbMarked) ||
|
||||
(nzbInfo->GetMarkStatus() == NzbInfo::ksBad && event != qeNzbMarked);
|
||||
|
||||
if (ignoreEvent)
|
||||
{
|
||||
it = m_queue.erase(it);
|
||||
if (curNzbInfo)
|
||||
{
|
||||
// process from the beginning, while "erase" invalidated "itCurItem"
|
||||
curNzbInfo = nullptr;
|
||||
it = m_queue.begin();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_curItem || queueItem->GetEvent() > m_curItem->GetEvent())
|
||||
{
|
||||
itCurItem = it;
|
||||
curNzbInfo = nzbInfo;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
if (curNzbInfo)
|
||||
{
|
||||
m_curItem = std::move(*itCurItem);
|
||||
m_queue.erase(itCurItem);
|
||||
QueueScriptController::StartScript(curNzbInfo, m_curItem->GetScript(), m_curItem->GetEvent());
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueScriptCoordinator::HasJob(int nzbId, bool* active)
|
||||
{
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
bool working = m_curItem && m_curItem->GetNzbId() == nzbId;
|
||||
if (active)
|
||||
{
|
||||
*active = working;
|
||||
}
|
||||
if (!working)
|
||||
{
|
||||
for (QueueItem* queueItem : &m_queue)
|
||||
{
|
||||
working = queueItem->GetNzbId() == nzbId;
|
||||
if (working)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
int QueueScriptCoordinator::GetQueueSize()
|
||||
{
|
||||
Guard guard(m_queueMutex);
|
||||
|
||||
int queuedCount = m_queue.size();
|
||||
if (m_curItem)
|
||||
{
|
||||
queuedCount++;
|
||||
}
|
||||
|
||||
return queuedCount;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2017 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUESCRIPT_H
|
||||
#define QUEUESCRIPT_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class QueueScriptCoordinator
|
||||
{
|
||||
public:
|
||||
enum EEvent
|
||||
{
|
||||
qeFileDownloaded, // lowest priority
|
||||
qeUrlCompleted,
|
||||
qeNzbMarked,
|
||||
qeNzbAdded,
|
||||
qeNzbNamed,
|
||||
qeNzbDownloaded,
|
||||
qeNzbDeleted // highest priority
|
||||
};
|
||||
|
||||
void Stop() { m_stopped = true; }
|
||||
void InitOptions();
|
||||
void EnqueueScript(NzbInfo* nzbInfo, EEvent event);
|
||||
void CheckQueue();
|
||||
bool HasJob(int nzbId, bool* active);
|
||||
int GetQueueSize();
|
||||
static NzbInfo* FindNzbInfo(DownloadQueue* downloadQueue, int nzbId);
|
||||
|
||||
private:
|
||||
class QueueItem
|
||||
{
|
||||
public:
|
||||
QueueItem(int nzbId, ScriptConfig::Script* script, EEvent event) :
|
||||
m_nzbId(nzbId), m_script(script), m_event(event) {}
|
||||
int GetNzbId() { return m_nzbId; }
|
||||
ScriptConfig::Script* GetScript() { return m_script; }
|
||||
EEvent GetEvent() { return m_event; }
|
||||
private:
|
||||
int m_nzbId;
|
||||
ScriptConfig::Script* m_script;
|
||||
EEvent m_event;
|
||||
};
|
||||
|
||||
typedef std::deque<std::unique_ptr<QueueItem>> Queue;
|
||||
|
||||
Queue m_queue;
|
||||
Mutex m_queueMutex;
|
||||
std::unique_ptr<QueueItem> m_curItem;
|
||||
bool m_hasQueueScripts = false;
|
||||
bool m_stopped = false;
|
||||
|
||||
bool UsableScript(ScriptConfig::Script& script, NzbInfo* nzbInfo, EEvent event);
|
||||
};
|
||||
|
||||
extern QueueScriptCoordinator* g_QueueScriptCoordinator;
|
||||
|
||||
#endif
|
||||
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ScanScript.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
class ScanScriptCheck : public NzbScriptController
|
||||
{
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script) { has |= script->GetScanScript(); }
|
||||
bool has = false;
|
||||
friend class ScanScriptController;
|
||||
};
|
||||
|
||||
|
||||
bool ScanScriptController::HasScripts()
|
||||
{
|
||||
ScanScriptCheck check;
|
||||
check.ExecuteScriptList(g_Options->GetExtensions());
|
||||
return check.has;
|
||||
}
|
||||
|
||||
void ScanScriptController::ExecuteScripts(const char* nzbFilename,
|
||||
const char* url, const char* directory, CString* nzbName, CString* category,
|
||||
int* priority, NzbParameterList* parameters, bool* addTop, bool* addPaused,
|
||||
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode)
|
||||
{
|
||||
ScanScriptController scriptController;
|
||||
scriptController.m_nzbFilename = nzbFilename;
|
||||
scriptController.m_url = url;
|
||||
scriptController.m_directory = directory;
|
||||
scriptController.m_nzbName = nzbName;
|
||||
scriptController.m_category = category;
|
||||
scriptController.m_parameters = parameters;
|
||||
scriptController.m_priority = priority;
|
||||
scriptController.m_addTop = addTop;
|
||||
scriptController.m_addPaused = addPaused;
|
||||
scriptController.m_dupeKey = dupeKey;
|
||||
scriptController.m_dupeScore = dupeScore;
|
||||
scriptController.m_dupeMode = dupeMode;
|
||||
scriptController.m_prefixLen = 0;
|
||||
|
||||
const char* extensions = g_Options->GetExtensions();
|
||||
|
||||
if (!Util::EmptyStr(*category))
|
||||
{
|
||||
Options::Category* categoryObj = g_Options->FindCategory(*category, false);
|
||||
if (categoryObj && !Util::EmptyStr(categoryObj->GetExtensions()))
|
||||
{
|
||||
extensions = categoryObj->GetExtensions();
|
||||
}
|
||||
}
|
||||
|
||||
scriptController.ExecuteScriptList(extensions);
|
||||
}
|
||||
|
||||
void ScanScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetScanScript() || !FileSystem::FileExists(m_nzbFilename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("scan-script %s for %s", script->GetName(), FileSystem::BaseFileName(m_nzbFilename));
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
m_prefixLen = strlen(script->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void ScanScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNP_FILENAME", m_nzbFilename);
|
||||
SetEnvVar("NZBNP_URL", m_url);
|
||||
SetEnvVar("NZBNP_NZBNAME", strlen(*m_nzbName) > 0 ? **m_nzbName : FileSystem::BaseFileName(m_nzbFilename));
|
||||
SetEnvVar("NZBNP_CATEGORY", *m_category);
|
||||
SetIntEnvVar("NZBNP_PRIORITY", *m_priority);
|
||||
SetIntEnvVar("NZBNP_TOP", *m_addTop ? 1 : 0);
|
||||
SetIntEnvVar("NZBNP_PAUSED", *m_addPaused ? 1 : 0);
|
||||
SetEnvVar("NZBNP_DUPEKEY", *m_dupeKey);
|
||||
SetIntEnvVar("NZBNP_DUPESCORE", *m_dupeScore);
|
||||
|
||||
const char* dupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNP_DUPEMODE", dupeModeName[*m_dupeMode]);
|
||||
|
||||
// remove trailing slash
|
||||
BString<1024> dir = m_directory;
|
||||
int len = strlen(dir);
|
||||
if (dir[len-1] == PATH_SEPARATOR)
|
||||
{
|
||||
dir[len-1] = '\0';
|
||||
}
|
||||
SetEnvVar("NZBNP_DIRECTORY", dir);
|
||||
|
||||
PrepareEnvScript(m_parameters, scriptName);
|
||||
}
|
||||
|
||||
void ScanScriptController::AddMessage(Message::EKind kind, const char* text)
|
||||
{
|
||||
const char* msgText = text + m_prefixLen;
|
||||
|
||||
if (!strncmp(msgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", msgText + 6);
|
||||
if (!strncmp(msgText + 6, "NZBNAME=", 8))
|
||||
{
|
||||
*m_nzbName = msgText + 6 + 8;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "CATEGORY=", 9))
|
||||
{
|
||||
*m_category = msgText + 6 + 9;
|
||||
g_Scanner->InitPPParameters(*m_category, m_parameters, true);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
CString param = msgText + 6 + 6;
|
||||
char* value = strchr(param, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = '\0';
|
||||
m_parameters->SetParameter(param, value + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "PRIORITY=", 9))
|
||||
{
|
||||
*m_priority = atoi(msgText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "TOP=", 4))
|
||||
{
|
||||
*m_addTop = atoi(msgText + 6 + 4) != 0;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "PAUSED=", 7))
|
||||
{
|
||||
*m_addPaused = atoi(msgText + 6 + 7) != 0;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPEKEY=", 8))
|
||||
{
|
||||
*m_dupeKey = msgText + 6 + 8;
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPESCORE=", 10))
|
||||
{
|
||||
*m_dupeScore = atoi(msgText + 6 + 10);
|
||||
}
|
||||
else if (!strncmp(msgText + 6, "DUPEMODE=", 9))
|
||||
{
|
||||
const char* dupeMode = msgText + 6 + 9;
|
||||
if (strcasecmp(dupeMode, "score") && strcasecmp(dupeMode, "all") && strcasecmp(dupeMode, "force"))
|
||||
{
|
||||
error("Invalid value \"%s\" for command \"DUPEMODE\" received from %s", dupeMode, GetInfoName());
|
||||
return;
|
||||
}
|
||||
*m_dupeMode = !strcasecmp(dupeMode, "all") ? dmAll :
|
||||
!strcasecmp(dupeMode, "force") ? dmForce : dmScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", msgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(kind, text);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCANSCRIPT_H
|
||||
#define SCANSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class ScanScriptController : public NzbScriptController
|
||||
{
|
||||
public:
|
||||
static void ExecuteScripts(const char* nzbFilename, const char* url,
|
||||
const char* directory, CString* nzbName, CString* category, int* priority,
|
||||
NzbParameterList* parameters, bool* addTop, bool* addPaused,
|
||||
CString* dupeKey, int* dupeScore, EDupeMode* dupeMode);
|
||||
static bool HasScripts();
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
virtual void AddMessage(Message::EKind kind, const char* text);
|
||||
|
||||
private:
|
||||
const char* m_nzbFilename;
|
||||
const char* m_url;
|
||||
const char* m_directory;
|
||||
CString* m_nzbName;
|
||||
CString* m_category;
|
||||
int* m_priority;
|
||||
NzbParameterList* m_parameters;
|
||||
bool* m_addTop;
|
||||
bool* m_addPaused;
|
||||
CString* m_dupeKey;
|
||||
int* m_dupeScore;
|
||||
EDupeMode* m_dupeMode;
|
||||
int m_prefixLen;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2007-2016 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "SchedulerScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
void SchedulerScriptController::StartScript(const char* param, bool externalProcess, int taskId)
|
||||
{
|
||||
std::vector<CString> argv;
|
||||
if (externalProcess && (argv = Util::SplitCommandLine(param)).empty())
|
||||
{
|
||||
error("Could not execute scheduled process-script, failed to parse command line: %s", param);
|
||||
return;
|
||||
}
|
||||
|
||||
SchedulerScriptController* scriptController = new SchedulerScriptController();
|
||||
|
||||
scriptController->m_externalProcess = externalProcess;
|
||||
scriptController->m_script = param;
|
||||
scriptController->m_taskId = taskId;
|
||||
|
||||
if (externalProcess)
|
||||
{
|
||||
scriptController->SetArgs(std::move(argv));
|
||||
}
|
||||
|
||||
scriptController->SetAutoDestroy(true);
|
||||
|
||||
scriptController->Start();
|
||||
}
|
||||
|
||||
void SchedulerScriptController::Run()
|
||||
{
|
||||
if (m_externalProcess)
|
||||
{
|
||||
ExecuteExternalProcess();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteScriptList(m_script);
|
||||
}
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* script)
|
||||
{
|
||||
if (!script->GetSchedulerScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BString<1024> taskName(" for Task%i", m_taskId);
|
||||
if (m_taskId == 0)
|
||||
{
|
||||
taskName = "";
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scheduler-script %s%s", script->GetName(), *taskName);
|
||||
|
||||
SetArgs({script->GetLocation()});
|
||||
|
||||
BString<1024> infoName("scheduler-script %s%s", script->GetName(), *taskName);
|
||||
SetInfoName(infoName);
|
||||
|
||||
SetLogPrefix(script->GetDisplayName());
|
||||
PrepareParams(script->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(nullptr);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::PrepareParams(const char* scriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBSP_TASKID", m_taskId);
|
||||
|
||||
PrepareEnvScript(nullptr, scriptName);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteExternalProcess()
|
||||
{
|
||||
info("Executing scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
|
||||
|
||||
BString<1024> infoName("scheduled process-script %s for Task%i", FileSystem::BaseFileName(GetScript()), m_taskId);
|
||||
SetInfoName(infoName);
|
||||
|
||||
BString<1024> logPrefix = FileSystem::BaseFileName(GetScript());
|
||||
if (char* ext = strrchr(logPrefix, '.')) *ext = '\0'; // strip file extension
|
||||
SetLogPrefix(logPrefix);
|
||||
|
||||
Execute();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user