mirror of
https://github.com/nzbget/nzbget.git
synced 2025-12-24 06:37:44 -05:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f07555c0d |
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.*)
|
||||
719
ArticleDownloader.cpp
Normal file
719
ArticleDownloader.cpp
Normal file
@@ -0,0 +1,719 @@
|
||||
/*
|
||||
* 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>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.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;
|
||||
|
||||
const char* ArticleDownloader::m_szJobStatus[] = { "WAITING", "RUNNING", "FINISHED", "FAILED", "DECODING", "JOINING", "NOT_FOUND", "FATAL_ERROR" };
|
||||
|
||||
ArticleDownloader::ArticleDownloader()
|
||||
{
|
||||
debug("Creating ArticleDownloader");
|
||||
|
||||
m_szResultFilename = NULL;
|
||||
m_szTempFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_pDecoder = NULL;
|
||||
m_eStatus = adUndefined;
|
||||
m_iBytes = 0;
|
||||
memset(&m_tStartTime, 0, sizeof(m_tStartTime));
|
||||
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_pDecoder)
|
||||
{
|
||||
delete m_pDecoder;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetTempFilename(const char* v)
|
||||
{
|
||||
m_szTempFilename = 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())
|
||||
{
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(m_szResultFilename, &buffer);
|
||||
if (fileExists)
|
||||
{
|
||||
// file exists from previous program's start
|
||||
info("Article %s already downloaded, skipping", m_szInfoName);
|
||||
m_semInitialized.Post();
|
||||
m_semWaited.Wait();
|
||||
SetStatus(adFinished);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info("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;
|
||||
|
||||
m_semInitialized.Post();
|
||||
m_semWaited.Wait();
|
||||
|
||||
//while (true) usleep(10); // DEBUG TEST
|
||||
|
||||
while (!IsStopped() && (retry > 0))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = adFailed;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = g_pServerPool->GetConnection(level);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
debug("m_pConnection is NULL");
|
||||
error("Serious error: Connection is NULL");
|
||||
}
|
||||
|
||||
// test connection
|
||||
bool connected = m_pConnection && m_pConnection->Connect() >= 0;
|
||||
if (connected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading!!
|
||||
Status = Download();
|
||||
}
|
||||
|
||||
if (connected)
|
||||
{
|
||||
// freeing connection allows other threads to start.
|
||||
// we doing this only if the problem was with article or group.
|
||||
// if the problem occurs by Connect() we do not free the connection,
|
||||
// to prevent starting of thousands of threads (cause each of them
|
||||
// will also free it's connection after the same connect-error).
|
||||
FreeConnection();
|
||||
}
|
||||
|
||||
if ((Status == adFailed) && ((retry > 1) || !connected) && !IsStopped())
|
||||
{
|
||||
info("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))
|
||||
{
|
||||
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("Aticle %s @ all servers failed: Article not found", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// do not count connect-errors, only article- and group-errors
|
||||
if (connected)
|
||||
{
|
||||
level++;
|
||||
if (level > iMaxLevel)
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
retry--;
|
||||
}
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
free(LevelStatus);
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
info("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Download %s failed", m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Existing ArticleDownloader-loop");
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
// at first, change group! dryan's level wants it this way... ;-)
|
||||
bool grpchanged = false;
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
grpchanged = m_pConnection->JoinGroup(*it) == 0;
|
||||
if (grpchanged)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!grpchanged)
|
||||
{
|
||||
warn("Article %s @ %s failed: Could not join group", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
// now, let's begin!
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "ARTICLE %s\r\n", m_pArticleInfo->GetMessageID());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = NULL;
|
||||
|
||||
for (int retry = 3; retry > 0; retry--)
|
||||
{
|
||||
answer = m_pConnection->Request(tmp);
|
||||
if (answer && (!strncmp(answer, "2", 1)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
warn("Article %s @ %s failed: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
return adFailed;
|
||||
}
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
warn("Article %s @ %s failed: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), answer);
|
||||
return adNotFound;
|
||||
}
|
||||
|
||||
// positive answer!
|
||||
|
||||
const char* dnfilename = m_szTempFilename;
|
||||
FILE* outfile = fopen(dnfilename, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", dnfilename);
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
gettimeofday(&m_tStartTime, 0);
|
||||
m_iBytes = 0;
|
||||
|
||||
EStatus Status = adRunning;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
// Throttle the bandwidth
|
||||
while (!IsStopped() && (g_pOptions->GetDownloadRate() > 0.0f) &&
|
||||
(g_pDownloadSpeedMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
struct _timeval tSpeedReadingStartTime;
|
||||
gettimeofday(&tSpeedReadingStartTime, 0);
|
||||
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize);
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
//detect end of article
|
||||
if ((!strcmp(line, ".\r\n")) || (!strcmp(line, ".\n")))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we meet an unexpected need for authorization?
|
||||
if (!strncmp(line, "480", 3))
|
||||
{
|
||||
m_pConnection->Authenticate();
|
||||
}
|
||||
|
||||
//detect lines starting with "." (marked as "..")
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
int wrcnt = (int)fwrite(line, 1, strlen(line), outfile);
|
||||
if (wrcnt > 0)
|
||||
{
|
||||
m_iBytes += wrcnt;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
fflush(outfile);
|
||||
fclose(outfile);
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
remove(dnfilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
warn("Unexpected end of %s", m_szInfoName);
|
||||
remove(dnfilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
// Give time to other threads. Help to avoid hangs on Asus WL500g router.
|
||||
usleep(10 * 1000);
|
||||
|
||||
SetStatus(adDecoding);
|
||||
struct _timeval StartTime, EndTime;
|
||||
gettimeofday(&StartTime, 0);
|
||||
bool OK = false;
|
||||
if (g_pOptions->GetDecoder() == Options::dcUulib)
|
||||
{
|
||||
m_pDecoder = new Decoder();
|
||||
m_pDecoder->SetKind(Decoder::dcUulib);
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
m_pDecoder = new Decoder();
|
||||
m_pDecoder->SetKind(Decoder::dcYenc);
|
||||
}
|
||||
if (m_pDecoder)
|
||||
{
|
||||
m_pDecoder->SetSrcFilename(dnfilename);
|
||||
|
||||
char tmpdestfile[1024];
|
||||
snprintf(tmpdestfile, 1024, "%s.dec", m_szResultFilename);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
|
||||
m_pDecoder->SetDestFilename(tmpdestfile);
|
||||
OK = m_pDecoder->Execute();
|
||||
if (OK)
|
||||
{
|
||||
rename(tmpdestfile, m_szResultFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
}
|
||||
if (m_pDecoder->GetArticleFilename())
|
||||
{
|
||||
m_szArticleFilename = strdup(m_pDecoder->GetArticleFilename());
|
||||
}
|
||||
delete m_pDecoder;
|
||||
m_pDecoder = NULL;
|
||||
}
|
||||
|
||||
gettimeofday(&EndTime, 0);
|
||||
remove(dnfilename);
|
||||
#ifdef WIN32
|
||||
float fDeltaTime = (float)((EndTime.time - StartTime.time) * 1000 + (EndTime.millitm - StartTime.millitm));
|
||||
#else
|
||||
float fDeltaTime = ((EndTime.tv_sec - StartTime.tv_sec) * 1000000 + (EndTime.tv_usec - StartTime.tv_usec)) / 1000.0;
|
||||
#endif
|
||||
if (OK)
|
||||
{
|
||||
info("Successfully downloaded %s", m_szInfoName);
|
||||
debug("Decode time %.1f ms", fDeltaTime);
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed", m_szInfoName);
|
||||
remove(m_szResultFilename);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
// rawmode
|
||||
rename(dnfilename, m_szResultFilename);
|
||||
info("Article %s successfully downloaded", m_szInfoName);
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
// should not occur
|
||||
error("Internal error: Decoding %s failed", m_szInfoName);
|
||||
return adFatalError;
|
||||
}
|
||||
}
|
||||
|
||||
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=%s, LastUpdateTime=%s, filename=%s", GetStatusText(), szTime, BaseFileName(GetTempFilename()));
|
||||
if (m_pDecoder)
|
||||
{
|
||||
m_pDecoder->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
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 successfuly");
|
||||
}
|
||||
|
||||
void ArticleDownloader::FreeConnection()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
m_pConnection->Disconnect();
|
||||
g_pServerPool->FreeConnection(m_pConnection);
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::CompleteFileParts()
|
||||
{
|
||||
debug("Completing file parts");
|
||||
debug("ArticleFilename: %s", m_pFileInfo->GetFilename());
|
||||
SetStatus(adJoining);
|
||||
|
||||
char ofn[1024];
|
||||
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
ofn[1024-1] = '\0';
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
m_pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
char InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
// Ensure the DstDir is created
|
||||
mkdir(m_pFileInfo->GetDestDir(), S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
info("Moving articles for %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Joining articles for %s", InfoFilename);
|
||||
}
|
||||
|
||||
// prevent overwriting existing files
|
||||
struct stat statbuf;
|
||||
int dupcount = 0;
|
||||
while (!stat(ofn, &statbuf))
|
||||
{
|
||||
dupcount++;
|
||||
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->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';
|
||||
remove(tmpdestfile);
|
||||
|
||||
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
outfile = fopen(tmpdestfile, "w+");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s!", tmpdestfile);
|
||||
SetStatus(adFinished);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
mkdir(ofn, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
}
|
||||
|
||||
bool complete = true;
|
||||
int iBrokenCount = 0;
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
char* 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->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
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();
|
||||
usleep(10); // give time to other threads
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete = false;
|
||||
iBrokenCount++;
|
||||
info("Could not find file %s. Status is broken", fn);
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
const char* fn = pa->GetResultFilename();
|
||||
char dstFileName[1024];
|
||||
snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
|
||||
dstFileName[1024-1] = '\0';
|
||||
rename(fn, dstFileName);
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
fclose(outfile);
|
||||
rename(tmpdestfile, ofn);
|
||||
}
|
||||
|
||||
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';
|
||||
bool OK = rename(ofn, brokenfn) == 0;
|
||||
if (OK)
|
||||
{
|
||||
info("Renaming broken file from %s to %s", ofn, brokenfn);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Not renaming broken file %s", ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
fprintf(file, "%s (%i/%i)\n", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size());
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
warn("%s is incomplete!", InfoFilename);
|
||||
}
|
||||
|
||||
SetStatus(adFinished);
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Terminate()
|
||||
{
|
||||
NNTPConnection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->Cancel();
|
||||
g_pServerPool->FreeConnection(pConnection);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void ArticleDownloader::WaitInit()
|
||||
{
|
||||
// waiting until the download becomes ready to catch connection,
|
||||
// but no longer then 30 seconds
|
||||
m_semInitialized.TimedWait(30000);
|
||||
m_semWaited.Post();
|
||||
}
|
||||
120
ArticleDownloader.h
Normal file
120
ArticleDownloader.h
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 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,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
adNotFound,
|
||||
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;
|
||||
time_t m_tLastUpdateTime;
|
||||
Decoder* m_pDecoder;
|
||||
Semaphore m_semInitialized;
|
||||
Semaphore m_semWaited;
|
||||
static const char* m_szJobStatus[];
|
||||
#ifdef WIN32
|
||||
struct _timeb m_tStartTime;
|
||||
#else
|
||||
struct timeval m_tStartTime;
|
||||
#endif
|
||||
int m_iBytes;
|
||||
|
||||
EStatus Download();
|
||||
void FreeConnection();
|
||||
|
||||
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; }
|
||||
const char* GetStatusText() { return m_szJobStatus[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);
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void SetInfoName(const char* v);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void CompleteFileParts();
|
||||
void WaitInit();
|
||||
#ifdef WIN32
|
||||
struct _timeb* GetStartTime() { return &m_tStartTime; }
|
||||
#else
|
||||
struct timeval* GetStartTime() { return &m_tStartTime; }
|
||||
#endif
|
||||
int GetBytes() { return m_iBytes; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
};
|
||||
|
||||
#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.
|
||||
|
||||
186
ColoredFrontend.cpp
Normal file
186
ColoredFrontend.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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';
|
||||
|
||||
if (m_fCurrentDownloadSpeed > 0.0f)
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long int)(m_fCurrentDownloadSpeed * 1024));
|
||||
int h = 0;
|
||||
int m = 0;
|
||||
int s = 0;
|
||||
while (remain_sec > 3600)
|
||||
{
|
||||
h++;
|
||||
remain_sec -= 3600;
|
||||
}
|
||||
|
||||
while (remain_sec > 60)
|
||||
{
|
||||
m++;
|
||||
remain_sec -= 60;
|
||||
}
|
||||
|
||||
s = remain_sec;
|
||||
|
||||
sprintf(timeString, "(~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
}
|
||||
|
||||
const char* szPause[] = { "Paused", "" };
|
||||
int iPauseIdx = m_bPause ? 0 : 1;
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
{
|
||||
sprintf(szDownloadLimit, "Limit %.0f KB/S", m_fDownloadLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
szDownloadLimit[0] = 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
char* szControlSeq = "";
|
||||
#else
|
||||
printf("\033[s");
|
||||
char* szControlSeq = "\033[K";
|
||||
#endif
|
||||
snprintf(tmp, 1024, "%d threads running, %.0f KB/s, %.2f MB remaining %s %s %s%s\n",
|
||||
m_iThreadCount, m_fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
|
||||
timeString, szPause[iPauseIdx], szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
}
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
const char* msg = pMessage->GetText();
|
||||
#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;
|
||||
}
|
||||
SetConsoleTextAttribute(m_hConsole, 7);
|
||||
printf(" %s\n", msg);
|
||||
#else
|
||||
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;
|
||||
}
|
||||
#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
|
||||
507
Connection.cpp
Normal file
507
Connection.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
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_bCanceling = false;
|
||||
m_iTimeout = 60;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Bind()
|
||||
{
|
||||
debug("Binding");
|
||||
|
||||
if (m_eStatus == csListening)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iRes = DoBind();
|
||||
|
||||
m_eStatus = csListening;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::WriteLine(char* line)
|
||||
{
|
||||
//debug("Connection::write(char* line)");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = DoWriteLine(line);
|
||||
|
||||
if (iRes == EOF)
|
||||
Connection::DoDisconnect();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Send(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)
|
||||
{
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* res = DoReadLine(pBuffer, iSize);
|
||||
|
||||
if (res == NULL)
|
||||
Connection::DoDisconnect();
|
||||
|
||||
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;
|
||||
// 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;
|
||||
#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);
|
||||
#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(char* szText)
|
||||
{
|
||||
//debug("Connection::doWrite()");
|
||||
return send(m_iSocket, szText, strlen(szText), 0);
|
||||
}
|
||||
|
||||
char* Connection::DoReadLine(char* pBuffer, int iSize)
|
||||
{
|
||||
//debug( "Connection::DoReadLine()" );
|
||||
char* pBufPtr = pBuffer;
|
||||
iSize--; // for trailing '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, ReadBufLen, 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 = p - szBufPtr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = iBufAvail;
|
||||
}
|
||||
|
||||
if (len > iSize)
|
||||
{
|
||||
len = iSize;
|
||||
}
|
||||
|
||||
memcpy(pBufPtr, szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
szBufPtr += len;
|
||||
iBufAvail -= len;
|
||||
|
||||
if (p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
iSize--;
|
||||
}
|
||||
*pBufPtr = '\0';
|
||||
|
||||
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
|
||||
m_szBufPtr = szBufPtr; // copy back to member
|
||||
|
||||
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;
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
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_bCanceling)
|
||||
{
|
||||
ReportError("Could not accept connection", NULL, 0);
|
||||
}
|
||||
|
||||
return iSocket;
|
||||
}
|
||||
|
||||
void Connection::Cancel()
|
||||
{
|
||||
debug("Cancelling connection");
|
||||
m_bCanceling = true;
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
int r = shutdown(m_iSocket, SHUT_RDWR);
|
||||
if (r == -1)
|
||||
{
|
||||
ReportError("Could not shutdown connection", NULL, 0);
|
||||
}
|
||||
m_eStatus = csCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
#else
|
||||
const char* szErrMsg = hstrerror(ErrCode);
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
#endif
|
||||
}
|
||||
84
Connection.h
Normal file
84
Connection.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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;
|
||||
static const int ReadBufLen = 1024;
|
||||
char m_szReadBuf[ReadBufLen + 1];
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
bool m_bCanceling;
|
||||
int m_iTimeout;
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int Bind();
|
||||
int Send(char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize);
|
||||
int WriteLine(char* text);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
|
||||
protected:
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(char* text);
|
||||
char* DoReadLine(char* pBuffer, int iSize);
|
||||
SOCKET DoAccept();
|
||||
};
|
||||
|
||||
#endif
|
||||
383
Decoder.cpp
Normal file
383
Decoder.cpp
Normal file
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
#ifdef ENABLE_UULIB
|
||||
#ifndef PROTOTYPES
|
||||
#define PROTOTYPES
|
||||
#endif
|
||||
#include <uudeview.h>
|
||||
#endif
|
||||
|
||||
//#define USEEXTERNALDECODER // not working
|
||||
//#define DEBUGDECODER
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
int g_iDecoderID = 0;
|
||||
#endif
|
||||
|
||||
Mutex Decoder::m_mutexDecoder;
|
||||
unsigned int Decoder::crc_tab[256];
|
||||
|
||||
void Decoder::Init()
|
||||
{
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
void Decoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
|
||||
Decoder::Decoder()
|
||||
{
|
||||
debug("Creating Decoder");
|
||||
|
||||
m_szSrcFilename = NULL;
|
||||
m_szDestFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_eKind = dcYenc;
|
||||
m_iDebugStatus = 0;
|
||||
m_iDebugLines = 0;
|
||||
}
|
||||
|
||||
Decoder::~ Decoder()
|
||||
{
|
||||
debug("Destroying Decoder");
|
||||
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::Execute()
|
||||
{
|
||||
if (m_eKind == dcUulib)
|
||||
{
|
||||
return DecodeUulib();
|
||||
}
|
||||
else
|
||||
{
|
||||
return DecodeYenc();
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::DecodeUulib()
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
#ifndef ENABLE_UULIB
|
||||
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
|
||||
#else
|
||||
|
||||
m_mutexDecoder.Lock();
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
debug("Decoding ID %i (%s)", g_iDecoderID, szSrcFilename);
|
||||
#endif
|
||||
|
||||
#ifndef USEEXTERNALDECODER
|
||||
UUInitialize();
|
||||
|
||||
UUSetOption(UUOPT_DESPERATE, 1, NULL);
|
||||
// UUSetOption(UUOPT_DUMBNESS,1,NULL);
|
||||
// UUSetOption( UUOPT_SAVEPATH, 1, szDestDir );
|
||||
|
||||
UULoadFile((char*) m_szSrcFilename, NULL, 0);
|
||||
|
||||
// choose right attachment
|
||||
|
||||
uulist* attachment = NULL;
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
uulist* att_tmp = UUGetFileListItem(i);
|
||||
|
||||
if (!att_tmp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((att_tmp) && (att_tmp->haveparts))
|
||||
{
|
||||
if (!attachment)
|
||||
{
|
||||
attachment = att_tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
//f**k, multiple attachments!? Can't handle this.
|
||||
attachment = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// okay, we got only one attachment, perfect!
|
||||
if ((attachment->haveparts) && (attachment->haveparts[0])) // && (!attachment->haveparts[1])) FUCK UULIB
|
||||
{
|
||||
int r = UUDecodeFile(attachment, (char*)m_szDestFilename);
|
||||
|
||||
if (r == UURET_OK)
|
||||
{
|
||||
// we did it!
|
||||
res = true;
|
||||
m_szArticleFilename = strdup(attachment->filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of parts!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of attachments!\n");
|
||||
}
|
||||
|
||||
UUCleanUp();
|
||||
#else
|
||||
execl("/usr/local/bin", "uudeview", szSrcFilename, szDestFilename);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
debug("Finished decoding ID %i (%s)", g_iDecoderID++, szDestFilename);
|
||||
#endif
|
||||
|
||||
m_mutexDecoder.Unlock();
|
||||
|
||||
#endif // ENABLE_UULIB
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Very primitive (but fast) implementation of yEnc-Decoder
|
||||
*/
|
||||
bool Decoder::DecodeYenc()
|
||||
{
|
||||
FILE* infile = fopen(m_szSrcFilename, "r");
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file \"%s\"", m_szSrcFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* outfile = fopen(m_szDestFilename, "w");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file \"%s\"", m_szDestFilename);
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int MAX_LINE_LEN = 1024;
|
||||
char buffer[MAX_LINE_LEN];
|
||||
bool body = false;
|
||||
bool end = false;
|
||||
unsigned long expectedCRC = 0;
|
||||
unsigned long calculatedCRC = 0xFFFFFFFF;
|
||||
m_iDebugStatus = 1;
|
||||
bool eof = !fgets(buffer, sizeof(buffer), infile);
|
||||
m_iDebugLines++;
|
||||
m_iDebugStatus = 2;
|
||||
while (!eof)
|
||||
{
|
||||
if (body)
|
||||
{
|
||||
if (strstr(buffer, "=yend size="))
|
||||
{
|
||||
end = true;
|
||||
m_iDebugStatus = 3;
|
||||
char* pc = strstr(buffer, "pcrc32=");
|
||||
if (pc)
|
||||
{
|
||||
pc += 7; //=strlen("pcrc32=")
|
||||
expectedCRC = strtoul(pc, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_iDebugStatus = 4;
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (*iptr)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
*optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
*optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
}
|
||||
m_iDebugStatus = 5;
|
||||
calculatedCRC = crc32m(calculatedCRC, (unsigned char *)buffer, optr - buffer);
|
||||
fwrite(buffer, 1, optr - buffer, outfile);
|
||||
m_iDebugStatus = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(buffer, "=ypart begin="))
|
||||
{
|
||||
m_iDebugStatus = 7;
|
||||
body = true;
|
||||
}
|
||||
else if (strstr(buffer, "=ybegin part="))
|
||||
{
|
||||
m_iDebugStatus = 8;
|
||||
char* pb = strstr(buffer, "name=");
|
||||
if (pb)
|
||||
{
|
||||
m_iDebugStatus = 9;
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
m_iDebugStatus = 10;
|
||||
}
|
||||
m_iDebugStatus = 11;
|
||||
}
|
||||
}
|
||||
m_iDebugStatus = 12;
|
||||
eof = !fgets(buffer, sizeof(buffer), infile);
|
||||
m_iDebugStatus = 13;
|
||||
m_iDebugLines++;
|
||||
}
|
||||
m_iDebugStatus = 14;
|
||||
|
||||
calculatedCRC ^= 0xFFFFFFFF;
|
||||
|
||||
debug("Expected pcrc32=%x", expectedCRC);
|
||||
debug("Calculated pcrc32=%x", calculatedCRC);
|
||||
if (expectedCRC != calculatedCRC)
|
||||
{
|
||||
warn("CRC-Error for \"%s\"", m_szDestFilename);
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
fclose(outfile);
|
||||
|
||||
return body && end;
|
||||
}
|
||||
|
||||
/* 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 Decoder::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 Decoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
{
|
||||
register unsigned long crc;
|
||||
unsigned long i;
|
||||
|
||||
crc = startCrc;
|
||||
for (i = 0; i < length; i++)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void Decoder::LogDebugInfo()
|
||||
{
|
||||
debug(" Decoder: status=%i, lines=%i, filename=%s, ArticleFileName=%s",
|
||||
m_iDebugStatus, m_iDebugLines, BaseFileName(m_szSrcFilename), m_szArticleFilename);
|
||||
}
|
||||
72
Decoder.h
Normal file
72
Decoder.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
//#define DECODER_INTERNAL_FGETS
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
dcUulib,
|
||||
dcYenc
|
||||
};
|
||||
|
||||
private:
|
||||
static Mutex m_mutexDecoder;
|
||||
static unsigned int crc_tab[256];
|
||||
EKind m_eKind;
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
int m_iDebugStatus;
|
||||
int m_iDebugLines;
|
||||
|
||||
bool DecodeUulib();
|
||||
bool DecodeYenc();
|
||||
static void crc32gentab();
|
||||
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
~Decoder();
|
||||
bool Execute();
|
||||
void SetKind(EKind eKind) { m_eKind = eKind; }
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void LogDebugInfo();
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
};
|
||||
|
||||
#endif
|
||||
381
DiskState.cpp
Normal file
381
DiskState.cpp
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
/* 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.
|
||||
* If parameter "OnlyOrder" is set to true, only the file "queue" will
|
||||
* be written to disk (It useful, if only the order of files in queue was changed).
|
||||
*/
|
||||
bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
|
||||
{
|
||||
debug("Saving queue to disk");
|
||||
|
||||
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, "nzbget diskstate file version 1\n");
|
||||
|
||||
int cnt = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetDeleted())
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
|
||||
if (!OnlyOrder)
|
||||
{
|
||||
SaveFileInfo(pFileInfo, fileName);
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (cnt == 0)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::Load(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Loading queue from disk");
|
||||
|
||||
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, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) != EOF)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
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->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
|
||||
|
||||
fprintf(outfile, "%lu,%lu\n", (unsigned long)(pFileInfo->GetSize() >> 32), (unsigned long)(pFileInfo->GetSize()));
|
||||
|
||||
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::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
|
||||
{
|
||||
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'
|
||||
pFileInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetFilename(buf);
|
||||
|
||||
int iFilenameConfirmed;
|
||||
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
||||
pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
|
||||
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'
|
||||
pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::Discard()
|
||||
{
|
||||
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, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) == 2)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not discard diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
if (res)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::Exists()
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(fileName, &buffer);
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo)
|
||||
{
|
||||
// delete diskstate-file
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return Save(pDownloadQueue, true);
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec");
|
||||
if (!del)
|
||||
{
|
||||
int id, part;
|
||||
if (sscanf(filename, "%i.%i", &id, &part) == 2)
|
||||
{
|
||||
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);
|
||||
}
|
||||
46
DiskState.h
Normal file
46
DiskState.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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 DISKSTATE_H
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
|
||||
public:
|
||||
bool Exists();
|
||||
bool Save(DownloadQueue* pDownloadQueue, bool OnlyOrder);
|
||||
bool Load(DownloadQueue* pDownloadQueue);
|
||||
bool Discard();
|
||||
bool DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
272
DownloadInfo.cpp
Normal file
272
DownloadInfo.cpp
Normal file
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* 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 <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int FileInfo::m_iIDGen = 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_szDestDir = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
FileInfo::~ FileInfo()
|
||||
{
|
||||
debug("Destroying FileInfo");
|
||||
|
||||
if (m_szSubject)
|
||||
{
|
||||
free(m_szSubject);
|
||||
}
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
|
||||
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Articles.clear();
|
||||
|
||||
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_Groups.clear();
|
||||
}
|
||||
|
||||
void FileInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetSubject(const char* szSubject)
|
||||
{
|
||||
m_szSubject = strdup(szSubject);
|
||||
}
|
||||
|
||||
void FileInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void FileInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(m_szNZBFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void FileInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
{
|
||||
strncpy(szBuffer, BaseFileName(szNZBFilename), iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
if (char* p = strrchr(szBuffer, '.')) *p = '\0';
|
||||
}
|
||||
|
||||
void FileInfo::ParseSubject()
|
||||
{
|
||||
char* fnstart = strstr(m_szSubject, "\"");
|
||||
char* fnend = NULL;
|
||||
if (fnstart)
|
||||
{
|
||||
fnstart++;
|
||||
fnend = strstr(fnstart, "\"");
|
||||
}
|
||||
if (fnend)
|
||||
{
|
||||
char fn[1024];
|
||||
strncpy(fn, fnstart, fnend - fnstart);
|
||||
fn[fnend - fnstart] = '\0';
|
||||
m_szFilename = strdup(fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", m_szSubject);
|
||||
m_szFilename = strdup(m_szSubject);
|
||||
}
|
||||
|
||||
//replace bad chars in filename
|
||||
char* p = m_szFilename;
|
||||
while (*p)
|
||||
{
|
||||
if (strchr("\\/:*?\"><'\n\r\t", *p))
|
||||
{
|
||||
*p = '_';
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::BuildDestDirName(const char* szNZBFilename)
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// wipe out certain structure
|
||||
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
// wipe out ".nzb"
|
||||
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), postname);
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
m_szDestDir = strdup(szBuffer);
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe()
|
||||
{
|
||||
debug("Checking if the file was already downloaded or queued");
|
||||
|
||||
struct stat buffer;
|
||||
char fileName[1024];
|
||||
bool exists = false;
|
||||
|
||||
snprintf(fileName, 1024, "%s%c%s", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
if (!exists)
|
||||
{
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
}
|
||||
|
||||
return exists;
|
||||
}
|
||||
125
DownloadInfo.h
Normal file
125
DownloadInfo.h
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* 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 DOWNLOADINFO_H
|
||||
#define DOWNLOADINFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
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;
|
||||
Articles m_Articles;
|
||||
Groups m_Groups;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szSubject;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
Articles* GetArticles() { return &m_Articles; }
|
||||
Groups* GetGroups() { return &m_Groups; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetSubject() { return m_szSubject; }
|
||||
void SetSubject(const char* szSubject);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
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; }
|
||||
void BuildDestDirName(const char* szNZBFilename);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void ParseSubject();
|
||||
bool IsDupe();
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
#endif
|
||||
446
Frontend.cpp
Normal file
446
Frontend.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/*
|
||||
* 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 <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "RemoteClient.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
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_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
printf("Unable to send request to nzbserver 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();
|
||||
}
|
||||
}
|
||||
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(EEditAction eAction, int iEntry)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int ID = 0;
|
||||
bool bPause = false;
|
||||
if (iEntry >= 0 && iEntry < (int)pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iEntry];
|
||||
ID = pFileInfo->GetID();
|
||||
bPause = !pFileInfo->GetPaused();
|
||||
}
|
||||
UnlockQueue();
|
||||
|
||||
if (ID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
switch (eAction)
|
||||
{
|
||||
case eaPauseUnpause:
|
||||
return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID, ID);
|
||||
case eaDelete:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID, ID);
|
||||
case eaMoveUp:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID, ID);
|
||||
case eaMoveDown:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID, ID);
|
||||
case eaMoveTop:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID, ID);
|
||||
case eaMoveBottom:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID, ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (eAction)
|
||||
{
|
||||
case eaPauseUnpause:
|
||||
return g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, bPause);
|
||||
case eaDelete:
|
||||
return g_pQueueCoordinator->EditQueueDeleteEntry(ID);
|
||||
case eaMoveUp:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1, false);
|
||||
case eaMoveDown:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1, false);
|
||||
case eaMoveTop:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
|
||||
case eaMoveBottom:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1000000, true);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
|
||||
pMessageBase->m_iType = iRequest;
|
||||
pMessageBase->m_iSize = 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, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = m_iNeededLogEntries;
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = 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
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
if (connection.Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
|
||||
if (!connection.RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
{
|
||||
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
|
||||
|
||||
Message* pMessage = new Message(pLogAnswer->m_iID, (Message::EKind)pLogAnswer->m_iKind, pLogAnswer->m_tTime, szText);
|
||||
m_RemoteMessages.push_back(pMessage);
|
||||
|
||||
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + 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, NZBMessageRequest::eRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = m_bFileList;
|
||||
ListRequest.m_bServerState = m_bSummary;
|
||||
|
||||
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
if (connection.Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
|
||||
if (!connection.RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ListRequestAnswer.m_bServerPaused;
|
||||
m_lRemainingSize = ListRequestAnswer.m_lRemainingSize;
|
||||
m_fCurrentDownloadSpeed = ListRequestAnswer.m_fDownloadRate;
|
||||
m_fDownloadLimit = ListRequestAnswer.m_fDownloadLimit;
|
||||
m_iThreadCount = ListRequestAnswer.m_iThreadCount;
|
||||
}
|
||||
|
||||
if (m_bFileList && ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
{
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen;
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen + pListAnswer->m_iFilenameLen;
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(pListAnswer->m_iID);
|
||||
pFileInfo->SetSize(pListAnswer->m_iFileSize);
|
||||
pFileInfo->SetRemainingSize(pListAnswer->m_iRemainingSize);
|
||||
pFileInfo->SetPaused(pListAnswer->m_bPaused);
|
||||
pFileInfo->SetNZBFilename(szNZBFilename);
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(pListAnswer->m_bFilenameConfirmed);
|
||||
pFileInfo->SetDestDir(szDestDir);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
|
||||
pListAnswer->m_iFilenameLen + 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 iIDFrom, int iIDTo)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo);
|
||||
}
|
||||
89
Frontend.h
Normal file
89
Frontend.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 FRONTEND_H
|
||||
#define FRONTEND_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
public:
|
||||
enum EEditAction
|
||||
{
|
||||
eaPauseUnpause,
|
||||
eaDelete,
|
||||
eaMoveUp,
|
||||
eaMoveDown,
|
||||
eaMoveTop,
|
||||
eaMoveBottom
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
float m_fDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
Log::Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNZBMessageBase* 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(EEditAction eAction, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
|
||||
|
||||
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.
|
||||
|
||||
334
Log.cpp
Normal file
334
Log.cpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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 <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
|
||||
struct stat buffer;
|
||||
m_bExtraDebug = !stat("extradebug", &buffer);
|
||||
#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, BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, 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 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("%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());
|
||||
}
|
||||
118
Log.h
Normal file
118
Log.h
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 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
|
||||
};
|
||||
|
||||
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, ...);
|
||||
#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
|
||||
130
LoggableFrontend.cpp
Normal file
130
LoggableFrontend.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 "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");
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
|
||||
BeforeExit();
|
||||
|
||||
debug("Exiting LoggableFrontend-loop");
|
||||
}
|
||||
|
||||
void LoggableFrontend::Update()
|
||||
{
|
||||
if (!PrepareData())
|
||||
{
|
||||
FreeData();
|
||||
return;
|
||||
}
|
||||
|
||||
BeforePrint();
|
||||
|
||||
Log::Messages* pMessages = LockMessages();
|
||||
if (!pMessages->empty())
|
||||
{
|
||||
Message* pFirstMessage = pMessages->front();
|
||||
int iStart = m_iNeededLogFirstID - pFirstMessage->GetID() + 1;
|
||||
if (iStart < 0)
|
||||
{
|
||||
PrintSkip();
|
||||
iStart = 0;
|
||||
}
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
PrintMessage((*pMessages)[i]);
|
||||
m_iNeededLogFirstID = (*pMessages)[i]->GetID();
|
||||
}
|
||||
}
|
||||
UnlockMessages();
|
||||
|
||||
PrintStatus();
|
||||
|
||||
FreeData();
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
const char* msg = pMessage->GetText();
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
fprintf(stdout, "[DEBUG] %s\n", msg);
|
||||
break;
|
||||
case Message::mkError:
|
||||
fprintf(stdout, "[ERROR] %s\n", msg);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
fprintf(stdout, "[WARNING] %s\n", msg);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
fprintf(stdout, "[INFO] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintSkip()
|
||||
{
|
||||
fprintf(stdout, ".....\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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
|
||||
564
Makefile.am
564
Makefile.am
@@ -1,554 +1,18 @@
|
||||
#
|
||||
# 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 RemoteServer.cpp RemoteServer.h \
|
||||
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
|
||||
RemoteClient.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 ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
|
||||
nzbget.cpp nzbget.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 \
|
||||
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 {} \;
|
||||
|
||||
|
||||
2123
Makefile.in
2123
Makefile.in
File diff suppressed because it is too large
Load Diff
187
MessageBase.h
Normal file
187
MessageBase.h
Normal file
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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 MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6201; // = "nzb"-version-1
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
// The pack-directive prevents aligning of structs.
|
||||
// This makes them more portable and allows to use together servers and clients
|
||||
// compiled on different cpu architectures
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
namespace NZBMessageRequest
|
||||
{
|
||||
enum
|
||||
{
|
||||
eRequestDownload = 1,
|
||||
eRequestPauseUnpause,
|
||||
eRequestList,
|
||||
eRequestSetDownloadRate,
|
||||
eRequestDumpDebug,
|
||||
eRequestEditQueue,
|
||||
eRequestLog,
|
||||
eRequestShutdown
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
enum
|
||||
{
|
||||
eActionMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eActionMoveTop, // move to top of queue
|
||||
eActionMoveBottom, // move to bottom of queue
|
||||
eActionPause, // pause
|
||||
eActionResume, // resume (unpause)
|
||||
eActionDelete // delete
|
||||
};
|
||||
}
|
||||
|
||||
// The basic NZBMessageBase struct
|
||||
struct SNZBMessageBase
|
||||
{
|
||||
int32_t m_iId; // Id must be 'nzbg' in integer-value
|
||||
int32_t m_iType; // message type, must be > 0
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
char m_szPassword[ NZBREQUESTPASSWORDSIZE ]; // Password needs to be in every request
|
||||
};
|
||||
|
||||
// A download request
|
||||
struct SNZBDownloadRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[ NZBREQUESTFILENAMESIZE ];
|
||||
int32_t m_bAddFirst;
|
||||
int32_t m_iTrailingDataLength;
|
||||
};
|
||||
|
||||
// A list request
|
||||
struct SNZBListRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList;
|
||||
int32_t m_bServerState;
|
||||
};
|
||||
|
||||
// A list request-answer
|
||||
struct SNZBListRequestAnswer
|
||||
{
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBListRequestAnswerEntry-struct
|
||||
long long m_lRemainingSize;
|
||||
float m_fDownloadRate;
|
||||
float m_fDownloadLimit;
|
||||
int32_t m_bServerPaused;
|
||||
int32_t m_iThreadCount;
|
||||
int32_t m_iNrTrailingEntries;
|
||||
int32_t m_iTrailingDataLength;
|
||||
};
|
||||
|
||||
// A list request-answer entry
|
||||
struct SNZBListRequestAnswerEntry
|
||||
{
|
||||
int32_t m_iNZBFilenameLen;
|
||||
int32_t m_iSubjectLen;
|
||||
int32_t m_iFilenameLen;
|
||||
int32_t m_iDestDirLen;
|
||||
int32_t m_iFileSize;
|
||||
int32_t m_bFilenameConfirmed;
|
||||
int32_t m_iRemainingSize;
|
||||
int32_t m_iID;
|
||||
int32_t m_bPaused;
|
||||
//char m_szNZBFilename[0]; // variable sized
|
||||
//char m_szSubject[0]; // variable sized
|
||||
//char m_szFilename[0]; // variable sized
|
||||
//char m_szDestDir[0]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
SNZBMessageBase 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 request-answer
|
||||
struct SNZBLogRequestAnswer
|
||||
{
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBLogRequestAnswerEntry-struct
|
||||
int32_t m_iNrTrailingEntries;
|
||||
int32_t m_iTrailingDataLength;
|
||||
};
|
||||
|
||||
// A log request-answer entry
|
||||
struct SNZBLogRequestAnswerEntry
|
||||
{
|
||||
int32_t m_iTextLen;
|
||||
int32_t m_iID;
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
time_t m_tTime;
|
||||
//char m_szText[0]; // variable sized
|
||||
};
|
||||
|
||||
// A Pause/Unpause request
|
||||
struct SNZBPauseUnpauseRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // The value g_bPause should be set to
|
||||
};
|
||||
|
||||
// Request setting the download rate
|
||||
struct SNZBSetDownloadRateRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
float m_fDownloadRate;
|
||||
};
|
||||
|
||||
// A download request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iIDFrom; // ID of the first file in the range
|
||||
int32_t m_iIDTo; // ID of the last file in the range, not used yet, must be same as m_iIDFrom
|
||||
int32_t m_iAction; // action to be done, see later
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
};
|
||||
|
||||
// Request dumping of debug info
|
||||
struct SNZBDumpDebugRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iLevel; // Future use
|
||||
};
|
||||
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
975
NCursesFrontend.cpp
Normal file
975
NCursesFrontend.cpp
Normal file
@@ -0,0 +1,975 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#ifndef DISABLE_CURSES
|
||||
|
||||
#ifdef HAVE_NCURSES_H
|
||||
#include <ncurses.h>
|
||||
#endif
|
||||
#ifdef HAVE_NCURSES_NCURSES_H
|
||||
#include <ncurses/ncurses.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NCursesFrontend.h"
|
||||
|
||||
#ifdef HAVE_CURSES_H
|
||||
// curses.h header must be included last to avoid problems on Solaris
|
||||
// (and possibly other systems, that uses curses.h (not ncurses.h)
|
||||
#include <curses.h>
|
||||
// "#undef erase" is neccessary on Solaris
|
||||
#undef erase
|
||||
#endif
|
||||
|
||||
extern void ExitProc();
|
||||
|
||||
static const int NCURSES_COLORPAIR_TEXT = 1;
|
||||
static const int NCURSES_COLORPAIR_INFO = 2;
|
||||
static const int NCURSES_COLORPAIR_WARNING = 3;
|
||||
static const int NCURSES_COLORPAIR_ERROR = 4;
|
||||
static const int NCURSES_COLORPAIR_DEBUG = 5;
|
||||
static const int NCURSES_COLORPAIR_STATUS = 6;
|
||||
static const int NCURSES_COLORPAIR_KEYBAR = 7;
|
||||
static const int NCURSES_COLORPAIR_INFOLINE = 8;
|
||||
static const int NCURSES_COLORPAIR_TEXTHIGHL = 9;
|
||||
static const int NCURSES_COLORPAIR_CURSOR = 10;
|
||||
|
||||
static const int MAX_SCREEN_WIDTH = 512;
|
||||
|
||||
#ifdef WIN32
|
||||
static const int COLOR_BLACK = 0;
|
||||
static const int COLOR_BLUE = FOREGROUND_BLUE;
|
||||
static const int COLOR_RED = FOREGROUND_RED;
|
||||
static const int COLOR_GREEN = FOREGROUND_GREEN;
|
||||
static const int COLOR_WHITE = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
static const int COLOR_MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE;
|
||||
static const int COLOR_CYAN = FOREGROUND_BLUE | FOREGROUND_GREEN;
|
||||
static const int COLOR_YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
|
||||
static const int READKEY_EMPTY = 0;
|
||||
|
||||
#define KEY_DOWN VK_DOWN
|
||||
#define KEY_UP VK_UP
|
||||
#define KEY_PPAGE VK_PRIOR
|
||||
#define KEY_NPAGE VK_NEXT
|
||||
#define KEY_END VK_END
|
||||
#define KEY_HOME VK_HOME
|
||||
#define KEY_BACKSPACE VK_BACK
|
||||
|
||||
#else
|
||||
|
||||
static const int READKEY_EMPTY = ERR;
|
||||
|
||||
#endif
|
||||
|
||||
NCursesFrontend::NCursesFrontend()
|
||||
{
|
||||
m_iScreenHeight = 0;
|
||||
m_iScreenWidth = 0;
|
||||
m_iInputNumberIndex = 0;
|
||||
m_eInputMode = eNormal;
|
||||
m_bSummary = true;
|
||||
m_bFileList = true;
|
||||
m_iNeededLogEntries = 0;
|
||||
m_iSkipUpdateData = 0;
|
||||
m_iQueueWinTop = 0;
|
||||
m_iQueueWinHeight = 0;
|
||||
m_iQueueWinClientHeight = 0;
|
||||
m_iMessagesWinTop = 0;
|
||||
m_iMessagesWinHeight = 0;
|
||||
m_iMessagesWinClientHeight = 0;
|
||||
m_iSelectedQueueEntry = 0;
|
||||
m_iQueueScrollOffset = 0;
|
||||
m_bShowNZBname = true;
|
||||
m_QueueWindowPercentage = 0.5f;
|
||||
|
||||
// Setup curses
|
||||
#ifdef WIN32
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
m_pScreenBuffer = NULL;
|
||||
m_pOldScreenBuffer = NULL;
|
||||
m_ColorAttr.clear();
|
||||
|
||||
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
|
||||
GetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
||||
ConsoleCursorInfo.bVisible = false;
|
||||
SetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
SetConsoleTitle("NZBGet - remote mode");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConsoleTitle("NZBGet");
|
||||
}
|
||||
|
||||
m_bUseColor = true;
|
||||
#else
|
||||
m_pWindow = initscr();
|
||||
if (m_pWindow == NULL)
|
||||
{
|
||||
printf("ERROR: m_pWindow == NULL\n");
|
||||
exit(-1);
|
||||
}
|
||||
keypad(stdscr, true);
|
||||
nodelay((WINDOW*)m_pWindow, true);
|
||||
noecho();
|
||||
curs_set(0);
|
||||
m_bUseColor = has_colors();
|
||||
#endif
|
||||
|
||||
if (m_bUseColor)
|
||||
{
|
||||
#ifndef WIN32
|
||||
start_color();
|
||||
#endif
|
||||
init_pair(0, COLOR_WHITE, COLOR_BLUE);
|
||||
init_pair(NCURSES_COLORPAIR_TEXT, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(NCURSES_COLORPAIR_INFO, COLOR_GREEN, COLOR_BLACK);
|
||||
init_pair(NCURSES_COLORPAIR_WARNING, COLOR_MAGENTA, COLOR_BLACK);
|
||||
init_pair(NCURSES_COLORPAIR_ERROR, COLOR_RED, COLOR_BLACK);
|
||||
init_pair(NCURSES_COLORPAIR_DEBUG, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(NCURSES_COLORPAIR_STATUS, COLOR_BLUE, COLOR_WHITE);
|
||||
init_pair(NCURSES_COLORPAIR_KEYBAR, COLOR_WHITE, COLOR_BLUE);
|
||||
init_pair(NCURSES_COLORPAIR_INFOLINE, COLOR_WHITE, COLOR_BLUE);
|
||||
init_pair(NCURSES_COLORPAIR_TEXTHIGHL, COLOR_BLACK, COLOR_CYAN);
|
||||
init_pair(NCURSES_COLORPAIR_CURSOR, COLOR_BLACK, COLOR_YELLOW);
|
||||
}
|
||||
}
|
||||
|
||||
NCursesFrontend::~NCursesFrontend()
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (m_pScreenBuffer)
|
||||
{
|
||||
free(m_pScreenBuffer);
|
||||
}
|
||||
if (m_pOldScreenBuffer)
|
||||
{
|
||||
free(m_pOldScreenBuffer);
|
||||
}
|
||||
m_ColorAttr.clear();
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_CURSOR_INFO ConsoleCursorInfo;
|
||||
GetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
||||
ConsoleCursorInfo.bVisible = true;
|
||||
SetConsoleCursorInfo(hConsole, &ConsoleCursorInfo);
|
||||
#else
|
||||
keypad(stdscr, false);
|
||||
echo();
|
||||
curs_set(1);
|
||||
endwin();
|
||||
#endif
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void NCursesFrontend::Run()
|
||||
{
|
||||
debug("Entering NCursesFrontend-loop");
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
// The data (queue and log) is updated each 200 msec,
|
||||
// but the window is updated more often for better reaction on user's input
|
||||
Update();
|
||||
usleep(25 * 1000);
|
||||
m_iSkipUpdateData -= 25;
|
||||
if (m_iSkipUpdateData < 0)
|
||||
{
|
||||
m_iSkipUpdateData = 200;
|
||||
}
|
||||
}
|
||||
|
||||
FreeData();
|
||||
|
||||
debug("Exiting NCursesFrontend-loop");
|
||||
}
|
||||
|
||||
void NCursesFrontend::Update()
|
||||
{
|
||||
// Figure out how big the screen is
|
||||
CalcWindowSizes();
|
||||
|
||||
if (!m_iSkipUpdateData)
|
||||
{
|
||||
FreeData();
|
||||
m_iNeededLogEntries = m_iMessagesWinClientHeight;
|
||||
if (!PrepareData())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
// Print Current NZBQueue
|
||||
//------------------------------------------
|
||||
if (m_iQueueWinHeight > 0)
|
||||
{
|
||||
PrintQueue();
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
// Print Messages
|
||||
//------------------------------------------
|
||||
if (m_iMessagesWinHeight > 0)
|
||||
{
|
||||
PrintMessages();
|
||||
}
|
||||
|
||||
PrintStatus();
|
||||
|
||||
PrintKeyInputBar();
|
||||
|
||||
// Update the input
|
||||
UpdateInput();
|
||||
|
||||
RefreshScreen();
|
||||
}
|
||||
|
||||
void NCursesFrontend::CalcWindowSizes()
|
||||
{
|
||||
int iNrRows, iNrColumns;
|
||||
#ifdef WIN32
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
||||
GetConsoleScreenBufferInfo(hConsole, &BufInfo);
|
||||
iNrRows = BufInfo.srWindow.Bottom - BufInfo.srWindow.Top + 1;
|
||||
iNrColumns = BufInfo.srWindow.Right - BufInfo.srWindow.Left + 1;
|
||||
#else
|
||||
getmaxyx(stdscr, iNrRows, iNrColumns);
|
||||
#endif
|
||||
if (iNrRows != m_iScreenHeight || iNrColumns != m_iScreenWidth)
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_iScreenBufferSize = iNrRows * iNrColumns * sizeof(CHAR_INFO);
|
||||
m_pScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
|
||||
memset(m_pScreenBuffer, 0, m_iScreenBufferSize);
|
||||
m_pOldScreenBuffer = (CHAR_INFO*)malloc(m_iScreenBufferSize);
|
||||
memset(m_pOldScreenBuffer, 0, m_iScreenBufferSize);
|
||||
#else
|
||||
clear();
|
||||
#endif
|
||||
m_iScreenHeight = iNrRows;
|
||||
m_iScreenWidth = iNrColumns;
|
||||
}
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int iQueueSize = pDownloadQueue->size();
|
||||
UnlockQueue();
|
||||
|
||||
m_iQueueWinTop = 0;
|
||||
m_iQueueWinHeight = (int)((float) (m_iScreenHeight - 2) * m_QueueWindowPercentage);
|
||||
if (m_iQueueWinHeight - 1 > iQueueSize)
|
||||
{
|
||||
m_iQueueWinHeight = iQueueSize > 0 ? iQueueSize + 1 : 1 + 1;
|
||||
}
|
||||
m_iQueueWinClientHeight = m_iQueueWinHeight - 1;
|
||||
if (m_iQueueWinClientHeight < 0)
|
||||
{
|
||||
m_iQueueWinClientHeight = 0;
|
||||
}
|
||||
|
||||
m_iMessagesWinTop = m_iQueueWinTop + m_iQueueWinHeight;
|
||||
m_iMessagesWinHeight = m_iScreenHeight - m_iQueueWinHeight - 2;
|
||||
m_iMessagesWinClientHeight = m_iMessagesWinHeight - 1;
|
||||
if (m_iMessagesWinClientHeight < 0)
|
||||
{
|
||||
m_iMessagesWinClientHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesFrontend::PlotLine(const char * szString, int iRow, int iPos, int iColorPair)
|
||||
{
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), "%-*s", m_iScreenWidth, szString);
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
int iLen = strlen(szBuffer);
|
||||
if (iLen > m_iScreenWidth - iPos && m_iScreenWidth - iPos < MAX_SCREEN_WIDTH)
|
||||
{
|
||||
szBuffer[m_iScreenWidth - iPos] = '\0';
|
||||
}
|
||||
|
||||
PlotText(szBuffer, iRow, iPos, iColorPair, false);
|
||||
}
|
||||
|
||||
void NCursesFrontend::PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink)
|
||||
{
|
||||
#ifdef WIN32
|
||||
int iBufPos = iRow * m_iScreenWidth + iPos;
|
||||
int len = strlen(szString);
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
m_pScreenBuffer[iBufPos + i].Char.AsciiChar = szString[i];
|
||||
m_pScreenBuffer[iBufPos + i].Attributes = m_ColorAttr[iColorPair];
|
||||
}
|
||||
#else
|
||||
if( m_bUseColor )
|
||||
{
|
||||
attron(COLOR_PAIR(iColorPair));
|
||||
if (bBlink)
|
||||
{
|
||||
attron(A_BLINK);
|
||||
}
|
||||
}
|
||||
mvaddstr(iRow, iPos, (char*)szString);
|
||||
if( m_bUseColor )
|
||||
{
|
||||
attroff(COLOR_PAIR(iColorPair));
|
||||
if (bBlink)
|
||||
{
|
||||
attroff(A_BLINK);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NCursesFrontend::RefreshScreen()
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool bBufChanged = memcmp(m_pScreenBuffer, m_pOldScreenBuffer, m_iScreenBufferSize);
|
||||
if (bBufChanged)
|
||||
{
|
||||
COORD BufSize;
|
||||
BufSize.X = m_iScreenWidth;
|
||||
BufSize.Y = m_iScreenHeight;
|
||||
|
||||
COORD BufCoord;
|
||||
BufCoord.X = 0;
|
||||
BufCoord.Y = 0;
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
CONSOLE_SCREEN_BUFFER_INFO BufInfo;
|
||||
GetConsoleScreenBufferInfo(hConsole, &BufInfo);
|
||||
WriteConsoleOutput(hConsole, m_pScreenBuffer, BufSize, BufCoord, &BufInfo.srWindow);
|
||||
|
||||
BufInfo.dwCursorPosition.X = BufInfo.srWindow.Right;
|
||||
BufInfo.dwCursorPosition.Y = BufInfo.srWindow.Bottom;
|
||||
SetConsoleCursorPosition(hConsole, BufInfo.dwCursorPosition);
|
||||
|
||||
memcpy(m_pOldScreenBuffer, m_pScreenBuffer, m_iScreenBufferSize);
|
||||
}
|
||||
#else
|
||||
// Cursor placement
|
||||
wmove((WINDOW*)m_pWindow, m_iScreenHeight, m_iScreenWidth);
|
||||
|
||||
// NCurses refresh
|
||||
refresh();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
void NCursesFrontend::init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor)
|
||||
{
|
||||
m_ColorAttr.resize(iColorNumber + 1);
|
||||
m_ColorAttr[iColorNumber] = wForeColor | (wBackColor << 4);
|
||||
}
|
||||
#endif
|
||||
|
||||
void NCursesFrontend::PrintMessages()
|
||||
{
|
||||
int iLineNr = m_iMessagesWinTop;
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), "%s Messages", m_bUseColor ? "" : "*** ");
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PlotLine(szBuffer, iLineNr++, 0, NCURSES_COLORPAIR_INFOLINE);
|
||||
|
||||
int iLine = iLineNr + m_iMessagesWinClientHeight - 1;
|
||||
int iLinesToPrint = m_iMessagesWinClientHeight;
|
||||
|
||||
Log::Messages* pMessages = LockMessages();
|
||||
|
||||
// print messages from bottom
|
||||
for (int i = (int)pMessages->size() - 1; i >= 0 && iLinesToPrint > 0; i--)
|
||||
{
|
||||
int iPrintedLines = PrintMessage((*pMessages)[i], iLine, iLinesToPrint);
|
||||
iLine -= iPrintedLines;
|
||||
iLinesToPrint -= iPrintedLines;
|
||||
}
|
||||
|
||||
if (iLinesToPrint > 0)
|
||||
{
|
||||
// too few messages, print them again from top
|
||||
iLine = iLineNr + m_iMessagesWinClientHeight - 1;
|
||||
while (iLinesToPrint-- > 0)
|
||||
{
|
||||
PlotLine("", iLine--, 0, NCURSES_COLORPAIR_TEXT);
|
||||
}
|
||||
int iLinesToPrint2 = m_iMessagesWinClientHeight;
|
||||
for (int i = (int)pMessages->size() - 1; i >= 0 && iLinesToPrint2 > 0; i--)
|
||||
{
|
||||
int iPrintedLines = PrintMessage((*pMessages)[i], iLine, iLinesToPrint2);
|
||||
iLine -= iPrintedLines;
|
||||
iLinesToPrint2 -= iPrintedLines;
|
||||
}
|
||||
}
|
||||
|
||||
UnlockMessages();
|
||||
}
|
||||
|
||||
int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
|
||||
{
|
||||
char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG "};
|
||||
const int iMessageTypeColor[] = { NCURSES_COLORPAIR_INFO, NCURSES_COLORPAIR_WARNING,
|
||||
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG };
|
||||
|
||||
const char* szText = Msg->GetText();
|
||||
int iLen = strlen(szText);
|
||||
int iWinWidth = m_iScreenWidth - 8;
|
||||
int iMsgLines = iLen / iWinWidth;
|
||||
if (iLen % iWinWidth > 0)
|
||||
{
|
||||
iMsgLines++;
|
||||
}
|
||||
|
||||
int iLines = 0;
|
||||
for (int i = iMsgLines - 1; i >= 0 && iLines < iMaxLines; i--)
|
||||
{
|
||||
int iR = iRow - iMsgLines + i + 1;
|
||||
PlotLine(szText + iWinWidth * i, iR, 8, NCURSES_COLORPAIR_TEXT);
|
||||
if (i == 0)
|
||||
{
|
||||
PlotText(szMessageType[Msg->GetKind()], iR, 0, iMessageTypeColor[Msg->GetKind()], false);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlotText(" ", iR, 0, iMessageTypeColor[Msg->GetKind()], false);
|
||||
}
|
||||
iLines++;
|
||||
}
|
||||
|
||||
return iLines;
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintStatus()
|
||||
{
|
||||
char tmp[MAX_SCREEN_WIDTH];
|
||||
int iStatusRow = m_iScreenHeight - 2;
|
||||
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
|
||||
if (m_fCurrentDownloadSpeed > 0.0)
|
||||
{
|
||||
long long remain_sec = (long long)(m_lRemainingSize / (m_fCurrentDownloadSpeed * 1024));
|
||||
int h = 0;
|
||||
int m = 0;
|
||||
int s = 0;
|
||||
while (remain_sec > 3600)
|
||||
{
|
||||
h++;
|
||||
remain_sec -= 3600;
|
||||
}
|
||||
while (remain_sec > 60)
|
||||
{
|
||||
m++;
|
||||
remain_sec -= 60;
|
||||
}
|
||||
s = remain_sec;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads running, %.0f KB/s, %.2f MB remaining %s %s %s", m_iThreadCount, m_fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0), timeString, m_bPause ? "Paused" : "", szDownloadLimit);
|
||||
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintKeyInputBar()
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int iQueueSize = pDownloadQueue->size();
|
||||
UnlockQueue();
|
||||
|
||||
int iInputBarRow = m_iScreenHeight - 1;
|
||||
|
||||
switch (m_eInputMode)
|
||||
{
|
||||
case eNormal:
|
||||
PlotLine("(Q)uit | (E)dit | (P)ause | (R)ate | n(Z)b | (W)indow", iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
||||
break;
|
||||
case eEditQueue:
|
||||
{
|
||||
char* szStatus = NULL;
|
||||
if (m_iSelectedQueueEntry > 0 && iQueueSize > 1 && m_iSelectedQueueEntry == iQueueSize - 1)
|
||||
{
|
||||
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/(T)op";
|
||||
}
|
||||
else if (iQueueSize > 1 && m_iSelectedQueueEntry == 0)
|
||||
{
|
||||
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | dow(N)/(B)ottom";
|
||||
}
|
||||
else if (iQueueSize > 1)
|
||||
{
|
||||
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/dow(N)/(T)op/(B)ottom";
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus = "(Q)uit";
|
||||
}
|
||||
|
||||
PlotLine(szStatus, iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
||||
break;
|
||||
}
|
||||
case eDownloadRate:
|
||||
char szString[128];
|
||||
snprintf(szString, 128, "Download rate: %i", m_iInputValue);
|
||||
szString[128-1] = '\0';
|
||||
PlotLine(szString, iInputBarRow, 0, NCURSES_COLORPAIR_KEYBAR);
|
||||
// Print the cursor
|
||||
PlotText(" ", iInputBarRow, 15 + m_iInputNumberIndex, NCURSES_COLORPAIR_CURSOR, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintQueue()
|
||||
{
|
||||
int iLineNr = m_iQueueWinTop;
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
if (pDownloadQueue->empty())
|
||||
{
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), "%s Files for downloading", m_bUseColor ? "" : "*** ");
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PlotLine(szBuffer, iLineNr++, 0, NCURSES_COLORPAIR_INFOLINE);
|
||||
PlotLine("Ready to receive nzb-job", iLineNr++, 0, NCURSES_COLORPAIR_TEXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
iLineNr++;
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
int iPausedFiles = 0;
|
||||
int i = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++, i++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
||||
{
|
||||
PrintFilename(pFileInfo, iLineNr++, i == m_iSelectedQueueEntry);
|
||||
}
|
||||
|
||||
if (pFileInfo->GetPaused())
|
||||
{
|
||||
iPausedFiles++;
|
||||
lPaused += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
lRemaining += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
|
||||
char szRemaining[20];
|
||||
FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
||||
|
||||
char szUnpaused[20];
|
||||
FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
|
||||
m_bUseColor ? "" : "*** ", pDownloadQueue->size(), pDownloadQueue->size() - iPausedFiles, szRemaining, szUnpaused);
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PlotLine(szBuffer, m_iQueueWinTop, 0, NCURSES_COLORPAIR_INFOLINE);
|
||||
}
|
||||
UnlockQueue();
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelected)
|
||||
{
|
||||
int color = 0;
|
||||
const char* Brace1 = "[";
|
||||
const char* Brace2 = "]";
|
||||
if (m_eInputMode == eEditQueue && bSelected)
|
||||
{
|
||||
color = NCURSES_COLORPAIR_TEXTHIGHL;
|
||||
if (!m_bUseColor)
|
||||
{
|
||||
Brace1 = "<";
|
||||
Brace2 = ">";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
color = NCURSES_COLORPAIR_TEXT;
|
||||
}
|
||||
|
||||
char szCompleted[20];
|
||||
szCompleted[0] = '\0';
|
||||
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
|
||||
{
|
||||
sprintf(szCompleted, ", %i%%", (int)(100 - pFileInfo->GetRemainingSize() * 100.0 / pFileInfo->GetSize()));
|
||||
}
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
if (m_bShowNZBname)
|
||||
{
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1023);
|
||||
int len = strlen(szNZBNiceName);
|
||||
szNZBNiceName[len] = PATH_SEPARATOR;
|
||||
szNZBNiceName[len + 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
szNZBNiceName[0] = '\0';
|
||||
}
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(), Brace2, szNZBNiceName, pFileInfo->GetFilename(), pFileInfo->GetSize() / 1024.0 / 1024.0, szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
|
||||
PlotLine(szBuffer, iRow, 0, color);
|
||||
}
|
||||
|
||||
void NCursesFrontend::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
|
||||
{
|
||||
if (lFileSize > 1024 * 1024 * 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f GB", (float)lFileSize / 1024 / 1024 / 1024);
|
||||
}
|
||||
else if (lFileSize > 1024 * 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f MB", (float)lFileSize / 1024 / 1024);
|
||||
}
|
||||
else if (lFileSize > 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f KB", (float)lFileSize / 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%i", (int)lFileSize);
|
||||
}
|
||||
szBuffer[iBufLen - 1] = '\0';
|
||||
}
|
||||
|
||||
void NCursesFrontend::SetCurrentQueueEntry(int iEntry)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int iQueueSize = pDownloadQueue->size();
|
||||
UnlockQueue();
|
||||
|
||||
if (iEntry < 0)
|
||||
{
|
||||
iEntry = 0;
|
||||
}
|
||||
else if (iEntry > iQueueSize - 1)
|
||||
{
|
||||
iEntry = iQueueSize - 1;
|
||||
}
|
||||
|
||||
if (iEntry > m_iQueueScrollOffset + m_iQueueWinClientHeight ||
|
||||
iEntry < m_iQueueScrollOffset - m_iQueueWinClientHeight)
|
||||
{
|
||||
m_iQueueScrollOffset = iEntry - m_iQueueWinClientHeight / 2;
|
||||
}
|
||||
else if (iEntry < m_iQueueScrollOffset)
|
||||
{
|
||||
m_iQueueScrollOffset -= m_iQueueWinClientHeight;
|
||||
}
|
||||
else if (iEntry >= m_iQueueScrollOffset + m_iQueueWinClientHeight)
|
||||
{
|
||||
m_iQueueScrollOffset += m_iQueueWinClientHeight;
|
||||
}
|
||||
|
||||
if (m_iQueueScrollOffset > iQueueSize - m_iQueueWinClientHeight)
|
||||
{
|
||||
m_iQueueScrollOffset = iQueueSize - m_iQueueWinClientHeight;
|
||||
}
|
||||
if (m_iQueueScrollOffset < 0)
|
||||
{
|
||||
m_iQueueScrollOffset = 0;
|
||||
}
|
||||
|
||||
m_iSelectedQueueEntry = iEntry;
|
||||
}
|
||||
|
||||
void NCursesFrontend::UpdateInput()
|
||||
{
|
||||
int iKey;
|
||||
while ((iKey = ReadConsoleKey()) != READKEY_EMPTY)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int iQueueSize = pDownloadQueue->size();
|
||||
UnlockQueue();
|
||||
|
||||
// Normal or edit queue mode
|
||||
if (m_eInputMode == eNormal || m_eInputMode == eEditQueue)
|
||||
{
|
||||
switch (iKey)
|
||||
{
|
||||
// Key 'q' for quit
|
||||
case 'q':
|
||||
ExitProc();
|
||||
break;
|
||||
// show/hide NZBFilename
|
||||
case 'z':
|
||||
m_bShowNZBname = !m_bShowNZBname;
|
||||
break;
|
||||
// swicth window sizes
|
||||
case 'w':
|
||||
if (m_QueueWindowPercentage == 0.5)
|
||||
{
|
||||
m_QueueWindowPercentage = 1;
|
||||
}
|
||||
else if (m_QueueWindowPercentage == 1 && m_eInputMode != eEditQueue)
|
||||
{
|
||||
m_QueueWindowPercentage = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_QueueWindowPercentage = 0.5;
|
||||
}
|
||||
CalcWindowSizes();
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Normal mode
|
||||
if (m_eInputMode == eNormal)
|
||||
{
|
||||
switch (iKey)
|
||||
{
|
||||
// Key 'p' for pause
|
||||
case 'p':
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
info(m_bPause ? "Unpausing download" : "Pausing download");
|
||||
}
|
||||
ServerPauseUnpause(!m_bPause);
|
||||
break;
|
||||
case '\'':
|
||||
ServerDumpDebug();
|
||||
break;
|
||||
case 'e':
|
||||
case 10: // return
|
||||
case 13: // enter
|
||||
if (iQueueSize > 0)
|
||||
{
|
||||
m_eInputMode = eEditQueue;
|
||||
if (m_QueueWindowPercentage == 0)
|
||||
{
|
||||
m_QueueWindowPercentage = 0.5;
|
||||
}
|
||||
}
|
||||
break;
|
||||
// Download rate
|
||||
case 'r':
|
||||
m_eInputMode = eDownloadRate;
|
||||
m_iInputNumberIndex = 0;
|
||||
m_iInputValue = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Edit Queue mode
|
||||
else if (m_eInputMode == eEditQueue)
|
||||
{
|
||||
switch (iKey)
|
||||
{
|
||||
// Key 'p' for pause
|
||||
case 'p':
|
||||
ServerEditQueue(eaPauseUnpause, m_iSelectedQueueEntry);
|
||||
break;
|
||||
// Delete entry
|
||||
case 'd':
|
||||
if (ServerEditQueue(eaDelete, m_iSelectedQueueEntry))
|
||||
{
|
||||
if (iQueueSize == 0)
|
||||
{
|
||||
m_iSelectedQueueEntry = 0;
|
||||
m_eInputMode = eNormal;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (ServerEditQueue(eaMoveUp, m_iSelectedQueueEntry))
|
||||
{
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry - 1);
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
if (ServerEditQueue(eaMoveDown, m_iSelectedQueueEntry))
|
||||
{
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry + 1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (ServerEditQueue(eaMoveTop, m_iSelectedQueueEntry))
|
||||
{
|
||||
SetCurrentQueueEntry(0);
|
||||
}
|
||||
break;
|
||||
case 'b':
|
||||
if (ServerEditQueue(eaMoveBottom, m_iSelectedQueueEntry))
|
||||
{
|
||||
SetCurrentQueueEntry(iQueueSize > 0 ? iQueueSize - 1 : 0);
|
||||
}
|
||||
break;
|
||||
case 'e':
|
||||
case 10: // return
|
||||
case 13: // enter
|
||||
m_eInputMode = eNormal;
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
if (m_iSelectedQueueEntry < iQueueSize - 1)
|
||||
{
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry + 1);
|
||||
}
|
||||
break;
|
||||
case KEY_UP:
|
||||
if (m_iSelectedQueueEntry > 0)
|
||||
{
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry - 1);
|
||||
}
|
||||
break;
|
||||
case KEY_PPAGE:
|
||||
if (m_iSelectedQueueEntry > 0)
|
||||
{
|
||||
if (m_iSelectedQueueEntry == m_iQueueScrollOffset)
|
||||
{
|
||||
m_iQueueScrollOffset -= m_iQueueWinClientHeight;
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry - m_iQueueWinClientHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrentQueueEntry(m_iQueueScrollOffset);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KEY_NPAGE:
|
||||
if (m_iSelectedQueueEntry < iQueueSize - 1)
|
||||
{
|
||||
if (m_iSelectedQueueEntry == m_iQueueScrollOffset + m_iQueueWinClientHeight - 1)
|
||||
{
|
||||
m_iQueueScrollOffset += m_iQueueWinClientHeight;
|
||||
SetCurrentQueueEntry(m_iSelectedQueueEntry + m_iQueueWinClientHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetCurrentQueueEntry(m_iQueueScrollOffset + m_iQueueWinClientHeight - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case KEY_HOME:
|
||||
SetCurrentQueueEntry(0);
|
||||
break;
|
||||
case KEY_END:
|
||||
SetCurrentQueueEntry(iQueueSize > 0 ? iQueueSize - 1 : 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Edit download rate input mode
|
||||
else if (m_eInputMode == eDownloadRate)
|
||||
{
|
||||
// Numbers
|
||||
if (m_iInputNumberIndex < 5 && iKey >= '0' && iKey <= '9')
|
||||
{
|
||||
m_iInputValue = (m_iInputValue * 10) + (iKey - '0');
|
||||
m_iInputNumberIndex++;
|
||||
}
|
||||
// Enter
|
||||
else if (iKey == 10 || iKey == 13)
|
||||
{
|
||||
ServerSetDownloadRate((float)m_iInputValue);
|
||||
m_eInputMode = eNormal;
|
||||
return;
|
||||
}
|
||||
// Escape
|
||||
else if (iKey == 27)
|
||||
{
|
||||
m_eInputMode = eNormal;
|
||||
return;
|
||||
}
|
||||
// Backspace
|
||||
else if (m_iInputNumberIndex > 0 && iKey == KEY_BACKSPACE)
|
||||
{
|
||||
int iRemain = m_iInputValue % 10;
|
||||
|
||||
m_iInputValue = (m_iInputValue - iRemain) / 10;
|
||||
m_iInputNumberIndex--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int NCursesFrontend::ReadConsoleKey()
|
||||
{
|
||||
#ifdef WIN32
|
||||
HANDLE hConsole = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD NumberOfEvents;
|
||||
BOOL bOK = GetNumberOfConsoleInputEvents(hConsole, &NumberOfEvents);
|
||||
if (bOK && NumberOfEvents > 0)
|
||||
{
|
||||
while (NumberOfEvents--)
|
||||
{
|
||||
INPUT_RECORD InputRecord;
|
||||
DWORD NumberOfEventsRead;
|
||||
if (ReadConsoleInput(hConsole, &InputRecord, 1, &NumberOfEventsRead) &&
|
||||
NumberOfEventsRead > 0 &&
|
||||
InputRecord.EventType == KEY_EVENT &&
|
||||
InputRecord.Event.KeyEvent.bKeyDown)
|
||||
{
|
||||
return tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return READKEY_EMPTY;
|
||||
#else
|
||||
return getch();
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
108
NCursesFrontend.h
Normal file
108
NCursesFrontend.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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_iSkipUpdateData;
|
||||
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_iQueueScrollOffset;
|
||||
|
||||
// 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;
|
||||
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 PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
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();
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
|
||||
public:
|
||||
NCursesFrontend();
|
||||
virtual ~NCursesFrontend();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
274
NNTPConnection.cpp
Normal file
274
NNTPConnection.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* 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 "Log.h"
|
||||
#include "NNTPConnection.h"
|
||||
#include "Connection.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
{
|
||||
m_UnavailableGroups.clear();
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(LineBufSize);
|
||||
}
|
||||
|
||||
NNTPConnection::~NNTPConnection()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
|
||||
{
|
||||
free(m_UnavailableGroups[i]);
|
||||
m_UnavailableGroups[i] = NULL;
|
||||
}
|
||||
m_UnavailableGroups.clear();
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
if (m_szLineBuf)
|
||||
{
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
}
|
||||
|
||||
char* NNTPConnection::Request(char* req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
WriteLine(req);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
|
||||
//authentication required!
|
||||
if (Authenticate() < 0)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//try again
|
||||
WriteLine(req);
|
||||
answer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
return answer;
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int NNTPConnection::Authenticate()
|
||||
{
|
||||
if ((!((NewsServer*)m_pNetAddress)->GetUser()) ||
|
||||
(!((NewsServer*)m_pNetAddress)->GetPassword()))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return AuthInfoUser();
|
||||
}
|
||||
|
||||
int NNTPConnection::AuthInfoUser(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
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, LineBufSize);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
else if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
return AuthInfoUser();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int NNTPConnection::AuthInfoPass(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* szAnswer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
if (!szAnswer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
else if (!strncmp(szAnswer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
}
|
||||
else if (!strncmp(szAnswer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), szAnswer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
return res;
|
||||
char* answer = DoReadLine(m_szLineBuf, LineBufSize);
|
||||
|
||||
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");
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
}
|
||||
|
||||
|
||||
int NNTPConnection::JoinGroup(char* grp)
|
||||
{
|
||||
if (!grp)
|
||||
{
|
||||
debug("joinGroup called with NULL-pointer!!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
|
||||
return 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
|
||||
{
|
||||
if (!strcmp(grp, m_UnavailableGroups[i]))
|
||||
{
|
||||
debug("Group %s unavailable on %s.", grp, this->GetServer()->GetHost());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = Request(tmp);
|
||||
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
warn("Error changing group on %s: Connection closed by remote host.",
|
||||
GetServer()->GetHost());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Error changing group on %s to %s: Answer was \"%s\".",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
m_UnavailableGroups.push_back(strdup(grp));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
60
NNTPConnection.h
Normal file
60
NNTPConnection.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 NNTPCONNECTION_H
|
||||
#define NNTPCONNECTION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include "NewsServer.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
std::vector <char*> m_UnavailableGroups;
|
||||
char* m_szActiveGroup;
|
||||
static const int LineBufSize = 1024*10;
|
||||
char* m_szLineBuf;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
char* Request(char* req);
|
||||
int Authenticate();
|
||||
int AuthInfoUser(int iRecur = 0);
|
||||
int AuthInfoPass(int iRecur = 0);
|
||||
int JoinGroup(char* grp);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
* This file if 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,161 +14,172 @@
|
||||
* 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;
|
||||
DWORD nServiceCurrentStatus;
|
||||
char* strServiceName = "NZBGet";
|
||||
SERVICE_STATUS_HANDLE nServiceStatusHandle;
|
||||
DWORD nServiceCurrentStatus;
|
||||
|
||||
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
{
|
||||
BOOL success;
|
||||
SERVICE_STATUS nServiceStatus;
|
||||
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
nServiceStatus.dwCurrentState = dwCurrentState;
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
}
|
||||
nServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||||
nServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
nServiceStatus.dwCheckPoint = 0;
|
||||
nServiceStatus.dwWaitHint = dwWaitHint;
|
||||
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
{
|
||||
BOOL success;
|
||||
SERVICE_STATUS nServiceStatus;
|
||||
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
nServiceStatus.dwCurrentState = dwCurrentState;
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
}
|
||||
nServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||||
nServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
nServiceStatus.dwCheckPoint = 0;
|
||||
nServiceStatus.dwWaitHint = dwWaitHint;
|
||||
|
||||
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
|
||||
|
||||
return success;
|
||||
void ServiceCtrlHandler(DWORD nControlCode)
|
||||
{
|
||||
switch(nControlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP:
|
||||
nServiceCurrentStatus = SERVICE_STOP_PENDING;
|
||||
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
|
||||
ExitProc();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UpdateServiceStatus(nServiceCurrentStatus, 0);
|
||||
}
|
||||
|
||||
void ServiceCtrlHandler(DWORD nControlCode)
|
||||
{
|
||||
switch(nControlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP:
|
||||
nServiceCurrentStatus = SERVICE_STOP_PENDING;
|
||||
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
|
||||
ExitProc();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UpdateServiceStatus(nServiceCurrentStatus, 0);
|
||||
}
|
||||
|
||||
void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
BOOL success;
|
||||
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
|
||||
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
|
||||
if(!nServiceStatusHandle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nServiceCurrentStatus=SERVICE_RUNNING;
|
||||
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Run();
|
||||
|
||||
UpdateServiceStatus(SERVICE_STOPPED, 0);
|
||||
}
|
||||
void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
BOOL success;
|
||||
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
|
||||
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
|
||||
if(!nServiceStatusHandle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nServiceCurrentStatus=SERVICE_RUNNING;
|
||||
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Run();
|
||||
|
||||
UpdateServiceStatus(SERVICE_STOPPED, 0);
|
||||
}
|
||||
|
||||
void StartService(RunProc RunProcPtr)
|
||||
{
|
||||
Run = RunProcPtr;
|
||||
|
||||
SERVICE_TABLE_ENTRY servicetable[]=
|
||||
{
|
||||
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{nullptr,nullptr}
|
||||
};
|
||||
BOOL success = StartServiceCtrlDispatcher(servicetable);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not start service");
|
||||
}
|
||||
SERVICE_TABLE_ENTRY servicetable[]=
|
||||
{
|
||||
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{NULL,NULL}
|
||||
};
|
||||
BOOL success = StartServiceCtrlDispatcher(servicetable);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not start service");
|
||||
}
|
||||
}
|
||||
|
||||
void InstallService(int argc, char *argv[])
|
||||
{
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char exeName[1024];
|
||||
GetModuleFileName(nullptr, exeName, 1024);
|
||||
exeName[1024-1] = '\0';
|
||||
|
||||
BString<1024> cmdLine("%s -D", exeName);
|
||||
|
||||
SC_HANDLE hService = CreateService(scm, strServiceName,
|
||||
strServiceName,
|
||||
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
cmdLine,
|
||||
0,0,0,0,0);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully installed\n", strServiceName);
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char szCmdLine[1024];
|
||||
snprintf(szCmdLine, 1024, "%s -D", argv[0]);
|
||||
szCmdLine[1024-1] = '\0';
|
||||
|
||||
SC_HANDLE hService = CreateService(scm, strServiceName,
|
||||
strServiceName,
|
||||
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
szCmdLine,
|
||||
0,0,0,0,0);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully installed\n", strServiceName);
|
||||
}
|
||||
|
||||
void UnInstallService()
|
||||
{
|
||||
BOOL success;
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
success = DeleteService(hService);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not uninstall service");
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
|
||||
BOOL success;
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
success = DeleteService(hService);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not uninstall service");
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
|
||||
}
|
||||
|
||||
void InstallUninstallServiceCheck(int argc, char *argv[])
|
||||
@@ -185,24 +196,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 if 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
|
||||
425
NZBFile.cpp
Normal file
425
NZBFile.cpp
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#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"
|
||||
|
||||
bool ArticleGreater(ArticleInfo* elem1, ArticleInfo* elem2)
|
||||
{
|
||||
return elem1->GetPartNumber() > elem2->GetPartNumber();
|
||||
}
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile::~NZBFile()
|
||||
{
|
||||
debug("Destroying NZBFile");
|
||||
|
||||
// Cleanup
|
||||
if (m_szFileName)
|
||||
{
|
||||
free(m_szFileName);
|
||||
}
|
||||
|
||||
for (std::vector<FileInfo*>::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
{
|
||||
debug(" NZBFile %s", m_szFileName);
|
||||
}
|
||||
|
||||
void NZBFile::DetachFileInfos()
|
||||
{
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
bool NZBFile::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
{
|
||||
FILE* pFile = fopen(szFileName, "r");
|
||||
if (!pFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
*pBuffer = (char*) malloc(iSize + 1);
|
||||
if (!*pBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(*pBuffer, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
(*pBuffer)[iSize] = 0;
|
||||
|
||||
*pBufferLength = iSize + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// /*
|
||||
//TEST
|
||||
int iBufferLength = 0;
|
||||
char* szBuffer = NULL;
|
||||
|
||||
if (!NZBFile::LoadFileIntoBuffer(szFileName, &szBuffer, &iBufferLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Create(szFileName, szBuffer, iBufferLength, true);
|
||||
// */
|
||||
}
|
||||
|
||||
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::DeleteEmptyArticles(FileInfo* pFileInfo)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
_variant_t v(szFileName);
|
||||
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))
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
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->SetNZBFilename(m_szFileName);
|
||||
pFileInfo->SetSubject(subject);
|
||||
pFileInfo->ParseSubject();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(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))
|
||||
{
|
||||
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->SetNZBFilename(m_szFileName);
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
if (!strcmp("subject",(char*)name))
|
||||
{
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
pFileInfo->ParseSubject();
|
||||
}
|
||||
}
|
||||
}
|
||||
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))
|
||||
{
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
65
NZBFile.h
Normal file
65
NZBFile.h
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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBFILE_H
|
||||
#define NZBFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NZBFile
|
||||
{
|
||||
public:
|
||||
typedef std::vector<FileInfo*> FileInfos;
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void DeleteEmptyArticles(FileInfo* pFileInfo);
|
||||
#ifdef WIN32
|
||||
bool parseNZB(IUnknown* nzb);
|
||||
#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);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
53
NetAddress.cpp
Normal file
53
NetAddress.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 "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
|
||||
64
NewsServer.cpp
Normal file
64
NewsServer.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 "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
|
||||
1069
Options.cpp
Normal file
1069
Options.cpp
Normal file
File diff suppressed because it is too large
Load Diff
221
Options.h
Normal file
221
Options.h
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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 OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EDecoder
|
||||
{
|
||||
dcNone,
|
||||
dcUulib,
|
||||
dcYenc
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
{
|
||||
plNone,
|
||||
plOne,
|
||||
plAll
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
// 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;
|
||||
EDecoder m_eDecoder;
|
||||
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;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
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;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
bool m_bDaemonMode;
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int m_iEditQueueIDFrom;
|
||||
int m_iEditQueueIDTo;
|
||||
char* m_szArgFilename;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
bool m_bTest;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
float m_fDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile(int argc, char* argv[]);
|
||||
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);
|
||||
|
||||
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; }
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
EDecoder GetDecoder() { return m_eDecoder; };
|
||||
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; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
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; }
|
||||
|
||||
// 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 GetEditQueueIDFrom() { return m_iEditQueueIDFrom; }
|
||||
int GetEditQueueIDTo() { return m_iEditQueueIDTo; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
bool GetTest() { return m_bTest; }
|
||||
|
||||
// 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
|
||||
532
ParChecker.cpp
Normal file
532
ParChecker.cpp
Normal file
@@ -0,0 +1,532 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
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_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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
debug("par: %s", m_szParFilename);
|
||||
CommandLine commandLine;
|
||||
const char* argv[] = { "par2", "r", "-q", "-q", 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* repairer = new Repairer();
|
||||
#ifdef ENABLE_PARPROGRESS
|
||||
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
#endif
|
||||
|
||||
res = repairer->PreProcess(commandLine);
|
||||
debug("ParChecker: PreProcess-result=%i", res);
|
||||
|
||||
if (res != eSuccess || IsStopped())
|
||||
{
|
||||
error("Could not verify %s: ", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
return;
|
||||
}
|
||||
|
||||
char BufReason[1024];
|
||||
BufReason[0] = '\0';
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
m_szErrMsg = NULL;
|
||||
|
||||
m_bRepairNotNeeded = false;
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
|
||||
while (!IsStopped() && res == eRepairNotPossible)
|
||||
{
|
||||
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
LoadMorePars(repairer);
|
||||
repairer->UpdateVerificationResults();
|
||||
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
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);
|
||||
m_bRepairing = true;
|
||||
res = repairer->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 repairer;
|
||||
}
|
||||
|
||||
bool ParChecker::ParseParFilename(const char * szParFilename, int* iBaseNameLen, int* iBlocks)
|
||||
{
|
||||
char szFilename[1024];
|
||||
strncpy(szFilename, szParFilename, 1024);
|
||||
szFilename[1024-1] = '\0';
|
||||
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
int iLen = strlen(szFilename);
|
||||
if (iLen < 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find last occurence of ".par2" and trim filename after it
|
||||
char* szEnd = szFilename;
|
||||
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
|
||||
*szEnd = '\0';
|
||||
iLen = strlen(szFilename);
|
||||
|
||||
if (strcasecmp(szFilename + iLen - 5, ".par2"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*(szFilename + iLen - 5) = '\0';
|
||||
|
||||
int blockcnt = 0;
|
||||
char* p = strrchr(szFilename, '.');
|
||||
if (p && !strncasecmp(p, ".vol", 4))
|
||||
{
|
||||
char* b = strchr(p, '+');
|
||||
if (!b)
|
||||
{
|
||||
b = strchr(p, '-');
|
||||
}
|
||||
if (b)
|
||||
{
|
||||
blockcnt = atoi(b+1);
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (iBaseNameLen)
|
||||
{
|
||||
*iBaseNameLen = strlen(szFilename);
|
||||
}
|
||||
if (iBlocks)
|
||||
{
|
||||
*iBlocks = blockcnt;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpause par2-files
|
||||
* returns true, if the files with required number of blocks were unpaused,
|
||||
* or false if there are no more files in queue for this collection or not enough blocks
|
||||
*/
|
||||
bool ParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
Blocks blocks;
|
||||
blocks.clear();
|
||||
int iBlockFound = 0;
|
||||
|
||||
FindPars(pDownloadQueue, &blocks, true, &iBlockFound);
|
||||
if (iBlockFound == 0 && !g_pOptions->GetStrictParName())
|
||||
{
|
||||
FindPars(pDownloadQueue, &blocks, false, &iBlockFound);
|
||||
}
|
||||
|
||||
if (iBlockFound >= iBlockNeeded)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(m_szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
|
||||
// starting from the file with max block count.
|
||||
// if par-collection was built exponentially and all par-files present,
|
||||
// this step selects par-files with exact number of blocks we need.
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBestBlockInfo = NULL;
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
BlockInfo* pBlockInfo = *it;
|
||||
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
|
||||
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
|
||||
{
|
||||
pBestBlockInfo = pBlockInfo;
|
||||
}
|
||||
}
|
||||
if (pBestBlockInfo)
|
||||
{
|
||||
if (pBestBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBestBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. then unpause other files
|
||||
// this step only needed if the par-collection was built not exponentially
|
||||
// or not all par-files present (or some of them were corrupted)
|
||||
// this step is not optimal, but we hope, that the first step will work good
|
||||
// in most cases and we will not need the second step often
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBlockInfo = blocks.front();
|
||||
if (pBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBlockInfo->m_iBlockCount;
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pBlockFound)
|
||||
{
|
||||
*pBlockFound = iBlockFound;
|
||||
}
|
||||
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
blocks.clear();
|
||||
|
||||
return iBlockNeeded <= 0;
|
||||
}
|
||||
|
||||
void ParChecker::FindPars(DownloadQueue * pDownloadQueue, Blocks * pBlocks, bool bStrictParName, int* pBlockFound)
|
||||
{
|
||||
*pBlockFound = 0;
|
||||
|
||||
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
|
||||
char* szBaseParFilename = BaseFileName(m_szParFilename);
|
||||
char szMainBaseFilename[1024];
|
||||
int iMainBaseLen = 0;
|
||||
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
|
||||
{
|
||||
// should not happen
|
||||
error("Internal error: could not parse filename %s", szBaseParFilename);
|
||||
return;
|
||||
}
|
||||
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
|
||||
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
|
||||
szMainBaseFilename[maxlen] = '\0';
|
||||
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int iBlocks = 0;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), m_szNZBFilename) &&
|
||||
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
|
||||
iBlocks > 0)
|
||||
{
|
||||
if (bStrictParName)
|
||||
{
|
||||
// the pFileInfo->GetFilename() may be not confirmed and may contain
|
||||
// additional texts if Subject could not be parsed correctly
|
||||
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
char szCandidateFileName[1024];
|
||||
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it is a par2-file with blocks and it was from the same NZB-request
|
||||
// and it belongs to the same file collection (same base name),
|
||||
// then OK, we can use it
|
||||
BlockInfo* pBlockInfo = new BlockInfo();
|
||||
pBlockInfo->m_pFileInfo = pFileInfo;
|
||||
pBlockInfo->m_iBlockCount = iBlocks;
|
||||
pBlocks->push_back(pBlockInfo);
|
||||
*pBlockFound += iBlocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::LoadMorePars(void* repairer)
|
||||
{
|
||||
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*)repairer)->LoadPacketsFromFile(szParFilename);
|
||||
if (loadedOK)
|
||||
{
|
||||
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void ParChecker::signal_filename(std::string str)
|
||||
{
|
||||
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
94
ParChecker.h
Normal file
94
ParChecker.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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 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
|
||||
};
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
bool m_bRepairing;
|
||||
|
||||
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
|
||||
void LoadMorePars(void* repairer);
|
||||
void signal_filename(std::string str);
|
||||
|
||||
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();
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
635
PrePostProcessor.cpp
Normal file
635
PrePostProcessor.cpp
Normal file
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
PrePostProcessor::PrePostProcessor()
|
||||
{
|
||||
debug("Creating PrePostProcessor");
|
||||
|
||||
struct stat buffer;
|
||||
m_bCheckIncomingNZBs = !stat(g_pOptions->GetNzbDir(), &buffer) && S_ISDIR(buffer.st_mode);
|
||||
m_bHasMoreJobs = false;
|
||||
|
||||
m_QueueCoordinatorObserver.owner = this;
|
||||
g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver);
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_ParQueue.clear();
|
||||
|
||||
m_ParCheckerObserver.owner = this;
|
||||
m_ParChecker.Attach(&m_ParCheckerObserver);
|
||||
#endif
|
||||
}
|
||||
|
||||
PrePostProcessor::~PrePostProcessor()
|
||||
{
|
||||
debug("Destroying PrePostProcessor");
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
for (ParQueue::iterator it = m_ParQueue.begin(); it != m_ParQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrePostProcessor::Run()
|
||||
{
|
||||
debug("Entering PrePostProcessor-loop");
|
||||
|
||||
int iNZBDirInterval = 0;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
int iParQueueInterval = 0;
|
||||
#endif
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (m_bCheckIncomingNZBs && iNZBDirInterval == 5000)
|
||||
{
|
||||
// check nzbdir every 5 seconds
|
||||
CheckIncomingNZBs();
|
||||
iNZBDirInterval = 0;
|
||||
}
|
||||
iNZBDirInterval += 200;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (iParQueueInterval == 1000 && g_pOptions->GetParCheck())
|
||||
{
|
||||
// check par-queue every 1 second
|
||||
CheckParQueue();
|
||||
iParQueueInterval = 0;
|
||||
}
|
||||
iParQueueInterval += 200;
|
||||
#endif
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
debug("Exiting PrePostProcessor-loop");
|
||||
}
|
||||
|
||||
void PrePostProcessor::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_mutexParChecker.Lock();
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
m_ParChecker.Stop();
|
||||
int iMSecWait = 5000;
|
||||
while (m_ParChecker.IsRunning() && iMSecWait > 0)
|
||||
{
|
||||
usleep(50 * 1000);
|
||||
iMSecWait -= 50;
|
||||
}
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
|
||||
m_ParChecker.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QueueCoordinator::Aspect* pAspect = (QueueCoordinator::Aspect*)Aspect;
|
||||
if (pAspect->eAction == QueueCoordinator::eaNZBFileAdded &&
|
||||
g_pOptions->GetLoadPars() != Options::plAll)
|
||||
{
|
||||
PausePars(pAspect->pDownloadQueue, pAspect->szNZBFilename);
|
||||
}
|
||||
else if ((pAspect->eAction == QueueCoordinator::eaFileCompleted ||
|
||||
pAspect->eAction == QueueCoordinator::eaFileDeleted))
|
||||
{
|
||||
if (
|
||||
#ifndef DISABLE_PARCHECK
|
||||
!AddPar(pAspect->pFileInfo, pAspect->eAction == QueueCoordinator::eaFileDeleted) &&
|
||||
#endif
|
||||
WasLastUnpausedInCollection(pAspect->pDownloadQueue, pAspect->pFileInfo))
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pAspect->pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
info("Collection %s completely downloaded", szNZBNiceName);
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (g_pOptions->GetParCheck())
|
||||
{
|
||||
CheckPars(pAspect->pDownloadQueue, pAspect->pFileInfo);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ExecPostScript(pAspect->pFileInfo->GetDestDir(), pAspect->pFileInfo->GetNZBFilename(), "", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the option "loadpars" is set to "none", then we pause all par2-files.
|
||||
* If the option "loadpars" is set to "one", we use the following strategy:
|
||||
* Firstly we find all par-files, which do not have "vol" in their names, then we pause
|
||||
* all vols and download all just-pars.
|
||||
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
|
||||
* and download only it.
|
||||
*/
|
||||
void PrePostProcessor::PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename)
|
||||
{
|
||||
debug("Pausing pars");
|
||||
|
||||
DownloadQueue Pars, Vols;
|
||||
Pars.clear();
|
||||
Vols.clear();
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
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 (g_pOptions->GetLoadPars() == Options::plNone)
|
||||
{
|
||||
info("Pausing %s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(szLoFileName, ".vol"))
|
||||
{
|
||||
Vols.push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pars.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetLoadPars() == Options::plOne ||
|
||||
(g_pOptions->GetLoadPars() == Options::plNone && g_pOptions->GetParCheck()))
|
||||
{
|
||||
if (!Pars.empty())
|
||||
{
|
||||
for (DownloadQueue::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
info("Pausing %s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pausing all Vol-files except the smallest one
|
||||
FileInfo* pSmallest = NULL;
|
||||
for (DownloadQueue::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pSmallest)
|
||||
{
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else if (pSmallest->GetSize() > pFileInfo->GetSize())
|
||||
{
|
||||
info("Pausing %s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, pSmallest->GetFilename());
|
||||
pSmallest->SetPaused(true);
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Pausing %s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are files in directory for incoming nzb-files
|
||||
* and add them to download queue
|
||||
*/
|
||||
void PrePostProcessor::CheckIncomingNZBs()
|
||||
{
|
||||
DirBrowser dir(g_pOptions->GetNzbDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int len = strlen(filename);
|
||||
if (len > 4 && !strcasecmp(filename + len - 4, ".nzb"))
|
||||
{
|
||||
// file found, checking modification-time
|
||||
struct stat buffer;
|
||||
char fullfilename[1024];
|
||||
snprintf(fullfilename, 1024, "%s%c%s", g_pOptions->GetNzbDir(), (int)PATH_SEPARATOR, filename);
|
||||
fullfilename[1024-1] = '\0';
|
||||
if (!stat(fullfilename, &buffer) &&
|
||||
time(NULL) - buffer.st_mtime > 60 &&
|
||||
time(NULL) - buffer.st_ctime > 60)
|
||||
{
|
||||
// the file is at least 60 seconds old, we can process it
|
||||
info("Collection %s found", filename);
|
||||
char bakname[1024];
|
||||
if (g_pQueueCoordinator->AddFileToQueue(fullfilename))
|
||||
{
|
||||
info("Collection %s added to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.queued", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not add collection %s to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.error", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
char bakname2[1024];
|
||||
strcpy(bakname2, bakname);
|
||||
int i = 2;
|
||||
while (!stat(bakname2, &buffer))
|
||||
{
|
||||
snprintf(bakname2, 1024, "%s%i", bakname, i++);
|
||||
bakname2[1024-1] = '\0';
|
||||
}
|
||||
|
||||
rename(fullfilename, bakname2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the completed file was last unpaused file in nzb-collection
|
||||
*/
|
||||
bool PrePostProcessor::WasLastUnpausedInCollection(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo)
|
||||
{
|
||||
debug("File %s completed or deleted", pFileInfo->GetFilename());
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 != pFileInfo && !pFileInfo2->GetPaused() &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
PrePostProcessor::QueuedFile::QueuedFile(const char * szNZBFilename, const char * szParFilename, const char * szInfoName)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
PrePostProcessor::QueuedFile::~ QueuedFile()
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::CheckPars(DownloadQueue * pDownloadQueue, FileInfo * pFileInfo)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
FileList fileList;
|
||||
if (FindMainPars(pFileInfo->GetDestDir(), &fileList))
|
||||
{
|
||||
for (FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
debug("Found par: %s", szParFilename);
|
||||
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, szParFilename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
int iBaseLen = 0;
|
||||
ParChecker::ParseParFilename(szParFilename, &iBaseLen, NULL);
|
||||
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
|
||||
strncpy(szInfoName, szParFilename, maxlen);
|
||||
szInfoName[maxlen] = '\0';
|
||||
|
||||
char szParInfoName[1024];
|
||||
snprintf(szParInfoName, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
szParInfoName[1024-1] = '\0';
|
||||
|
||||
info("Queueing %s%c%s for par-check", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
QueuedFile* pQueuedFile = new QueuedFile(pFileInfo->GetNZBFilename(), szFullFilename, szParInfoName);
|
||||
m_ParQueue.push_back(pQueuedFile);
|
||||
m_bHasMoreJobs = true;
|
||||
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::FindMainPars(const char * szPath, FileList * pFileList)
|
||||
{
|
||||
pFileList->clear();
|
||||
DirBrowser dir(szPath);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int iBaseLen = 0;
|
||||
if (ParChecker::ParseParFilename(filename, &iBaseLen, NULL))
|
||||
{
|
||||
// check if the base file already added to list
|
||||
bool exists = false;
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
const char* filename2 = *it;
|
||||
exists = SameParCollection(filename, filename2);
|
||||
if (exists)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
pFileList->push_back(strdup(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
return !pFileList->empty();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::AddPar(FileInfo * pFileInfo, bool bDeleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
bool bSameCollection = m_ParChecker.IsRunning() &&
|
||||
!strcmp(pFileInfo->GetNZBFilename(), m_ParChecker.GetNZBFilename()) &&
|
||||
SameParCollection(pFileInfo->GetFilename(), BaseFileName(m_ParChecker.GetParFilename()));
|
||||
if (bSameCollection)
|
||||
{
|
||||
if (!bDeleted)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
szFullFilename[1024-1] = '\0';
|
||||
m_ParChecker.AddParFile(szFullFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParChecker.QueueChanged();
|
||||
}
|
||||
}
|
||||
m_mutexParChecker.Unlock();
|
||||
return bSameCollection;
|
||||
}
|
||||
|
||||
bool PrePostProcessor::SameParCollection(const char* szFilename1, const char* szFilename2)
|
||||
{
|
||||
int iBaseLen1 = 0, iBaseLen2 = 0;
|
||||
return ParChecker::ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
|
||||
ParChecker::ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
|
||||
iBaseLen1 == iBaseLen2 &&
|
||||
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
|
||||
}
|
||||
|
||||
void PrePostProcessor::CheckParQueue()
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
if (!m_ParChecker.IsRunning() && !m_ParQueue.empty())
|
||||
{
|
||||
QueuedFile* pQueuedFile = m_ParQueue.front();
|
||||
|
||||
info("Checking pars for %s", pQueuedFile->GetInfoName());
|
||||
m_ParChecker.SetNZBFilename(pQueuedFile->GetNZBFilename());
|
||||
m_ParChecker.SetParFilename(pQueuedFile->GetParFilename());
|
||||
m_ParChecker.SetInfoName(pQueuedFile->GetInfoName());
|
||||
m_ParChecker.Start();
|
||||
|
||||
m_ParQueue.pop_front();
|
||||
delete pQueuedFile;
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
void PrePostProcessor::ParCheckerUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFinished ||
|
||||
m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
char szPath[1024];
|
||||
strncpy(szPath, m_ParChecker.GetParFilename(), 1024);
|
||||
szPath[1024-1] = '\0';
|
||||
if (char* p = strrchr(szPath, PATH_SEPARATOR)) *p = '\0';
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szPath, (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
|
||||
bool bExists = false;
|
||||
if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
struct stat buffer;
|
||||
bExists = !stat(szBrokenLogName, &buffer);
|
||||
}
|
||||
if (!m_ParChecker.GetRepairNotNeeded() || bExists)
|
||||
{
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
fprintf(file, "Repair failed for %s: %s\n", m_ParChecker.GetInfoName(), m_ParChecker.GetErrMsg() ? m_ParChecker.GetErrMsg() : "");
|
||||
}
|
||||
else if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
fprintf(file, "Repair not needed for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
fprintf(file, "Successfully repaired %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "Repair possible for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
ExecPostScript(szPath, m_ParChecker.GetNZBFilename(), m_ParChecker.GetParFilename(),
|
||||
m_ParChecker.GetStatus() == ParChecker::psFinished);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
m_bHasMoreJobs = !m_ParQueue.empty();
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void PrePostProcessor::ExecPostScript(const char * szPath, const char * szNZBFilename, const char * szParFilename, bool bParOK)
|
||||
{
|
||||
const char* szScript = g_pOptions->GetPostProcess();
|
||||
if (!szScript || strlen(szScript) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing post-process for %s (%s)", szPath, BaseFileName(szNZBFilename));
|
||||
struct stat buffer;
|
||||
bool bExists = !stat(szScript, &buffer);
|
||||
if (!bExists)
|
||||
{
|
||||
error("Could not start post-process: could not find file %s", szScript);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bCollectionCompleted = true;
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (!pFileInfo2->GetPaused() &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
bCollectionCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (bCollectionCompleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
bCollectionCompleted = m_ParQueue.empty();
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
int iParStatus = 0;
|
||||
if (strlen(szParFilename) != 0)
|
||||
{
|
||||
iParStatus = (int)bParOK + 1;
|
||||
}
|
||||
char szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", iParStatus);
|
||||
szParStatus[10-1] = '\0';
|
||||
|
||||
char szCollectionCompleted[10];
|
||||
snprintf(szCollectionCompleted, 10, "%i", (int)bCollectionCompleted);
|
||||
szCollectionCompleted[10-1] = '\0';
|
||||
|
||||
#ifdef WIN32
|
||||
char szCmdLine[2048];
|
||||
snprintf(szCmdLine, 2048, "%s \"%s\" \"%s\" \"%s\" %s %s", szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted);
|
||||
szCmdLine[2048-1] = '\0';
|
||||
UINT ErrCode = WinExec(szCmdLine, SW_HIDE);
|
||||
if (ErrCode < 32)
|
||||
{
|
||||
char szErrMsg[255];
|
||||
szErrMsg[255-1] = '\0';
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, ErrCode, 0, szErrMsg, 255, NULL);
|
||||
error("Could not start post-process: %s", szErrMsg);
|
||||
}
|
||||
#else
|
||||
if (fork())
|
||||
{
|
||||
// continue the first instance
|
||||
return;
|
||||
}
|
||||
|
||||
// here goes the second instance
|
||||
|
||||
int h;
|
||||
for (h = getdtablesize(); h >= 0;--h) close(h); /* close all descriptors */
|
||||
h = open("/dev/null", O_RDWR); dup(h); dup(h); /* handle standart I/O */
|
||||
|
||||
execlp(szScript, szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted, NULL);
|
||||
error("Could not start post-process: %s", strerror(errno));
|
||||
exit(-1);
|
||||
#endif
|
||||
}
|
||||
110
PrePostProcessor.h
Normal file
110
PrePostProcessor.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 PREPOSTPROCESSOR_H
|
||||
#define PREPOSTPROCESSOR_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.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 QueuedFile
|
||||
{
|
||||
private:
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
|
||||
public:
|
||||
QueuedFile(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
|
||||
~QueuedFile();
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
};
|
||||
|
||||
typedef std::deque<QueuedFile*> ParQueue;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool m_bCheckIncomingNZBs;
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool WasLastUnpausedInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, bool bParOK);
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
ParChecker m_ParChecker;
|
||||
Mutex m_mutexParChecker;
|
||||
ParQueue m_ParQueue;
|
||||
ParCheckerObserver m_ParCheckerObserver;
|
||||
|
||||
void ParCheckerUpdate(Subject* Caller, void* Aspect);
|
||||
void CheckParQueue();
|
||||
void 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);
|
||||
#endif
|
||||
|
||||
public:
|
||||
PrePostProcessor();
|
||||
virtual ~PrePostProcessor();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
};
|
||||
|
||||
#endif
|
||||
728
QueueCoordinator.cpp
Normal file
728
QueueCoordinator.cpp
Normal file
@@ -0,0 +1,728 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
{
|
||||
debug("Creating QueueCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
m_DownloadQueue.clear();
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
Decoder::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();
|
||||
|
||||
Decoder::Final();
|
||||
|
||||
debug("QueueCoordinator destroyed");
|
||||
}
|
||||
|
||||
void QueueCoordinator::Run()
|
||||
{
|
||||
debug("Entering QueueCoordinator-loop");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
m_DiskState.CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && m_DiskState.Exists())
|
||||
{
|
||||
if (g_pOptions->GetReloadQueue())
|
||||
{
|
||||
m_DiskState.Load(&m_DownloadQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DiskState.Discard();
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
while (g_pOptions->GetPause() && !IsStopped())
|
||||
{
|
||||
// Sleep for a while
|
||||
usleep(500 * 1000);
|
||||
}
|
||||
|
||||
if (g_pServerPool->HasFreeConnection())
|
||||
{
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
m_bHasMoreJobs = bHasMoreArticles || !m_ActiveDownloads.empty();
|
||||
if (bHasMoreArticles && !IsStopped())
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
// two possibilities:
|
||||
// 1) hasMoreArticles==false: there are no jobs, waiting for a while
|
||||
// 2) hasMoreArticles==true: the pause prevents starting of many threads, before the download-thread locks the connection
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// there are no free connection available, waiting for a while
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->BuildDestDirName(pNZBFile->GetFileName());
|
||||
if (g_pOptions->GetDupeCheck() && IsDupe(pFileInfo))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
{
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
{
|
||||
m_DownloadQueue.push_front(*it);
|
||||
}
|
||||
}
|
||||
|
||||
pNZBFile->DetachFileInfos();
|
||||
|
||||
Aspect aspect = { eaNZBFileAdded, NULL, &m_DownloadQueue, pNZBFile->GetFileName() };
|
||||
Notify(&aspect);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, false);
|
||||
}
|
||||
|
||||
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 Qeue
|
||||
AddNZBFileToQueue(pNZBFile, false);
|
||||
|
||||
delete pNZBFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
float QueueCoordinator::CalcCurrentDownloadSpeed()
|
||||
{
|
||||
float fSpeedAllDownloads = 0;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
struct _timeval curtime;
|
||||
gettimeofday(&curtime, 0);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
|
||||
float fSpeed = 0.0f;
|
||||
struct _timeval* arttime = pArticleDownloader->GetStartTime();
|
||||
|
||||
#ifdef WIN32
|
||||
if (arttime->time != 0)
|
||||
#else
|
||||
if (arttime->tv_sec != 0)
|
||||
#endif
|
||||
{
|
||||
#ifdef WIN32
|
||||
float tdiff = (float)((curtime.time - arttime->time) + (curtime.millitm - arttime->millitm) / 1000.0);
|
||||
#else
|
||||
float tdiff = (float)((curtime.tv_sec - arttime->tv_sec) + (curtime.tv_usec - arttime->tv_usec) / 1000000.0);
|
||||
#endif
|
||||
if (tdiff > 0)
|
||||
{
|
||||
fSpeed = (pArticleDownloader->GetBytes() / tdiff / 1024);
|
||||
}
|
||||
}
|
||||
|
||||
fSpeedAllDownloads += fSpeed;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
return fSpeedAllDownloads;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int QueueCoordinator::GetFileInfoID(unsigned int iEntry)
|
||||
{
|
||||
int ID = 0;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (iEntry < m_DownloadQueue.size())
|
||||
{
|
||||
ID = m_DownloadQueue[iEntry]->GetID();
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return ID;
|
||||
}
|
||||
|
||||
FileInfo* QueueCoordinator::FindFileInfo(int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueCoordinator::GetFileInfoEntry(int iID)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
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
|
||||
*/
|
||||
bool QueueCoordinator::EditQueuePauseUnpauseEntry(int iID, bool bPause)
|
||||
{
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
FileInfo* pFileInfo = FindFileInfo(iID);
|
||||
|
||||
if (pFileInfo)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
res = true;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, true);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
bool QueueCoordinator::EditQueueDeleteEntry(int iID)
|
||||
{
|
||||
debug("Deleting queue entry");
|
||||
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
FileInfo* pFileInfo = FindFileInfo(iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
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)
|
||||
{
|
||||
DeleteFileInfo(pFileInfo);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
debug("Queue entry deleted");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
bool QueueCoordinator::EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection)
|
||||
{
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
int iEntry = GetFileInfoEntry(iID);
|
||||
if (iEntry >= 0)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (bAutoCorrection && iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if (bAutoCorrection && (unsigned int)iNewEntry > m_DownloadQueue.size() - 1)
|
||||
{
|
||||
iNewEntry = (int)m_DownloadQueue.size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= m_DownloadQueue.size() - 1)
|
||||
{
|
||||
FileInfo* fi = m_DownloadQueue[iEntry];
|
||||
m_DownloadQueue.erase(m_DownloadQueue.begin() + iEntry);
|
||||
m_DownloadQueue.insert(m_DownloadQueue.begin() + iNewEntry, fi);
|
||||
res = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, true);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
debug("Starting new ArticleDownloader");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = new ArticleDownloader();
|
||||
pArticleDownloader->SetAutoDestroy(true);
|
||||
pArticleDownloader->Attach(this);
|
||||
pArticleDownloader->SetFileInfo(pFileInfo);
|
||||
pArticleDownloader->SetArticleInfo(pArticleInfo);
|
||||
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
|
||||
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pArticleDownloader);
|
||||
pArticleDownloader->Start();
|
||||
pArticleDownloader->WaitInit();
|
||||
}
|
||||
|
||||
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->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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
bool deleteFileObj = false;
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
else if (pFileInfo->GetDeleted())
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
int cnt = 0;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
if ((*it)->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
if (cnt == 1)
|
||||
{
|
||||
// this was the last Download for a file deleted from queue
|
||||
deleteFileObj = true;
|
||||
}
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
m_mutexDownloadQueue.Lock();
|
||||
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)
|
||||
{
|
||||
// delete File from Queue
|
||||
pFileInfo->SetDeleted(true);
|
||||
|
||||
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo)
|
||||
{
|
||||
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())
|
||||
{
|
||||
m_DiskState.DiscardFileInfo(&m_DownloadQueue, pFileInfo);
|
||||
}
|
||||
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
{
|
||||
debug("Checking if the file is already queued");
|
||||
|
||||
if (pFileInfo->IsDupe())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pQueueEntry = *it;
|
||||
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()))
|
||||
{
|
||||
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", 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();
|
||||
}
|
||||
98
QueueCoordinator.h
Normal file
98
QueueCoordinator.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "DiskState.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;
|
||||
DiskState m_DiskState;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
|
||||
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
bool IsDupe(FileInfo* pFileInfo);
|
||||
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
|
||||
FileInfo* FindFileInfo(int iID);
|
||||
int GetFileInfoEntry(int iID);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo);
|
||||
void ResetHangingDownloads();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// Editing the queue
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue() ;
|
||||
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
|
||||
bool AddFileToQueue(const char* szFileName);
|
||||
bool EditQueuePauseUnpauseEntry(int iID, bool bPause);
|
||||
bool EditQueueDeleteEntry(int iID);
|
||||
bool EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection);
|
||||
int GetFileInfoID(unsigned int iEntry);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
411
README
411
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,27 +29,33 @@ 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
|
||||
executable file "NZBGet". The mode in which the program works
|
||||
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 developen on Linux.
|
||||
It was ported to Windows later and tested for compatibility with
|
||||
several POSIX-OS'es.
|
||||
|
||||
The current version (0.3.0) should run at least on:
|
||||
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
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 few platforms (including Windows), 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 +64,9 @@ 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 (tested on FreeBSD and Solaris for x86; although
|
||||
the par-support and uulib were not tested).
|
||||
|
||||
NZBGet absolutely needs the following libraries:
|
||||
|
||||
@@ -72,178 +75,185 @@ NZBGet absolutely needs the following libraries:
|
||||
|
||||
And the following libraries are optional:
|
||||
|
||||
- for curses-output-mode (enabled by default):
|
||||
- for curses-output-mode:
|
||||
- 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:
|
||||
- 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)
|
||||
- for support of encoding-formats other than yEnc:
|
||||
- libuu (http://www.fpx.de/fp/Software/UUDeview)
|
||||
|
||||
All these libraries are included in modern POSIX 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).
|
||||
All these libraries are included in modern Linux distributions and
|
||||
should be available as installable packages. 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:
|
||||
(you may get some warnings concerning 'mktemp', simply ignore them!)
|
||||
- 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:
|
||||
|
||||
--enable-uulib - to make with uulib-library, in a case you want it
|
||||
(see later section "Optional package: uulib"). This option is not
|
||||
enabled by default.
|
||||
|
||||
--disable-curses - to make without curses-support. Use this option
|
||||
if you can not use curses/ncurses.
|
||||
|
||||
--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++.
|
||||
|
||||
--disable-parprogress - to not show progress during par-check. This option
|
||||
may be needed on some systems, where sigc++ does not work very well
|
||||
(uClibc on MIPSEL is one of them). If par-check segfaults right after
|
||||
start, then you probably need this option.
|
||||
|
||||
--enable-debug - to build in debug-mode, if you want to see and log
|
||||
debug-messages.
|
||||
|
||||
|
||||
Optional package: uulib
|
||||
-----------------------
|
||||
uulib is not required to compile and run nzbget, because nzbget includes
|
||||
internal decoder for yEnc-format. However, uulib supports many other formats,
|
||||
you may possibly want to have support for. In this case you can build the
|
||||
program with uulib-support enabled.
|
||||
|
||||
NOTE: enabling uulib does not disable internal decoder. The program built with
|
||||
uulib-support can use both decoders (internal and uulib), depending on option
|
||||
"decoder" in program's configuration file.
|
||||
|
||||
To build with uulib use option "--enable-uulib" while running configure:
|
||||
|
||||
./configure --enable-uulib
|
||||
|
||||
The uulib must be installed on your system. On most linux distributions
|
||||
the package uulib-dev is available. So you only need to install this package
|
||||
and run configure with parameter "--enable-uulib".
|
||||
If you do not have this package you can compile uulib yourself:
|
||||
|
||||
- download source code of uudeview from
|
||||
http://www.fpx.de/fp/Software/UUDeview;
|
||||
- build uudeview as usually:
|
||||
/.confugure
|
||||
make
|
||||
- start nzbget's configure-script with following parameters:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=<path to uudeview>/uulib \
|
||||
--with-uulib-libraries=<path to uudeview>/uulib
|
||||
for example:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=/home/user/uudeview-0.5.20/uulib \
|
||||
--with-uulib-libraries=/home/user/uudeview-0.5.20/uulib
|
||||
- now you can compile nzbget.
|
||||
|
||||
NOTE: after nzbget is compiled, the code of uulib-library is built into
|
||||
nzbget's executable. You do not need to have uulib on target system
|
||||
to run nzbget.
|
||||
|
||||
Optional package: par-check
|
||||
---------------------------
|
||||
NZBGet can check and repair downloaded files for you. For this purpose
|
||||
it uses library par2.
|
||||
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.
|
||||
To build with par-check use option "--enable-parcheck" while running
|
||||
configure:
|
||||
|
||||
./configure --enable-parcheck
|
||||
|
||||
In a case errors occur during this process the inclusion of par2-module
|
||||
can be disabled using configure option "--disable-parcheck":
|
||||
The libpar2 and libsigc++ (version 2 or later) must be installed on your
|
||||
system. On most linux distributions these libraries are available as packages.
|
||||
So you only need to install theme and run configure with parameter
|
||||
"--enable-parcheck".
|
||||
If you do not have these package you can compile them yourself. Please
|
||||
refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. Following configure-parameters may be usefull:
|
||||
|
||||
./configure --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.
|
||||
|
||||
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:
|
||||
Please refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. 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 +277,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,70 +304,41 @@ 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
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default).
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default, the
|
||||
directory must exist on server's start; otherwise it will not be monitored).
|
||||
|
||||
To check the status of server start client and connect it to server:
|
||||
|
||||
nzbget -C
|
||||
|
||||
The client have three different (display) outputmodes, which you can select
|
||||
in configuration file (on client computer) or in command line. Try them:
|
||||
|
||||
nzbget -o outputmode=log -C
|
||||
|
||||
nzbget -o outputmode=color -C
|
||||
|
||||
nzbget -o outputmode=curses -C
|
||||
|
||||
To list files in server's queue:
|
||||
|
||||
nzbget -L
|
||||
|
||||
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)
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.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:
|
||||
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:
|
||||
|
||||
[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:
|
||||
|
||||
nzbget -E T 2
|
||||
nzbget -E T -I 2
|
||||
|
||||
or to pause files with IDs from 10 to 20:
|
||||
|
||||
nzbget -E P 10-20
|
||||
nzbget -E P -I 10-20
|
||||
|
||||
or to delete files from queue:
|
||||
or to delete file 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:
|
||||
|
||||
nzbget -E G D 1
|
||||
nzbget -E D -I 3
|
||||
|
||||
The switch "o" is useful to override options in configuration files.
|
||||
For example:
|
||||
@@ -380,7 +355,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 +371,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.
|
||||
489
RemoteClient.cpp
Normal file
489
RemoteClient.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* This file if 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>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Options.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
RemoteClient::RemoteClient()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_pNetAddress = NULL;
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
printf("sizeof(SNZBMessageBase)=%i\n", sizeof(SNZBMessageBase));
|
||||
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
|
||||
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
|
||||
printf("sizeof(SNZBListRequestAnswer)=%i\n", sizeof(SNZBListRequestAnswer));
|
||||
printf("sizeof(SNZBListRequestAnswerEntry)=%i\n", sizeof(SNZBListRequestAnswerEntry));
|
||||
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
|
||||
printf("sizeof(SNZBLogRequestAnswer)=%i\n", sizeof(SNZBLogRequestAnswer));
|
||||
printf("sizeof(SNZBLogRequestAnswerEntry)=%i\n", sizeof(SNZBLogRequestAnswerEntry));
|
||||
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(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
|
||||
pMessageBase->m_iType = iRequest;
|
||||
pMessageBase->m_iSize = iSize;
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
void RemoteClient::ReceiveCommandResult()
|
||||
{
|
||||
char szAnswer[1024];
|
||||
strcpy(szAnswer, "N/A");
|
||||
printf("request sent\n");
|
||||
m_pConnection->Recv(szAnswer, 1024);
|
||||
printf("nzbserver returned: %s\n", szAnswer);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 (!NZBFile::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, NZBMessageRequest::eRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = bAddFirst;
|
||||
DownloadRequest.m_iTrailingDataLength = 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);
|
||||
ReceiveCommandResult();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (szBuffer)
|
||||
{
|
||||
free(szBuffer);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerList()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = true;
|
||||
ListRequest.m_bServerState = true;
|
||||
|
||||
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
if (m_pConnection->Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
|
||||
if (!m_pConnection->RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ListRequestAnswer.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 (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
{
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
|
||||
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (pListAnswer->m_iRemainingSize < pListAnswer->m_iFileSize)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - pListAnswer->m_iRemainingSize * 100.0 / pListAnswer->m_iFileSize), "\%");
|
||||
}
|
||||
char szStatus[100];
|
||||
if (pListAnswer->m_bPaused)
|
||||
{
|
||||
sprintf(szStatus, " (paused)");
|
||||
lPaused += pListAnswer->m_iRemainingSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus[0] = '\0';
|
||||
lRemaining += pListAnswer->m_iRemainingSize;
|
||||
}
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
strncpy(szNZBNiceName, BaseFileName(szNZBFilename), 1024);
|
||||
szNZBNiceName[1024-1] = '\0';
|
||||
if (char* p = strrchr(szNZBNiceName, '.')) *p = '\0';
|
||||
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", pListAnswer->m_iID, szNZBNiceName, (int)PATH_SEPARATOR, szFilename, pListAnswer->m_iFileSize / 1024.0 / 1024.0, szCompleted, szStatus);
|
||||
|
||||
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
|
||||
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Files: %i\n", ListRequestAnswer.m_iNrTrailingEntries);
|
||||
if (lPaused > 0)
|
||||
{
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", lRemaining / 1024.0 / 1024.0, lPaused / 1024.0 / 1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Remaining size: %.2f MB\n", lRemaining / 1024.0 / 1024.0);
|
||||
}
|
||||
printf("Download rate: %.1f KB/s\n", ListRequestAnswer.m_fDownloadRate);
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
printf("Threads running: %i\n", ListRequestAnswer.m_iThreadCount);
|
||||
printf("Server state: %s\n", ListRequestAnswer.m_bServerPaused ? "Paused" : "Running");
|
||||
|
||||
if (ListRequestAnswer.m_fDownloadLimit > 0)
|
||||
{
|
||||
printf("Speed limit: %.1f KB/s\n", ListRequestAnswer.m_fDownloadLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Speed limit: Unlimited\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerLog(int iLines)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = iLines;
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
if (m_pConnection->Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
|
||||
if (!m_pConnection->RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (LogRequestAnswer.m_iTrailingDataLength == 0)
|
||||
{
|
||||
printf("Log is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Log (last %i entries)\n", LogRequestAnswer.m_iNrTrailingEntries);
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
{
|
||||
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
|
||||
switch (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;
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, NZBMessageRequest::eRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = bPause;
|
||||
|
||||
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBSetDownloadRateRequest SetDownloadRateRequest;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, NZBMessageRequest::eRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_fDownloadRate = fRate;
|
||||
|
||||
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerDumpDebug()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBDumpDebugRequest DumpDebugInfo;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, NZBMessageRequest::eRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
DumpDebugInfo.m_iLevel = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
|
||||
{
|
||||
if (iIDTo <= 0)
|
||||
{
|
||||
printf("File(s) not specified (use option -I)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, NZBMessageRequest::eRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = iAction;
|
||||
EditQueueRequest.m_iOffset = iOffset;
|
||||
EditQueueRequest.m_iIDFrom = iIDFrom;
|
||||
EditQueueRequest.m_iIDTo = iIDTo;
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
ReceiveCommandResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerShutdown()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBMessageBase QuitRequest;
|
||||
|
||||
InitMessageBase(&QuitRequest, NZBMessageRequest::eRequestShutdown, sizeof(QuitRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&QuitRequest), sizeof(QuitRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
ReceiveCommandResult();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
61
RemoteClient.h
Normal file
61
RemoteClient.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
|
||||
void ReceiveCommandResult();
|
||||
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 iIDFrom, int iIDTo);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
};
|
||||
|
||||
#endif
|
||||
527
RemoteServer.cpp
Normal file
527
RemoteServer.cpp
Normal file
@@ -0,0 +1,527 @@
|
||||
/*
|
||||
* This file if 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 "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequests[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List",
|
||||
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
|
||||
RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
}
|
||||
|
||||
RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
delete m_pNetAddress;
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
m_pConnection->Bind();
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = m_pConnection->Accept();
|
||||
if (iSocket == INVALID_SOCKET)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
// error binding on port. wait 0.5 sec. and retry
|
||||
usleep(500 * 1000);
|
||||
delete m_pConnection;
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->Bind();
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageCommand* commandThread = new MessageCommand();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->Start();
|
||||
}
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
debug("Exiting RemoteServer-loop");
|
||||
}
|
||||
|
||||
void RemoteServer::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
m_pConnection->Cancel();
|
||||
#ifdef WIN32
|
||||
m_pConnection->Disconnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// MessageCommand
|
||||
|
||||
void MessageCommand::SendResponse(char* szAnswer)
|
||||
{
|
||||
send(m_iSocket, szAnswer, strlen(szAnswer), 0);
|
||||
}
|
||||
|
||||
void MessageCommand::ProcessRequest()
|
||||
{
|
||||
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
|
||||
|
||||
switch (pMessageBase->m_iType)
|
||||
{
|
||||
case NZBMessageRequest::eRequestDownload:
|
||||
{
|
||||
RequestDownload();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestList:
|
||||
{
|
||||
RequestList();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestLog:
|
||||
{
|
||||
RequestLog();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestPauseUnpause:
|
||||
{
|
||||
SNZBPauseUnpauseRequest* pPauseUnpauseRequest = (SNZBPauseUnpauseRequest*) & m_RequestBuffer;
|
||||
g_pOptions->SetPause(pPauseUnpauseRequest->m_bPause);
|
||||
SendResponse("Pause-/Unpause-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestEditQueue:
|
||||
{
|
||||
RequestEditQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestSetDownloadRate:
|
||||
{
|
||||
SNZBSetDownloadRateRequest* pSetDownloadRequest = (SNZBSetDownloadRateRequest*) & m_RequestBuffer;
|
||||
g_pOptions->SetDownloadRate(pSetDownloadRequest->m_fDownloadRate);
|
||||
SendResponse("Rate-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestDumpDebug:
|
||||
{
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendResponse("Debug-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestShutdown:
|
||||
{
|
||||
SendResponse("Stopping server");
|
||||
ExitProc();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("NZB-Request not yet supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageCommand::RequestDownload()
|
||||
{
|
||||
SNZBDownloadRequest* pDownloadRequest = (SNZBDownloadRequest*) & m_RequestBuffer;
|
||||
const char* pExtraData = (m_iExtraDataLength > 0) ? ((char*)pDownloadRequest + pDownloadRequest->m_MessageBase.m_iSize) : NULL;
|
||||
int NeedBytes = pDownloadRequest->m_iTrailingDataLength - m_iExtraDataLength;
|
||||
char* pRecvBuffer = (char*)malloc(pDownloadRequest->m_iTrailingDataLength + 1);
|
||||
memcpy(pRecvBuffer, pExtraData, m_iExtraDataLength);
|
||||
char* pBufPtr = pRecvBuffer + m_iExtraDataLength;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
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;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(pDownloadRequest->m_szFilename, pRecvBuffer, pDownloadRequest->m_iTrailingDataLength);
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", pDownloadRequest->m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pDownloadRequest->m_bAddFirst);
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(pDownloadRequest->m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendResponse(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(pDownloadRequest->m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendResponse(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void MessageCommand::RequestList()
|
||||
{
|
||||
SNZBListRequest* pListRequest = (SNZBListRequest*) & m_RequestBuffer;
|
||||
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
memset(&ListRequestAnswer, 0, sizeof(ListRequestAnswer));
|
||||
ListRequestAnswer.m_iSize = sizeof(ListRequestAnswer);
|
||||
ListRequestAnswer.m_iEntrySize = sizeof(SNZBListRequestAnswerEntry);
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (pListRequest->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(SNZBListRequestAnswerEntry);
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bufsize += strlen(pFileInfo->GetNZBFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetSubject()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetDestDir()) + 1;
|
||||
}
|
||||
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) bufptr;
|
||||
pListAnswer->m_iID = pFileInfo->GetID();
|
||||
pListAnswer->m_iFileSize = (int)pFileInfo->GetSize();
|
||||
pListAnswer->m_bFilenameConfirmed = pFileInfo->GetFilenameConfirmed();
|
||||
pListAnswer->m_iRemainingSize = (int)pFileInfo->GetRemainingSize();
|
||||
pListAnswer->m_bPaused = pFileInfo->GetPaused();
|
||||
pListAnswer->m_iNZBFilenameLen = strlen(pFileInfo->GetNZBFilename()) + 1;
|
||||
pListAnswer->m_iSubjectLen = strlen(pFileInfo->GetSubject()) + 1;
|
||||
pListAnswer->m_iFilenameLen = strlen(pFileInfo->GetFilename()) + 1;
|
||||
pListAnswer->m_iDestDirLen = strlen(pFileInfo->GetDestDir()) + 1;
|
||||
bufptr += sizeof(SNZBListRequestAnswerEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBFilename());
|
||||
bufptr += pListAnswer->m_iNZBFilenameLen;
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += pListAnswer->m_iSubjectLen;
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += pListAnswer->m_iFilenameLen;
|
||||
strcpy(bufptr, pFileInfo->GetDestDir());
|
||||
bufptr += pListAnswer->m_iDestDirLen;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListRequestAnswer.m_iNrTrailingEntries = NrEntries;
|
||||
ListRequestAnswer.m_iTrailingDataLength = bufsize;
|
||||
}
|
||||
|
||||
if (pListRequest->m_bServerState)
|
||||
{
|
||||
ListRequestAnswer.m_fDownloadRate = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
ListRequestAnswer.m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
ListRequestAnswer.m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
ListRequestAnswer.m_bServerPaused = g_pOptions->GetPause();
|
||||
ListRequestAnswer.m_iThreadCount = Thread::GetThreadCount() - 1; // not counting itself
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListRequestAnswer, sizeof(ListRequestAnswer), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageCommand::RequestLog()
|
||||
{
|
||||
SNZBLogRequest* pLogRequest = (SNZBLogRequest*) & m_RequestBuffer;
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = pLogRequest->m_iLines;
|
||||
unsigned int iIDFrom = pLogRequest->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(SNZBLogRequestAnswerEntry);
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
bufsize += strlen(pMessage->GetText()) + 1;
|
||||
}
|
||||
|
||||
char* buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) bufptr;
|
||||
pLogAnswer->m_iID = pMessage->GetID();
|
||||
pLogAnswer->m_iKind = pMessage->GetKind();
|
||||
pLogAnswer->m_tTime = pMessage->GetTime();
|
||||
pLogAnswer->m_iTextLen = strlen(pMessage->GetText()) + 1;
|
||||
bufptr += sizeof(SNZBLogRequestAnswerEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += pLogAnswer->m_iTextLen;
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
LogRequestAnswer.m_iSize = sizeof(LogRequestAnswer);
|
||||
LogRequestAnswer.m_iEntrySize = sizeof(SNZBLogRequestAnswerEntry);
|
||||
LogRequestAnswer.m_iNrTrailingEntries = iNrEntries;
|
||||
LogRequestAnswer.m_iTrailingDataLength = bufsize;
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogRequestAnswer, sizeof(LogRequestAnswer), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void MessageCommand::RequestEditQueue()
|
||||
{
|
||||
SNZBEditQueueRequest* pEditQueueRequest = (SNZBEditQueueRequest*) & m_RequestBuffer;
|
||||
|
||||
int From = pEditQueueRequest->m_iIDFrom;
|
||||
int To = pEditQueueRequest->m_iIDTo;
|
||||
int Step = 1;
|
||||
if ((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveTop) ||
|
||||
((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveOffset) &&
|
||||
(pEditQueueRequest->m_iOffset < 0)))
|
||||
{
|
||||
Step = -1;
|
||||
int tmp = From; From = To; To = tmp;
|
||||
}
|
||||
|
||||
for (int ID = From; ID != To + Step; ID += Step)
|
||||
{
|
||||
switch (pEditQueueRequest->m_iAction)
|
||||
{
|
||||
case NZBMessageRequest::eActionPause:
|
||||
case NZBMessageRequest::eActionResume:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, pEditQueueRequest->m_iAction == NZBMessageRequest::eActionPause);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveOffset:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, pEditQueueRequest->m_iOffset, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveTop:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveBottom:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, + 1000000, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionDelete:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueDeleteEntry(ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SendResponse("Edit-Command completed successfully");
|
||||
}
|
||||
|
||||
void MessageCommand::SetSocket(SOCKET iSocket)
|
||||
{
|
||||
m_iSocket = iSocket;
|
||||
}
|
||||
|
||||
void MessageCommand::Run()
|
||||
{
|
||||
int iRequestReceived = 0;
|
||||
|
||||
// Read the first package which needs to be a request
|
||||
|
||||
iRequestReceived = recv(m_iSocket, (char*) & m_RequestBuffer, sizeof(m_RequestBuffer), 0);
|
||||
if (iRequestReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if (pMessageBase->m_iId != NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(pMessageBase->m_szPassword, g_pOptions->GetServerPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i, but password invalid", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Info - connection received
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* ip = inet_ntoa(PeerName.sin_addr);
|
||||
#else
|
||||
char ip[20];
|
||||
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
|
||||
#endif
|
||||
debug("%s request received from %s", g_szMessageRequests[pMessageBase->m_iType], ip);
|
||||
}
|
||||
|
||||
m_iExtraDataLength = iRequestReceived - pMessageBase->m_iSize;
|
||||
|
||||
ProcessRequest();
|
||||
|
||||
// Close the socket
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
69
RemoteServer.h
Normal file
69
RemoteServer.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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 REMOTESERVER_H
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
static int const REQUESTBUFFERSIZE = 8192;
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
RemoteServer();
|
||||
~RemoteServer();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
};
|
||||
|
||||
class MessageCommand : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
char m_RequestBuffer[REQUESTBUFFERSIZE];
|
||||
int m_iExtraDataLength;
|
||||
|
||||
void ProcessRequest();
|
||||
void RequestDownload();
|
||||
void RequestList();
|
||||
void RequestLog();
|
||||
void RequestEditQueue();
|
||||
void SendResponse(char* szAnswer);
|
||||
|
||||
public:
|
||||
void SetSocket(SOCKET iSocket);
|
||||
virtual void Run();
|
||||
};
|
||||
|
||||
#endif
|
||||
210
ServerPool.cpp
Normal file
210
ServerPool.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
ServerPool::ServerPool()
|
||||
{
|
||||
debug("Creating ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_FreeConnections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
m_FreeConnections.clear();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
m_FreeConnections.push_back(pNewsServer);
|
||||
}
|
||||
}
|
||||
|
||||
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 level)
|
||||
{
|
||||
debug("Getting connection");
|
||||
|
||||
debug("sem_wait...");
|
||||
// decrease m_Semaphore counter or block
|
||||
bool bWaitVal = m_Semaphores[level]->Wait();
|
||||
debug("sem_wait...OK");
|
||||
|
||||
if (!bWaitVal)
|
||||
{
|
||||
debug("semaphore error: %i", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexFree.Lock();
|
||||
|
||||
NNTPConnection* pConnection = NULL;
|
||||
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
|
||||
{
|
||||
NewsServer* server = *it;
|
||||
if (server->GetLevel() == level)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = new NNTPConnection(server);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_FreeConnections.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexFree.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection)
|
||||
{
|
||||
debug("Freeing connection");
|
||||
|
||||
// give back free connection
|
||||
m_mutexFree.Lock();
|
||||
m_FreeConnections.push_back(pConnection->GetNewsServer());
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
m_mutexFree.Unlock();
|
||||
|
||||
delete pConnection;
|
||||
}
|
||||
|
||||
bool ServerPool::HasFreeConnection()
|
||||
{
|
||||
return !m_Semaphores[0]->IsLocked();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
{
|
||||
debug(" ServerPool");
|
||||
debug(" ----------------");
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
|
||||
m_mutexFree.Lock();
|
||||
|
||||
debug(" Free Connections: %i", m_FreeConnections.size());
|
||||
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
|
||||
{
|
||||
debug(" Free Connection: level=%i", (*it)->GetLevel());
|
||||
}
|
||||
/*
|
||||
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_mutexFree.Unlock();
|
||||
}
|
||||
64
ServerPool.h
Normal file
64
ServerPool.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 <list>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NewsServer.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class ServerPool
|
||||
{
|
||||
private:
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
|
||||
Servers m_Servers;
|
||||
Servers m_FreeConnections;
|
||||
Semaphores m_Semaphores;
|
||||
int m_iMaxLevel;
|
||||
Mutex m_mutexFree;
|
||||
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 level);
|
||||
bool HasFreeConnection();
|
||||
void FreeConnection(NNTPConnection* con);
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
280
Thread.cpp
Normal file
280
Thread.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* 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>
|
||||
|
||||
#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);
|
||||
#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::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
|
||||
DWORD ThreadId;
|
||||
m_Thread = CreateThread(NULL, 0, Thread::thread_handler, (void *) this, 0, &ThreadId);
|
||||
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
|
||||
DWORD WINAPI 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();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
int iThreadCount = m_iThreadCount;
|
||||
m_mutexThread.Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
|
||||
114
Thread.h
Normal file
114
Thread.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 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 DWORD WINAPI 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
|
||||
219
Util.cpp
Normal file
219
Util.cpp
Normal file
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
|
||||
char* BaseFileName(const char* filename)
|
||||
{
|
||||
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
|
||||
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
|
||||
if (p1)
|
||||
{
|
||||
if ((p && p < p1) || !p)
|
||||
{
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (char*)filename;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// getopt for WIN32:
|
||||
// from http://www.codeproject.com/cpp/xgetopt.asp
|
||||
// Original Author: Hans Dietrich (hdietrich2@hotmail.com)
|
||||
// Released to public domain from author (thanks)
|
||||
// Slightly modified by Andrei Prygounkov
|
||||
|
||||
char *optarg; // global argument pointer
|
||||
int optind = 0; // global argv index
|
||||
|
||||
int getopt(int argc, char *argv[], char *optstring)
|
||||
{
|
||||
static char *next = NULL;
|
||||
if (optind == 0)
|
||||
next = NULL;
|
||||
|
||||
optarg = NULL;
|
||||
|
||||
if (next == NULL || *next == '\0')
|
||||
{
|
||||
if (optind == 0)
|
||||
optind++;
|
||||
|
||||
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
|
||||
{
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[optind], "--") == 0)
|
||||
{
|
||||
optind++;
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
next = argv[optind];
|
||||
next++; // skip past -
|
||||
optind++;
|
||||
}
|
||||
|
||||
char c = *next++;
|
||||
char *cp = strchr(optstring, c);
|
||||
|
||||
if (cp == NULL || c == ':')
|
||||
return '?';
|
||||
|
||||
cp++;
|
||||
if (*cp == ':')
|
||||
{
|
||||
if (*next != '\0')
|
||||
{
|
||||
optarg = next;
|
||||
next = NULL;
|
||||
}
|
||||
else if (optind < argc)
|
||||
{
|
||||
optarg = argv[optind];
|
||||
optind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
char szMask[MAX_PATH + 1];
|
||||
int len = strlen(szPath);
|
||||
if (szPath[len] == '\\' || szPath[len] == '/')
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s*.*", szPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
|
||||
}
|
||||
szMask[MAX_PATH] = '\0';
|
||||
m_hFile = _findfirst(szMask, &m_FindData);
|
||||
m_bFirst = true;
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_hFile != -1L)
|
||||
{
|
||||
_findclose(m_hFile);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
bool bOK = false;
|
||||
if (m_bFirst)
|
||||
{
|
||||
bOK = m_hFile != -1L;
|
||||
m_bFirst = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bOK = _findnext(m_hFile, &m_FindData) == 0;
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
return m_FindData.name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
m_pDir = opendir(szPath);
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
closedir(m_pDir);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
m_pFindData = readdir(m_pDir);
|
||||
if (m_pFindData)
|
||||
{
|
||||
return m_pFindData->d_name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void NormalizePathSeparators(char* Path)
|
||||
{
|
||||
for (char* p = Path; *p; p++)
|
||||
{
|
||||
if (*p == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
*p = PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
64
Util.h
Normal file
64
Util.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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 UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.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();
|
||||
};
|
||||
|
||||
char* BaseFileName(const char* filename);
|
||||
|
||||
void NormalizePathSeparators(char* Path);
|
||||
|
||||
#endif
|
||||
1220
aclocal.m4
vendored
1220
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
110
config.h.in
110
config.h.in
@@ -6,28 +6,20 @@
|
||||
/* 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 show progress during par-check (it must be disabled if
|
||||
sigc++ doesn't work correctly) */
|
||||
#undef ENABLE_PARPROGRESS
|
||||
|
||||
/* Define to 1 to enable unit and integration tests */
|
||||
#undef ENABLE_TESTS
|
||||
/* Define to 1 to include support for uulib */
|
||||
#undef ENABLE_UULIB
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
|
||||
/* 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 +29,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 +50,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 to use pragma pack directive in MessageBase.h */
|
||||
#undef HAVE_PRAGMA_PACK
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
@@ -115,18 +59,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 +77,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 +92,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*)
|
||||
810
configure.ac
810
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.3.0, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.3.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,16 @@ 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])],)
|
||||
|
||||
|
||||
dnl
|
||||
dnl fsync
|
||||
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_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
|
||||
|
||||
dnl
|
||||
@@ -119,7 +95,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 +108,93 @@ 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 check for pragma pack
|
||||
dnl
|
||||
AC_MSG_CHECKING(for pragma pack)
|
||||
AC_TRY_COMPILE([#pragma pack(1)
|
||||
#pragma pack()],,
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_PRAGMA_PACK],1,[Define to 1 to use pragma pack directive in MessageBase.h]),
|
||||
AC_MSG_RESULT([no]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
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 +202,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 +236,128 @@ 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 uulib. Deafult: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use uulib for decoding and joining)
|
||||
AC_ARG_ENABLE(uulib,
|
||||
[ --enable-uulib use uulib for decoding and joining],
|
||||
[ ENABLEUULIB=$enableval ],
|
||||
[ ENABLEUULIB=no] )
|
||||
AC_MSG_RESULT($ENABLEUULIB)
|
||||
if test "$ENABLEUULIB" = "yes"; then
|
||||
AC_DEFINE([ENABLE_UULIB],1,[Define to 1 to include support for uulib])
|
||||
|
||||
dnl
|
||||
dnl checks for uulib includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(uulib_includes,
|
||||
[ --with-uulib-includes=DIR uulib include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER($INCVAL/uudeview.h,,
|
||||
AC_MSG_ERROR("uulib header files were not found in $INCVAL."))
|
||||
|
||||
AC_ARG_WITH(uulib_libraries,
|
||||
[ --with-uulib-libraries=DIR uulib library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_SEARCH_LIBS([UUInitialize], [uu],,
|
||||
AC_MSG_ERROR("uulib library not found in $LIBVAL."))
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use lib2par for par-checking. Deafult: no
|
||||
dnl
|
||||
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 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_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"
|
||||
|
||||
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
|
||||
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++)
|
||||
|
||||
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
|
||||
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"])
|
||||
|
||||
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
|
||||
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++)
|
||||
|
||||
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_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
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
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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"])
|
||||
dnl
|
||||
dnl Enable par-check-progress (via sigc++)? Deafult: yes
|
||||
dnl It doesn't work on WL500gP (uClibc), so we need a way to disable it
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to show progress during par-check )
|
||||
AC_ARG_ENABLE(parprogress,
|
||||
[ --disable-parprogress do not show progress during par-check (it must be disabled if sigc++ doesn't work correctly)],
|
||||
[ USEPARPROGRESS=$enableval ],
|
||||
[ USEPARPROGRESS=yes] )
|
||||
AC_MSG_RESULT($USEPARPROGRESS)
|
||||
if test "$USEPARPROGRESS" = "yes"; then
|
||||
AC_DEFINE([ENABLE_PARPROGRESS],1,[Define to 1 to show progress during par-check (it must be disabled if sigc++ doesn't work correctly)])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(zlib.h,,
|
||||
AC_MSG_ERROR("zlib header files not found"))
|
||||
AC_SEARCH_LIBS([deflateBound], [z], ,
|
||||
AC_MSG_ERROR("zlib library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl 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 +366,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();
|
||||
}
|
||||
@@ -1,45 +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 SCHEDULERSCRIPT_H
|
||||
#define SCHEDULERSCRIPT_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "NzbScript.h"
|
||||
|
||||
class SchedulerScriptController : public Thread, public NzbScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(const char* param, bool externalProcess, int taskId);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* script);
|
||||
|
||||
private:
|
||||
CString m_script;
|
||||
bool m_externalProcess;
|
||||
int m_taskId;
|
||||
|
||||
void PrepareParams(const char* scriptName);
|
||||
void ExecuteExternalProcess();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,500 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 "Util.h"
|
||||
#include "FileSystem.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
static const char* BEGIN_SCRIPT_SIGNATURE = "### NZBGET ";
|
||||
static const char* POST_SCRIPT_SIGNATURE = "POST-PROCESSING";
|
||||
static const char* SCAN_SCRIPT_SIGNATURE = "SCAN";
|
||||
static const char* QUEUE_SCRIPT_SIGNATURE = "QUEUE";
|
||||
static const char* SCHEDULER_SCRIPT_SIGNATURE = "SCHEDULER";
|
||||
static const char* FEED_SCRIPT_SIGNATURE = "FEED";
|
||||
static const char* END_SCRIPT_SIGNATURE = " SCRIPT";
|
||||
static const char* QUEUE_EVENTS_SIGNATURE = "### QUEUE EVENTS:";
|
||||
static const char* TASK_TIME_SIGNATURE = "### TASK TIME:";
|
||||
static const char* DEFINITION_SIGNATURE = "###";
|
||||
|
||||
void ScriptConfig::InitOptions()
|
||||
{
|
||||
InitScripts();
|
||||
InitConfigTemplates();
|
||||
CreateTasks();
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfig(Options::OptEntries* optEntries)
|
||||
{
|
||||
// read config file
|
||||
DiskFile infile;
|
||||
|
||||
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omRead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename());
|
||||
CString buf;
|
||||
buf.Reserve(fileLen);
|
||||
|
||||
while (infile.ReadLine(buf, fileLen + 1))
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
Util::TrimRight(buf);
|
||||
|
||||
// skip comments and empty lines
|
||||
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CString optname;
|
||||
CString optvalue;
|
||||
if (Options::SplitOptionString(buf, optname, optvalue))
|
||||
{
|
||||
optEntries->emplace_back(optname, optvalue);
|
||||
}
|
||||
}
|
||||
|
||||
infile.Close();
|
||||
|
||||
Options::ConvertOldOptions(optEntries);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::SaveConfig(Options::OptEntries* optEntries)
|
||||
{
|
||||
// save to config file
|
||||
DiskFile infile;
|
||||
|
||||
if (!infile.Open(g_Options->GetConfigFilename(), DiskFile::omReadWrite))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<CString> config;
|
||||
std::set<Options::OptEntry*> writtenOptions;
|
||||
|
||||
// read config file into memory array
|
||||
int fileLen = (int)FileSystem::FileSize(g_Options->GetConfigFilename()) + 1;
|
||||
CString content;
|
||||
content.Reserve(fileLen);
|
||||
while (infile.ReadLine(content, fileLen + 1))
|
||||
{
|
||||
config.push_back(*content);
|
||||
}
|
||||
content.Clear();
|
||||
|
||||
// write config file back to disk, replace old values of existing options with new values
|
||||
infile.Seek(0);
|
||||
for (CString& buf : config)
|
||||
{
|
||||
const char* eq = strchr(buf, '=');
|
||||
if (eq && buf[0] != '#')
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
buf.TrimRight();
|
||||
|
||||
CString optname;
|
||||
CString optvalue;
|
||||
if (g_Options->SplitOptionString(buf, optname, optvalue))
|
||||
{
|
||||
Options::OptEntry* optEntry = optEntries->FindOption(optname);
|
||||
if (optEntry)
|
||||
{
|
||||
infile.Print("%s=%s\n", optEntry->GetName(), optEntry->GetValue());
|
||||
writtenOptions.insert(optEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
infile.Print("%s", *buf);
|
||||
}
|
||||
}
|
||||
|
||||
// write new options
|
||||
for (Options::OptEntry& optEntry : *optEntries)
|
||||
{
|
||||
std::set<Options::OptEntry*>::iterator fit = writtenOptions.find(&optEntry);
|
||||
if (fit == writtenOptions.end())
|
||||
{
|
||||
infile.Print("%s=%s\n", optEntry.GetName(), optEntry.GetValue());
|
||||
}
|
||||
}
|
||||
|
||||
// close and truncate the file
|
||||
int pos = (int)infile.Position();
|
||||
infile.Close();
|
||||
|
||||
FileSystem::TruncateFile(g_Options->GetConfigFilename(), pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* configTemplates)
|
||||
{
|
||||
CharBuffer buffer;
|
||||
if (!FileSystem::LoadFileIntoBuffer(g_Options->GetConfigTemplate(), buffer, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
configTemplates->emplace_back(Script("", ""), buffer);
|
||||
|
||||
if (!g_Options->GetScriptDir())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Scripts scriptList;
|
||||
LoadScripts(&scriptList);
|
||||
|
||||
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
|
||||
|
||||
for (Script& script : scriptList)
|
||||
{
|
||||
DiskFile infile;
|
||||
if (!infile.Open(script.GetLocation(), DiskFile::omRead))
|
||||
{
|
||||
configTemplates->emplace_back(std::move(script), "");
|
||||
continue;
|
||||
}
|
||||
|
||||
StringBuilder templ;
|
||||
char buf[1024];
|
||||
bool inConfig = false;
|
||||
bool inHeader = false;
|
||||
|
||||
while (infile.ReadLine(buf, sizeof(buf) - 1))
|
||||
{
|
||||
if (!strncmp(buf, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) &&
|
||||
strstr(buf, END_SCRIPT_SIGNATURE) &&
|
||||
(strstr(buf, POST_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCAN_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, QUEUE_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCHEDULER_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, FEED_SCRIPT_SIGNATURE)))
|
||||
{
|
||||
if (inConfig)
|
||||
{
|
||||
break;
|
||||
}
|
||||
inConfig = true;
|
||||
inHeader = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
inHeader &= !strncmp(buf, DEFINITION_SIGNATURE, definitionSignatureLen);
|
||||
|
||||
if (inConfig && !inHeader)
|
||||
{
|
||||
templ.Append(buf);
|
||||
}
|
||||
}
|
||||
|
||||
infile.Close();
|
||||
|
||||
configTemplates->emplace_back(std::move(script), templ);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptConfig::InitConfigTemplates()
|
||||
{
|
||||
if (!LoadConfigTemplates(&m_configTemplates))
|
||||
{
|
||||
error("Could not read configuration templates");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::InitScripts()
|
||||
{
|
||||
LoadScripts(&m_scripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScripts(Scripts* scripts)
|
||||
{
|
||||
if (Util::EmptyStr(g_Options->GetScriptDir()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Scripts tmpScripts;
|
||||
|
||||
Tokenizer tokDir(g_Options->GetScriptDir(), ",;");
|
||||
while (const char* scriptDir = tokDir.Next())
|
||||
{
|
||||
LoadScriptDir(&tmpScripts, scriptDir, false);
|
||||
}
|
||||
|
||||
tmpScripts.sort(
|
||||
[](Script& script1, Script& script2)
|
||||
{
|
||||
return strcmp(script1.GetName(), script2.GetName()) < 0;
|
||||
});
|
||||
|
||||
// first add all scripts from ScriptOrder
|
||||
Tokenizer tokOrder(g_Options->GetScriptOrder(), ",;");
|
||||
while (const char* scriptName = tokOrder.Next())
|
||||
{
|
||||
Scripts::iterator pos = std::find_if(tmpScripts.begin(), tmpScripts.end(),
|
||||
[scriptName](Script& script)
|
||||
{
|
||||
return !strcmp(script.GetName(), scriptName);
|
||||
});
|
||||
|
||||
if (pos != tmpScripts.end())
|
||||
{
|
||||
scripts->splice(scripts->end(), tmpScripts, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// then add all other scripts from scripts directory
|
||||
scripts->splice(scripts->end(), std::move(tmpScripts));
|
||||
|
||||
BuildScriptDisplayNames(scripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir)
|
||||
{
|
||||
DirBrowser dir(directory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (filename[0] != '.' && filename[0] != '_')
|
||||
{
|
||||
BString<1024> fullFilename("%s%c%s", directory, PATH_SEPARATOR, filename);
|
||||
|
||||
if (!FileSystem::DirectoryExists(fullFilename))
|
||||
{
|
||||
BString<1024> scriptName = BuildScriptName(directory, filename, isSubDir);
|
||||
if (ScriptExists(scripts, scriptName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Script script(scriptName, fullFilename);
|
||||
if (LoadScriptFile(&script))
|
||||
{
|
||||
scripts->push_back(std::move(script));
|
||||
}
|
||||
}
|
||||
else if (!isSubDir)
|
||||
{
|
||||
LoadScriptDir(scripts, fullFilename, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadScriptFile(Script* script)
|
||||
{
|
||||
DiskFile infile;
|
||||
if (!infile.Open(script->GetLocation(), DiskFile::omRead))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CharBuffer buffer(1024 * 10 + 1);
|
||||
|
||||
const int beginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int queueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
|
||||
const int taskTimeSignatureLen = strlen(TASK_TIME_SIGNATURE);
|
||||
const int definitionSignatureLen = strlen(DEFINITION_SIGNATURE);
|
||||
|
||||
// check if the file contains pp-script-signature
|
||||
// read first 10KB of the file and look for signature
|
||||
int readBytes = (int)infile.Read(buffer, buffer.Size() - 1);
|
||||
infile.Close();
|
||||
buffer[readBytes] = '\0';
|
||||
|
||||
bool postScript = false;
|
||||
bool scanScript = false;
|
||||
bool queueScript = false;
|
||||
bool schedulerScript = false;
|
||||
bool feedScript = false;
|
||||
char* queueEvents = nullptr;
|
||||
char* taskTime = nullptr;
|
||||
|
||||
bool inConfig = false;
|
||||
bool afterConfig = false;
|
||||
|
||||
// Declarations "QUEUE EVENT:" and "TASK TIME:" can be placed:
|
||||
// - in script definition body (between opening and closing script signatures);
|
||||
// - immediately before script definition (before opening script signature);
|
||||
// - immediately after script definition (after closing script signature).
|
||||
// The last two pissibilities are provided to increase compatibility of scripts with older
|
||||
// nzbget versions which do not expect the extra declarations in the script defintion body.
|
||||
|
||||
Tokenizer tok(buffer, "\n\r", true);
|
||||
while (char* line = tok.Next())
|
||||
{
|
||||
if (!strncmp(line, QUEUE_EVENTS_SIGNATURE, queueEventsSignatureLen))
|
||||
{
|
||||
queueEvents = line + queueEventsSignatureLen;
|
||||
}
|
||||
else if (!strncmp(line, TASK_TIME_SIGNATURE, taskTimeSignatureLen))
|
||||
{
|
||||
taskTime = line + taskTimeSignatureLen;
|
||||
}
|
||||
|
||||
bool header = !strncmp(line, DEFINITION_SIGNATURE, definitionSignatureLen);
|
||||
if (!header && !inConfig)
|
||||
{
|
||||
queueEvents = nullptr;
|
||||
taskTime = nullptr;
|
||||
}
|
||||
|
||||
if (!header && afterConfig)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strncmp(line, BEGIN_SCRIPT_SIGNATURE, beginSignatureLen) && strstr(line, END_SCRIPT_SIGNATURE))
|
||||
{
|
||||
if (!inConfig)
|
||||
{
|
||||
inConfig = true;
|
||||
postScript = strstr(line, POST_SCRIPT_SIGNATURE);
|
||||
scanScript = strstr(line, SCAN_SCRIPT_SIGNATURE);
|
||||
queueScript = strstr(line, QUEUE_SCRIPT_SIGNATURE);
|
||||
schedulerScript = strstr(line, SCHEDULER_SCRIPT_SIGNATURE);
|
||||
feedScript = strstr(line, FEED_SCRIPT_SIGNATURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
afterConfig = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(postScript || scanScript || queueScript || schedulerScript || feedScript))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// trim decorations
|
||||
char* p;
|
||||
while (queueEvents && *queueEvents && *(p = queueEvents + strlen(queueEvents) - 1) == '#') *p = '\0';
|
||||
if (queueEvents) queueEvents = Util::Trim(queueEvents);
|
||||
while (taskTime && *taskTime && *(p = taskTime + strlen(taskTime) - 1) == '#') *p = '\0';
|
||||
if (taskTime) taskTime = Util::Trim(taskTime);
|
||||
|
||||
script->SetPostScript(postScript);
|
||||
script->SetScanScript(scanScript);
|
||||
script->SetQueueScript(queueScript);
|
||||
script->SetSchedulerScript(schedulerScript);
|
||||
script->SetFeedScript(feedScript);
|
||||
script->SetQueueEvents(queueEvents);
|
||||
script->SetTaskTime(taskTime);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
BString<1024> ScriptConfig::BuildScriptName(const char* directory, const char* filename, bool isSubDir)
|
||||
{
|
||||
if (isSubDir)
|
||||
{
|
||||
BString<1024> directory2 = directory;
|
||||
int len = strlen(directory2);
|
||||
if (directory2[len-1] == PATH_SEPARATOR || directory2[len-1] == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
// trim last path-separator
|
||||
directory2[len-1] = '\0';
|
||||
}
|
||||
|
||||
return BString<1024>("%s%c%s", FileSystem::BaseFileName(directory2), PATH_SEPARATOR, filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
}
|
||||
|
||||
bool ScriptConfig::ScriptExists(Scripts* scripts, const char* scriptName)
|
||||
{
|
||||
return std::find_if(scripts->begin(), scripts->end(),
|
||||
[scriptName](Script& script)
|
||||
{
|
||||
return !strcmp(script.GetName(), scriptName);
|
||||
}) != scripts->end();
|
||||
}
|
||||
|
||||
void ScriptConfig::BuildScriptDisplayNames(Scripts* scripts)
|
||||
{
|
||||
// trying to use short name without path and extension.
|
||||
// if there are other scripts with the same short name - using a longer name instead (with ot without extension)
|
||||
|
||||
for (Script& script : scripts)
|
||||
{
|
||||
BString<1024> shortName = script.GetName();
|
||||
if (char* ext = strrchr(shortName, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* displayName = FileSystem::BaseFileName(shortName);
|
||||
|
||||
for (Script& script2 : scripts)
|
||||
{
|
||||
BString<1024> shortName2 = script2.GetName();
|
||||
if (char* ext = strrchr(shortName2, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* displayName2 = FileSystem::BaseFileName(shortName2);
|
||||
|
||||
if (!strcmp(displayName, displayName2) && script.GetName() != script2.GetName())
|
||||
{
|
||||
if (!strcmp(shortName, shortName2))
|
||||
{
|
||||
displayName = script.GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
displayName = shortName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
script.SetDisplayName(displayName);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::CreateTasks()
|
||||
{
|
||||
for (Script& script : m_scripts)
|
||||
{
|
||||
if (script.GetSchedulerScript() && !Util::EmptyStr(script.GetTaskTime()))
|
||||
{
|
||||
Tokenizer tok(g_Options->GetExtensions(), ",;");
|
||||
while (const char* scriptName = tok.Next())
|
||||
{
|
||||
if (FileSystem::SameFilename(scriptName, script.GetName()))
|
||||
{
|
||||
g_Options->CreateSchedulerTask(0, script.GetTaskTime(),
|
||||
nullptr, Options::scScript, script.GetName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 SCRIPTCONFIG_H
|
||||
#define SCRIPTCONFIG_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Container.h"
|
||||
#include "Options.h"
|
||||
|
||||
class ScriptConfig
|
||||
{
|
||||
public:
|
||||
class Script
|
||||
{
|
||||
public:
|
||||
Script(const char* name, const char* location) :
|
||||
m_name(name), m_location(location), m_displayName(name) {};
|
||||
Script(Script&&) = default;
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetLocation() { return m_location; }
|
||||
void SetDisplayName(const char* displayName) { m_displayName = displayName; }
|
||||
const char* GetDisplayName() { return m_displayName; }
|
||||
bool GetPostScript() { return m_postScript; }
|
||||
void SetPostScript(bool postScript) { m_postScript = postScript; }
|
||||
bool GetScanScript() { return m_scanScript; }
|
||||
void SetScanScript(bool scanScript) { m_scanScript = scanScript; }
|
||||
bool GetQueueScript() { return m_queueScript; }
|
||||
void SetQueueScript(bool queueScript) { m_queueScript = queueScript; }
|
||||
bool GetSchedulerScript() { return m_schedulerScript; }
|
||||
void SetSchedulerScript(bool schedulerScript) { m_schedulerScript = schedulerScript; }
|
||||
bool GetFeedScript() { return m_feedScript; }
|
||||
void SetFeedScript(bool feedScript) { m_feedScript = feedScript; }
|
||||
void SetQueueEvents(const char* queueEvents) { m_queueEvents = queueEvents; }
|
||||
const char* GetQueueEvents() { return m_queueEvents; }
|
||||
void SetTaskTime(const char* taskTime) { m_taskTime = taskTime; }
|
||||
const char* GetTaskTime() { return m_taskTime; }
|
||||
|
||||
private:
|
||||
CString m_name;
|
||||
CString m_location;
|
||||
CString m_displayName;
|
||||
bool m_postScript = false;
|
||||
bool m_scanScript = false;
|
||||
bool m_queueScript = false;
|
||||
bool m_schedulerScript = false;
|
||||
bool m_feedScript = false;
|
||||
CString m_queueEvents;
|
||||
CString m_taskTime;
|
||||
};
|
||||
|
||||
typedef std::list<Script> Scripts;
|
||||
|
||||
class ConfigTemplate
|
||||
{
|
||||
public:
|
||||
ConfigTemplate(Script&& script, const char* templ) :
|
||||
m_script(std::move(script)), m_template(templ) {}
|
||||
Script* GetScript() { return &m_script; }
|
||||
const char* GetTemplate() { return m_template; }
|
||||
|
||||
private:
|
||||
Script m_script;
|
||||
CString m_template;
|
||||
};
|
||||
|
||||
typedef std::deque<ConfigTemplate> ConfigTemplates;
|
||||
|
||||
void InitOptions();
|
||||
Scripts* GetScripts() { return &m_scripts; }
|
||||
bool LoadConfig(Options::OptEntries* optEntries);
|
||||
bool SaveConfig(Options::OptEntries* optEntries);
|
||||
bool LoadConfigTemplates(ConfigTemplates* configTemplates);
|
||||
ConfigTemplates* GetConfigTemplates() { return &m_configTemplates; }
|
||||
|
||||
private:
|
||||
Scripts m_scripts;
|
||||
ConfigTemplates m_configTemplates;
|
||||
|
||||
void InitScripts();
|
||||
void InitConfigTemplates();
|
||||
void CreateTasks();
|
||||
void LoadScriptDir(Scripts* scripts, const char* directory, bool isSubDir);
|
||||
void BuildScriptDisplayNames(Scripts* scripts);
|
||||
void LoadScripts(Scripts* scripts);
|
||||
bool LoadScriptFile(Script* script);
|
||||
BString<1024>BuildScriptName(const char* directory, const char* filename, bool isSubDir);
|
||||
bool ScriptExists(Scripts* scripts, const char* scriptName);
|
||||
};
|
||||
|
||||
extern ScriptConfig* g_ScriptConfig;
|
||||
|
||||
#endif
|
||||
@@ -1,724 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 "FeedCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WorkState.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Util.h"
|
||||
#include "FileSystem.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedFilter.h"
|
||||
#include "FeedScript.h"
|
||||
#include "DiskState.h"
|
||||
#include "DupeCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
|
||||
std::unique_ptr<RegEx>& FeedCoordinator::FilterHelper::GetRegEx(int id)
|
||||
{
|
||||
m_regExes.resize(id);
|
||||
return m_regExes[id - 1];
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterHelper::CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen)
|
||||
{
|
||||
const char* dupeStatusName[] = { "", "QUEUED", "DOWNLOADING", "3", "SUCCESS", "5", "6", "7", "WARNING",
|
||||
"9", "10", "11", "12", "13", "14", "15", "FAILURE" };
|
||||
|
||||
DupeCoordinator::EDupeStatus dupeStatus = g_DupeCoordinator->GetDupeStatus(DownloadQueue::Guard(), title, dupeKey);
|
||||
|
||||
BString<1024> statuses;
|
||||
for (int i = 1; i <= (int)DupeCoordinator::dsFailure; i = i << 1)
|
||||
{
|
||||
if (dupeStatus & i)
|
||||
{
|
||||
if (!statuses.Empty())
|
||||
{
|
||||
statuses.Append(",");
|
||||
}
|
||||
statuses.Append(dupeStatusName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(statusBuf, statuses, bufLen);
|
||||
}
|
||||
|
||||
FeedCoordinator::FeedCoordinator()
|
||||
{
|
||||
debug("Creating FeedCoordinator");
|
||||
|
||||
m_downloadQueueObserver.m_owner = this;
|
||||
DownloadQueue::Guard()->Attach(&m_downloadQueueObserver);
|
||||
|
||||
m_workStateObserver.m_owner = this;
|
||||
g_WorkState->Attach(&m_workStateObserver);
|
||||
}
|
||||
|
||||
FeedCoordinator::~FeedCoordinator()
|
||||
{
|
||||
debug("Destroying FeedCoordinator");
|
||||
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
delete feedDownloader;
|
||||
}
|
||||
m_activeDownloads.clear();
|
||||
}
|
||||
|
||||
void FeedCoordinator::Run()
|
||||
{
|
||||
debug("Entering FeedCoordinator-loop");
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
Util::Sleep(20);
|
||||
}
|
||||
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
g_DiskState->LoadFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
|
||||
time_t lastCleanup = 0;
|
||||
while (!IsStopped())
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
if (!g_WorkState->GetPauseDownload() || m_force || g_Options->GetUrlForce())
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t current = Util::CurrentTime();
|
||||
if ((int)m_activeDownloads.size() < g_Options->GetUrlConnections())
|
||||
{
|
||||
m_force = false;
|
||||
// check feed list and update feeds
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (((feedInfo->GetInterval() > 0 &&
|
||||
(feedInfo->GetNextUpdate() == 0 ||
|
||||
current >= feedInfo->GetNextUpdate() ||
|
||||
current < feedInfo->GetNextUpdate() - feedInfo->GetInterval() * 60)) ||
|
||||
feedInfo->GetFetch()) &&
|
||||
feedInfo->GetStatus() != FeedInfo::fsRunning)
|
||||
{
|
||||
StartFeedDownload(feedInfo, feedInfo->GetFetch());
|
||||
}
|
||||
else if (feedInfo->GetFetch())
|
||||
{
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckSaveFeeds();
|
||||
ResetHangingDownloads();
|
||||
|
||||
if (std::abs(Util::CurrentTime() - lastCleanup) >= 60)
|
||||
{
|
||||
// clean up feed history once a minute
|
||||
CleanupHistory();
|
||||
CleanupCache();
|
||||
CheckSaveFeeds();
|
||||
lastCleanup = Util::CurrentTime();
|
||||
}
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_force)
|
||||
{
|
||||
// don't sleep too long if there active feeds scheduled for redownload
|
||||
m_waitCond.WaitFor(m_downloadsMutex, 1000, [&]{ return IsStopped(); });
|
||||
}
|
||||
else
|
||||
{
|
||||
// no active jobs, we can sleep longer:
|
||||
// - if option "UrlForce" is active or if the feed list is empty we need to wake up
|
||||
// only when a new feed preview is requested. We could wait indefinitely for that
|
||||
// but we need to do some job every now and then and therefore we sleep only 60 seconds.
|
||||
// - if option "UrlForce" is disabled we need also to wake up when state "DownloadPaused"
|
||||
// is changed. We detect this via notification from 'WorkState'. However such
|
||||
// notifications are not 100% reliable due to possible race conditions. Therefore
|
||||
// we sleep for max. 5 seconds.
|
||||
int waitInterval = g_Options->GetUrlForce() || m_feeds.empty() ? 60000 : 5000;
|
||||
m_waitCond.WaitFor(m_downloadsMutex, waitInterval, [&]{ return m_force || IsStopped(); });
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("FeedCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
completed = m_activeDownloads.size() == 0;
|
||||
}
|
||||
CheckSaveFeeds();
|
||||
Util::Sleep(100);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("FeedCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting FeedCoordinator-loop");
|
||||
}
|
||||
|
||||
void FeedCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
debug("UrlDownloads are notified");
|
||||
|
||||
// Resume Run() to exit it
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::WorkStateUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
void FeedCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
if (g_Options->GetUrlTimeout() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
time_t tm = Util::CurrentTime();
|
||||
|
||||
for (FeedDownloader* feedDownloader: m_activeDownloads)
|
||||
{
|
||||
if (tm - feedDownloader->GetLastUpdateTime() > g_Options->GetUrlTimeout() + 10 &&
|
||||
feedDownloader->GetStatus() == FeedDownloader::adRunning)
|
||||
{
|
||||
error("Cancelling hanging feed download %s", feedDownloader->GetInfoName());
|
||||
feedDownloader->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::LogDebugInfo()
|
||||
{
|
||||
info(" ---------- FeedCoordinator");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
info(" Active Downloads: %i", (int)m_activeDownloads.size());
|
||||
for (FeedDownloader* feedDownloader : m_activeDownloads)
|
||||
{
|
||||
feedDownloader->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::StartFeedDownload(FeedInfo* feedInfo, bool force)
|
||||
{
|
||||
debug("Starting new FeedDownloader for %s", feedInfo->GetName());
|
||||
|
||||
FeedDownloader* feedDownloader = new FeedDownloader();
|
||||
feedDownloader->SetAutoDestroy(true);
|
||||
feedDownloader->Attach(this);
|
||||
feedDownloader->SetFeedInfo(feedInfo);
|
||||
feedDownloader->SetUrl(feedInfo->GetUrl());
|
||||
feedDownloader->SetInfoName(feedInfo->GetName());
|
||||
feedDownloader->SetForce(force || g_Options->GetUrlForce());
|
||||
|
||||
BString<1024> outFilename;
|
||||
if (feedInfo->GetId() > 0)
|
||||
{
|
||||
outFilename.Format("%s%cfeed-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, feedInfo->GetId());
|
||||
}
|
||||
else
|
||||
{
|
||||
outFilename.Format("%s%cfeed-%i-%i.tmp", g_Options->GetTempDir(), PATH_SEPARATOR, (int)Util::CurrentTime(), rand());
|
||||
}
|
||||
feedDownloader->SetOutputFilename(outFilename);
|
||||
|
||||
feedInfo->SetStatus(FeedInfo::fsRunning);
|
||||
feedInfo->SetForce(force);
|
||||
feedInfo->SetFetch(false);
|
||||
|
||||
m_activeDownloads.push_back(feedDownloader);
|
||||
feedDownloader->Start();
|
||||
}
|
||||
|
||||
void FeedCoordinator::Update(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from FeedDownloader received");
|
||||
|
||||
FeedDownloader* feedDownloader = (FeedDownloader*) caller;
|
||||
if ((feedDownloader->GetStatus() == WebDownloader::adFinished) ||
|
||||
(feedDownloader->GetStatus() == WebDownloader::adFailed) ||
|
||||
(feedDownloader->GetStatus() == WebDownloader::adRetry))
|
||||
{
|
||||
FeedCompleted(feedDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::FeedCompleted(FeedDownloader* feedDownloader)
|
||||
{
|
||||
debug("Feed downloaded");
|
||||
|
||||
FeedInfo* feedInfo = feedDownloader->GetFeedInfo();
|
||||
bool statusOK = feedDownloader->GetStatus() == WebDownloader::adFinished;
|
||||
if (statusOK)
|
||||
{
|
||||
feedInfo->SetOutputFilename(feedDownloader->GetOutputFilename());
|
||||
}
|
||||
|
||||
// remove downloader from downloader list
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
m_activeDownloads.erase(std::find(m_activeDownloads.begin(), m_activeDownloads.end(), feedDownloader));
|
||||
}
|
||||
|
||||
SchedulerNextUpdate(feedInfo, statusOK);
|
||||
|
||||
if (statusOK)
|
||||
{
|
||||
if (!feedInfo->GetPreview())
|
||||
{
|
||||
bool scriptSuccess = true;
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
|
||||
feedInfo->GetOutputFilename(), feedInfo->GetId(), &scriptSuccess);
|
||||
if (!scriptSuccess)
|
||||
{
|
||||
feedInfo->SetStatus(FeedInfo::fsFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo);
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
|
||||
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (feedFile)
|
||||
{
|
||||
std::unique_ptr<FeedItemList> feedItems = feedFile->DetachFeedItems();
|
||||
addedNzbs = ProcessFeed(feedInfo, feedItems.get());
|
||||
feedFile.reset();
|
||||
}
|
||||
feedInfo->SetLastUpdate(Util::CurrentTime());
|
||||
feedInfo->SetForce(false);
|
||||
m_save = true;
|
||||
}
|
||||
|
||||
for (std::unique_ptr<NzbInfo>& nzbInfo : addedNzbs)
|
||||
{
|
||||
g_UrlCoordinator->AddUrlToQueue(std::move(nzbInfo), false);
|
||||
}
|
||||
}
|
||||
feedInfo->SetStatus(FeedInfo::fsFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
feedInfo->SetStatus(FeedInfo::fsFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::SchedulerNextUpdate(FeedInfo* feedInfo, bool success)
|
||||
{
|
||||
time_t current = Util::CurrentTime();
|
||||
int interval;
|
||||
|
||||
if (success)
|
||||
{
|
||||
interval = feedInfo->GetInterval() * 60;
|
||||
feedInfo->SetLastInterval(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// On failure schedule next update sooner:
|
||||
// starting with 1 minute and increasing, but not greater than FeedX.Interval
|
||||
interval = feedInfo->GetLastInterval() * 2;
|
||||
interval = std::max(interval, 60);
|
||||
interval = std::min(interval, feedInfo->GetInterval() * 60);
|
||||
feedInfo->SetLastInterval(interval);
|
||||
}
|
||||
|
||||
detail("Scheduling update for feed %s in %i minute(s)", feedInfo->GetName(), interval / 60);
|
||||
feedInfo->SetNextUpdate(current + interval);
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
|
||||
{
|
||||
debug("Filtering feed %s", feedInfo->GetName());
|
||||
|
||||
FilterHelper filterHelper;
|
||||
std::unique_ptr<FeedFilter> feedFilter;
|
||||
|
||||
if (!Util::EmptyStr(feedInfo->GetFilter()))
|
||||
{
|
||||
feedFilter = std::make_unique<FeedFilter>(feedInfo->GetFilter());
|
||||
}
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems)
|
||||
{
|
||||
feedItemInfo.SetMatchStatus(FeedItemInfo::msAccepted);
|
||||
feedItemInfo.SetMatchRule(0);
|
||||
feedItemInfo.SetPauseNzb(feedInfo->GetPauseNzb());
|
||||
feedItemInfo.SetPriority(feedInfo->GetPriority());
|
||||
feedItemInfo.SetAddCategory(feedInfo->GetCategory());
|
||||
feedItemInfo.SetDupeScore(0);
|
||||
feedItemInfo.SetDupeMode(dmScore);
|
||||
feedItemInfo.SetFeedFilterHelper(&filterHelper);
|
||||
feedItemInfo.BuildDupeKey(nullptr, nullptr, nullptr, nullptr);
|
||||
if (feedFilter)
|
||||
{
|
||||
feedFilter->Match(feedItemInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> FeedCoordinator::ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems)
|
||||
{
|
||||
debug("Process feed %s", feedInfo->GetName());
|
||||
|
||||
FilterFeed(feedInfo, feedItems);
|
||||
|
||||
std::vector<std::unique_ptr<NzbInfo>> addedNzbs;
|
||||
bool firstFetch = feedInfo->GetLastUpdate() == 0;
|
||||
int added = 0;
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems)
|
||||
{
|
||||
if (feedItemInfo.GetMatchStatus() == FeedItemInfo::msAccepted)
|
||||
{
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
|
||||
FeedHistoryInfo::EStatus status = FeedHistoryInfo::hsUnknown;
|
||||
if (firstFetch && feedInfo->GetBacklog())
|
||||
{
|
||||
status = FeedHistoryInfo::hsBacklog;
|
||||
}
|
||||
else if (!feedHistoryInfo)
|
||||
{
|
||||
addedNzbs.push_back(CreateNzbInfo(feedInfo, feedItemInfo));
|
||||
status = FeedHistoryInfo::hsFetched;
|
||||
added++;
|
||||
}
|
||||
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedHistoryInfo->SetLastSeen(Util::CurrentTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_feedHistory.emplace_back(feedItemInfo.GetUrl(), status, Util::CurrentTime());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (added)
|
||||
{
|
||||
info("%s has %i new item(s)", feedInfo->GetName(), added);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("%s has no new items", feedInfo->GetName());
|
||||
}
|
||||
|
||||
return addedNzbs;
|
||||
}
|
||||
|
||||
std::unique_ptr<NzbInfo> FeedCoordinator::CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo)
|
||||
{
|
||||
debug("Download %s from %s", feedItemInfo.GetUrl(), feedInfo->GetName());
|
||||
|
||||
std::unique_ptr<NzbInfo> nzbInfo = std::make_unique<NzbInfo>();
|
||||
nzbInfo->SetKind(NzbInfo::nkUrl);
|
||||
nzbInfo->SetFeedId(feedInfo->GetId());
|
||||
nzbInfo->SetUrl(feedItemInfo.GetUrl());
|
||||
|
||||
// add .nzb-extension if not present
|
||||
BString<1024> nzbName = feedItemInfo.GetFilename();
|
||||
char* ext = strrchr(nzbName, '.');
|
||||
if (ext && !strcasecmp(ext, ".nzb"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
if (!nzbName.Empty())
|
||||
{
|
||||
BString<1024> nzbName2("%s.nzb", *nzbName);
|
||||
nzbInfo->SetFilename(FileSystem::MakeValidFilename(nzbName2));
|
||||
}
|
||||
|
||||
nzbInfo->SetCategory(feedItemInfo.GetAddCategory());
|
||||
nzbInfo->SetPriority(feedItemInfo.GetPriority());
|
||||
nzbInfo->SetAddUrlPaused(feedItemInfo.GetPauseNzb());
|
||||
nzbInfo->SetDupeKey(feedItemInfo.GetDupeKey());
|
||||
nzbInfo->SetDupeScore(feedItemInfo.GetDupeScore());
|
||||
nzbInfo->SetDupeMode(feedItemInfo.GetDupeMode());
|
||||
nzbInfo->SetSize(feedItemInfo.GetSize());
|
||||
nzbInfo->SetMinTime(feedItemInfo.GetTime());
|
||||
nzbInfo->SetMaxTime(feedItemInfo.GetTime());
|
||||
|
||||
return nzbInfo;
|
||||
}
|
||||
|
||||
std::shared_ptr<FeedItemList> FeedCoordinator::ViewFeed(int id)
|
||||
{
|
||||
if (id < 1 || id > (int)m_feeds.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedInfo>& feedInfo = m_feeds[id - 1];
|
||||
|
||||
return PreviewFeed(feedInfo->GetId(), feedInfo->GetName(), feedInfo->GetUrl(), feedInfo->GetFilter(),
|
||||
feedInfo->GetBacklog(), feedInfo->GetPauseNzb(), feedInfo->GetCategory(),
|
||||
feedInfo->GetPriority(), feedInfo->GetInterval(), feedInfo->GetExtensions(), 0, nullptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<FeedItemList> FeedCoordinator::PreviewFeed(int id,
|
||||
const char* name, const char* url, const char* filter, bool backlog, bool pauseNzb,
|
||||
const char* category, int priority, int interval, const char* feedScript,
|
||||
int cacheTimeSec, const char* cacheId)
|
||||
{
|
||||
debug("Preview feed %s", name);
|
||||
|
||||
std::unique_ptr<FeedInfo> feedInfo = std::make_unique<FeedInfo>(id, name, url, backlog, interval,
|
||||
filter, pauseNzb, category, priority, feedScript);
|
||||
feedInfo->SetPreview(true);
|
||||
|
||||
std::shared_ptr<FeedItemList> feedItems;
|
||||
bool hasCache = false;
|
||||
if (cacheTimeSec > 0 && *cacheId != '\0')
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedCacheItem& feedCacheItem : m_feedCache)
|
||||
{
|
||||
if (!strcmp(feedCacheItem.GetCacheId(), cacheId))
|
||||
{
|
||||
feedCacheItem.SetLastUsage(Util::CurrentTime());
|
||||
feedItems = feedCacheItem.GetFeedItems();
|
||||
hasCache = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasCache)
|
||||
{
|
||||
bool firstFetch = true;
|
||||
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
for (FeedInfo* feedInfo2 : &m_feeds)
|
||||
{
|
||||
if (!strcmp(feedInfo2->GetUrl(), feedInfo->GetUrl()) &&
|
||||
!strcmp(feedInfo2->GetFilter(), feedInfo->GetFilter()) &&
|
||||
feedInfo2->GetLastUpdate() > 0)
|
||||
{
|
||||
firstFetch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StartFeedDownload(feedInfo.get(), true);
|
||||
|
||||
m_force = true;
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
// wait until the download in a separate thread completes
|
||||
while (feedInfo->GetStatus() == FeedInfo::fsRunning)
|
||||
{
|
||||
Util::Sleep(100);
|
||||
}
|
||||
|
||||
// now can process the feed
|
||||
|
||||
if (feedInfo->GetStatus() != FeedInfo::fsFinished)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(feedInfo->GetExtensions()) ? feedInfo->GetExtensions(): g_Options->GetExtensions(),
|
||||
feedInfo->GetOutputFilename(), feedInfo->GetId(), nullptr);
|
||||
|
||||
std::unique_ptr<FeedFile> feedFile = parseFeed(feedInfo.get());
|
||||
if (!feedFile)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
feedItems = feedFile->DetachFeedItems();
|
||||
feedFile.reset();
|
||||
|
||||
for (FeedItemInfo& feedItemInfo : feedItems.get())
|
||||
{
|
||||
feedItemInfo.SetStatus(firstFetch && feedInfo->GetBacklog() ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(feedItemInfo.GetUrl());
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedItemInfo.SetStatus((FeedItemInfo::EStatus)feedHistoryInfo->GetStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterFeed(feedInfo.get(), feedItems.get());
|
||||
feedInfo.reset();
|
||||
|
||||
if (cacheTimeSec > 0 && *cacheId != '\0' && !hasCache)
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
m_feedCache.emplace_back(url, cacheTimeSec, cacheId, Util::CurrentTime(), feedItems);
|
||||
}
|
||||
|
||||
return feedItems;
|
||||
}
|
||||
|
||||
void FeedCoordinator::FetchFeed(int id)
|
||||
{
|
||||
debug("FetchFeeds");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (feedInfo->GetId() == id || id == 0)
|
||||
{
|
||||
feedInfo->SetFetch(true);
|
||||
m_force = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_waitCond.NotifyAll();
|
||||
}
|
||||
|
||||
std::unique_ptr<FeedFile> FeedCoordinator::parseFeed(FeedInfo* feedInfo)
|
||||
{
|
||||
std::unique_ptr<FeedFile> feedFile = std::make_unique<FeedFile>(feedInfo->GetOutputFilename(), feedInfo->GetName());
|
||||
if (feedFile->Parse())
|
||||
{
|
||||
FileSystem::DeleteFile(feedInfo->GetOutputFilename());
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Feed file %s kept for troubleshooting (will be deleted on next successful feed fetch)", feedInfo->GetOutputFilename());
|
||||
feedFile.reset();
|
||||
}
|
||||
return feedFile;
|
||||
}
|
||||
|
||||
void FeedCoordinator::DownloadQueueUpdate(Subject* caller, void* aspect)
|
||||
{
|
||||
debug("Notification from URL-Coordinator received");
|
||||
|
||||
DownloadQueue::Aspect* queueAspect = (DownloadQueue::Aspect*)aspect;
|
||||
if (queueAspect->action == DownloadQueue::eaUrlCompleted)
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
FeedHistoryInfo* feedHistoryInfo = m_feedHistory.Find(queueAspect->nzbInfo->GetUrl());
|
||||
if (feedHistoryInfo)
|
||||
{
|
||||
feedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_feedHistory.emplace_back(queueAspect->nzbInfo->GetUrl(), FeedHistoryInfo::hsFetched, Util::CurrentTime());
|
||||
}
|
||||
m_save = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedCoordinator::HasActiveDownloads()
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
return !m_activeDownloads.empty();
|
||||
}
|
||||
|
||||
void FeedCoordinator::CheckSaveFeeds()
|
||||
{
|
||||
Guard guard(m_downloadsMutex);
|
||||
if (m_save)
|
||||
{
|
||||
debug("CheckSaveFeeds: save");
|
||||
if (g_Options->GetServerMode())
|
||||
{
|
||||
g_DiskState->SaveFeeds(&m_feeds, &m_feedHistory);
|
||||
}
|
||||
m_save = false;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupHistory()
|
||||
{
|
||||
debug("CleanupHistory");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t oldestUpdate = Util::CurrentTime();
|
||||
|
||||
for (FeedInfo* feedInfo : &m_feeds)
|
||||
{
|
||||
if (feedInfo->GetLastUpdate() < oldestUpdate)
|
||||
{
|
||||
oldestUpdate = feedInfo->GetLastUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
time_t borderDate = oldestUpdate - g_Options->GetFeedHistory() * 60*60*24;
|
||||
|
||||
m_feedHistory.erase(std::remove_if(m_feedHistory.begin(), m_feedHistory.end(),
|
||||
[borderDate, this](FeedHistoryInfo& feedHistoryInfo)
|
||||
{
|
||||
if (feedHistoryInfo.GetLastSeen() < borderDate)
|
||||
{
|
||||
detail("Deleting %s from feed history", feedHistoryInfo.GetUrl());
|
||||
m_save = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}),
|
||||
m_feedHistory.end());
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupCache()
|
||||
{
|
||||
debug("CleanupCache");
|
||||
|
||||
Guard guard(m_downloadsMutex);
|
||||
|
||||
time_t curTime = Util::CurrentTime();
|
||||
|
||||
m_feedCache.remove_if(
|
||||
[curTime](FeedCacheItem& feedCacheItem)
|
||||
{
|
||||
if (feedCacheItem.GetLastUsage() + feedCacheItem.GetCacheTimeSec() < curTime ||
|
||||
feedCacheItem.GetLastUsage() > curTime)
|
||||
{
|
||||
debug("Deleting %s from feed cache", feedCacheItem.GetUrl());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDCOORDINATOR_H
|
||||
#define FEEDCOORDINATOR_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
class FeedDownloader;
|
||||
|
||||
class FeedCoordinator : public Thread, public Observer, public Subject, public Debuggable
|
||||
{
|
||||
public:
|
||||
FeedCoordinator();
|
||||
virtual ~FeedCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* caller, void* aspect);
|
||||
void AddFeed(std::unique_ptr<FeedInfo> feedInfo) { m_feeds.push_back(std::move(feedInfo)); }
|
||||
|
||||
/* may return empty pointer on error */
|
||||
std::shared_ptr<FeedItemList> PreviewFeed(int id, const char* name, const char* url,
|
||||
const char* filter, bool backlog, bool pauseNzb, const char* category, int priority,
|
||||
int interval, const char* feedScript, int cacheTimeSec, const char* cacheId);
|
||||
|
||||
/* may return empty pointer on error */
|
||||
std::shared_ptr<FeedItemList> ViewFeed(int id);
|
||||
|
||||
void FetchFeed(int id);
|
||||
bool HasActiveDownloads();
|
||||
Feeds* GetFeeds() { return &m_feeds; }
|
||||
|
||||
protected:
|
||||
virtual void LogDebugInfo();
|
||||
|
||||
private:
|
||||
class DownloadQueueObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->DownloadQueueUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class WorkStateObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_owner;
|
||||
virtual void Update(Subject* caller, void* aspect) { m_owner->WorkStateUpdate(caller, aspect); }
|
||||
};
|
||||
|
||||
class FeedCacheItem
|
||||
{
|
||||
public:
|
||||
FeedCacheItem(const char* url, int cacheTimeSec,const char* cacheId,
|
||||
time_t lastUsage, std::shared_ptr<FeedItemList> feedItems) :
|
||||
m_url(url), m_cacheTimeSec(cacheTimeSec), m_cacheId(cacheId),
|
||||
m_lastUsage(lastUsage), m_feedItems(feedItems) {}
|
||||
const char* GetUrl() { return m_url; }
|
||||
int GetCacheTimeSec() { return m_cacheTimeSec; }
|
||||
const char* GetCacheId() { return m_cacheId; }
|
||||
time_t GetLastUsage() { return m_lastUsage; }
|
||||
void SetLastUsage(time_t lastUsage) { m_lastUsage = lastUsage; }
|
||||
std::shared_ptr<FeedItemList> GetFeedItems() { return m_feedItems; }
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
int m_cacheTimeSec;
|
||||
CString m_cacheId;
|
||||
time_t m_lastUsage;
|
||||
std::shared_ptr<FeedItemList> m_feedItems;
|
||||
};
|
||||
|
||||
class FilterHelper : public FeedFilterHelper
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<RegEx>& GetRegEx(int id);
|
||||
virtual void CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen);
|
||||
private:
|
||||
std::vector<std::unique_ptr<RegEx>> m_regExes;
|
||||
};
|
||||
|
||||
typedef std::list<FeedCacheItem> FeedCache;
|
||||
typedef std::deque<FeedDownloader*> ActiveDownloads;
|
||||
|
||||
Feeds m_feeds;
|
||||
ActiveDownloads m_activeDownloads;
|
||||
FeedHistory m_feedHistory;
|
||||
Mutex m_downloadsMutex;
|
||||
DownloadQueueObserver m_downloadQueueObserver;
|
||||
WorkStateObserver m_workStateObserver;
|
||||
bool m_force = false;
|
||||
bool m_save = false;
|
||||
FeedCache m_feedCache;
|
||||
ConditionVar m_waitCond;
|
||||
bool m_wokenUp = false;
|
||||
|
||||
void StartFeedDownload(FeedInfo* feedInfo, bool force);
|
||||
void FeedCompleted(FeedDownloader* feedDownloader);
|
||||
void FilterFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
|
||||
std::vector<std::unique_ptr<NzbInfo>> ProcessFeed(FeedInfo* feedInfo, FeedItemList* feedItems);
|
||||
std::unique_ptr<NzbInfo> CreateNzbInfo(FeedInfo* feedInfo, FeedItemInfo& feedItemInfo);
|
||||
void ResetHangingDownloads();
|
||||
void DownloadQueueUpdate(Subject* caller, void* aspect);
|
||||
void CleanupHistory();
|
||||
void CleanupCache();
|
||||
void CheckSaveFeeds();
|
||||
std::unique_ptr<FeedFile> parseFeed(FeedInfo* feedInfo);
|
||||
void SchedulerNextUpdate(FeedInfo* feedInfo, bool success);
|
||||
void WorkStateUpdate(Subject* caller, void* aspect);
|
||||
};
|
||||
|
||||
extern FeedCoordinator* g_FeedCoordinator;
|
||||
|
||||
class FeedDownloader : public WebDownloader
|
||||
{
|
||||
public:
|
||||
void SetFeedInfo(FeedInfo* feedInfo) { m_feedInfo = feedInfo; }
|
||||
FeedInfo* GetFeedInfo() { return m_feedInfo; }
|
||||
|
||||
private:
|
||||
FeedInfo* m_feedInfo;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,607 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 "FeedFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
FeedFile::FeedFile(const char* fileName, const char* infoName) :
|
||||
m_fileName(fileName), m_infoName(infoName)
|
||||
{
|
||||
debug("Creating FeedFile");
|
||||
|
||||
m_feedItems = std::make_unique<FeedItemList>();
|
||||
|
||||
#ifndef WIN32
|
||||
m_feedItemInfo = nullptr;
|
||||
m_tagContent.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::LogDebugInfo()
|
||||
{
|
||||
info(" FeedFile %s", *m_fileName);
|
||||
}
|
||||
|
||||
void FeedFile::ParseSubject(FeedItemInfo& feedItemInfo)
|
||||
{
|
||||
// if title has quatation marks we use only part within quatation marks
|
||||
char* p = (char*)feedItemInfo.GetTitle();
|
||||
char* start = strchr(p, '\"');
|
||||
if (start)
|
||||
{
|
||||
start++;
|
||||
char* end = strchr(start + 1, '\"');
|
||||
if (end)
|
||||
{
|
||||
int len = (int)(end - start);
|
||||
char* point = strchr(start + 1, '.');
|
||||
if (point && point < end)
|
||||
{
|
||||
CString filename(start, len);
|
||||
|
||||
char* ext = strrchr(filename, '.');
|
||||
if (ext && !strcasecmp(ext, ".par2"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
|
||||
feedItemInfo.SetFilename(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
feedItemInfo.SetFilename(feedItemInfo.GetTitle());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
bool FeedFile::Parse()
|
||||
{
|
||||
CoInitialize(nullptr);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MSXML::IXMLDOMDocumentPtr doc;
|
||||
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load the XML document file...
|
||||
doc->put_resolveExternals(VARIANT_FALSE);
|
||||
doc->put_validateOnParse(VARIANT_FALSE);
|
||||
doc->put_async(VARIANT_FALSE);
|
||||
|
||||
_variant_t vFilename(*WString(m_fileName));
|
||||
|
||||
// 1. first trying to load via filename without URL-encoding (certain charaters doesn't work when encoded)
|
||||
VARIANT_BOOL success = doc->load(vFilename);
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
// 2. now trying filename encoded as URL
|
||||
char url[2048];
|
||||
EncodeUrl(m_fileName, url, 2048);
|
||||
debug("url=\"%s\"", url);
|
||||
_variant_t vUrl(url);
|
||||
|
||||
success = doc->load(vUrl);
|
||||
}
|
||||
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
const char* errMsg = r;
|
||||
error("Error parsing rss feed %s: %s", *m_infoName, errMsg);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = ParseFeed(doc);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
void FeedFile::EncodeUrl(const char* filename, char* url, int bufLen)
|
||||
{
|
||||
WString widefilename(filename);
|
||||
|
||||
char* end = url + bufLen;
|
||||
for (wchar_t* p = widefilename; *p && url < end - 3; p++)
|
||||
{
|
||||
wchar_t ch = *p;
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z') ||
|
||||
ch == '-' || ch == '.' || ch == '_' || ch == '~')
|
||||
{
|
||||
*url++ = (char)ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
*url++ = '%';
|
||||
uint32 a = (uint32)ch >> 4;
|
||||
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*url++ = a > 9 ? a - 10 + 'A' : a + '0';
|
||||
}
|
||||
}
|
||||
*url = '\0';
|
||||
}
|
||||
|
||||
bool FeedFile::ParseFeed(IUnknown* nzb)
|
||||
{
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr itemList = root->selectNodes("/rss/channel/item");
|
||||
for (int i = 0; i < itemList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
|
||||
|
||||
m_feedItems->emplace_back();
|
||||
FeedItemInfo& feedItemInfo = m_feedItems->back();
|
||||
|
||||
MSXML::IXMLDOMNodePtr tag;
|
||||
MSXML::IXMLDOMNodePtr attr;
|
||||
|
||||
// <title>Debian 6</title>
|
||||
tag = node->selectSingleNode("title");
|
||||
if (!tag)
|
||||
{
|
||||
// bad rss feed
|
||||
return false;
|
||||
}
|
||||
_bstr_t title(tag->Gettext());
|
||||
feedItemInfo.SetTitle(title);
|
||||
ParseSubject(feedItemInfo);
|
||||
|
||||
// <pubDate>Wed, 26 Jun 2013 00:02:54 -0600</pubDate>
|
||||
tag = node->selectSingleNode("pubDate");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t time(tag->Gettext());
|
||||
time_t unixtime = WebUtil::ParseRfc822DateTime(time);
|
||||
if (unixtime > 0)
|
||||
{
|
||||
feedItemInfo.SetTime(unixtime);
|
||||
}
|
||||
}
|
||||
|
||||
// <category>Movies > HD</category>
|
||||
tag = node->selectSingleNode("category");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t category(tag->Gettext());
|
||||
feedItemInfo.SetCategory(category);
|
||||
}
|
||||
|
||||
// <description>long text</description>
|
||||
tag = node->selectSingleNode("description");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t bdescription(tag->Gettext());
|
||||
// cleanup CDATA
|
||||
CString description = (const char*)bdescription;
|
||||
WebUtil::XmlStripTags(description);
|
||||
WebUtil::XmlDecode(description);
|
||||
WebUtil::XmlRemoveEntities(description);
|
||||
feedItemInfo.SetDescription(description);
|
||||
}
|
||||
|
||||
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
|
||||
tag = node->selectSingleNode("enclosure");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("url");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t url(attr->Gettext());
|
||||
feedItemInfo.SetUrl(url);
|
||||
}
|
||||
|
||||
attr = tag->Getattributes()->getNamedItem("length");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bsize(attr->Gettext());
|
||||
int64 size = atoll(bsize);
|
||||
feedItemInfo.SetSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
if (!feedItemInfo.GetUrl())
|
||||
{
|
||||
// <link>https://nzb.org/fetch/334534ce/4364564564</link>
|
||||
tag = node->selectSingleNode("link");
|
||||
if (!tag)
|
||||
{
|
||||
// bad rss feed
|
||||
return false;
|
||||
}
|
||||
_bstr_t link(tag->Gettext());
|
||||
feedItemInfo.SetUrl(link);
|
||||
}
|
||||
|
||||
|
||||
// newznab special
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (feedItemInfo.GetSize() == 0)
|
||||
{
|
||||
tag = node->selectSingleNode("newznab:attr[@name='size'] | nZEDb:attr[@name='size']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bsize(attr->Gettext());
|
||||
int64 size = atoll(bsize);
|
||||
feedItemInfo.SetSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='imdb'] | nZEDb:attr[@name='imdb']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetImdbId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='rageid'] | nZEDb:attr[@name='rageid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetRageId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="tdvdbid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='tvdbid'] | nZEDb:attr[@name='tvdbid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetTvdbId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvmazeid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='tvmazeid'] | nZEDb:attr[@name='tvmazeid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t bval(attr->Gettext());
|
||||
int val = atoi(bval);
|
||||
feedItemInfo.SetTvmazeId(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='episode'] | nZEDb:attr[@name='episode']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
feedItemInfo.SetEpisode(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='season'] | nZEDb:attr[@name='season']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
feedItemInfo.SetSeason(val);
|
||||
}
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab:attr | nZEDb:attr");
|
||||
for (int i = 0; i < itemList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = itemList->Getitem(i);
|
||||
MSXML::IXMLDOMNodePtr name = node->Getattributes()->getNamedItem("name");
|
||||
MSXML::IXMLDOMNodePtr value = node->Getattributes()->getNamedItem("value");
|
||||
if (name && value)
|
||||
{
|
||||
_bstr_t bname(name->Gettext());
|
||||
_bstr_t bval(value->Gettext());
|
||||
feedItemInfo.GetAttributes()->emplace_back(bname, bval);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
bool FeedFile::Parse()
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
error("Could not parse rss feed, program was compiled without libxml2 support");
|
||||
return false;
|
||||
#else
|
||||
xmlSAXHandler SAX_handler = {0};
|
||||
SAX_handler.startElement = reinterpret_cast<startElementSAXFunc>(SAX_StartElement);
|
||||
SAX_handler.endElement = reinterpret_cast<endElementSAXFunc>(SAX_EndElement);
|
||||
SAX_handler.characters = reinterpret_cast<charactersSAXFunc>(SAX_characters);
|
||||
SAX_handler.error = reinterpret_cast<errorSAXFunc>(SAX_error);
|
||||
SAX_handler.getEntity = reinterpret_cast<getEntitySAXFunc>(SAX_getEntity);
|
||||
|
||||
m_ignoreNextError = false;
|
||||
|
||||
int ret = xmlSAXUserParseFile(&SAX_handler, this, m_fileName);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse rss feed %s", *m_infoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::Parse_StartElement(const char *name, const char **atts)
|
||||
{
|
||||
ResetTagContent();
|
||||
|
||||
if (!strcmp("item", name))
|
||||
{
|
||||
m_feedItems->emplace_back();
|
||||
m_feedItemInfo = &m_feedItems->back();
|
||||
}
|
||||
else if (!strcmp("enclosure", name) && m_feedItemInfo)
|
||||
{
|
||||
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
|
||||
for (; *atts; atts+=2)
|
||||
{
|
||||
if (!strcmp("url", atts[0]))
|
||||
{
|
||||
CString url = atts[1];
|
||||
WebUtil::XmlDecode(url);
|
||||
m_feedItemInfo->SetUrl(url);
|
||||
}
|
||||
else if (!strcmp("length", atts[0]))
|
||||
{
|
||||
int64 size = atoll(atts[1]);
|
||||
m_feedItemInfo->SetSize(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_feedItemInfo &&
|
||||
(!strcmp("newznab:attr", name) || !strcmp("nZEDb:attr", name)) &&
|
||||
atts[0] && atts[1] && atts[2] && atts[3] &&
|
||||
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
|
||||
{
|
||||
m_feedItemInfo->GetAttributes()->emplace_back(atts[1], atts[3]);
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (m_feedItemInfo->GetSize() == 0 &&
|
||||
!strcmp("size", atts[1]))
|
||||
{
|
||||
int64 size = atoll(atts[3]);
|
||||
m_feedItemInfo->SetSize(size);
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
else if (!strcmp("imdb", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetImdbId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
else if (!strcmp("rageid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetRageId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvdbid" value="33877"/>
|
||||
else if (!strcmp("tvdbid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetTvdbId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="tvmazeid" value="33877"/>
|
||||
else if (!strcmp("tvmazeid", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetTvmazeId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
else if (!strcmp("episode", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetEpisode(atts[3]);
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
else if (!strcmp("season", atts[1]))
|
||||
{
|
||||
m_feedItemInfo->SetSeason(atts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_EndElement(const char *name)
|
||||
{
|
||||
if (!strcmp("title", name) && m_feedItemInfo)
|
||||
{
|
||||
m_feedItemInfo->SetTitle(m_tagContent);
|
||||
ResetTagContent();
|
||||
ParseSubject(*m_feedItemInfo);
|
||||
}
|
||||
else if (!strcmp("link", name) && m_feedItemInfo &&
|
||||
(!m_feedItemInfo->GetUrl() || strlen(m_feedItemInfo->GetUrl()) == 0))
|
||||
{
|
||||
m_feedItemInfo->SetUrl(m_tagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("category", name) && m_feedItemInfo)
|
||||
{
|
||||
m_feedItemInfo->SetCategory(m_tagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("description", name) && m_feedItemInfo)
|
||||
{
|
||||
// cleanup CDATA
|
||||
CString description = *m_tagContent;
|
||||
WebUtil::XmlStripTags(description);
|
||||
WebUtil::XmlDecode(description);
|
||||
WebUtil::XmlRemoveEntities(description);
|
||||
m_feedItemInfo->SetDescription(description);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("pubDate", name) && m_feedItemInfo)
|
||||
{
|
||||
time_t unixtime = WebUtil::ParseRfc822DateTime(m_tagContent);
|
||||
if (unixtime > 0)
|
||||
{
|
||||
m_feedItemInfo->SetTime(unixtime);
|
||||
}
|
||||
ResetTagContent();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_Content(const char *buf, int len)
|
||||
{
|
||||
m_tagContent.Append(buf, len);
|
||||
}
|
||||
|
||||
void FeedFile::ResetTagContent()
|
||||
{
|
||||
m_tagContent.Clear();
|
||||
}
|
||||
|
||||
void FeedFile::SAX_StartElement(FeedFile* file, const char *name, const char **atts)
|
||||
{
|
||||
file->Parse_StartElement(name, atts);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_EndElement(FeedFile* file, const char *name)
|
||||
{
|
||||
file->Parse_EndElement(name);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_characters(FeedFile* file, const char * xmlstr, int len)
|
||||
{
|
||||
char* str = (char*)xmlstr;
|
||||
|
||||
// trim starting blanks
|
||||
int off = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
off++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int newlen = len - off;
|
||||
|
||||
// trim ending blanks
|
||||
for (int i = len - 1; i >= off; i--)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
newlen--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newlen > 0)
|
||||
{
|
||||
// interpret tag content
|
||||
file->Parse_Content(str + off, newlen);
|
||||
}
|
||||
}
|
||||
|
||||
void* FeedFile::SAX_getEntity(FeedFile* file, const char * name)
|
||||
{
|
||||
#ifdef DISABLE_LIBXML2
|
||||
void* e = nullptr;
|
||||
#else
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
#endif
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
file->m_ignoreNextError = true;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void FeedFile::SAX_error(FeedFile* file, const char *msg, ...)
|
||||
{
|
||||
if (file->m_ignoreNextError)
|
||||
{
|
||||
file->m_ignoreNextError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, msg);
|
||||
char errMsg[1024];
|
||||
vsnprintf(errMsg, sizeof(errMsg), msg, argp);
|
||||
errMsg[1024-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// remove trailing CRLF
|
||||
for (char* pend = errMsg + strlen(errMsg) - 1; pend >= errMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
error("Error parsing rss feed %s: %s", *file->m_infoName, errMsg);
|
||||
}
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 FEEDFILE_H
|
||||
#define FEEDFILE_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "FeedInfo.h"
|
||||
|
||||
class FeedFile
|
||||
{
|
||||
public:
|
||||
FeedFile(const char* fileName, const char* infoName);
|
||||
bool Parse();
|
||||
std::unique_ptr<FeedItemList> DetachFeedItems() { return std::move(m_feedItems); }
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
private:
|
||||
std::unique_ptr<FeedItemList> m_feedItems;
|
||||
CString m_fileName;
|
||||
CString m_infoName;
|
||||
|
||||
void ParseSubject(FeedItemInfo& feedItemInfo);
|
||||
#ifdef WIN32
|
||||
bool ParseFeed(IUnknown* nzb);
|
||||
static void EncodeUrl(const char* filename, char* url, int bufLen);
|
||||
#else
|
||||
FeedItemInfo* m_feedItemInfo;
|
||||
StringBuilder m_tagContent;
|
||||
bool m_ignoreNextError;
|
||||
|
||||
static void SAX_StartElement(FeedFile* file, const char *name, const char **atts);
|
||||
static void SAX_EndElement(FeedFile* file, const char *name);
|
||||
static void SAX_characters(FeedFile* file, const char * xmlstr, int len);
|
||||
static void* SAX_getEntity(FeedFile* file, const char * name);
|
||||
static void SAX_error(FeedFile* file, const char *msg, ...);
|
||||
void Parse_StartElement(const char *name, const char **atts);
|
||||
void Parse_EndElement(const char *name);
|
||||
void Parse_Content(const char *buf, int len);
|
||||
void ResetTagContent();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 FEEDFILTER_H
|
||||
#define FEEDFILTER_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
class FeedFilter
|
||||
{
|
||||
public:
|
||||
FeedFilter(const char* filter);
|
||||
void Match(FeedItemInfo& feedItemInfo);
|
||||
|
||||
private:
|
||||
typedef std::vector<CString> RefValues;
|
||||
|
||||
enum ETermCommand
|
||||
{
|
||||
fcText,
|
||||
fcRegex,
|
||||
fcEqual,
|
||||
fcLess,
|
||||
fcLessEqual,
|
||||
fcGreater,
|
||||
fcGreaterEqual,
|
||||
fcOpeningBrace,
|
||||
fcClosingBrace,
|
||||
fcOrOperator
|
||||
};
|
||||
|
||||
class Term
|
||||
{
|
||||
public:
|
||||
Term() {}
|
||||
Term(Term&&) = delete; // catch performance issues
|
||||
void SetRefValues(RefValues* refValues) { m_refValues = refValues; }
|
||||
bool Compile(char* token);
|
||||
bool Match(FeedItemInfo& feedItemInfo);
|
||||
ETermCommand GetCommand() { return m_command; }
|
||||
|
||||
private:
|
||||
bool m_positive;
|
||||
CString m_field;
|
||||
ETermCommand m_command;
|
||||
CString m_param;
|
||||
int64 m_intParam = 0;
|
||||
double m_floatParam = 0.0;
|
||||
bool m_float = false;
|
||||
std::unique_ptr<RegEx> m_regEx;
|
||||
RefValues* m_refValues = nullptr;
|
||||
|
||||
bool GetFieldData(const char* field, FeedItemInfo* feedItemInfo,
|
||||
const char** StrValue, int64* IntValue);
|
||||
bool ParseParam(const char* field, const char* param);
|
||||
bool ParseSizeParam(const char* param);
|
||||
bool ParseAgeParam(const char* param);
|
||||
bool ParseNumericParam(const char* param);
|
||||
bool MatchValue(const char* strValue, int64 intValue);
|
||||
bool MatchText(const char* strValue);
|
||||
bool MatchRegex(const char* strValue);
|
||||
void FillWildMaskRefValues(const char* strValue, WildMask* mask, int refOffset);
|
||||
void FillRegExRefValues(const char* strValue, RegEx* regEx);
|
||||
};
|
||||
|
||||
typedef std::deque<Term> TermList;
|
||||
|
||||
enum ERuleCommand
|
||||
{
|
||||
frAccept,
|
||||
frReject,
|
||||
frRequire,
|
||||
frOptions,
|
||||
frComment
|
||||
};
|
||||
|
||||
class Rule
|
||||
{
|
||||
public:
|
||||
Rule() {}
|
||||
Rule(Rule&&) = delete; // catch performance issues
|
||||
void Compile(char* rule);
|
||||
bool IsValid() { return m_isValid; }
|
||||
ERuleCommand GetCommand() { return m_command; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
int GetPriority() { return m_priority; }
|
||||
int GetAddPriority() { return m_addPriority; }
|
||||
bool GetPause() { return m_pause; }
|
||||
const char* GetDupeKey() { return m_dupeKey; }
|
||||
const char* GetAddDupeKey() { return m_addDupeKey; }
|
||||
int GetDupeScore() { return m_dupeScore; }
|
||||
int GetAddDupeScore() { return m_addDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_dupeMode; }
|
||||
const char* GetRageId() { return m_rageId; }
|
||||
const char* GetTvdbId() { return m_tvdbId; }
|
||||
const char* GetTvmazeId() { return m_tvmazeId; }
|
||||
const char* GetSeries() { return m_series; }
|
||||
bool HasCategory() { return m_hasCategory; }
|
||||
bool HasPriority() { return m_hasPriority; }
|
||||
bool HasAddPriority() { return m_hasAddPriority; }
|
||||
bool HasPause() { return m_hasPause; }
|
||||
bool HasDupeScore() { return m_hasDupeScore; }
|
||||
bool HasAddDupeScore() { return m_hasAddDupeScore; }
|
||||
bool HasDupeKey() { return m_hasDupeKey; }
|
||||
bool HasAddDupeKey() { return m_hasAddDupeKey; }
|
||||
bool HasDupeMode() { return m_hasDupeMode; }
|
||||
bool HasRageId() { return m_hasRageId; }
|
||||
bool HasTvdbId() { return m_hasTvdbId; }
|
||||
bool HasTvmazeId() { return m_hasTvmazeId; }
|
||||
bool HasSeries() { return m_hasSeries; }
|
||||
bool Match(FeedItemInfo& feedItemInfo);
|
||||
void ExpandRefValues(FeedItemInfo& feedItemInfo, CString* destStr, const char* patStr);
|
||||
const char* GetRefValue(FeedItemInfo& feedItemInfo, const char* varName);
|
||||
|
||||
private:
|
||||
bool m_isValid = false;
|
||||
ERuleCommand m_command = frAccept;
|
||||
CString m_category;
|
||||
int m_priority = 0;
|
||||
int m_addPriority = 0;
|
||||
bool m_pause = false;
|
||||
int m_dupeScore;
|
||||
int m_addDupeScore = 0;
|
||||
CString m_dupeKey;
|
||||
CString m_addDupeKey;
|
||||
EDupeMode m_dupeMode = dmScore;
|
||||
CString m_series;
|
||||
CString m_rageId;
|
||||
CString m_tvdbId;
|
||||
CString m_tvmazeId;
|
||||
bool m_hasCategory = false;
|
||||
bool m_hasPriority = false;
|
||||
bool m_hasAddPriority = false;
|
||||
bool m_hasPause = false;
|
||||
bool m_hasDupeScore = false;
|
||||
bool m_hasAddDupeScore = false;
|
||||
bool m_hasDupeKey = false;
|
||||
bool m_hasAddDupeKey = false;
|
||||
bool m_hasDupeMode = false;
|
||||
bool m_hasPatCategory = false;
|
||||
bool m_hasPatDupeKey = false;
|
||||
bool m_hasPatAddDupeKey = false;
|
||||
bool m_hasSeries = false;
|
||||
bool m_hasRageId = false;
|
||||
bool m_hasTvdbId = false;
|
||||
bool m_hasTvmazeId = false;
|
||||
CString m_patCategory;
|
||||
CString m_patDupeKey;
|
||||
CString m_patAddDupeKey;
|
||||
TermList m_terms;
|
||||
RefValues m_refValues;
|
||||
|
||||
char* CompileCommand(char* rule);
|
||||
char* CompileOptions(char* rule);
|
||||
bool CompileTerm(char* term);
|
||||
bool MatchExpression(FeedItemInfo& feedItemInfo);
|
||||
};
|
||||
|
||||
typedef std::deque<Rule> RuleList;
|
||||
|
||||
RuleList m_rules;
|
||||
|
||||
void Compile(const char* filter);
|
||||
void CompileRule(char* rule);
|
||||
void ApplyOptions(Rule& rule, FeedItemInfo& feedItemInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
FeedInfo::FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
|
||||
const char* filter, bool pauseNzb, const char* category, int priority, const char* extensions) :
|
||||
m_backlog(backlog), m_interval(interval), m_pauseNzb(pauseNzb), m_priority(priority)
|
||||
{
|
||||
m_id = id;
|
||||
m_name = name ? name : "";
|
||||
if (m_name.Length() == 0)
|
||||
{
|
||||
m_name.Format("Feed%i", m_id);
|
||||
}
|
||||
m_url = url ? url : "";
|
||||
m_filter = filter ? filter : "";
|
||||
m_filterHash = Util::HashBJ96(m_filter, strlen(m_filter), 0);
|
||||
m_category = category ? category : "";
|
||||
m_extensions = extensions ? extensions : "";
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* name)
|
||||
{
|
||||
for (Attr& attr : this)
|
||||
{
|
||||
if (!strcasecmp(attr.GetName(), name))
|
||||
{
|
||||
return &attr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void FeedItemInfo::SetSeason(const char* season)
|
||||
{
|
||||
m_season = season;
|
||||
m_seasonNum = season ? ParsePrefixedInt(season) : 0;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetEpisode(const char* episode)
|
||||
{
|
||||
m_episode = episode;
|
||||
m_episodeNum = episode ? ParsePrefixedInt(episode) : 0;
|
||||
}
|
||||
|
||||
int FeedItemInfo::ParsePrefixedInt(const char *value)
|
||||
{
|
||||
const char* val = value;
|
||||
if (!strchr("0123456789", *val))
|
||||
{
|
||||
val++;
|
||||
}
|
||||
return atoi(val);
|
||||
}
|
||||
|
||||
void FeedItemInfo::AppendDupeKey(const char* extraDupeKey)
|
||||
{
|
||||
if (!m_dupeKey.Empty() && !Util::EmptyStr(extraDupeKey))
|
||||
{
|
||||
m_dupeKey.AppendFmt("-%s", extraDupeKey);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedItemInfo::BuildDupeKey(const char* rageId, const char* tvdbId, const char* tvmazeId, const char* series)
|
||||
{
|
||||
int rageIdVal = !Util::EmptyStr(rageId) ? atoi(rageId) : m_rageId;
|
||||
int tvdbIdVal = !Util::EmptyStr(tvdbId) ? atoi(tvdbId) : m_tvdbId;
|
||||
int tvmazeIdVal = !Util::EmptyStr(tvmazeId) ? atoi(tvmazeId) : m_tvmazeId;
|
||||
|
||||
if (m_imdbId != 0)
|
||||
{
|
||||
m_dupeKey.Format("imdb=%i", m_imdbId);
|
||||
}
|
||||
else if (!Util::EmptyStr(series) && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("series=%s-%s-%s", series, *m_season, *m_episode);
|
||||
}
|
||||
else if (rageIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("rageid=%i-%s-%s", rageIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else if (tvdbIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("tvdbid=%i-%s-%s", tvdbIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else if (tvmazeIdVal != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_dupeKey.Format("tvmazeid=%i-%s-%s", tvmazeIdVal, *m_season, *m_episode);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_dupeKey = "";
|
||||
}
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetSeasonNum()
|
||||
{
|
||||
if (!m_season && !m_seasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_seasonNum;
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetEpisodeNum()
|
||||
{
|
||||
if (!m_episode && !m_seasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_episodeNum;
|
||||
}
|
||||
|
||||
void FeedItemInfo::ParseSeasonEpisode()
|
||||
{
|
||||
m_seasonEpisodeParsed = true;
|
||||
|
||||
const char* pattern = "[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]";
|
||||
|
||||
std::unique_ptr<RegEx>& regEx = m_feedFilterHelper->GetRegEx(1);
|
||||
if (!regEx)
|
||||
{
|
||||
regEx = std::make_unique<RegEx>(pattern, 10);
|
||||
}
|
||||
|
||||
if (regEx->Match(m_title))
|
||||
{
|
||||
SetSeason(BString<100>("S%02d", atoi(m_title + regEx->GetMatchStart(1))));
|
||||
|
||||
BString<100> regValue;
|
||||
regValue.Set(m_title + regEx->GetMatchStart(2), regEx->GetMatchLen(2));
|
||||
|
||||
BString<100> episode("E%s", *regValue);
|
||||
Util::ReduceStr(episode, "-", "");
|
||||
for (char* p = episode; *p; p++) *p = toupper(*p); // convert string to uppercase e02 -> E02
|
||||
SetEpisode(episode);
|
||||
}
|
||||
}
|
||||
|
||||
const char* FeedItemInfo::GetDupeStatus()
|
||||
{
|
||||
if (!m_dupeStatus)
|
||||
{
|
||||
BString<1024> statuses;
|
||||
m_feedFilterHelper->CalcDupeStatus(m_title, m_dupeKey, statuses, statuses.Capacity());
|
||||
m_dupeStatus = statuses;
|
||||
}
|
||||
|
||||
return m_dupeStatus;
|
||||
}
|
||||
|
||||
|
||||
void FeedHistory::Remove(const char* url)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
FeedHistoryInfo& feedHistoryInfo = *it;
|
||||
if (!strcmp(feedHistoryInfo.GetUrl(), url))
|
||||
{
|
||||
erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedHistoryInfo* FeedHistory::Find(const char* url)
|
||||
{
|
||||
for (FeedHistoryInfo& feedHistoryInfo : this)
|
||||
{
|
||||
if (!strcmp(feedHistoryInfo.GetUrl(), url))
|
||||
{
|
||||
return &feedHistoryInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
/*
|
||||
* This file is part of nzbget. See <http://nzbget.net>.
|
||||
*
|
||||
* Copyright (C) 2013-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 FEEDINFO_H
|
||||
#define FEEDINFO_H
|
||||
|
||||
#include "NString.h"
|
||||
#include "Util.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class FeedInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
fsUndefined,
|
||||
fsRunning,
|
||||
fsFinished,
|
||||
fsFailed
|
||||
};
|
||||
|
||||
FeedInfo(int id, const char* name, const char* url, bool backlog, int interval,
|
||||
const char* filter, bool pauseNzb, const char* category, int priority,
|
||||
const char* extensions);
|
||||
int GetId() { return m_id; }
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetUrl() { return m_url; }
|
||||
int GetInterval() { return m_interval; }
|
||||
const char* GetFilter() { return m_filter; }
|
||||
uint32 GetFilterHash() { return m_filterHash; }
|
||||
bool GetPauseNzb() { return m_pauseNzb; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
int GetPriority() { return m_priority; }
|
||||
const char* GetExtensions() { return m_extensions; }
|
||||
time_t GetLastUpdate() { return m_lastUpdate; }
|
||||
void SetLastUpdate(time_t lastUpdate) { m_lastUpdate = lastUpdate; }
|
||||
time_t GetNextUpdate() { return m_nextUpdate; }
|
||||
void SetNextUpdate(time_t nextUpdate) { m_nextUpdate = nextUpdate; }
|
||||
int GetLastInterval() { return m_lastInterval; }
|
||||
void SetLastInterval(int lastInterval) { m_lastInterval = lastInterval; }
|
||||
bool GetPreview() { return m_preview; }
|
||||
void SetPreview(bool preview) { m_preview = preview; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus Status) { m_status = Status; }
|
||||
const char* GetOutputFilename() { return m_outputFilename; }
|
||||
void SetOutputFilename(const char* outputFilename) { m_outputFilename = outputFilename; }
|
||||
bool GetFetch() { return m_fetch; }
|
||||
void SetFetch(bool fetch) { m_fetch = fetch; }
|
||||
bool GetForce() { return m_force; }
|
||||
void SetForce(bool force) { m_force = force; }
|
||||
bool GetBacklog() { return m_backlog; }
|
||||
void SetBacklog(bool backlog) { m_backlog = backlog; }
|
||||
|
||||
private:
|
||||
int m_id;
|
||||
CString m_name;
|
||||
CString m_url;
|
||||
bool m_backlog;
|
||||
int m_interval;
|
||||
CString m_filter;
|
||||
uint32 m_filterHash;
|
||||
bool m_pauseNzb;
|
||||
CString m_category;
|
||||
CString m_extensions;
|
||||
int m_priority;
|
||||
time_t m_lastUpdate = 0;
|
||||
time_t m_nextUpdate = 0;
|
||||
int m_lastInterval = 0;
|
||||
bool m_preview = false;
|
||||
EStatus m_status = fsUndefined;
|
||||
CString m_outputFilename;
|
||||
bool m_fetch = false;
|
||||
bool m_force = false;
|
||||
};
|
||||
|
||||
typedef std::deque<std::unique_ptr<FeedInfo>> Feeds;
|
||||
|
||||
class FeedFilterHelper
|
||||
{
|
||||
public:
|
||||
virtual std::unique_ptr<RegEx>& GetRegEx(int id) = 0;
|
||||
virtual void CalcDupeStatus(const char* title, const char* dupeKey, char* statusBuf, int bufLen) = 0;
|
||||
};
|
||||
|
||||
class FeedItemInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
isUnknown,
|
||||
isBacklog,
|
||||
isFetched,
|
||||
isNew
|
||||
};
|
||||
|
||||
enum EMatchStatus
|
||||
{
|
||||
msIgnored,
|
||||
msAccepted,
|
||||
msRejected
|
||||
};
|
||||
|
||||
class Attr
|
||||
{
|
||||
public:
|
||||
Attr(const char* name, const char* value) :
|
||||
m_name(name ? name : ""), m_value(value ? value : "") {}
|
||||
const char* GetName() { return m_name; }
|
||||
const char* GetValue() { return m_value; }
|
||||
private:
|
||||
CString m_name;
|
||||
CString m_value;
|
||||
};
|
||||
|
||||
typedef std::deque<Attr> AttributesBase;
|
||||
|
||||
class Attributes: public AttributesBase
|
||||
{
|
||||
public:
|
||||
Attr* Find(const char* name);
|
||||
};
|
||||
|
||||
FeedItemInfo() {}
|
||||
FeedItemInfo(FeedItemInfo&&) = delete; // catch performance issues
|
||||
void SetFeedFilterHelper(FeedFilterHelper* feedFilterHelper) { m_feedFilterHelper = feedFilterHelper; }
|
||||
const char* GetTitle() { return m_title; }
|
||||
void SetTitle(const char* title) { m_title = title; }
|
||||
const char* GetFilename() { return m_filename; }
|
||||
void SetFilename(const char* filename) { m_filename = filename; }
|
||||
const char* GetUrl() { return m_url; }
|
||||
void SetUrl(const char* url) { m_url = url; }
|
||||
int64 GetSize() { return m_size; }
|
||||
void SetSize(int64 size) { m_size = size; }
|
||||
const char* GetCategory() { return m_category; }
|
||||
void SetCategory(const char* category) { m_category = category; }
|
||||
int GetImdbId() { return m_imdbId; }
|
||||
void SetImdbId(int imdbId) { m_imdbId = imdbId; }
|
||||
int GetRageId() { return m_rageId; }
|
||||
void SetRageId(int rageId) { m_rageId = rageId; }
|
||||
int GetTvdbId() { return m_tvdbId; }
|
||||
void SetTvdbId(int tvdbId) { m_tvdbId = tvdbId; }
|
||||
int GetTvmazeId() { return m_tvmazeId; }
|
||||
void SetTvmazeId(int tvmazeId) { m_tvmazeId = tvmazeId; }
|
||||
const char* GetDescription() { return m_description; }
|
||||
void SetDescription(const char* description) { m_description = description ? description: ""; }
|
||||
const char* GetSeason() { return m_season; }
|
||||
void SetSeason(const char* season);
|
||||
const char* GetEpisode() { return m_episode; }
|
||||
void SetEpisode(const char* episode);
|
||||
int GetSeasonNum();
|
||||
int GetEpisodeNum();
|
||||
const char* GetAddCategory() { return m_addCategory; }
|
||||
void SetAddCategory(const char* addCategory) { m_addCategory = addCategory ? addCategory : ""; }
|
||||
bool GetPauseNzb() { return m_pauseNzb; }
|
||||
void SetPauseNzb(bool pauseNzb) { m_pauseNzb = pauseNzb; }
|
||||
int GetPriority() { return m_priority; }
|
||||
void SetPriority(int priority) { m_priority = priority; }
|
||||
time_t GetTime() { return m_time; }
|
||||
void SetTime(time_t time) { m_time = time; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus status) { m_status = status; }
|
||||
EMatchStatus GetMatchStatus() { return m_matchStatus; }
|
||||
void SetMatchStatus(EMatchStatus matchStatus) { m_matchStatus = matchStatus; }
|
||||
int GetMatchRule() { return m_matchRule; }
|
||||
void SetMatchRule(int matchRule) { m_matchRule = matchRule; }
|
||||
const char* GetDupeKey() { return m_dupeKey; }
|
||||
void SetDupeKey(const char* dupeKey) { m_dupeKey = dupeKey ? dupeKey : ""; }
|
||||
void AppendDupeKey(const char* extraDupeKey);
|
||||
void BuildDupeKey(const char* rageId, const char* tvdbId, const char* tvmazeId, const char* series);
|
||||
int GetDupeScore() { return m_dupeScore; }
|
||||
void SetDupeScore(int dupeScore) { m_dupeScore = dupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_dupeMode; }
|
||||
void SetDupeMode(EDupeMode dupeMode) { m_dupeMode = dupeMode; }
|
||||
const char* GetDupeStatus();
|
||||
Attributes* GetAttributes() { return &m_attributes; }
|
||||
|
||||
private:
|
||||
CString m_title;
|
||||
CString m_filename;
|
||||
CString m_url;
|
||||
time_t m_time = 0;
|
||||
int64 m_size = 0;
|
||||
CString m_category = "";
|
||||
int m_imdbId = 0;
|
||||
int m_rageId = 0;
|
||||
int m_tvdbId = 0;
|
||||
int m_tvmazeId = 0;
|
||||
CString m_description = "";
|
||||
CString m_season;
|
||||
CString m_episode;
|
||||
int m_seasonNum = 0;
|
||||
int m_episodeNum = 0;
|
||||
bool m_seasonEpisodeParsed = false;
|
||||
CString m_addCategory = "";
|
||||
bool m_pauseNzb = false;
|
||||
int m_priority = 0;
|
||||
EStatus m_status = isUnknown;
|
||||
EMatchStatus m_matchStatus = msIgnored;
|
||||
int m_matchRule = 0;
|
||||
CString m_dupeKey;
|
||||
int m_dupeScore = 0;
|
||||
EDupeMode m_dupeMode = dmScore;
|
||||
CString m_dupeStatus;
|
||||
FeedFilterHelper* m_feedFilterHelper = nullptr;
|
||||
Attributes m_attributes;
|
||||
|
||||
int ParsePrefixedInt(const char *value);
|
||||
void ParseSeasonEpisode();
|
||||
};
|
||||
|
||||
typedef std::deque<FeedItemInfo> FeedItemList;
|
||||
|
||||
class FeedHistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
hsUnknown,
|
||||
hsBacklog,
|
||||
hsFetched
|
||||
};
|
||||
|
||||
FeedHistoryInfo(const char* url, EStatus status, time_t lastSeen) :
|
||||
m_url(url), m_status(status), m_lastSeen(lastSeen) {}
|
||||
const char* GetUrl() { return m_url; }
|
||||
EStatus GetStatus() { return m_status; }
|
||||
void SetStatus(EStatus Status) { m_status = Status; }
|
||||
time_t GetLastSeen() { return m_lastSeen; }
|
||||
void SetLastSeen(time_t lastSeen) { m_lastSeen = lastSeen; }
|
||||
|
||||
private:
|
||||
CString m_url;
|
||||
EStatus m_status;
|
||||
time_t m_lastSeen;
|
||||
};
|
||||
|
||||
typedef std::deque<FeedHistoryInfo> FeedHistoryBase;
|
||||
|
||||
class FeedHistory : public FeedHistoryBase
|
||||
{
|
||||
public:
|
||||
void Remove(const char* url);
|
||||
FeedHistoryInfo* Find(const char* url);
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user