Files
nzbget/daemon/main/Options.cpp
Andrey Prygunkov 9e2d8544da #126: full use of class BString
1) replaced characters arrays with class BString through the whole
program. The string formatting code has become much cleaner.
2) class Util returns error message via CString instead of character
buffers.
3) few more places to use CString.
2015-12-19 18:43:52 +01:00

1885 lines
54 KiB
C++

/*
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2015 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#include "nzbget.h"
#include "Util.h"
#include "Options.h"
#include "Log.h"
#include "MessageBase.h"
#include "DownloadInfo.h"
// Program options
static const char* OPTION_CONFIGFILE = "ConfigFile";
static const char* OPTION_APPBIN = "AppBin";
static const char* OPTION_APPDIR = "AppDir";
static const char* OPTION_VERSION = "Version";
static const char* OPTION_MAINDIR = "MainDir";
static const char* OPTION_DESTDIR = "DestDir";
static const char* OPTION_INTERDIR = "InterDir";
static const char* OPTION_TEMPDIR = "TempDir";
static const char* OPTION_QUEUEDIR = "QueueDir";
static const char* OPTION_NZBDIR = "NzbDir";
static const char* OPTION_WEBDIR = "WebDir";
static const char* OPTION_CONFIGTEMPLATE = "ConfigTemplate";
static const char* OPTION_SCRIPTDIR = "ScriptDir";
static const char* OPTION_REQUIREDDIR = "RequiredDir";
static const char* OPTION_LOGFILE = "LogFile";
static const char* OPTION_WRITELOG = "WriteLog";
static const char* OPTION_ROTATELOG = "RotateLog";
static const char* OPTION_APPENDCATEGORYDIR = "AppendCategoryDir";
static const char* OPTION_LOCKFILE = "LockFile";
static const char* OPTION_DAEMONUSERNAME = "DaemonUsername";
static const char* OPTION_OUTPUTMODE = "OutputMode";
static const char* OPTION_DUPECHECK = "DupeCheck";
static const char* OPTION_DOWNLOADRATE = "DownloadRate";
static const char* OPTION_CONTROLIP = "ControlIp";
static const char* OPTION_CONTROLPORT = "ControlPort";
static const char* OPTION_CONTROLUSERNAME = "ControlUsername";
static const char* OPTION_CONTROLPASSWORD = "ControlPassword";
static const char* OPTION_RESTRICTEDUSERNAME = "RestrictedUsername";
static const char* OPTION_RESTRICTEDPASSWORD = "RestrictedPassword";
static const char* OPTION_ADDUSERNAME = "AddUsername";
static const char* OPTION_ADDPASSWORD = "AddPassword";
static const char* OPTION_SECURECONTROL = "SecureControl";
static const char* OPTION_SECUREPORT = "SecurePort";
static const char* OPTION_SECURECERT = "SecureCert";
static const char* OPTION_SECUREKEY = "SecureKey";
static const char* OPTION_AUTHORIZEDIP = "AuthorizedIP";
static const char* OPTION_ARTICLETIMEOUT = "ArticleTimeout";
static const char* OPTION_URLTIMEOUT = "UrlTimeout";
static const char* OPTION_SAVEQUEUE = "SaveQueue";
static const char* OPTION_FLUSHQUEUE = "FlushQueue";
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
static const char* OPTION_BROKENLOG = "BrokenLog";
static const char* OPTION_NZBLOG = "NzbLog";
static const char* OPTION_DECODE = "Decode";
static const char* OPTION_RETRIES = "Retries";
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
static const char* OPTION_CONTINUEPARTIAL = "ContinuePartial";
static const char* OPTION_URLCONNECTIONS = "UrlConnections";
static const char* OPTION_LOGBUFFERSIZE = "LogBufferSize";
static const char* OPTION_INFOTARGET = "InfoTarget";
static const char* OPTION_WARNINGTARGET = "WarningTarget";
static const char* OPTION_ERRORTARGET = "ErrorTarget";
static const char* OPTION_DEBUGTARGET = "DebugTarget";
static const char* OPTION_DETAILTARGET = "DetailTarget";
static const char* OPTION_PARCHECK = "ParCheck";
static const char* OPTION_PARREPAIR = "ParRepair";
static const char* OPTION_PARSCAN = "ParScan";
static const char* OPTION_PARQUICK = "ParQuick";
static const char* OPTION_PARRENAME = "ParRename";
static const char* OPTION_PARBUFFER = "ParBuffer";
static const char* OPTION_PARTHREADS = "ParThreads";
static const char* OPTION_HEALTHCHECK = "HealthCheck";
static const char* OPTION_SCANSCRIPT = "ScanScript";
static const char* OPTION_QUEUESCRIPT = "QueueScript";
static const char* OPTION_FEEDSCRIPT = "FeedScript";
static const char* OPTION_UMASK = "UMask";
static const char* OPTION_UPDATEINTERVAL = "UpdateInterval";
static const char* OPTION_CURSESNZBNAME = "CursesNzbName";
static const char* OPTION_CURSESTIME = "CursesTime";
static const char* OPTION_CURSESGROUP = "CursesGroup";
static const char* OPTION_CRCCHECK = "CrcCheck";
static const char* OPTION_DIRECTWRITE = "DirectWrite";
static const char* OPTION_WRITEBUFFER = "WriteBuffer";
static const char* OPTION_NZBDIRINTERVAL = "NzbDirInterval";
static const char* OPTION_NZBDIRFILEAGE = "NzbDirFileAge";
static const char* OPTION_PARCLEANUPQUEUE = "ParCleanupQueue";
static const char* OPTION_DISKSPACE = "DiskSpace";
static const char* OPTION_DUMPCORE = "DumpCore";
static const char* OPTION_PARPAUSEQUEUE = "ParPauseQueue";
static const char* OPTION_SCRIPTPAUSEQUEUE = "ScriptPauseQueue";
static const char* OPTION_NZBCLEANUPDISK = "NzbCleanupDisk";
static const char* OPTION_DELETECLEANUPDISK = "DeleteCleanupDisk";
static const char* OPTION_PARTIMELIMIT = "ParTimeLimit";
static const char* OPTION_KEEPHISTORY = "KeepHistory";
static const char* OPTION_ACCURATERATE = "AccurateRate";
static const char* OPTION_UNPACK = "Unpack";
static const char* OPTION_UNPACKCLEANUPDISK = "UnpackCleanupDisk";
static const char* OPTION_UNRARCMD = "UnrarCmd";
static const char* OPTION_SEVENZIPCMD = "SevenZipCmd";
static const char* OPTION_UNPACKPASSFILE = "UnpackPassFile";
static const char* OPTION_UNPACKPAUSEQUEUE = "UnpackPauseQueue";
static const char* OPTION_SCRIPTORDER = "ScriptOrder";
static const char* OPTION_POSTSCRIPT = "PostScript";
static const char* OPTION_EXTCLEANUPDISK = "ExtCleanupDisk";
static const char* OPTION_PARIGNOREEXT = "ParIgnoreExt";
static const char* OPTION_FEEDHISTORY = "FeedHistory";
static const char* OPTION_URLFORCE = "UrlForce";
static const char* OPTION_TIMECORRECTION = "TimeCorrection";
static const char* OPTION_PROPAGATIONDELAY = "PropagationDelay";
static const char* OPTION_ARTICLECACHE = "ArticleCache";
static const char* OPTION_EVENTINTERVAL = "EventInterval";
// obsolete options
static const char* OPTION_POSTLOGKIND = "PostLogKind";
static const char* OPTION_NZBLOGKIND = "NZBLogKind";
static const char* OPTION_RETRYONCRCERROR = "RetryOnCrcError";
static const char* OPTION_ALLOWREPROCESS = "AllowReProcess";
static const char* OPTION_POSTPROCESS = "PostProcess";
static const char* OPTION_LOADPARS = "LoadPars";
static const char* OPTION_THREADLIMIT = "ThreadLimit";
static const char* OPTION_PROCESSLOGKIND = "ProcessLogKind";
static const char* OPTION_APPENDNZBDIR = "AppendNzbDir";
static const char* OPTION_RENAMEBROKEN = "RenameBroken";
static const char* OPTION_MERGENZB = "MergeNzb";
static const char* OPTION_STRICTPARNAME = "StrictParName";
static const char* OPTION_RELOADURLQUEUE = "ReloadUrlQueue";
static const char* OPTION_RELOADPOSTQUEUE = "ReloadPostQueue";
static const char* OPTION_NZBPROCESS = "NZBProcess";
static const char* OPTION_NZBADDEDPROCESS = "NZBAddedProcess";
static const char* OPTION_CREATELOG = "CreateLog";
static const char* OPTION_RESETLOG = "ResetLog";
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable", "enabled", "disabled" };
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
const int BoolCount = 12;
#ifndef WIN32
const char* PossibleConfigLocations[] =
{
"~/.nzbget",
"/etc/nzbget.conf",
"/usr/etc/nzbget.conf",
"/usr/local/etc/nzbget.conf",
"/opt/etc/nzbget.conf",
NULL
};
#endif
Options* g_Options = NULL;
Options::OptEntry::OptEntry()
{
m_name = NULL;
m_value = NULL;
m_defValue = NULL;
m_lineNo = 0;
}
Options::OptEntry::OptEntry(const char* name, const char* value)
{
m_name = name;
m_value = value;
m_lineNo = 0;
}
void Options::OptEntry::SetValue(const char* value)
{
m_value = value;
if (!m_defValue)
{
m_defValue = value;
}
}
bool Options::OptEntry::Restricted()
{
char loName[256];
strncpy(loName, m_name, sizeof(loName));
loName[256-1] = '\0';
for (char* p = loName; *p; p++) *p = tolower(*p); // convert string to lowercase
bool restricted = !strcasecmp(m_name, OPTION_CONTROLIP) ||
!strcasecmp(m_name, OPTION_CONTROLPORT) ||
!strcasecmp(m_name, OPTION_SECURECONTROL) ||
!strcasecmp(m_name, OPTION_SECUREPORT) ||
!strcasecmp(m_name, OPTION_SECURECERT) ||
!strcasecmp(m_name, OPTION_SECUREKEY) ||
!strcasecmp(m_name, OPTION_AUTHORIZEDIP) ||
!strcasecmp(m_name, OPTION_DAEMONUSERNAME) ||
!strcasecmp(m_name, OPTION_UMASK) ||
strchr(m_name, ':') || // All extension script options
strstr(loName, "username") || // ServerX.Username, ControlUsername, etc.
strstr(loName, "password"); // ServerX.Password, ControlPassword, etc.
return restricted;
}
Options::OptEntries::~OptEntries()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
Options::OptEntry* Options::OptEntries::FindOption(const char* name)
{
if (!name)
{
return NULL;
}
for (iterator it = begin(); it != end(); it++)
{
OptEntry* optEntry = *it;
if (!strcasecmp(optEntry->GetName(), name))
{
return optEntry;
}
}
return NULL;
}
Options::Category::Category(const char* name, const char* destDir, bool unpack, const char* postScript)
{
m_name = name;
m_destDir = destDir;
m_unpack = unpack;
m_postScript = postScript;
}
Options::Categories::~Categories()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
Options::Category* Options::Categories::FindCategory(const char* name, bool searchAliases)
{
if (!name)
{
return NULL;
}
for (iterator it = begin(); it != end(); it++)
{
Category* category = *it;
if (!strcasecmp(category->GetName(), name))
{
return category;
}
}
if (searchAliases)
{
for (iterator it = begin(); it != end(); it++)
{
Category* category = *it;
for (NameList::iterator it2 = category->GetAliases()->begin(); it2 != category->GetAliases()->end(); it2++)
{
const char* alias = *it2;
WildMask mask(alias);
if (mask.Match(name))
{
return category;
}
}
}
}
return NULL;
}
Options::Options(const char* exeName, const char* configFilename, bool noConfig,
CmdOptList* commandLineOptions, Extender* extender)
{
Init(exeName, configFilename, noConfig, commandLineOptions, false, extender);
}
Options::Options(CmdOptList* commandLineOptions, Extender* extender)
{
Init("nzbget/nzbget", NULL, true, commandLineOptions, true, extender);
}
void Options::Init(const char* exeName, const char* configFilename, bool noConfig,
CmdOptList* commandLineOptions, bool noDiskAccess, Extender* extender)
{
g_Options = this;
m_extender = extender;
m_noDiskAccess = noDiskAccess;
m_noConfig = noConfig;
m_configErrors = false;
m_configLine = 0;
m_serverMode = false;
m_remoteClientMode = false;
m_fatalError = false;
// initialize options with default values
m_infoTarget = mtScreen;
m_warningTarget = mtScreen;
m_errorTarget = mtScreen;
m_debugTarget = mtScreen;
m_detailTarget = mtScreen;
m_decode = true;
m_pauseDownload = false;
m_pausePostProcess = false;
m_pauseScan = false;
m_tempPauseDownload = true;
m_tempPausePostprocess = true;
m_brokenLog = false;
m_nzbLog = false;
m_downloadRate = 0;
m_articleTimeout = 0;
m_urlTimeout = 0;
m_terminateTimeout = 0;
m_appendCategoryDir = false;
m_continuePartial = false;
m_saveQueue = false;
m_flushQueue = false;
m_dupeCheck = false;
m_retries = 0;
m_retryInterval = 0;
m_controlPort = 0;
m_secureControl = false;
m_securePort = 0;
m_outputMode = omLoggable;
m_reloadQueue = false;
m_urlConnections = 0;
m_logBufferSize = 0;
m_writeLog = wlAppend;
m_rotateLog = 0;
m_parCheck = pcManual;
m_parRepair = false;
m_parScan = psLimited;
m_parQuick = true;
m_parRename = false;
m_parBuffer = 0;
m_parThreads = 0;
m_healthCheck = hcNone;
m_umask = 0;
m_updateInterval = 0;
m_cursesNzbName = false;
m_cursesTime = false;
m_cursesGroup = false;
m_crcCheck = false;
m_directWrite = false;
m_writeBuffer = 0;
m_nzbDirInterval = 0;
m_nzbDirFileAge = 0;
m_parCleanupQueue = false;
m_diskSpace = 0;
m_tls = false;
m_dumpCore = false;
m_parPauseQueue = false;
m_scriptPauseQueue = false;
m_nzbCleanupDisk = false;
m_deleteCleanupDisk = false;
m_parTimeLimit = 0;
m_keepHistory = 0;
m_accurateRate = false;
m_resumeTime = 0;
m_unpack = false;
m_unpackCleanupDisk = false;
m_unpackPauseQueue = false;
m_feedHistory = 0;
m_urlForce = false;
m_timeCorrection = 0;
m_localTimeOffset = 0;
m_propagationDelay = 0;
m_articleCache = 0;
m_eventInterval = 0;
m_noDiskAccess = noDiskAccess;
m_configFilename = configFilename;
SetOption(OPTION_CONFIGFILE, "");
char filename[MAX_PATH + 1];
if (m_noDiskAccess)
{
strncpy(filename, exeName, sizeof(filename));
filename[sizeof(filename)-1] = '\0';
}
else
{
Util::GetExeFileName(exeName, filename, sizeof(filename));
}
Util::NormalizePathSeparators(filename);
SetOption(OPTION_APPBIN, filename);
char* end = strrchr(filename, PATH_SEPARATOR);
if (end) *end = '\0';
SetOption(OPTION_APPDIR, filename);
m_appDir = filename;
SetOption(OPTION_VERSION, Util::VersionRevision());
InitDefaults();
InitOptFile();
if (m_fatalError)
{
return;
}
if (commandLineOptions)
{
InitCommandLineOptions(commandLineOptions);
}
if (!m_configFilename && !noConfig)
{
printf("No configuration-file found\n");
#ifdef WIN32
printf("Please put configuration-file \"nzbget.conf\" into the directory with exe-file\n");
#else
printf("Please use option \"-c\" or put configuration-file in one of the following locations:\n");
int p = 0;
while (const char* filename = PossibleConfigLocations[p++])
{
printf("%s\n", filename);
}
#endif
m_fatalError = true;
return;
}
InitOptions();
CheckOptions();
InitServers();
InitCategories();
InitScheduler();
InitFeeds();
}
Options::~Options()
{
g_Options = NULL;
}
void Options::Dump()
{
for (OptEntries::iterator it = m_optEntries.begin(); it != m_optEntries.end(); it++)
{
OptEntry* optEntry = *it;
printf("%s = \"%s\"\n", optEntry->GetName(), optEntry->GetValue());
}
}
void Options::ConfigError(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);
printf("%s(%i): %s\n", m_configFilename ? Util::BaseFileName(m_configFilename) : "<noconfig>", m_configLine, tmp2);
error("%s(%i): %s", m_configFilename ? Util::BaseFileName(m_configFilename) : "<noconfig>", m_configLine, tmp2);
m_configErrors = true;
}
void Options::ConfigWarn(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);
printf("%s(%i): %s\n", Util::BaseFileName(m_configFilename), m_configLine, tmp2);
warn("%s(%i): %s", Util::BaseFileName(m_configFilename), m_configLine, tmp2);
}
void Options::LocateOptionSrcPos(const char *optionName)
{
OptEntry* optEntry = FindOption(optionName);
if (optEntry)
{
m_configLine = optEntry->GetLineNo();
}
else
{
m_configLine = 0;
}
}
void Options::InitDefaults()
{
#ifdef WIN32
SetOption(OPTION_MAINDIR, "${AppDir}");
SetOption(OPTION_WEBDIR, "${AppDir}\\webui");
SetOption(OPTION_CONFIGTEMPLATE, "${AppDir}\\nzbget.conf.template");
#else
SetOption(OPTION_MAINDIR, "~/downloads");
SetOption(OPTION_WEBDIR, "");
SetOption(OPTION_CONFIGTEMPLATE, "");
#endif
SetOption(OPTION_TEMPDIR, "${MainDir}/tmp");
SetOption(OPTION_DESTDIR, "${MainDir}/dst");
SetOption(OPTION_INTERDIR, "");
SetOption(OPTION_QUEUEDIR, "${MainDir}/queue");
SetOption(OPTION_NZBDIR, "${MainDir}/nzb");
SetOption(OPTION_LOCKFILE, "${MainDir}/nzbget.lock");
SetOption(OPTION_LOGFILE, "${DestDir}/nzbget.log");
SetOption(OPTION_SCRIPTDIR, "${MainDir}/scripts");
SetOption(OPTION_REQUIREDDIR, "");
SetOption(OPTION_WRITELOG, "append");
SetOption(OPTION_ROTATELOG, "3");
SetOption(OPTION_APPENDCATEGORYDIR, "yes");
SetOption(OPTION_OUTPUTMODE, "curses");
SetOption(OPTION_DUPECHECK, "yes");
SetOption(OPTION_DOWNLOADRATE, "0");
SetOption(OPTION_CONTROLIP, "0.0.0.0");
SetOption(OPTION_CONTROLUSERNAME, "nzbget");
SetOption(OPTION_CONTROLPASSWORD, "tegbzn6789");
SetOption(OPTION_RESTRICTEDUSERNAME, "");
SetOption(OPTION_RESTRICTEDPASSWORD, "");
SetOption(OPTION_ADDUSERNAME, "");
SetOption(OPTION_ADDPASSWORD, "");
SetOption(OPTION_CONTROLPORT, "6789");
SetOption(OPTION_SECURECONTROL, "no");
SetOption(OPTION_SECUREPORT, "6791");
SetOption(OPTION_SECURECERT, "");
SetOption(OPTION_SECUREKEY, "");
SetOption(OPTION_AUTHORIZEDIP, "");
SetOption(OPTION_ARTICLETIMEOUT, "60");
SetOption(OPTION_URLTIMEOUT, "60");
SetOption(OPTION_SAVEQUEUE, "yes");
SetOption(OPTION_FLUSHQUEUE, "yes");
SetOption(OPTION_RELOADQUEUE, "yes");
SetOption(OPTION_BROKENLOG, "yes");
SetOption(OPTION_NZBLOG, "yes");
SetOption(OPTION_DECODE, "yes");
SetOption(OPTION_RETRIES, "3");
SetOption(OPTION_RETRYINTERVAL, "10");
SetOption(OPTION_TERMINATETIMEOUT, "600");
SetOption(OPTION_CONTINUEPARTIAL, "no");
SetOption(OPTION_URLCONNECTIONS, "4");
SetOption(OPTION_LOGBUFFERSIZE, "1000");
SetOption(OPTION_INFOTARGET, "both");
SetOption(OPTION_WARNINGTARGET, "both");
SetOption(OPTION_ERRORTARGET, "both");
SetOption(OPTION_DEBUGTARGET, "none");
SetOption(OPTION_DETAILTARGET, "both");
SetOption(OPTION_PARCHECK, "auto");
SetOption(OPTION_PARREPAIR, "yes");
SetOption(OPTION_PARSCAN, "extended");
SetOption(OPTION_PARQUICK, "yes");
SetOption(OPTION_PARRENAME, "yes");
SetOption(OPTION_PARBUFFER, "16");
SetOption(OPTION_PARTHREADS, "1");
SetOption(OPTION_HEALTHCHECK, "none");
SetOption(OPTION_SCRIPTORDER, "");
SetOption(OPTION_POSTSCRIPT, "");
SetOption(OPTION_SCANSCRIPT, "");
SetOption(OPTION_QUEUESCRIPT, "");
SetOption(OPTION_FEEDSCRIPT, "");
SetOption(OPTION_DAEMONUSERNAME, "root");
SetOption(OPTION_UMASK, "1000");
SetOption(OPTION_UPDATEINTERVAL, "200");
SetOption(OPTION_CURSESNZBNAME, "yes");
SetOption(OPTION_CURSESTIME, "no");
SetOption(OPTION_CURSESGROUP, "no");
SetOption(OPTION_CRCCHECK, "yes");
SetOption(OPTION_DIRECTWRITE, "yes");
SetOption(OPTION_WRITEBUFFER, "0");
SetOption(OPTION_NZBDIRINTERVAL, "5");
SetOption(OPTION_NZBDIRFILEAGE, "60");
SetOption(OPTION_PARCLEANUPQUEUE, "yes");
SetOption(OPTION_DISKSPACE, "250");
SetOption(OPTION_DUMPCORE, "no");
SetOption(OPTION_PARPAUSEQUEUE, "no");
SetOption(OPTION_SCRIPTPAUSEQUEUE, "no");
SetOption(OPTION_NZBCLEANUPDISK, "no");
SetOption(OPTION_DELETECLEANUPDISK, "no");
SetOption(OPTION_PARTIMELIMIT, "0");
SetOption(OPTION_KEEPHISTORY, "7");
SetOption(OPTION_ACCURATERATE, "no");
SetOption(OPTION_UNPACK, "no");
SetOption(OPTION_UNPACKCLEANUPDISK, "no");
#ifdef WIN32
SetOption(OPTION_UNRARCMD, "unrar.exe");
SetOption(OPTION_SEVENZIPCMD, "7z.exe");
#else
SetOption(OPTION_UNRARCMD, "unrar");
SetOption(OPTION_SEVENZIPCMD, "7z");
#endif
SetOption(OPTION_UNPACKPASSFILE, "");
SetOption(OPTION_UNPACKPAUSEQUEUE, "no");
SetOption(OPTION_EXTCLEANUPDISK, "");
SetOption(OPTION_PARIGNOREEXT, "");
SetOption(OPTION_FEEDHISTORY, "7");
SetOption(OPTION_URLFORCE, "yes");
SetOption(OPTION_TIMECORRECTION, "0");
SetOption(OPTION_PROPAGATIONDELAY, "0");
SetOption(OPTION_ARTICLECACHE, "0");
SetOption(OPTION_EVENTINTERVAL, "0");
}
void Options::InitOptFile()
{
if (!m_configFilename && !m_noConfig)
{
// search for config file in default locations
#ifdef WIN32
BString<1024> filename("%s\\nzbget.conf", *m_appDir);
if (!Util::FileExists(filename))
{
char appDataPath[MAX_PATH];
SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, appDataPath);
filename.Format("%s\\NZBGet\\nzbget.conf", appDataPath);
if (m_extender && !Util::FileExists(filename))
{
m_extender->SetupFirstStart();
}
}
if (Util::FileExists(filename))
{
m_configFilename = filename;
}
#else
// look in the exe-directory first
BString<1024> filename("%s/nzbget.conf", *m_appDir);
if (Util::FileExists(filename))
{
m_configFilename = filename;
}
else
{
int p = 0;
while (const char* filename = PossibleConfigLocations[p++])
{
// substitute HOME-variable
char expandedFilename[1024];
if (Util::ExpandHomePath(filename, expandedFilename, sizeof(expandedFilename)))
{
filename = expandedFilename;
}
if (Util::FileExists(filename))
{
m_configFilename = filename;
break;
}
}
}
#endif
}
if (m_configFilename)
{
// normalize path in filename
char filename[MAX_PATH + 1];
Util::ExpandFileName(m_configFilename, filename, sizeof(filename));
filename[MAX_PATH] = '\0';
#ifndef WIN32
// substitute HOME-variable
char expandedFilename[1024];
if (Util::ExpandHomePath(filename, expandedFilename, sizeof(expandedFilename)))
{
strncpy(filename, expandedFilename, sizeof(filename));
}
#endif
m_configFilename = filename;
SetOption(OPTION_CONFIGFILE, m_configFilename);
LoadConfigFile();
}
}
void Options::CheckDir(CString& dir, const char* optionName,
const char* parentDir, bool allowEmpty, bool create)
{
const char* tempdir = GetOption(optionName);
if (m_noDiskAccess)
{
dir = tempdir;
return;
}
if (Util::EmptyStr(tempdir))
{
if (!allowEmpty)
{
ConfigError("Invalid value for option \"%s\": <empty>", optionName);
}
dir = "";
return;
}
dir = tempdir;
Util::NormalizePathSeparators((char*)dir);
if (dir[dir.Length() - 1] != PATH_SEPARATOR)
{
dir.AppendFmt("%c", (int)PATH_SEPARATOR);
}
if (!(dir[0] == PATH_SEPARATOR || dir[0] == ALT_PATH_SEPARATOR ||
(dir[0] && dir[1] == ':')) &&
!Util::EmptyStr(parentDir))
{
// convert relative path to absolute path
int plen = strlen(parentDir);
BString<1024> usedir2;
if (parentDir[plen-1] == PATH_SEPARATOR || parentDir[plen-1] == ALT_PATH_SEPARATOR)
{
usedir2.Format("%s%s", parentDir, *dir);
}
else
{
usedir2.Format("%s%c%s", parentDir, PATH_SEPARATOR, *dir);
}
Util::NormalizePathSeparators((char*)usedir2);
dir = usedir2;
usedir2[usedir2.Length() - 1] = '\0';
SetOption(optionName, usedir2);
}
// Ensure the dir is created
CString errmsg;
if (create && !Util::ForceDirectories(dir, errmsg))
{
ConfigError("Invalid value for option \"%s\" (%s): %s", optionName, *dir, *errmsg);
}
}
void Options::InitOptions()
{
const char* mainDir = GetOption(OPTION_MAINDIR);
CheckDir(m_destDir, OPTION_DESTDIR, mainDir, false, false);
CheckDir(m_interDir, OPTION_INTERDIR, mainDir, true, false);
CheckDir(m_tempDir, OPTION_TEMPDIR, mainDir, false, true);
CheckDir(m_queueDir, OPTION_QUEUEDIR, mainDir, false, true);
CheckDir(m_webDir, OPTION_WEBDIR, NULL, true, false);
CheckDir(m_scriptDir, OPTION_SCRIPTDIR, mainDir, true, false);
CheckDir(m_nzbDir, OPTION_NZBDIR, mainDir, false, true);
m_requiredDir = GetOption(OPTION_REQUIREDDIR);
m_configTemplate = GetOption(OPTION_CONFIGTEMPLATE);
m_scriptOrder = GetOption(OPTION_SCRIPTORDER);
m_postScript = GetOption(OPTION_POSTSCRIPT);
m_scanScript = GetOption(OPTION_SCANSCRIPT);
m_queueScript = GetOption(OPTION_QUEUESCRIPT);
m_feedScript = GetOption(OPTION_FEEDSCRIPT);
m_controlIp = GetOption(OPTION_CONTROLIP);
m_controlUsername = GetOption(OPTION_CONTROLUSERNAME);
m_controlPassword = GetOption(OPTION_CONTROLPASSWORD);
m_restrictedUsername = GetOption(OPTION_RESTRICTEDUSERNAME);
m_restrictedPassword = GetOption(OPTION_RESTRICTEDPASSWORD);
m_addUsername = GetOption(OPTION_ADDUSERNAME);
m_addPassword = GetOption(OPTION_ADDPASSWORD);
m_secureCert = GetOption(OPTION_SECURECERT);
m_secureKey = GetOption(OPTION_SECUREKEY);
m_authorizedIp = GetOption(OPTION_AUTHORIZEDIP);
m_lockFile = GetOption(OPTION_LOCKFILE);
m_daemonUsername = GetOption(OPTION_DAEMONUSERNAME);
m_logFile = GetOption(OPTION_LOGFILE);
m_unrarCmd = GetOption(OPTION_UNRARCMD);
m_sevenZipCmd = GetOption(OPTION_SEVENZIPCMD);
m_unpackPassFile = GetOption(OPTION_UNPACKPASSFILE);
m_extCleanupDisk = GetOption(OPTION_EXTCLEANUPDISK);
m_parIgnoreExt = GetOption(OPTION_PARIGNOREEXT);
m_downloadRate = ParseIntValue(OPTION_DOWNLOADRATE, 10) * 1024;
m_articleTimeout = ParseIntValue(OPTION_ARTICLETIMEOUT, 10);
m_urlTimeout = ParseIntValue(OPTION_URLTIMEOUT, 10);
m_terminateTimeout = ParseIntValue(OPTION_TERMINATETIMEOUT, 10);
m_retries = ParseIntValue(OPTION_RETRIES, 10);
m_retryInterval = ParseIntValue(OPTION_RETRYINTERVAL, 10);
m_controlPort = ParseIntValue(OPTION_CONTROLPORT, 10);
m_securePort = ParseIntValue(OPTION_SECUREPORT, 10);
m_urlConnections = ParseIntValue(OPTION_URLCONNECTIONS, 10);
m_logBufferSize = ParseIntValue(OPTION_LOGBUFFERSIZE, 10);
m_rotateLog = ParseIntValue(OPTION_ROTATELOG, 10);
m_umask = ParseIntValue(OPTION_UMASK, 8);
m_updateInterval = ParseIntValue(OPTION_UPDATEINTERVAL, 10);
m_writeBuffer = ParseIntValue(OPTION_WRITEBUFFER, 10);
m_nzbDirInterval = ParseIntValue(OPTION_NZBDIRINTERVAL, 10);
m_nzbDirFileAge = ParseIntValue(OPTION_NZBDIRFILEAGE, 10);
m_diskSpace = ParseIntValue(OPTION_DISKSPACE, 10);
m_parTimeLimit = ParseIntValue(OPTION_PARTIMELIMIT, 10);
m_keepHistory = ParseIntValue(OPTION_KEEPHISTORY, 10);
m_feedHistory = ParseIntValue(OPTION_FEEDHISTORY, 10);
m_timeCorrection = ParseIntValue(OPTION_TIMECORRECTION, 10);
if (-24 <= m_timeCorrection && m_timeCorrection <= 24)
{
m_timeCorrection *= 60;
}
m_timeCorrection *= 60;
m_propagationDelay = ParseIntValue(OPTION_PROPAGATIONDELAY, 10) * 60;
m_articleCache = ParseIntValue(OPTION_ARTICLECACHE, 10);
m_eventInterval = ParseIntValue(OPTION_EVENTINTERVAL, 10);
m_parBuffer = ParseIntValue(OPTION_PARBUFFER, 10);
m_parThreads = ParseIntValue(OPTION_PARTHREADS, 10);
m_brokenLog = (bool)ParseEnumValue(OPTION_BROKENLOG, BoolCount, BoolNames, BoolValues);
m_nzbLog = (bool)ParseEnumValue(OPTION_NZBLOG, BoolCount, BoolNames, BoolValues);
m_appendCategoryDir = (bool)ParseEnumValue(OPTION_APPENDCATEGORYDIR, BoolCount, BoolNames, BoolValues);
m_continuePartial = (bool)ParseEnumValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, BoolValues);
m_saveQueue = (bool)ParseEnumValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, BoolValues);
m_flushQueue = (bool)ParseEnumValue(OPTION_FLUSHQUEUE, BoolCount, BoolNames, BoolValues);
m_dupeCheck = (bool)ParseEnumValue(OPTION_DUPECHECK, BoolCount, BoolNames, BoolValues);
m_parRepair = (bool)ParseEnumValue(OPTION_PARREPAIR, BoolCount, BoolNames, BoolValues);
m_parQuick = (bool)ParseEnumValue(OPTION_PARQUICK, BoolCount, BoolNames, BoolValues);
m_parRename = (bool)ParseEnumValue(OPTION_PARRENAME, BoolCount, BoolNames, BoolValues);
m_reloadQueue = (bool)ParseEnumValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, BoolValues);
m_cursesNzbName = (bool)ParseEnumValue(OPTION_CURSESNZBNAME, BoolCount, BoolNames, BoolValues);
m_cursesTime = (bool)ParseEnumValue(OPTION_CURSESTIME, BoolCount, BoolNames, BoolValues);
m_cursesGroup = (bool)ParseEnumValue(OPTION_CURSESGROUP, BoolCount, BoolNames, BoolValues);
m_crcCheck = (bool)ParseEnumValue(OPTION_CRCCHECK, BoolCount, BoolNames, BoolValues);
m_directWrite = (bool)ParseEnumValue(OPTION_DIRECTWRITE, BoolCount, BoolNames, BoolValues);
m_parCleanupQueue = (bool)ParseEnumValue(OPTION_PARCLEANUPQUEUE, BoolCount, BoolNames, BoolValues);
m_decode = (bool)ParseEnumValue(OPTION_DECODE, BoolCount, BoolNames, BoolValues);
m_dumpCore = (bool)ParseEnumValue(OPTION_DUMPCORE, BoolCount, BoolNames, BoolValues);
m_parPauseQueue = (bool)ParseEnumValue(OPTION_PARPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_scriptPauseQueue = (bool)ParseEnumValue(OPTION_SCRIPTPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_nzbCleanupDisk = (bool)ParseEnumValue(OPTION_NZBCLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_deleteCleanupDisk = (bool)ParseEnumValue(OPTION_DELETECLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_accurateRate = (bool)ParseEnumValue(OPTION_ACCURATERATE, BoolCount, BoolNames, BoolValues);
m_secureControl = (bool)ParseEnumValue(OPTION_SECURECONTROL, BoolCount, BoolNames, BoolValues);
m_unpack = (bool)ParseEnumValue(OPTION_UNPACK, BoolCount, BoolNames, BoolValues);
m_unpackCleanupDisk = (bool)ParseEnumValue(OPTION_UNPACKCLEANUPDISK, BoolCount, BoolNames, BoolValues);
m_unpackPauseQueue = (bool)ParseEnumValue(OPTION_UNPACKPAUSEQUEUE, BoolCount, BoolNames, BoolValues);
m_urlForce = (bool)ParseEnumValue(OPTION_URLFORCE, BoolCount, BoolNames, BoolValues);
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
const int OutputModeCount = 7;
m_outputMode = (EOutputMode)ParseEnumValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, OutputModeValues);
const char* ParCheckNames[] = { "auto", "always", "force", "manual" };
const int ParCheckValues[] = { pcAuto, pcAlways, pcForce, pcManual };
const int ParCheckCount = 6;
m_parCheck = (EParCheck)ParseEnumValue(OPTION_PARCHECK, ParCheckCount, ParCheckNames, ParCheckValues);
const char* ParScanNames[] = { "limited", "extended", "full", "dupe" };
const int ParScanValues[] = { psLimited, psExtended, psFull, psDupe };
const int ParScanCount = 4;
m_parScan = (EParScan)ParseEnumValue(OPTION_PARSCAN, ParScanCount, ParScanNames, ParScanValues);
const char* HealthCheckNames[] = { "pause", "delete", "none" };
const int HealthCheckValues[] = { hcPause, hcDelete, hcNone };
const int HealthCheckCount = 3;
m_healthCheck = (EHealthCheck)ParseEnumValue(OPTION_HEALTHCHECK, HealthCheckCount, HealthCheckNames, HealthCheckValues);
const char* TargetNames[] = { "screen", "log", "both", "none" };
const int TargetValues[] = { mtScreen, mtLog, mtBoth, mtNone };
const int TargetCount = 4;
m_infoTarget = (EMessageTarget)ParseEnumValue(OPTION_INFOTARGET, TargetCount, TargetNames, TargetValues);
m_warningTarget = (EMessageTarget)ParseEnumValue(OPTION_WARNINGTARGET, TargetCount, TargetNames, TargetValues);
m_errorTarget = (EMessageTarget)ParseEnumValue(OPTION_ERRORTARGET, TargetCount, TargetNames, TargetValues);
m_debugTarget = (EMessageTarget)ParseEnumValue(OPTION_DEBUGTARGET, TargetCount, TargetNames, TargetValues);
m_detailTarget = (EMessageTarget)ParseEnumValue(OPTION_DETAILTARGET, TargetCount, TargetNames, TargetValues);
const char* WriteLogNames[] = { "none", "append", "reset", "rotate" };
const int WriteLogValues[] = { wlNone, wlAppend, wlReset, wlRotate };
const int WriteLogCount = 4;
m_writeLog = (EWriteLog)ParseEnumValue(OPTION_WRITELOG, WriteLogCount, WriteLogNames, WriteLogValues);
}
int Options::ParseEnumValue(const char* OptName, int argc, const char * argn[], const int argv[])
{
OptEntry* optEntry = FindOption(OptName);
if (!optEntry)
{
ConfigError("Undefined value for option \"%s\"", OptName);
return argv[0];
}
int defNum = 0;
for (int i = 0; i < argc; i++)
{
if (!strcasecmp(optEntry->GetValue(), argn[i]))
{
// normalizing option value in option list, for example "NO" -> "no"
for (int j = 0; j < argc; j++)
{
if (argv[j] == argv[i])
{
if (strcmp(argn[j], optEntry->GetValue()))
{
optEntry->SetValue(argn[j]);
}
break;
}
}
return argv[i];
}
if (!strcasecmp(optEntry->GetDefValue(), argn[i]))
{
defNum = i;
}
}
m_configLine = optEntry->GetLineNo();
ConfigError("Invalid value for option \"%s\": \"%s\"", OptName, optEntry->GetValue());
optEntry->SetValue(argn[defNum]);
return argv[defNum];
}
int Options::ParseIntValue(const char* OptName, int base)
{
OptEntry* optEntry = FindOption(OptName);
if (!optEntry)
{
ConfigError("Undefined value for option \"%s\"", OptName);
return 0;
}
char *endptr;
int val = strtol(optEntry->GetValue(), &endptr, base);
if (endptr && *endptr != '\0')
{
m_configLine = optEntry->GetLineNo();
ConfigError("Invalid value for option \"%s\": \"%s\"", OptName, optEntry->GetValue());
optEntry->SetValue(optEntry->GetDefValue());
val = strtol(optEntry->GetDefValue(), NULL, base);
}
return val;
}
void Options::SetOption(const char* optname, const char* value)
{
OptEntry* optEntry = FindOption(optname);
if (!optEntry)
{
optEntry = new OptEntry();
optEntry->SetName(optname);
m_optEntries.push_back(optEntry);
}
CString curvalue;
#ifndef WIN32
if (value && (value[0] == '~') && (value[1] == '/'))
{
curvalue.Reserve(MAX_PATH);
if (m_noDiskAccess)
{
curvalue = value;
}
else if (!Util::ExpandHomePath(value, (char*)curvalue, curvalue.Capacity()))
{
ConfigError("Invalid value for option\"%s\": unable to determine home-directory", optname);
curvalue = "";
}
}
else
#endif
{
curvalue = value;
}
optEntry->SetLineNo(m_configLine);
// expand variables
while (const char* dollar = strstr(curvalue, "${"))
{
const char* end = strchr(dollar, '}');
if (end)
{
int varlen = (int)(end - dollar - 2);
BString<100> variable;
variable.Set(dollar + 2, varlen);
const char* varvalue = GetOption(variable);
if (varvalue)
{
curvalue.Replace(dollar - curvalue, 2 + varlen + 1, varvalue);
}
else
{
break;
}
}
else
{
break;
}
}
optEntry->SetValue(curvalue);
}
Options::OptEntry* Options::FindOption(const char* optname)
{
OptEntry* optEntry = m_optEntries.FindOption(optname);
// normalize option name in option list; for example "server1.joingroup" -> "Server1.JoinGroup"
if (optEntry && strcmp(optEntry->GetName(), optname))
{
optEntry->SetName(optname);
}
return optEntry;
}
const char* Options::GetOption(const char* optname)
{
OptEntry* optEntry = FindOption(optname);
if (optEntry)
{
if (optEntry->GetLineNo() > 0)
{
m_configLine = optEntry->GetLineNo();
}
return optEntry->GetValue();
}
return NULL;
}
void Options::InitServers()
{
int n = 1;
while (true)
{
const char* nactive = GetOption(BString<100>("Server%i.Active", n));
bool active = true;
if (nactive)
{
active = (bool)ParseEnumValue(BString<100>("Server%i.Active", n), BoolCount, BoolNames, BoolValues);
}
const char* nname = GetOption(BString<100>("Server%i.Name", n));
const char* nlevel = GetOption(BString<100>("Server%i.Level", n));
const char* ngroup = GetOption(BString<100>("Server%i.Group", n));
const char* nhost = GetOption(BString<100>("Server%i.Host", n));
const char* nport = GetOption(BString<100>("Server%i.Port", n));
const char* nusername = GetOption(BString<100>("Server%i.Username", n));
const char* npassword = GetOption(BString<100>("Server%i.Password", n));
const char* njoingroup = GetOption(BString<100>("Server%i.JoinGroup", n));
bool joinGroup = false;
if (njoingroup)
{
joinGroup = (bool)ParseEnumValue(BString<100>("Server%i.JoinGroup", n), BoolCount, BoolNames, BoolValues);
}
const char* ntls = GetOption(BString<100>("Server%i.Encryption", n));
bool tls = false;
if (ntls)
{
tls = (bool)ParseEnumValue(BString<100>("Server%i.Encryption", n), BoolCount, BoolNames, BoolValues);
#ifdef DISABLE_TLS
if (tls)
{
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", optname);
tls = false;
}
#endif
m_tls |= tls;
}
const char* ncipher = GetOption(BString<100>("Server%i.Cipher", n));
const char* nconnections = GetOption(BString<100>("Server%i.Connections", n));
const char* nretention = GetOption(BString<100>("Server%i.Retention", n));
bool definition = nactive || nname || nlevel || ngroup || nhost || nport ||
nusername || npassword || nconnections || njoingroup || ntls || ncipher || nretention;
bool completed = nhost && nport && nconnections;
if (!definition)
{
break;
}
if (completed)
{
if (m_extender)
{
m_extender->AddNewsServer(n, active, nname,
nhost,
nport ? atoi(nport) : 119,
nusername, npassword,
joinGroup, tls, ncipher,
nconnections ? atoi(nconnections) : 1,
nretention ? atoi(nretention) : 0,
nlevel ? atoi(nlevel) : 0,
ngroup ? atoi(ngroup) : 0);
}
}
else
{
ConfigError("Server definition not complete for \"Server%i\"", n);
}
n++;
}
}
void Options::InitCategories()
{
int n = 1;
while (true)
{
const char* nname = GetOption(BString<100>("Category%i.Name", n));
const char* ndestdir = GetOption(BString<100>("Category%i.DestDir", n));
const char* nunpack = GetOption(BString<100>("Category%i.Unpack", n));
bool unpack = true;
if (nunpack)
{
unpack = (bool)ParseEnumValue(BString<100>("Category%i.Unpack", n), BoolCount, BoolNames, BoolValues);
}
const char* npostscript = GetOption(BString<100>("Category%i.PostScript", n));
const char* naliases = GetOption(BString<100>("Category%i.Aliases", n));
bool definition = nname || ndestdir || nunpack || npostscript || naliases;
bool completed = nname && strlen(nname) > 0;
if (!definition)
{
break;
}
if (completed)
{
CString destDir;
if (ndestdir && ndestdir[0] != '\0')
{
CheckDir(destDir, BString<100>("Category%i.DestDir", n), m_destDir, false, false);
}
Category* category = new Category(nname, destDir, unpack, npostscript);
m_categories.push_back(category);
// split Aliases into tokens and create items for each token
if (naliases)
{
Tokenizer tok(naliases, ",;");
while (const char* aliasName = tok.Next())
{
category->GetAliases()->push_back(aliasName);
}
}
}
else
{
ConfigError("Category definition not complete for \"Category%i\"", n);
}
n++;
}
}
void Options::InitFeeds()
{
int n = 1;
while (true)
{
const char* nname = GetOption(BString<100>("Feed%i.Name", n));
const char* nurl = GetOption(BString<100>("Feed%i.URL", n));
const char* nfilter = GetOption(BString<100>("Feed%i.Filter", n));
const char* ncategory = GetOption(BString<100>("Feed%i.Category", n));
const char* nfeedscript = GetOption(BString<100>("Feed%i.FeedScript", n));
const char* nbacklog = GetOption(BString<100>("Feed%i.Backlog", n));
bool backlog = true;
if (nbacklog)
{
backlog = (bool)ParseEnumValue(BString<100>("Feed%i.Backlog", n), BoolCount, BoolNames, BoolValues);
}
const char* npausenzb = GetOption(BString<100>("Feed%i.PauseNzb", n));
bool pauseNzb = false;
if (npausenzb)
{
pauseNzb = (bool)ParseEnumValue(BString<100>("Feed%i.PauseNzb", n), BoolCount, BoolNames, BoolValues);
}
const char* ninterval = GetOption(BString<100>("Feed%i.Interval", n));
const char* npriority = GetOption(BString<100>("Feed%i.Priority", n));
bool definition = nname || nurl || nfilter || ncategory || nbacklog || npausenzb ||
ninterval || npriority || nfeedscript;
bool completed = nurl;
if (!definition)
{
break;
}
if (completed)
{
if (m_extender)
{
m_extender->AddFeed(n, nname, nurl, ninterval ? atoi(ninterval) : 0, nfilter,
backlog, pauseNzb, ncategory, npriority ? atoi(npriority) : 0, nfeedscript);
}
}
else
{
ConfigError("Feed definition not complete for \"Feed%i\"", n);
}
n++;
}
}
void Options::InitScheduler()
{
for (int n = 1; ; n++)
{
const char* time = GetOption(BString<100>("Task%i.Time", n));
const char* weekDays = GetOption(BString<100>("Task%i.WeekDays", n));
const char* command = GetOption(BString<100>("Task%i.Command", n));
const char* downloadRate = GetOption(BString<100>("Task%i.DownloadRate", n));
const char* process = GetOption(BString<100>("Task%i.Process", n));
const char* param = GetOption(BString<100>("Task%i.Param", n));
if (Util::EmptyStr(param) && !Util::EmptyStr(process))
{
param = process;
}
if (Util::EmptyStr(param) && !Util::EmptyStr(downloadRate))
{
param = downloadRate;
}
bool definition = time || weekDays || command || downloadRate || param;
bool completed = time && command;
if (!definition)
{
break;
}
if (!completed)
{
ConfigError("Task definition not complete for \"Task%i\"", n);
continue;
}
const char* CommandNames[] = { "pausedownload", "pause", "unpausedownload", "resumedownload", "unpause", "resume",
"pausepostprocess", "unpausepostprocess", "resumepostprocess", "pausepost", "unpausepost", "resumepost",
"downloadrate", "setdownloadrate", "rate", "speed", "script", "process", "pausescan", "unpausescan", "resumescan",
"activateserver", "activateservers", "deactivateserver", "deactivateservers", "fetchfeed", "fetchfeeds" };
const int CommandValues[] = { scPauseDownload, scPauseDownload, scUnpauseDownload,
scUnpauseDownload, scUnpauseDownload, scUnpauseDownload,
scPausePostProcess, scUnpausePostProcess, scUnpausePostProcess,
scPausePostProcess, scUnpausePostProcess, scUnpausePostProcess,
scDownloadRate, scDownloadRate, scDownloadRate, scDownloadRate,
scScript, scProcess, scPauseScan, scUnpauseScan, scUnpauseScan,
scActivateServer, scActivateServer, scDeactivateServer,
scDeactivateServer, scFetchFeed, scFetchFeed };
const int CommandCount = 27;
ESchedulerCommand taskCommand = (ESchedulerCommand)ParseEnumValue(
BString<100>("Task%i.Command", n), CommandCount, CommandNames, CommandValues);
if (param && strlen(param) > 0 && taskCommand == scProcess &&
!Util::SplitCommandLine(param, NULL))
{
ConfigError("Invalid value for option \"Task%i.Param\"", n);
continue;
}
int weekDaysVal = 0;
if (weekDays && !ParseWeekDays(weekDays, &weekDaysVal))
{
ConfigError("Invalid value for option \"Task%i.WeekDays\": \"%s\"", n, weekDays);
continue;
}
if (taskCommand == scDownloadRate)
{
if (param)
{
char* err;
int downloadRateVal = strtol(param, &err, 10);
if (!err || *err != '\0' || downloadRateVal < 0)
{
ConfigError("Invalid value for option \"Task%i.Param\": \"%s\"", n, downloadRate);
continue;
}
}
else
{
ConfigError("Task definition not complete for \"Task%i\". Option \"Task%i.Param\" is missing", n, n);
continue;
}
}
if ((taskCommand == scScript ||
taskCommand == scProcess ||
taskCommand == scActivateServer ||
taskCommand == scDeactivateServer ||
taskCommand == scFetchFeed) &&
Util::EmptyStr(param))
{
ConfigError("Task definition not complete for \"Task%i\". Option \"Task%i.Param\" is missing", n, n);
continue;
}
int hours, minutes;
Tokenizer tok(time, ";,");
while (const char* oneTime = tok.Next())
{
if (!ParseTime(oneTime, &hours, &minutes))
{
ConfigError("Invalid value for option \"Task%i.Time\": \"%s\"", n, oneTime);
break;
}
if (m_extender)
{
if (hours == -1)
{
for (int everyHour = 0; everyHour < 24; everyHour++)
{
m_extender->AddTask(n, everyHour, minutes, weekDaysVal, taskCommand, param);
}
}
else
{
m_extender->AddTask(n, hours, minutes, weekDaysVal, taskCommand, param);
}
}
}
}
}
bool Options::ParseTime(const char* time, int* hours, int* minutes)
{
int colons = 0;
const char* p = time;
while (*p)
{
if (!strchr("0123456789: *", *p))
{
return false;
}
if (*p == ':')
{
colons++;
}
p++;
}
if (colons != 1)
{
return false;
}
const char* colon = strchr(time, ':');
if (!colon)
{
return false;
}
if (time[0] == '*')
{
*hours = -1;
}
else
{
*hours = atoi(time);
if (*hours < 0 || *hours > 23)
{
return false;
}
}
if (colon[1] == '*')
{
return false;
}
*minutes = atoi(colon + 1);
if (*minutes < 0 || *minutes > 59)
{
return false;
}
return true;
}
bool Options::ParseWeekDays(const char* weekDays, int* weekDaysBits)
{
*weekDaysBits = 0;
const char* p = weekDays;
int firstDay = 0;
bool range = false;
while (*p)
{
if (strchr("1234567", *p))
{
int day = *p - '0';
if (range)
{
if (day <= firstDay || firstDay == 0)
{
return false;
}
for (int i = firstDay; i <= day; i++)
{
*weekDaysBits |= 1 << (i - 1);
}
firstDay = 0;
}
else
{
*weekDaysBits |= 1 << (day - 1);
firstDay = day;
}
range = false;
}
else if (*p == ',')
{
range = false;
}
else if (*p == '-')
{
range = true;
}
else if (*p == ' ')
{
// skip spaces
}
else
{
return false;
}
p++;
}
return true;
}
void Options::LoadConfigFile()
{
SetOption(OPTION_CONFIGFILE, m_configFilename);
FILE* infile = fopen(m_configFilename, FOPEN_RB);
if (!infile)
{
ConfigError("Could not open file %s", *m_configFilename);
m_fatalError = true;
return;
}
m_configLine = 0;
int bufLen = (int)Util::FileSize(m_configFilename) + 1;
char* buf = (char*)malloc(bufLen);
int line = 0;
while (fgets(buf, bufLen - 1, infile))
{
m_configLine = ++line;
if (buf[0] != 0 && buf[strlen(buf)-1] == '\n')
{
buf[strlen(buf)-1] = 0; // remove traling '\n'
}
if (buf[0] != 0 && buf[strlen(buf)-1] == '\r')
{
buf[strlen(buf)-1] = 0; // remove traling '\r' (for windows line endings)
}
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
{
continue;
}
SetOptionString(buf);
}
fclose(infile);
free(buf);
m_configLine = 0;
}
void Options::InitCommandLineOptions(CmdOptList* commandLineOptions)
{
for (CmdOptList::iterator it = commandLineOptions->begin(); it != commandLineOptions->end(); it++)
{
const char* option = *it;
SetOptionString(option);
}
}
bool Options::SetOptionString(const char* option)
{
char* optname;
char* optvalue;
if (!SplitOptionString(option, &optname, &optvalue))
{
ConfigError("Invalid option \"%s\"", option);
return false;
}
bool ok = ValidateOptionName(optname, optvalue);
if (ok)
{
SetOption(optname, optvalue);
}
else
{
ConfigError("Invalid option \"%s\"", optname);
}
free(optname);
free(optvalue);
return ok;
}
/*
* Splits option string into name and value;
* Converts old names and values if necessary;
* Allocates buffers for name and value;
* Returns true if the option string has name and value;
* If "true" is returned the caller is responsible for freeing optname and optvalue.
*/
bool Options::SplitOptionString(const char* option, char** optName, char** optValue)
{
const char* eq = strchr(option, '=');
if (!eq)
{
return false;
}
const char* value = eq + 1;
char optname[1001];
char optvalue[1001];
int maxlen = (int)(eq - option < 1000 ? eq - option : 1000);
strncpy(optname, option, maxlen);
optname[maxlen] = '\0';
strncpy(optvalue, eq + 1, 1000);
optvalue[1000] = '\0';
if (strlen(optname) == 0)
{
return false;
}
ConvertOldOption(optname, sizeof(optname), optvalue, sizeof(optvalue));
// if value was (old-)converted use the new value, which is linited to 1000 characters,
// otherwise use original (length-unlimited) value
if (strncmp(value, optvalue, 1000))
{
value = optvalue;
}
*optName = strdup(optname);
*optValue = strdup(value);
return true;
}
bool Options::ValidateOptionName(const char* optname, const char* optvalue)
{
if (!strcasecmp(optname, OPTION_CONFIGFILE) || !strcasecmp(optname, OPTION_APPBIN) ||
!strcasecmp(optname, OPTION_APPDIR) || !strcasecmp(optname, OPTION_VERSION))
{
// read-only options
return false;
}
const char* v = GetOption(optname);
if (v)
{
// it's predefined option, OK
return true;
}
if (!strncasecmp(optname, "server", 6))
{
char* p = (char*)optname + 6;
while (*p >= '0' && *p <= '9') p++;
if (p &&
(!strcasecmp(p, ".active") || !strcasecmp(p, ".name") ||
!strcasecmp(p, ".level") || !strcasecmp(p, ".host") ||
!strcasecmp(p, ".port") || !strcasecmp(p, ".username") ||
!strcasecmp(p, ".password") || !strcasecmp(p, ".joingroup") ||
!strcasecmp(p, ".encryption") || !strcasecmp(p, ".connections") ||
!strcasecmp(p, ".cipher") || !strcasecmp(p, ".group") ||
!strcasecmp(p, ".retention")))
{
return true;
}
}
if (!strncasecmp(optname, "task", 4))
{
char* p = (char*)optname + 4;
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".time") || !strcasecmp(p, ".weekdays") ||
!strcasecmp(p, ".command") || !strcasecmp(p, ".param") ||
!strcasecmp(p, ".downloadrate") || !strcasecmp(p, ".process")))
{
return true;
}
}
if (!strncasecmp(optname, "category", 8))
{
char* p = (char*)optname + 8;
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".name") || !strcasecmp(p, ".destdir") || !strcasecmp(p, ".postscript") ||
!strcasecmp(p, ".unpack") || !strcasecmp(p, ".aliases")))
{
return true;
}
}
if (!strncasecmp(optname, "feed", 4))
{
char* p = (char*)optname + 4;
while (*p >= '0' && *p <= '9') p++;
if (p && (!strcasecmp(p, ".name") || !strcasecmp(p, ".url") || !strcasecmp(p, ".interval") ||
!strcasecmp(p, ".filter") || !strcasecmp(p, ".backlog") || !strcasecmp(p, ".pausenzb") ||
!strcasecmp(p, ".category") || !strcasecmp(p, ".priority") || !strcasecmp(p, ".feedscript")))
{
return true;
}
}
// scripts options
if (strchr(optname, ':'))
{
return true;
}
// print warning messages for obsolete options
if (!strcasecmp(optname, OPTION_RETRYONCRCERROR) ||
!strcasecmp(optname, OPTION_ALLOWREPROCESS) ||
!strcasecmp(optname, OPTION_LOADPARS) ||
!strcasecmp(optname, OPTION_THREADLIMIT) ||
!strcasecmp(optname, OPTION_POSTLOGKIND) ||
!strcasecmp(optname, OPTION_NZBLOGKIND) ||
!strcasecmp(optname, OPTION_PROCESSLOGKIND) ||
!strcasecmp(optname, OPTION_APPENDNZBDIR) ||
!strcasecmp(optname, OPTION_RENAMEBROKEN) ||
!strcasecmp(optname, OPTION_MERGENZB) ||
!strcasecmp(optname, OPTION_STRICTPARNAME) ||
!strcasecmp(optname, OPTION_RELOADURLQUEUE) ||
!strcasecmp(optname, OPTION_RELOADPOSTQUEUE))
{
ConfigWarn("Option \"%s\" is obsolete, ignored", optname);
return true;
}
if (!strcasecmp(optname, OPTION_POSTPROCESS) ||
!strcasecmp(optname, OPTION_NZBPROCESS) ||
!strcasecmp(optname, OPTION_NZBADDEDPROCESS))
{
if (optvalue && strlen(optvalue) > 0)
{
ConfigError("Option \"%s\" is obsolete, ignored, use \"%s\" and \"%s\" instead", optname, OPTION_SCRIPTDIR,
!strcasecmp(optname, OPTION_POSTPROCESS) ? OPTION_POSTSCRIPT :
!strcasecmp(optname, OPTION_NZBPROCESS) ? OPTION_SCANSCRIPT :
!strcasecmp(optname, OPTION_NZBADDEDPROCESS) ? OPTION_QUEUESCRIPT :
"ERROR");
}
return true;
}
if (!strcasecmp(optname, OPTION_CREATELOG) || !strcasecmp(optname, OPTION_RESETLOG))
{
ConfigWarn("Option \"%s\" is obsolete, ignored, use \"%s\" instead", optname, OPTION_WRITELOG);
return true;
}
return false;
}
void Options::ConvertOldOption(char *option, int optionBufLen, char *value, int valueBufLen)
{
// for compatibility with older versions accept old option names
if (!strcasecmp(option, "$MAINDIR"))
{
strncpy(option, "MainDir", optionBufLen);
}
if (!strcasecmp(option, "ServerIP"))
{
strncpy(option, "ControlIP", optionBufLen);
}
if (!strcasecmp(option, "ServerPort"))
{
strncpy(option, "ControlPort", optionBufLen);
}
if (!strcasecmp(option, "ServerPassword"))
{
strncpy(option, "ControlPassword", optionBufLen);
}
if (!strcasecmp(option, "PostPauseQueue"))
{
strncpy(option, "ScriptPauseQueue", optionBufLen);
}
if (!strcasecmp(option, "ParCheck") && !strcasecmp(value, "yes"))
{
strncpy(value, "always", valueBufLen);
}
if (!strcasecmp(option, "ParCheck") && !strcasecmp(value, "no"))
{
strncpy(value, "auto", valueBufLen);
}
if (!strcasecmp(option, "ParScan") && !strcasecmp(value, "auto"))
{
strncpy(value, "extended", valueBufLen);
}
if (!strcasecmp(option, "DefScript"))
{
strncpy(option, "PostScript", optionBufLen);
}
int nameLen = strlen(option);
if (!strncasecmp(option, "Category", 8) && nameLen > 10 &&
!strcasecmp(option + nameLen - 10, ".DefScript"))
{
strncpy(option + nameLen - 10, ".PostScript", optionBufLen - 9 /* strlen("Category.") */);
}
if (!strcasecmp(option, "WriteBufferSize"))
{
strncpy(option, "WriteBuffer", optionBufLen);
int val = strtol(value, NULL, 10);
val = val == -1 ? 1024 : val / 1024;
snprintf(value, valueBufLen, "%i", val);
}
if (!strcasecmp(option, "ConnectionTimeout"))
{
strncpy(option, "ArticleTimeout", optionBufLen);
}
if (!strcasecmp(option, "CreateBrokenLog"))
{
strncpy(option, "BrokenLog", optionBufLen);
}
option[optionBufLen-1] = '\0';
option[valueBufLen-1] = '\0';
}
void Options::CheckOptions()
{
#ifdef DISABLE_PARCHECK
if (m_parCheck != pcManual)
{
LocateOptionSrcPos(OPTION_PARCHECK);
ConfigError("Invalid value for option \"%s\": program was compiled without parcheck-support", OPTION_PARCHECK);
}
if (m_parRename)
{
LocateOptionSrcPos(OPTION_PARRENAME);
ConfigError("Invalid value for option \"%s\": program was compiled without parcheck-support", OPTION_PARRENAME);
}
#endif
#ifdef DISABLE_CURSES
if (m_outputMode == omNCurses)
{
LocateOptionSrcPos(OPTION_OUTPUTMODE);
ConfigError("Invalid value for option \"%s\": program was compiled without curses-support", OPTION_OUTPUTMODE);
}
#endif
#ifdef DISABLE_TLS
if (m_secureControl)
{
LocateOptionSrcPos(OPTION_SECURECONTROL);
ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", OPTION_SECURECONTROL);
}
#endif
if (!m_decode)
{
m_directWrite = false;
}
// if option "ConfigTemplate" is not set, use "WebDir" as default location for template
// (for compatibility with versions 9 and 10).
if (m_configTemplate.Empty() && !m_noDiskAccess)
{
m_configTemplate.Format("%s%s", *m_webDir, "nzbget.conf");
if (!Util::FileExists(m_configTemplate))
{
m_configTemplate = "";
}
}
if (m_articleCache < 0)
{
m_articleCache = 0;
}
else if (sizeof(void*) == 4 && m_articleCache > 1900)
{
ConfigError("Invalid value for option \"ArticleCache\": %i. Changed to 1900", m_articleCache);
m_articleCache = 1900;
}
else if (sizeof(void*) == 4 && m_parBuffer > 1900)
{
ConfigError("Invalid value for option \"ParBuffer\": %i. Changed to 1900", m_parBuffer);
m_parBuffer = 1900;
}
if (sizeof(void*) == 4 && m_parBuffer + m_articleCache > 1900)
{
ConfigError("Options \"ArticleCache\" and \"ParBuffer\" in total cannot use more than 1900MB of memory in 32-Bit mode. Changed to 1500 and 400");
m_articleCache = 1900;
m_parBuffer = 400;
}
if (!m_unpackPassFile.Empty() && !Util::FileExists(m_unpackPassFile))
{
ConfigError("Invalid value for option \"UnpackPassFile\": %s. File not found", *m_unpackPassFile);
}
}
Options::OptEntries* Options::LockOptEntries()
{
m_optEntriesMutex.Lock();
return &m_optEntries;
}
void Options::UnlockOptEntries()
{
m_optEntriesMutex.Unlock();
}