Compare commits

..

1 Commits
v12.0 ... v11.0

Author SHA1 Message Date
Andrey Prygunkov
3b87cf1b23 version 11.0 2013-07-01 20:03:52 +00:00
122 changed files with 3301 additions and 21753 deletions

View File

@@ -77,10 +77,22 @@ ArticleDownloader::~ArticleDownloader()
{
debug("Destroying ArticleDownloader");
free(m_szTempFilename);
free(m_szArticleFilename);
free(m_szInfoName);
free(m_szOutputFilename);
if (m_szTempFilename)
{
free(m_szTempFilename);
}
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
}
void ArticleDownloader::SetTempFilename(const char* v)
@@ -146,19 +158,18 @@ void ArticleDownloader::Run()
int iRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedRetries = iRetries;
Servers failedServers;
ServerPool::Servers failedServers;
failedServers.reserve(g_pServerPool->GetServers()->size());
NewsServer* pWantServer = NULL;
NewsServer* pLastServer = NULL;
int iLevel = 0;
int iServerConfigGeneration = g_pServerPool->GetGeneration();
while (!IsStopped())
{
Status = adFailed;
SetStatus(adWaiting);
while (!m_pConnection && !(IsStopped() || iServerConfigGeneration != g_pServerPool->GetGeneration()))
while (!IsStopped() && !m_pConnection)
{
m_pConnection = g_pServerPool->GetConnection(iLevel, pWantServer, &failedServers);
usleep(5 * 1000);
@@ -166,8 +177,7 @@ void ArticleDownloader::Run()
SetLastUpdateTimeNow();
SetStatus(adRunning);
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration())
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
@@ -181,15 +191,10 @@ void ArticleDownloader::Run()
bool bConnected = m_pConnection && m_pConnection->Connect();
if (bConnected && !IsStopped())
{
NewsServer* pNewsServer = m_pConnection->GetNewsServer();
detail("Downloading %s @ %s (%s)", m_szInfoName, pNewsServer->GetName(), m_pConnection->GetHost());
// Okay, we got a Connection. Now start downloading.
detail("Downloading %s @ server%i (%s)", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
Status = Download();
if (Status == adFinished || Status == adFailed || Status == adNotFound || Status == adCrcError)
{
m_ServerStats.SetStat(pNewsServer->GetID(), Status == adFinished ? 1 : 0, Status == adFinished ? 0 : 1, false);
}
}
if (bConnected)
@@ -229,16 +234,14 @@ void ArticleDownloader::Run()
pWantServer = pLastServer;
}
if (pWantServer &&
!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration()))
if (pWantServer && !IsStopped() &&
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
SetStatus(adWaiting);
int msec = 0;
while (!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration()) &&
msec < g_pOptions->GetRetryInterval() * 1000)
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
usleep(100 * 1000);
msec += 100;
@@ -247,8 +250,7 @@ void ArticleDownloader::Run()
SetStatus(adRunning);
}
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
iServerConfigGeneration != g_pServerPool->GetGeneration())
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
@@ -262,25 +264,22 @@ void ArticleDownloader::Run()
// if all servers from all levels were tried, break the loop with failure status
bool bAllServersOnLevelFailed = true;
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
for (ServerPool::Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pCandidateServer = *it;
if (pCandidateServer->GetNormLevel() == iLevel)
if (pCandidateServer->GetLevel() == iLevel)
{
bool bServerFailed = !pCandidateServer->GetActive() || pCandidateServer->GetMaxConnections() == 0;
if (!bServerFailed)
bool bServerFailed = false;
for (ServerPool::Servers::iterator it = failedServers.begin(); it != failedServers.end(); it++)
{
for (Servers::iterator it = failedServers.begin(); it != failedServers.end(); it++)
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetLevel() == pCandidateServer->GetLevel()))
{
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
{
bServerFailed = true;
break;
}
}
bServerFailed = true;
break;
}
}
if (!bServerFailed)
{
@@ -292,14 +291,14 @@ void ArticleDownloader::Run()
if (bAllServersOnLevelFailed)
{
if (iLevel < g_pServerPool->GetMaxNormLevel())
if (iLevel < g_pServerPool->GetMaxLevel())
{
detail("Article %s @ all level %i servers failed, increasing level", m_szInfoName, iLevel);
iLevel++;
}
else
{
detail("Article %s @ all servers failed", m_szInfoName);
warn("Article %s @ all servers failed", m_szInfoName);
Status = adFailed;
break;
}
@@ -329,7 +328,7 @@ void ArticleDownloader::Run()
if (Status == adFailed)
{
detail("Download %s failed", m_szInfoName);
warn("Download %s failed", m_szInfoName);
}
SetStatus(Status);
@@ -421,8 +420,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
{
if (!IsStopped())
{
detail("Article %s @ %s (%s) failed: Unexpected end of article", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
warn("Article %s @ server%i (%s) failed: Unexpected end of article", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
}
Status = adFailed;
break;
@@ -456,8 +455,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
{
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
detail("Article %s @ %s (%s) failed: Wrong message-id, expected %s, returned %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
warn("Article %s @ server%i (%s) failed: Wrong message-id, expected %s, returned %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
Status = adFailed;
break;
}
@@ -485,8 +484,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
if (!bEnd && Status == adRunning && !IsStopped())
{
detail("Article %s @ %s (%s) failed: article incomplete", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
warn("Article %s @ server%i (%s) failed: article incomplete", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost());
Status = adFailed;
}
@@ -513,21 +512,21 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
{
if (!IsStopped())
{
detail("Article %s @ %s (%s) failed, %s: Connection closed by remote host", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment);
warn("Article %s @ server%i (%s) failed, %s: Connection closed by remote host", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment);
}
return adConnectError;
}
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
{
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adConnectError;
}
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2) || !strncmp(szResponse, "43", 2))
{
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adNotFound;
}
else if (!strncmp(szResponse, "2", 1))
@@ -538,8 +537,8 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
else
{
// unknown error, no special handling
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
warn("Article %s @ server%i (%s) failed, %s: %s", m_szInfoName,
m_pConnection->GetNewsServer()->GetID(), m_pConnection->GetHost(), szComment, szResponse);
return adFailed;
}
}
@@ -564,13 +563,13 @@ bool ArticleDownloader::Write(char* szLine, int iLen)
}
else
{
detail("Decoding %s failed: unsupported encoding", m_szInfoName);
warn("Decoding %s failed: unsupported encoding", m_szInfoName);
return false;
}
if (!bOK)
{
debug("Failed line: %s", szLine);
detail("Decoding %s failed", m_szInfoName);
warn("Decoding %s failed", m_szInfoName);
}
return bOK;
}
@@ -589,9 +588,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
{
if (!strncmp(szLine, "=ybegin ", 8))
{
if (g_pOptions->GetDupeCheck() &&
m_pFileInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
!m_pFileInfo->GetNZBInfo()->GetManyDupeFiles())
if (g_pOptions->GetDupeCheck())
{
m_pFileInfo->LockOutputFile();
bool bOutputInitialized = m_pFileInfo->GetOutputInitialized();
@@ -616,8 +613,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pFileInfo->SetOutputInitialized(true);
}
m_pFileInfo->UnlockOutputFile();
if (!bOutputInitialized && m_szArticleFilename &&
Util::FileExists(m_pFileInfo->GetNZBInfo()->GetDestDir(), m_szArticleFilename))
if (!bOutputInitialized && m_szArticleFilename && m_pFileInfo->IsDupe(m_szArticleFilename))
{
m_bDuplicate = true;
return false;
@@ -663,9 +659,7 @@ bool ArticleDownloader::PrepareFile(char* szLine)
m_pOutFile = fopen(szFilename, bDirectWrite ? "rb+" : "wb");
if (!m_pOutFile)
{
char szSysErrStr[256];
error("Could not %s file %s! Errcode: %i, %s", bDirectWrite ? "open" : "create", szFilename,
errno, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
error("Could not %s file %s", bDirectWrite ? "open" : "create", szFilename);
return false;
}
if (g_pOptions->GetWriteBufferSize() == -1)
@@ -772,7 +766,7 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
}
else
{
detail("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
return adFailed;
}
@@ -817,27 +811,27 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
remove(m_szResultFilename);
if (eStatus == Decoder::eCrcError)
{
detail("Decoding %s failed: CRC-Error", m_szInfoName);
warn("Decoding %s failed: CRC-Error", m_szInfoName);
return adCrcError;
}
else if (eStatus == Decoder::eArticleIncomplete)
{
detail("Decoding %s failed: article incomplete", m_szInfoName);
warn("Decoding %s failed: article incomplete", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eInvalidSize)
{
detail("Decoding %s failed: size mismatch", m_szInfoName);
warn("Decoding %s failed: size mismatch", m_szInfoName);
return adFailed;
}
else if (eStatus == Decoder::eNoBinaryData)
{
detail("Decoding %s failed: no binary data found", m_szInfoName);
warn("Decoding %s failed: no binary data found", m_szInfoName);
return adFailed;
}
else
{
detail("Decoding %s failed", m_szInfoName);
warn("Decoding %s failed", m_szInfoName);
return adFailed;
}
}
@@ -960,7 +954,17 @@ void ArticleDownloader::CompleteFileParts()
}
char ofn[1024];
Util::MakeUniqueFilename(ofn, 1024, szNZBDestDir, m_pFileInfo->GetFilename());
snprintf(ofn, 1024, "%s%c%s", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
ofn[1024-1] = '\0';
// prevent overwriting existing files
int dupcount = 0;
while (Util::FileExists(ofn))
{
dupcount++;
snprintf(ofn, 1024, "%s%c%s_duplicate%d", szNZBDestDir, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
ofn[1024-1] = '\0';
}
FILE* outfile = NULL;
char tmpdestfile[1024];
@@ -1054,7 +1058,10 @@ void ArticleDownloader::CompleteFileParts()
}
}
free(buffer);
if (buffer)
{
free(buffer);
}
if (outfile)
{
@@ -1106,9 +1113,7 @@ void ArticleDownloader::CompleteFileParts()
}
else
{
warn("%i of %i article downloads failed for \"%s\"",
iBrokenCount + m_pFileInfo->GetMissedArticles(),
m_pFileInfo->GetTotalArticles(), InfoFilename);
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
if (g_pOptions->GetCreateBrokenLog())
{
@@ -1116,14 +1121,12 @@ void ArticleDownloader::CompleteFileParts()
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szNZBDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
FILE* file = fopen(szBrokenLogName, "ab");
fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(),
m_pFileInfo->GetTotalArticles() - iBrokenCount - m_pFileInfo->GetMissedArticles(),
m_pFileInfo->GetTotalArticles(), LINE_ENDING);
fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size(), LINE_ENDING);
fclose(file);
}
}
// the locking is needed for accessing the members of NZBInfo
// the locking is needed for accessing the memebers of NZBInfo
g_pDownloadQueueHolder->LockQueue();
m_pFileInfo->GetNZBInfo()->GetCompletedFiles()->push_back(strdup(ofn));
if (strcmp(m_pFileInfo->GetNZBInfo()->GetDestDir(), szNZBDestDir))
@@ -1163,7 +1166,13 @@ bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldD
if (strcmp(szFileName, szNewFileName))
{
// prevent overwriting of existing files
Util::MakeUniqueFilename(szNewFileName, 1024, pNZBInfo->GetDestDir(), Util::BaseFileName(szFileName));
int dupcount = 0;
while (Util::FileExists(szNewFileName))
{
dupcount++;
snprintf(szNewFileName, 1024, "%s%c%s_duplicate%d", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, Util::BaseFileName(szFileName), dupcount);
szNewFileName[1024-1] = '\0';
}
detail("Moving file %s to %s", szFileName, szNewFileName);
if (Util::MoveFile(szFileName, szNewFileName))

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -72,7 +72,6 @@ private:
UDecoder m_UDecoder;
FILE* m_pOutFile;
bool m_bDuplicate;
ServerStatList m_ServerStats;
EStatus Download();
bool Write(char* szLine, int iLen);
@@ -95,7 +94,6 @@ public:
void SetArticleInfo(ArticleInfo* pArticleInfo) { m_pArticleInfo = pArticleInfo; }
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
EStatus GetStatus() { return m_eStatus; }
ServerStatList* GetServerStats() { return &m_ServerStats; }
virtual void Run();
virtual void Stop();
bool Terminate();

View File

@@ -358,7 +358,7 @@ void DownloadBinCommand::Execute()
bool bAddTop = ntohl(DownloadRequest.m_bAddFirst);
bool bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szFilename, DownloadRequest.m_szCategory,
iPriority, NULL, 0, dmScore, NULL, bAddTop, bAddPaused, NULL, pRecvBuffer, iBufLen) != Scanner::asFailed;
iPriority, NULL, bAddTop, bAddPaused, NULL, pRecvBuffer, iBufLen, true);
char tmp[1024];
snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s",
@@ -563,7 +563,10 @@ void ListBinCommand::Execute()
g_pQueueCoordinator->UnlockQueue();
delete pRegEx;
if (pRegEx)
{
delete pRegEx;
}
ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries);
ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries);
@@ -609,7 +612,10 @@ void ListBinCommand::Execute()
m_pConnection->Send(buf, bufsize);
}
free(buf);
if (buf)
{
free(buf);
}
}
void LogBinCommand::Execute()

335
ChangeLog
View File

@@ -1,325 +1,3 @@
nzbget-12.0:
- added RSS feeds support:
- new options "FeedX.Name", "FeedX.URL", "FeedX.Filter",
"FeedX.Interval", "FeedX.PauseNzb", "FeedX.Category",
"FeedX.Priority" (section "Rss Feeds");
- new option "FeedHistory" (section "Download Queue");
- button "Preview Feed" on settings tab near each feed definition;
- new toolbar button "Feeds" on downloads tab with menu to
view feeds or fetch new nzbs from all feeds (the button is
visible only if there are feeds defined in settings);
- new dialog to see feed content showing status of each item (new,
fetched, backlog) with ability to manually fetch selected items;
- powerful filters for RSS feeds;
- new dialog to build filters in web-interface with instant preview;
- added download health monitoring:
- health indicates download status, whether the file is damaged
and how much;
- 100% health means no download errors occurred; 0% means all
articles failed;
- there is also a critical health which is calculated for each
nzb-file based on number and size of par-files;
- if during download the health goes down below 100% a health
badge appears near download name indicating the necessity of
par-repair; the indicator can be orange (repair may be possible)
or red (unrepairable) if the health goes down below critical health;
- new option "HealthCheck" to define what to do with unhealthy
(unrepairable) downloads (pause, delete, none);
- health and critical health are displayed in download-edit dialog;
health is displayed in history dialog; if download was aborted
(HealthCheck=delete) this is indicated in history dialog;
- health allows to determine download status for downloads which
have unpack and/or par-check disabled; for such downloads the
status in history is shown based on health: success (health=100%),
damaged (health > critical) or failure (health < critical);
- par-check is now automatically started for downloads having
health below 100%; this works independently of unpack (even if
unpack is disabled);
- for downloads having health less than critical health no par-check
is performed (it would fail); Instead the par-check status is
set to "failure" automatically saving time of actual par-check;
- new fields "Health" and "CriticalHealth" are returned by
RPC-Method "listgroups";
- new fields "Health", "CriticalHealth", "Deleted" and "HealthDeleted"
are returned by RPC-Method "history";
- new parameters "NZBPP_HEALTH" and "NZBPP_CRITICALHEALTH" are passed
to pp-scripts;
- added collecting of server usage statistical data for each download:
- number of successful and failed article downloads per news server;
- new page in history dialog shows collected statistics;
- new fields in RPC-method "history": ServerStats (array),
TotalArticles, SuccessArticles, FailedArticles;
- new env. vars passed to pp-scripts: NZBPP_TOTALARTICLES,
NZBPP_SUCCESSARTICLES, NZBPP_FAILEDARTICLES and per used news
server: NZBPP_SERVERX_SUCCESSARTICLES, NZBPP_SERVERX_FAILEDARTICLES;
- also new env.var HEALTHDELETED;
- added smart duplicates feature:
- mostly for use with RSS feeds;
- automatic detection of duplicate nzb-files to avoid download of
duplicates;
- nzb-files can be also manually marked as duplicates;
- if download fails - automatically choose another release (duplicate);
- if download succeeds all remaining duplicates are skipped (not downloaded);
- download items have new properties to tune duplicate handling
behavior: duplicate key, duplicate score and duplicate mode;
- if download was deleted by duplicate check its status in the
history is shown as "DUPE";
- new actions "GroupSetDupeKey", "GroupSetDupeScore", "GroupSetDupeMode",
"HistorySetDupeKey", "HistorySetDupeScore", "HistorySetDupeMode",
"HistoryMarkBad" and "HistoryMarkGood" of RPC-command "editqueue";
new actions "B" and "G" of command "--edit/-E" for history items
(subcommand "H");
- when deleting downloads from queue there are three options now:
"move to history", "move to history as duplicate" and "delete
without history tracking";
- new actions "GroupDupeDelete", "GroupFinalDelete" and
"HistorySetDupeBackup" in RPC-method "editqueue";
- RPC-commands "listgroups", "postqueue" and "history" now return
more info about nzb-item (many new fields);
- removed option "MergeNzb" because it conflicts with duplicate
handling, items can be merged manually if necessary;
- automatic detection of exactly same nzb-files (same content)
coming from different sources (different RSS feeds etc.);
individual files (inside nzb-file) having extensions listed in
option "ExtCleanupDisk" are excluded from content comparison
(unless these are par2-files, which are never excluded);
- when history item expires (as defined by option "KeepHistory")
and the duplicate check is active (option "DupeCheck") the item
is not completely deleted from history; instead the amount of
stored data reduces to minimum required for duplicate check
(about 200 bytes vs 2000 bytes for full history item);
- such old history items are not shown in web-interface by default
(to avoid transferring of large amount of history items);
- new button "Hidden" in web-interface to show hidden history items;
the items are marked with badge "hidden";
- RPC-method "editqueue" has now two actions to delete history
records: "HistoryDelete", "HistoryFinal"; action "HistoryDelete"
which has existed before now hides records, already hidden records
are ignored;
- added functions "Mark as Bad" and "Mark as Good" for history
items;
- duplicate properties (dupekey, dupescore and dupemode) can now
be viewed and changed in download-edit-dialog and
history-edit-dialog via new button "Dupe";
- for full documentation see http://nzbget.sourceforge.net/RSS#Duplicates;
- created NZBGet.app - NZBGet is now a user friendly Mac OSX application
with easy installation and seamless integration into OS UI:
works in background, is controlled from a web-browser, few
important functions are accessible via menubar icon;
- better Windows package:
- unrar is included;
- several options are set to better defaults;
- all paths are set as relative paths to program directory;
the program can be started after installation without editing
anything in config;
- included two new batch-files:
- nzbget-start.bat - starts program in normal mode (dos box);
- nzbget-recovery-mode.bat - starts with empty password (dos box);
- both batch files open browser window with correct address;
- config-file template is stored in nzbget.conf.template;
- nzbget.conf is not included in the package. When the program is
started for the first time (using one of batch files) the template
config file is copied into nzbget.conf;
- updates will be easy in the future: to update the program all
files from newer archive must be extracted over old files. Since
the archive doesn't have nzbget.conf, the existing config is kept
unmodified. The template config file will be updated;
- added file README-WINDOWS.txt with small instructions;
- version string now includes revision number (like "r789");
- added automatic updates:
- new button "Check for updates" on settings tab of web-interface,
in section "SYSTEM", initiates check and shows dialog allowing to
install new version;
- it is possible to choose between stable, testing and development
branches;
- this feature is for end-users using binary packages created and
updated by maintainers, who need to write an update script specific
for platform;
- the script is then called by NZBGet when user clicks on install-button;
- the script must download and install new version;
- for more info visit http://nzbget.sourceforge.net/Packaging;
- news servers can now be temporarily disabled via speed limit dialog
without reloading of the program:
- new option "ServerX.Active" to disable servers via settings;
- new option "ServerX.Name" to use for logging and in UI;
- changed the way how option "Unpack" works:
- instead of enabling/disabling the unpacker as a whole, it now
defines the initial value of post-processing parameter "Unpack"
for nzb-file when it is added to queue;
- this makes it now possible to disable Unpack globally but still
enable it for selected nzb-files;
- new option "CategoryX.Unpack" to set unpack on a per category basis;
- combined all footer buttons into one button "Actions" with menu:
- in download-edit-dialog: "Pause/Resume", "Delete" and "Cancel
Post-Processing";
- in history-dialog: "Delete", "Post-Process Again" and "Download
Remaining Files (Return to Queue)";
- DirectNZB headers X-DNZB-MoreInfo and X-DNZB-Details are now processed
when downloading URLs and the links "More Info" and "Details" are shown
in download-edit-dialog and in history-dialog in Actions menu;
- program can now be stopped via web-interface: new button "shutdown"
in section "SYSTEM";
- added menu "View" to settings page which allows to switch to "Compact Mode"
when option descriptions are hidden;
- added confirmation dialog by leaving settings page if there are unsaved
changes;
- downloads manually deleted from queue are shown with status "deleted"
in the history (instead of "unknown");
- all table columns except "Name" now have fixed widths to avoid annoying
layout changes especially during post-processing when long status messages
are displayed in the name-column;
- added filter buttons to messages tab (info, warning, etc.);
- added automatic par-renaming of extracted files if archive includes
par-files;
- added support for http redirects when fetching URLs;
- added new command "Download again" for history items; new action
"HistoryRedownload" of RPC-method "editqueue"; for controlling via command
line: new action "A" of subcommand "H" of command "--edit/-E";
- download queue is now saved in a more safe way to avoid potential loss
of queue if the program crashes during saving of queue;
- destination directory for option "CategoryX.DestDir" is not checked/created
on program start anymore (only when a download starts for that category);
this helps when certain categories are configured for external disks,
which are not always connected;
- added new option "CategoryX.Aliases" to configure category name matching
with nzb-sites; especially useful with rss feeds;
- in RPC-Method "appendurl" parameter "addtop" adds nzb to the top of
the main download queue (not only to the top of the URL queue);
- new logo (thanks to dogzipp for the logo);
- added support for metatag "password" in nzb-files;
- pp-scripts which move files can now inform the program about new
location by printing text "[NZB] FINALDIR=/path/to/files"; the final
path is then shown in history dialog instead of download path;
- new env-var "NZBPP_FINALDIR" passed to pp-scripts;
- pp-scripts can now set post-processing parameters by printing
command "[NZB] NZBPR_varname=value"; this allows scripts which are
executed sooner to pass data for scripts executed later;
- added new option "AuthorizedIP" to set the list of IP-addresses which
may connect without authorization;
- new option "ParRename" to force par-renaming as a first post-processing
step (active by default); this saves an unpack attempt and is even more
useful if unpack is disabled;
- post-processing progress label is now automatically trimmed if it
doesn't fill into one line; this avoids layout breaking if the text
is too long;
- reversed the order of priorities in comboboxes in dialogs: the highest
priority - at the top, the lowest - at the bottom;
- small changes in button captions: edit dialogs called from settings
page (choose script, choose order, build rss filter) now have buttons
"Discard/Apply" instead of "Close/Save"; in all other dialogs button
"Close" renamed to "Cancel" unless it was the only button in dialog;
- small change in css: slightly reduced the max height of modal dialogs
to better work on notebooks;
- options "DeleteCleanupDisk" and "NzbCleanupDisk" are now active by
default (in the example config file);
- extended add-dialog with options "Add paused" and "Disable duplicate check";
- source nzb-files are now deleted when download-item leaves queue and
history (option "NzbCleanupDisk");
- when deleting downloads from queue the messages about deleted
individual files are now printed as "detail" instead of "info";
- failed article downloads are now logged as "detail" instead of
"warning" to reduce number of warnings for downloads removed from
server (DMCA); one warning is printed for a file with a summary of
number of failed downloads for the file;
- tuned algorithm calculating maximum threads limit to allow more
threads for backup server connections (related to option "TreadLimit"
removed in v11); this may sometimes increase speed when backup servers
were used;
- by adding nzb-file to queue via RPC-methods "append" and "appendurl"
the actual format of the file is checked and if nzb-format is detected
the file is added even if it does not have .nzb extension;
- added new option "UrlForce" to allow URL-downloads (including fetching
of RSS feeds and nzb-files from feeds) even if download is paused;
the option is active by default;
- destination directory for option "DestDir" is not checked/created on
program start anymore (only when a download starts); this helps when
DestDir is mounted to a network drive which is not available on program start;
- added special handling for files ".AppleDouble" and ".DS_Store" during
unpack to avoid problems on NAS having support for AFP protocol (used
on Mac OSX);
- history records with failed script status are now shown as "PP-FAILURE"
in history list (instead of just "FAILURE");
- option "DiskSpace" now checks space on "InterDir" in addition to
"DestDir";
- support for rar-archives with non-standard extensions is now limited
to file extensions consisting of digits; this is to avoid extracting
of rar-archives having non-rar extensions on purpose (example: .cbr);
- if option "ParRename" is disabled (not recommended) unpacker does
not initiate par-rename anymore, instead the full par-verify is
performed then;
- for external script the exec-permissions are now added automatically;
this makes the installation of pp-scripts and other scripts easier;
- option "InterDir" is now active by default;
- when option "InterDir" is used the intermediate destination directory
names now include unique numbers to avoid several downloads with same
name to use the same directory and interfere with each other;
- when option "UnpackCleanupDisk" is active all archive files are now
deleted from download directory without relying on output printed by
unrar; this solves issues with non-ascii-characters in archive file
names on some platforms and especially in combination with rar5;
- improved handling of non-ascii characters in file names on windows;
- added support for rar5-format when checking signatures of archives
with non-standard file extensions;
- small restructure in settings order:
- combined sections "REMOTE CONTROL" and "PERMISSIONS" into one
section with name "SECURITY";
- moved sections "CATEGORIES" and "RSS FEEDS" higher in the
section list;
- improved par-check: if main par2-file is corrupted and can not be
loaded other par2-files are downloaded and then used as replacement
for main par2-file;
- if unpack did not find archive files the par-check is not requested
anymore if par-rename was already done;
- better handling of obfuscated nzb-files containing multiple files
with same names; removed option "StrictParName" which was not working
good with obfuscated files; if more par-files are required for repair
the files with strict names are tried first and then other par-files;
- added new scheduler commands "ActivateServer", "DeactivateServer" and
"FetchFeed"; combined options "TaskX.DownloadRate" and "TaskX.Process"
into one option "TaskX.Param", also used by new commands;
- added status filter buttons to history page;
- if unpack fails with write error (usually because of not enough space
on disk) this is shown as status "Unpack: space" in web-interface;
this unpack-status is handled as "success" by duplicate handling
(no download of other duplicate); also added new unpack-status "wrong
password" (only for rar5-archives); env.var. NZBPP_UNPACKSTATUS has
two new possible values: 3 (write error) and 4 (wrong password);
updated pp-script "EMail.py" to support new unpack-statuses;
- fixed a potential seg. fault in a commonly used function;
- added new option "TimeCorrection" to adjust conversion from system
time to local time (solves issues with scheduler when using a
binary compiled for other platform);
- NZBIDs are now generated with more care avoiding numbering holes
possible in previous versions;
- fixed: invalid "Offset" passed to RPC-method "editqueue" or command
line action "-E/--edit" could crash the program;
- fixed: crash after downloading of an URL (happen only on certain systems);
- fixed: restoring of settings didn't work for multi-sections (servers,
categories, etc.) if they were empty;
- fixed: choosing local files didn't work in Opera;
- fixed: certain characters printed by pp-scripts could crash the
program;
- fixed: malformed nzb-file could cause a memory leak;
- fixed: when a duplicate file was detected in collection it was
automatically deleted (if option DupeCheck is active) but the
total size of collection was not updated;
- when deleting individual files the total count of files in collection
was not updated;
- fixed: when multiple nzb-files were added via URL (rss including) at
the same time the info about category and priority could get lost for
some of files;
- fixed: if unpack fails the created destination directory was not
automatically removed (only if option "InterDir" was active);
- fixed scrolling to the top of page happening by clicking on items in
downloads/history lists and on action-buttons in edit-download and
history dialogs;
- fixed potential buffer overflow in remote client;
- improved error reporting when creation of temporary output file fails;
- fixed: when deleting download, if all remaining queued files are
par2-files the disk cleanup should not be performed, but it was
sometimes;
- fixed a potential problem in incorrect using of one library function.
nzbget-11.0:
- reworked concept of post-processing scripts:
- multiple scripts can be assigned to each nzb-file;
@@ -711,17 +389,6 @@ nzbget-10.0:
and in configuration options were not displayed properly and could be
discarded on saving;
nzbget-9.1:
- added full par-scan feature needed to par-check/repair files which
were renamed after creation of par-files:
- new option <ParScan> to activate full par-scan (always or automatic);
the automatic full par-scan activates if missing files are detected
during par-check, this avoids unnecessary full scan for normal
(not renamed) par sets;
- improved the post-processing script to better handle renamed rar-files;
- replaced a browser error message when trying to add local files in
IE9 with a better message dialog;
nzbget-9.0:
- changed version naming scheme by removing the leading zero: current
version is now called 9.0 instead of 0.9.0 (it's really the 9th major
@@ -1438,7 +1105,7 @@ nzbget-0.3.0:
of completing and state (paused or not) for every file is printed.
The header of queue shows number of total files, number of unpaused
files and size for all and unpaused files. Better using of screen estate
space - no more empty lines and separate header for status (total seven
space <EFBFBD> no more empty lines and separate header for status (total seven
lines gain). The messages are printed on several lines (if they not fill
in one line) without trimming now;
- configure.ac-file updated to work with recent versions of autoconf/automake.

View File

@@ -164,11 +164,21 @@ Connection::~Connection()
Disconnect();
free(m_szHost);
free(m_szCipher);
if (m_szHost)
{
free(m_szHost);
}
if (m_szCipher)
{
free(m_szCipher);
}
free(m_szReadBuf);
#ifndef DISABLE_TLS
delete m_pTLSSocket;
if (m_pTLSSocket)
{
delete m_pTLSSocket;
}
#endif
}
@@ -185,7 +195,10 @@ void Connection::SetSuppressErrors(bool bSuppressErrors)
void Connection::SetCipher(const char* szCipher)
{
free(m_szCipher);
if (m_szCipher)
{
free(m_szCipher);
}
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
@@ -731,7 +744,11 @@ bool Connection::StartTLS(bool bIsClient, const char* szCertFile, const char* sz
{
debug("Starting TLS");
delete m_pTLSSocket;
if (m_pTLSSocket)
{
delete m_pTLSSocket;
}
m_pTLSSocket = new TLSSocket(m_iSocket, bIsClient, szCertFile, szKeyFile, m_szCipher);
m_pTLSSocket->SetSuppressErrors(m_bSuppressErrors);

View File

@@ -60,12 +60,18 @@ Decoder::~ Decoder()
{
debug("Destroying Decoder");
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
}
void Decoder::Clear()
{
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = NULL;
}
@@ -262,7 +268,10 @@ BreakLoop:
pb += 6; //=strlen(" name=")
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';
@@ -395,7 +404,10 @@ unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
// extracting filename
char* pe;
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
free(m_szArticleFilename);
if (m_szArticleFilename)
{
free(m_szArticleFilename);
}
m_szArticleFilename = (char*)malloc(pe - pb + 1);
strncpy(m_szArticleFilename, pb, pe - pb);
m_szArticleFilename[pe - pb] = '\0';

View File

File diff suppressed because it is too large Load Diff

View File

@@ -27,13 +27,10 @@
#define DISKSTATE_H
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "NewsServer.h"
class DiskState
{
private:
int fscanf(FILE* infile, const char* Format, ...);
int ParseFormatVersion(const char* szFormatSignature);
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
@@ -48,32 +45,19 @@ private:
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
void SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile);
bool LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion);
void SaveDupInfo(DupInfo* pDupInfo, FILE* outfile);
bool LoadDupInfo(DupInfo* pDupInfo, FILE* infile, int iFormatVersion);
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool SaveFeedStatus(Feeds* pFeeds, FILE* outfile);
bool LoadFeedStatus(Feeds* pFeeds, FILE* infile, int iFormatVersion);
bool SaveFeedHistory(FeedHistory* pFeedHistory, FILE* outfile);
bool LoadFeedHistory(FeedHistory* pFeedHistory, FILE* infile, int iFormatVersion);
void CalcCriticalHealth(DownloadQueue* pDownloadQueue);
bool SaveServerStats(Servers* pServers, FILE* outfile);
bool LoadServerStats(Servers* pServers, FILE* infile, int iFormatVersion);
void ConvertDupeKey(char* buf, int bufsize);
public:
bool DownloadQueueExists();
bool PostQueueExists(bool bCompleted);
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
bool SaveFile(FileInfo* pFileInfo);
bool LoadArticles(FileInfo* pFileInfo);
void DiscardDownloadQueue();
bool DiscardFile(FileInfo* pFileInfo);
bool SaveFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
bool LoadFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
bool SaveStats(Servers* pServers);
bool LoadStats(Servers* pServers);
void CleanupTempDir(DownloadQueue* pDownloadQueue);
};

View File

@@ -42,21 +42,16 @@
#include "nzbget.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
extern Options* g_pOptions;
int FileInfo::m_iIDGen = 0;
int FileInfo::m_iIDMax = 0;
int NZBInfo::m_iIDGen = 0;
int NZBInfo::m_iIDMax = 0;
int PostInfo::m_iIDGen = 0;
int PostInfo::m_iIDMax = 0;
int UrlInfo::m_iIDGen = 0;
int UrlInfo::m_iIDMax = 0;
int HistoryInfo::m_iIDGen = 0;
int HistoryInfo::m_iIDMax = 0;
NZBParameter::NZBParameter(const char* szName)
{
@@ -66,31 +61,26 @@ NZBParameter::NZBParameter(const char* szName)
NZBParameter::~NZBParameter()
{
free(m_szName);
free(m_szValue);
if (m_szName)
{
free(m_szName);
}
if (m_szValue)
{
free(m_szValue);
}
}
void NZBParameter::SetValue(const char* szValue)
{
free(m_szValue);
if (m_szValue)
{
free(m_szValue);
}
m_szValue = strdup(szValue);
}
NZBParameterList::~NZBParameterList()
{
Clear();
}
void NZBParameterList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void NZBParameterList::SetParameter(const char* szName, const char* szValue)
{
NZBParameter* pParameter = NULL;
@@ -126,30 +116,6 @@ void NZBParameterList::SetParameter(const char* szName, const char* szValue)
pParameter->SetValue(szValue);
}
NZBParameter* NZBParameterList::Find(const char* szName, bool bCaseSensitive)
{
for (iterator it = begin(); it != end(); it++)
{
NZBParameter* pParameter = *it;
if ((bCaseSensitive && !strcmp(pParameter->GetName(), szName)) ||
(!bCaseSensitive && !strcasecmp(pParameter->GetName(), szName)))
{
return pParameter;
}
}
return NULL;
}
void NZBParameterList::CopyFrom(NZBParameterList* pSourceParameters)
{
for (iterator it = pSourceParameters->begin(); it != pSourceParameters->end(); it++)
{
NZBParameter* pParameter = *it;
SetParameter(pParameter->GetName(), pParameter->GetValue());
}
}
ScriptStatus::ScriptStatus(const char* szName, EStatus eStatus)
{
@@ -159,7 +125,10 @@ ScriptStatus::ScriptStatus(const char* szName, EStatus eStatus)
ScriptStatus::~ScriptStatus()
{
free(m_szName);
if (m_szName)
{
free(m_szName);
}
}
@@ -201,85 +170,17 @@ ScriptStatus::EStatus ScriptStatusList::CalcTotalStatus()
}
ServerStat::ServerStat(int iServerID)
{
m_iServerID = iServerID;
m_iSuccessArticles = 0;
m_iFailedArticles = 0;
}
ServerStatList::~ServerStatList()
{
Clear();
}
void ServerStatList::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void ServerStatList::SetStat(int iServerID, int iSuccessArticles, int iFailedArticles, bool bAdd)
{
ServerStat* pServerStat = NULL;
for (iterator it = begin(); it != end(); it++)
{
ServerStat* pServerStat1 = *it;
if (pServerStat1->GetServerID() == iServerID)
{
pServerStat = pServerStat1;
break;
}
}
if (!pServerStat)
{
pServerStat = new ServerStat(iServerID);
push_back(pServerStat);
}
pServerStat->SetSuccessArticles((bAdd ? pServerStat->GetSuccessArticles() : 0) + iSuccessArticles);
pServerStat->SetFailedArticles((bAdd ? pServerStat->GetFailedArticles() : 0) + iFailedArticles);
}
void ServerStatList::Add(ServerStatList* pServerStats)
{
for (iterator it = pServerStats->begin(); it != pServerStats->end(); it++)
{
ServerStat* pServerStat = *it;
SetStat(pServerStat->GetServerID(), pServerStat->GetSuccessArticles(), pServerStat->GetFailedArticles(), true);
}
}
NZBInfo::NZBInfo(bool bPersistent)
NZBInfo::NZBInfo()
{
debug("Creating NZBInfo");
m_szFilename = NULL;
m_szDestDir = NULL;
m_szFinalDir = strdup("");
m_szCategory = strdup("");
m_szName = NULL;
m_iFileCount = 0;
m_iParkedFileCount = 0;
m_lSize = 0;
m_lSuccessSize = 0;
m_lFailedSize = 0;
m_lCurrentSuccessSize = 0;
m_lCurrentFailedSize = 0;
m_lParSize = 0;
m_lParSuccessSize = 0;
m_lParFailedSize = 0;
m_lParCurrentSuccessSize = 0;
m_lParCurrentFailedSize = 0;
m_iTotalArticles = 0;
m_iSuccessArticles = 0;
m_iFailedArticles = 0;
m_iRefCount = 0;
m_bPostProcess = false;
m_eRenameStatus = rsNone;
@@ -287,42 +188,51 @@ NZBInfo::NZBInfo(bool bPersistent)
m_eUnpackStatus = usNone;
m_eCleanupStatus = csNone;
m_eMoveStatus = msNone;
m_eDeleteStatus = dsNone;
m_eMarkStatus = ksNone;
m_bDeleting = false;
m_bDeletePaused = false;
m_bManyDupeFiles = false;
m_bAvoidHistory = false;
m_bHealthPaused = false;
m_bDeleted = false;
m_bParCleanup = false;
m_bCleanupDisk = false;
m_bUnpackCleanedUpDisk = false;
m_szQueuedFilename = strdup("");
m_szDupeKey = strdup("");
m_iDupeScore = 0;
m_eDupeMode = dmScore;
m_iFullContentHash = 0;
m_iFilteredContentHash = 0;
m_Owner = NULL;
m_Messages.clear();
m_iIDMessageGen = 0;
m_iID = bPersistent ? ++m_iIDGen : 0;
m_iIDGen++;
m_iID = m_iIDGen;
}
NZBInfo::~NZBInfo()
{
debug("Destroying NZBInfo");
free(m_szFilename);
free(m_szDestDir);
free(m_szFinalDir);
free(m_szCategory);
free(m_szName);
free(m_szQueuedFilename);
free(m_szDupeKey);
if (m_szFilename)
{
free(m_szFilename);
}
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szCategory)
{
free(m_szCategory);
}
if (m_szName)
{
free(m_szName);
}
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
ClearCompletedFiles();
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
{
delete *it;
}
m_ppParameters.clear();
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
@@ -335,7 +245,7 @@ NZBInfo::~NZBInfo()
}
}
void NZBInfo::Retain()
void NZBInfo::AddReference()
{
m_iRefCount++;
}
@@ -352,22 +262,9 @@ void NZBInfo::Release()
void NZBInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDMax < m_iID)
if (m_iIDGen < m_iID)
{
m_iIDMax = m_iID;
}
}
void NZBInfo::ResetGenID(bool bMax)
{
if (bMax)
{
m_iIDGen = m_iIDMax;
}
else
{
m_iIDGen = 0;
m_iIDMax = 0;
m_iIDGen = m_iID;
}
}
@@ -382,19 +279,19 @@ void NZBInfo::ClearCompletedFiles()
void NZBInfo::SetDestDir(const char* szDestDir)
{
free(m_szDestDir);
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void NZBInfo::SetFinalDir(const char* szFinalDir)
{
free(m_szFinalDir);
m_szFinalDir = strdup(szFinalDir);
}
void NZBInfo::SetFilename(const char * szFilename)
{
free(m_szFilename);
if (m_szFilename)
{
free(m_szFilename);
}
m_szFilename = strdup(szFilename);
if (!m_szName)
@@ -402,37 +299,37 @@ void NZBInfo::SetFilename(const char * szFilename)
char szNZBNicename[1024];
MakeNiceNZBName(m_szFilename, szNZBNicename, sizeof(szNZBNicename), true);
szNZBNicename[1024-1] = '\0';
#ifdef WIN32
WebUtil::AnsiToUtf8(szNZBNicename, 1024);
#endif
SetName(szNZBNicename);
}
}
void NZBInfo::SetName(const char* szName)
{
free(m_szName);
if (m_szName)
{
free(m_szName);
}
m_szName = szName ? strdup(szName) : NULL;
}
void NZBInfo::SetCategory(const char* szCategory)
{
free(m_szCategory);
if (m_szCategory)
{
free(m_szCategory);
}
m_szCategory = strdup(szCategory);
}
void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
{
free(m_szQueuedFilename);
if (m_szQueuedFilename)
{
free(m_szQueuedFilename);
}
m_szQueuedFilename = strdup(szQueuedFilename);
}
void NZBInfo::SetDupeKey(const char* szDupeKey)
{
free(m_szDupeKey);
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
}
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize, bool bRemoveExt)
{
char postname[1024];
@@ -458,20 +355,16 @@ void NZBInfo::BuildDestDirName()
{
char szDestDir[1024];
if (Util::EmptyStr(g_pOptions->GetInterDir()))
if (strlen(g_pOptions->GetInterDir()) == 0)
{
BuildFinalDirName(szDestDir, 1024);
}
else
{
snprintf(szDestDir, 1024, "%s%s.#%i", g_pOptions->GetInterDir(), GetName(), GetID());
snprintf(szDestDir, 1024, "%s%s", g_pOptions->GetInterDir(), GetName());
szDestDir[1024-1] = '\0';
}
#ifdef WIN32
WebUtil::Utf8ToAnsi(szDestDir, 1024);
#endif
SetDestDir(szDestDir);
}
@@ -485,7 +378,7 @@ void NZBInfo::BuildFinalDirName(char* szFinalDirBuf, int iBufSize)
if (bUseCategory)
{
Options::Category *pCategory = g_pOptions->FindCategory(m_szCategory, false);
Options::Category *pCategory = g_pOptions->FindCategory(m_szCategory);
if (pCategory && pCategory->GetDestDir() && pCategory->GetDestDir()[0] != '\0')
{
snprintf(szFinalDirBuf, iBufSize, "%s", pCategory->GetDestDir());
@@ -509,47 +402,11 @@ void NZBInfo::BuildFinalDirName(char* szFinalDirBuf, int iBufSize)
snprintf(szBuffer, 1024, "%s%s", szFinalDirBuf, GetName());
szBuffer[1024-1] = '\0';
strncpy(szFinalDirBuf, szBuffer, iBufSize);
#ifdef WIN32
WebUtil::Utf8ToAnsi(szFinalDirBuf, iBufSize);
#endif
}
int NZBInfo::CalcHealth()
void NZBInfo::SetParameter(const char* szName, const char* szValue)
{
if (m_lCurrentFailedSize == 0 || m_lSize == m_lParSize)
{
return 1000;
}
int iHealth = (int)(Util::Int64ToFloat(m_lSize - m_lParSize -
(m_lCurrentFailedSize - m_lParCurrentFailedSize)) * 1000.0 /
Util::Int64ToFloat(m_lSize - m_lParSize));
if (iHealth == 1000 && m_lCurrentFailedSize - m_lParCurrentFailedSize > 0)
{
iHealth = 999;
}
return iHealth;
}
int NZBInfo::CalcCriticalHealth()
{
long long lGoodParSize = m_lParSize - m_lParCurrentFailedSize;
int iCriticalHealth = (int)(Util::Int64ToFloat(m_lSize - lGoodParSize*2) * 1000.0 /
Util::Int64ToFloat(m_lSize - lGoodParSize));
if (lGoodParSize*2 > m_lSize)
{
iCriticalHealth = 0;
}
else if (iCriticalHealth == 1000 && m_lParSize > 0)
{
iCriticalHealth = 999;
}
return iCriticalHealth;
m_ppParameters.SetParameter(szName, szValue);
}
NZBInfo::Messages* NZBInfo::LockMessages()
@@ -628,19 +485,27 @@ ArticleInfo::~ ArticleInfo()
{
//debug("Destroying ArticleInfo");
free(m_szMessageID);
free(m_szResultFilename);
if (m_szMessageID)
{
free(m_szMessageID);
}
if (m_szResultFilename)
{
free(m_szResultFilename);
}
}
void ArticleInfo::SetMessageID(const char * szMessageID)
{
free(m_szMessageID);
m_szMessageID = strdup(szMessageID);
}
void ArticleInfo::SetResultFilename(const char * v)
{
free(m_szResultFilename);
if (m_szResultFilename)
{
free(m_szResultFilename);
}
m_szResultFilename = strdup(v);
}
@@ -658,35 +523,40 @@ FileInfo::FileInfo()
m_bFilenameConfirmed = false;
m_lSize = 0;
m_lRemainingSize = 0;
m_lMissedSize = 0;
m_lSuccessSize = 0;
m_lFailedSize = 0;
m_iTotalArticles = 0;
m_iMissedArticles = 0;
m_iFailedArticles = 0;
m_iSuccessArticles = 0;
m_tTime = 0;
m_bPaused = false;
m_bDeleted = false;
m_iCompleted = 0;
m_bParFile = false;
m_bOutputInitialized = false;
m_pNZBInfo = NULL;
m_iPriority = 0;
m_bExtraPriority = false;
m_iActiveDownloads = 0;
m_bAutoDeleted = false;
m_iID = ++m_iIDGen;
m_iIDGen++;
m_iID = m_iIDGen;
}
FileInfo::~ FileInfo()
{
debug("Destroying FileInfo");
free(m_szSubject);
free(m_szFilename);
free(m_szOutputFilename);
delete m_pMutexOutputFile;
if (m_szSubject)
{
free(m_szSubject);
}
if (m_szFilename)
{
free(m_szFilename);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
if (m_pMutexOutputFile)
{
delete m_pMutexOutputFile;
}
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
{
@@ -714,22 +584,9 @@ void FileInfo::ClearArticles()
void FileInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDMax < m_iID)
if (m_iIDGen < m_iID)
{
m_iIDMax = m_iID;
}
}
void FileInfo::ResetGenID(bool bMax)
{
if (bMax)
{
m_iIDGen = m_iIDMax;
}
else
{
m_iIDGen = 0;
m_iIDMax = 0;
m_iIDGen = m_iID;
}
}
@@ -740,7 +597,7 @@ void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
m_pNZBInfo->Release();
}
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->Retain();
m_pNZBInfo->AddReference();
}
void FileInfo::SetSubject(const char* szSubject)
@@ -750,7 +607,10 @@ void FileInfo::SetSubject(const char* szSubject)
void FileInfo::SetFilename(const char* szFilename)
{
free(m_szFilename);
if (m_szFilename)
{
free(m_szFilename);
}
m_szFilename = strdup(szFilename);
}
@@ -771,7 +631,10 @@ void FileInfo::UnlockOutputFile()
void FileInfo::SetOutputFilename(const char* szOutputFilename)
{
free(m_szOutputFilename);
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
m_szOutputFilename = strdup(szOutputFilename);
}
@@ -790,6 +653,24 @@ void FileInfo::SetActiveDownloads(int iActiveDownloads)
}
}
bool FileInfo::IsDupe(const char* szFilename)
{
char fileName[1024];
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
fileName[1024-1] = '\0';
if (Util::FileExists(fileName))
{
return true;
}
return false;
}
GroupInfo::GroupInfo()
{
@@ -815,22 +696,6 @@ GroupInfo::~GroupInfo()
}
}
GroupQueue::~GroupQueue()
{
Clear();
}
void GroupQueue::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
PostInfo::PostInfo()
{
debug("Creating PostInfo");
@@ -840,6 +705,7 @@ PostInfo::PostInfo()
m_bWorking = false;
m_bDeleted = false;
m_bRequestParCheck = false;
m_bRequestParRename = false;
m_szProgressLabel = strdup("");
m_iFileProgress = 0;
m_iStageProgress = 0;
@@ -849,15 +715,22 @@ PostInfo::PostInfo()
m_pPostThread = NULL;
m_Messages.clear();
m_iIDMessageGen = 0;
m_iID = ++m_iIDGen;
m_iIDGen++;
m_iID = m_iIDGen;
}
PostInfo::~ PostInfo()
{
debug("Destroying PostInfo");
free(m_szInfoName);
free(m_szProgressLabel);
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
@@ -878,7 +751,7 @@ void PostInfo::SetNZBInfo(NZBInfo* pNZBInfo)
m_pNZBInfo->Release();
}
m_pNZBInfo = pNZBInfo;
m_pNZBInfo->Retain();
m_pNZBInfo->AddReference();
}
void PostInfo::SetInfoName(const char* szInfoName)
@@ -888,7 +761,10 @@ void PostInfo::SetInfoName(const char* szInfoName)
void PostInfo::SetProgressLabel(const char* szProgressLabel)
{
free(m_szProgressLabel);
if (m_szProgressLabel)
{
free(m_szProgressLabel);
}
m_szProgressLabel = strdup(szProgressLabel);
}
@@ -918,7 +794,6 @@ void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
m_mutexLog.Unlock();
}
void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
{
std::map<int, GroupInfo*> groupMap;
@@ -931,7 +806,7 @@ void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
{
pGroupInfo = new GroupInfo();
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
pGroupInfo->m_pNZBInfo->Retain();
pGroupInfo->m_pNZBInfo->AddReference();
pGroupInfo->m_iFirstID = pFileInfo->GetID();
pGroupInfo->m_iLastID = pFileInfo->GetID();
pGroupInfo->m_tMinTime = pFileInfo->GetTime();
@@ -991,75 +866,70 @@ void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
UrlInfo::UrlInfo()
{
//debug("Creating UrlInfo");
//debug("Creating ArticleInfo");
m_szURL = NULL;
m_szNZBFilename = strdup("");
m_szCategory = strdup("");
m_iPriority = 0;
m_iDupeScore = 0;
m_szDupeKey = strdup("");
m_eDupeMode = dmScore;
m_bAddTop = false;
m_bAddPaused = false;
m_bForce = false;
m_eStatus = aiUndefined;
m_iID = ++m_iIDGen;
m_iIDGen++;
m_iID = m_iIDGen;
}
UrlInfo::~ UrlInfo()
{
free(m_szURL);
free(m_szNZBFilename);
free(m_szCategory);
free(m_szDupeKey);
if (m_szURL)
{
free(m_szURL);
}
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
if (m_szCategory)
{
free(m_szCategory);
}
}
void UrlInfo::SetURL(const char* szURL)
{
free(m_szURL);
if (m_szURL)
{
free(m_szURL);
}
m_szURL = strdup(szURL);
}
void UrlInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDMax < m_iID)
if (m_iIDGen < m_iID)
{
m_iIDMax = m_iID;
}
}
void UrlInfo::ResetGenID(bool bMax)
{
if (bMax)
{
m_iIDGen = m_iIDMax;
}
else
{
m_iIDGen = 0;
m_iIDMax = 0;
m_iIDGen = m_iID;
}
}
void UrlInfo::SetNZBFilename(const char* szNZBFilename)
{
free(m_szNZBFilename);
if (m_szNZBFilename)
{
free(m_szNZBFilename);
}
m_szNZBFilename = strdup(szNZBFilename);
}
void UrlInfo::SetCategory(const char* szCategory)
{
free(m_szCategory);
if (m_szCategory)
{
free(m_szCategory);
}
m_szCategory = strdup(szCategory);
}
void UrlInfo::SetDupeKey(const char* szDupeKey)
{
free(m_szDupeKey);
m_szDupeKey = strdup(szDupeKey);
}
void UrlInfo::GetName(char* szBuffer, int iSize)
{
MakeNiceName(m_szURL, m_szNZBFilename, szBuffer, iSize);
@@ -1084,44 +954,14 @@ void UrlInfo::MakeNiceName(const char* szURL, const char* szNZBFilename, char* s
}
DupInfo::DupInfo()
{
m_szName = NULL;
m_szDupeKey = NULL;
m_iDupeScore = 0;
m_eDupeMode = dmScore;
m_lSize = 0;
m_iFullContentHash = 0;
m_iFilteredContentHash = 0;
m_eStatus = dsUndefined;
}
DupInfo::~DupInfo()
{
free(m_szName);
free(m_szDupeKey);
}
void DupInfo::SetName(const char* szName)
{
free(m_szName);
m_szName = strdup(szName);
}
void DupInfo::SetDupeKey(const char* szDupeKey)
{
free(m_szDupeKey);
m_szDupeKey = strdup(szDupeKey);
}
HistoryInfo::HistoryInfo(NZBInfo* pNZBInfo)
{
m_eKind = hkNZBInfo;
m_pInfo = pNZBInfo;
pNZBInfo->Retain();
pNZBInfo->AddReference();
m_tTime = 0;
m_iID = ++m_iIDGen;
m_iIDGen++;
m_iID = m_iIDGen;
}
HistoryInfo::HistoryInfo(UrlInfo* pUrlInfo)
@@ -1129,15 +969,8 @@ HistoryInfo::HistoryInfo(UrlInfo* pUrlInfo)
m_eKind = hkUrlInfo;
m_pInfo = pUrlInfo;
m_tTime = 0;
m_iID = ++m_iIDGen;
}
HistoryInfo::HistoryInfo(DupInfo* pDupInfo)
{
m_eKind = hkDupInfo;
m_pInfo = pDupInfo;
m_tTime = 0;
m_iID = ++m_iIDGen;
m_iIDGen++;
m_iID = m_iIDGen;
}
HistoryInfo::~HistoryInfo()
@@ -1150,31 +983,14 @@ HistoryInfo::~HistoryInfo()
{
delete (UrlInfo*)m_pInfo;
}
else if (m_eKind == hkDupInfo && m_pInfo)
{
delete (DupInfo*)m_pInfo;
}
}
void HistoryInfo::SetID(int iID)
{
m_iID = iID;
if (m_iIDMax < m_iID)
if (m_iIDGen < m_iID)
{
m_iIDMax = m_iID;
}
}
void HistoryInfo::ResetGenID(bool bMax)
{
if (bMax)
{
m_iIDGen = m_iIDMax;
}
else
{
m_iIDGen = 0;
m_iIDMax = 0;
m_iIDGen = m_iID;
}
}
@@ -1189,11 +1005,6 @@ void HistoryInfo::GetName(char* szBuffer, int iSize)
{
GetUrlInfo()->GetName(szBuffer, iSize);
}
else if (m_eKind == hkDupInfo)
{
strncpy(szBuffer, GetDupInfo()->GetName(), iSize);
szBuffer[iSize-1] = '\0';
}
else
{
strncpy(szBuffer, "<unknown>", iSize);

View File

@@ -85,18 +85,10 @@ private:
char* m_szFilename;
long long m_lSize;
long long m_lRemainingSize;
long long m_lSuccessSize;
long long m_lFailedSize;
long long m_lMissedSize;
int m_iTotalArticles;
int m_iMissedArticles;
int m_iFailedArticles;
int m_iSuccessArticles;
time_t m_tTime;
bool m_bPaused;
bool m_bDeleted;
bool m_bFilenameConfirmed;
bool m_bParFile;
int m_iCompleted;
bool m_bOutputInitialized;
char* m_szOutputFilename;
@@ -107,14 +99,12 @@ private:
bool m_bAutoDeleted;
static int m_iIDGen;
static int m_iIDMax;
public:
FileInfo();
~FileInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
void SetNZBInfo(NZBInfo* pNZBInfo);
Articles* GetArticles() { return &m_Articles; }
@@ -126,24 +116,10 @@ public:
void MakeValidFilename();
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
void SetSize(long long lSize) { m_lSize = lSize; m_lRemainingSize = lSize; }
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 lRemainingSize) { m_lRemainingSize = lRemainingSize; }
long long GetMissedSize() { return m_lMissedSize; }
void SetMissedSize(long long lMissedSize) { m_lMissedSize = lMissedSize; }
long long GetSuccessSize() { return m_lSuccessSize; }
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
long long GetFailedSize() { return m_lFailedSize; }
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
int GetTotalArticles() { return m_iTotalArticles; }
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
int GetMissedArticles() { return m_iMissedArticles; }
void SetMissedArticles(int iMissedArticles) { m_iMissedArticles = iMissedArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
bool GetPaused() { return m_bPaused; }
@@ -151,9 +127,7 @@ public:
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
int GetCompleted() { return m_iCompleted; }
void SetCompleted(int iCompleted) { m_iCompleted = iCompleted; }
bool GetParFile() { return m_bParFile; }
void SetParFile(bool bParFile) { m_bParFile = bParFile; }
void SetCompleted(int s) { m_iCompleted = s; }
void ClearArticles();
void LockOutputFile();
void UnlockOutputFile();
@@ -161,6 +135,7 @@ public:
void SetOutputFilename(const char* szOutputFilename);
bool GetOutputInitialized() { return m_bOutputInitialized; }
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
bool IsDupe(const char* szFilename);
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
bool GetExtraPriority() { return m_bExtraPriority; }
@@ -210,15 +185,7 @@ public:
int GetActiveDownloads() { return m_iActiveDownloads; }
};
typedef std::deque<GroupInfo*> GroupQueueBase;
class GroupQueue : public GroupQueueBase
{
public:
~GroupQueue();
void Clear();
};
typedef std::deque<GroupInfo*> GroupQueue;
class NZBParameter
{
@@ -242,11 +209,7 @@ typedef std::deque<NZBParameter*> NZBParameterListBase;
class NZBParameterList : public NZBParameterListBase
{
public:
~NZBParameterList();
void SetParameter(const char* szName, const char* szValue);
NZBParameter* Find(const char* szName, bool bCaseSensitive);
void Clear();
void CopyFrom(NZBParameterList* pSourceParameters);
};
class ScriptStatus
@@ -283,40 +246,6 @@ public:
ScriptStatus::EStatus CalcTotalStatus();
};
class ServerStat
{
private:
int m_iServerID;
int m_iSuccessArticles;
int m_iFailedArticles;
public:
ServerStat(int iServerID);
int GetServerID() { return m_iServerID; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
};
typedef std::vector<ServerStat*> ServerStatListBase;
class ServerStatList : public ServerStatListBase
{
public:
~ServerStatList();
void SetStat(int iServerID, int iSuccessArticles, int iFailedArticles, bool bAdd);
void Add(ServerStatList* pServerStats);
void Clear();
};
enum EDupeMode
{
dmScore,
dmAll,
dmForce
};
class NZBInfoList;
class NZBInfo
@@ -345,9 +274,7 @@ public:
usNone,
usSkipped,
usFailure,
usSuccess,
usSpace,
usPassword
usSuccess
};
enum ECleanupStatus
@@ -364,21 +291,6 @@ public:
msSuccess
};
enum EDeleteStatus
{
dsNone,
dsManual,
dsHealth,
dsDupe
};
enum EMarkStatus
{
ksNone,
ksBad,
ksGood
};
typedef std::vector<char*> Files;
typedef std::deque<Message*> Messages;
@@ -388,23 +300,10 @@ private:
char* m_szFilename;
char* m_szName;
char* m_szDestDir;
char* m_szFinalDir;
char* m_szCategory;
int m_iFileCount;
int m_iParkedFileCount;
long long m_lSize;
long long m_lSuccessSize;
long long m_lFailedSize;
long long m_lCurrentSuccessSize;
long long m_lCurrentFailedSize;
long long m_lParSize;
long long m_lParSuccessSize;
long long m_lParFailedSize;
long long m_lParCurrentSuccessSize;
long long m_lParCurrentFailedSize;
int m_iTotalArticles;
int m_iSuccessArticles;
int m_iFailedArticles;
Files m_completedFiles;
bool m_bPostProcess;
ERenameStatus m_eRenameStatus;
@@ -412,85 +311,45 @@ private:
EUnpackStatus m_eUnpackStatus;
ECleanupStatus m_eCleanupStatus;
EMoveStatus m_eMoveStatus;
EDeleteStatus m_eDeleteStatus;
EMarkStatus m_eMarkStatus;
bool m_bDeletePaused;
bool m_bManyDupeFiles;
char* m_szQueuedFilename;
bool m_bDeleting;
bool m_bAvoidHistory;
bool m_bHealthPaused;
bool m_bDeleted;
bool m_bParCleanup;
bool m_bParManual;
bool m_bCleanupDisk;
bool m_bUnpackCleanedUpDisk;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
unsigned int m_iFullContentHash;
unsigned int m_iFilteredContentHash;
NZBInfoList* m_Owner;
NZBParameterList m_ppParameters;
ScriptStatusList m_scriptStatuses;
ServerStatList m_ServerStats;
Mutex m_mutexLog;
Messages m_Messages;
int m_iIDMessageGen;
static int m_iIDGen;
static int m_iIDMax;
friend class NZBInfoList;
public:
NZBInfo(bool bPersistent = true);
NZBInfo();
~NZBInfo();
void Retain();
void AddReference();
void Release();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
const char* GetFinalDir() { return m_szFinalDir; } // needs locking (for shared objects)
void SetFinalDir(const char* szFinalDir); // needs locking (for shared objects)
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
void SetCategory(const char* szCategory); // needs locking (for shared objects)
const char* GetName() { return m_szName; } // needs locking (for shared objects)
void SetName(const char* szName); // needs locking (for shared objects)
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
int GetFileCount() { return m_iFileCount; }
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
int GetParkedFileCount() { return m_iParkedFileCount; }
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
long long GetSuccessSize() { return m_lSuccessSize; }
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
long long GetFailedSize() { return m_lFailedSize; }
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
long long GetCurrentSuccessSize() { return m_lCurrentSuccessSize; }
void SetCurrentSuccessSize(long long lCurrentSuccessSize) { m_lCurrentSuccessSize = lCurrentSuccessSize; }
long long GetCurrentFailedSize() { return m_lCurrentFailedSize; }
void SetCurrentFailedSize(long long lCurrentFailedSize) { m_lCurrentFailedSize = lCurrentFailedSize; }
long long GetParSize() { return m_lParSize; }
void SetParSize(long long lParSize) { m_lParSize = lParSize; }
long long GetParSuccessSize() { return m_lParSuccessSize; }
void SetParSuccessSize(long long lParSuccessSize) { m_lParSuccessSize = lParSuccessSize; }
long long GetParFailedSize() { return m_lParFailedSize; }
void SetParFailedSize(long long lParFailedSize) { m_lParFailedSize = lParFailedSize; }
long long GetParCurrentSuccessSize() { return m_lParCurrentSuccessSize; }
void SetParCurrentSuccessSize(long long lParCurrentSuccessSize) { m_lParCurrentSuccessSize = lParCurrentSuccessSize; }
long long GetParCurrentFailedSize() { return m_lParCurrentFailedSize; }
void SetParCurrentFailedSize(long long lParCurrentFailedSize) { m_lParCurrentFailedSize = lParCurrentFailedSize; }
int GetTotalArticles() { return m_iTotalArticles; }
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
int GetSuccessArticles() { return m_iSuccessArticles; }
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
int GetFailedArticles() { return m_iFailedArticles; }
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
void BuildDestDirName();
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
@@ -507,22 +366,10 @@ public:
void SetCleanupStatus(ECleanupStatus eCleanupStatus) { m_eCleanupStatus = eCleanupStatus; }
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
EDeleteStatus GetDeleteStatus() { return m_eDeleteStatus; }
void SetDeleteStatus(EDeleteStatus eDeleteStatus) { m_eDeleteStatus = eDeleteStatus; }
EMarkStatus GetMarkStatus() { return m_eMarkStatus; }
void SetMarkStatus(EMarkStatus eMarkStatus) { m_eMarkStatus = eMarkStatus; }
const char* GetQueuedFilename() { return m_szQueuedFilename; }
void SetQueuedFilename(const char* szQueuedFilename);
bool GetDeleting() { return m_bDeleting; }
void SetDeleting(bool bDeleting) { m_bDeleting = bDeleting; }
bool GetDeletePaused() { return m_bDeletePaused; }
void SetDeletePaused(bool bDeletePaused) { m_bDeletePaused = bDeletePaused; }
bool GetManyDupeFiles() { return m_bManyDupeFiles; }
void SetManyDupeFiles(bool bManyDupeFiles) { m_bManyDupeFiles = bManyDupeFiles; }
bool GetAvoidHistory() { return m_bAvoidHistory; }
void SetAvoidHistory(bool bAvoidHistory) { m_bAvoidHistory = bAvoidHistory; }
bool GetHealthPaused() { return m_bHealthPaused; }
void SetHealthPaused(bool bHealthPaused) { m_bHealthPaused = bHealthPaused; }
bool GetDeleted() { return m_bDeleted; }
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetParCleanup() { return m_bParCleanup; }
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
bool GetCleanupDisk() { return m_bCleanupDisk; }
@@ -530,20 +377,8 @@ public:
bool GetUnpackCleanedUpDisk() { return m_bUnpackCleanedUpDisk; }
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
ScriptStatusList* GetScriptStatuses() { return &m_scriptStatuses; } // needs locking (for shared objects)
ServerStatList* GetServerStats() { return &m_ServerStats; }
int CalcHealth();
int CalcCriticalHealth();
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
unsigned int GetFullContentHash() { return m_iFullContentHash; }
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Messages* LockMessages();
void UnlockMessages();
@@ -585,6 +420,7 @@ private:
bool m_bWorking;
bool m_bDeleted;
bool m_bRequestParCheck;
bool m_bRequestParRename;
EStage m_eStage;
char* m_szProgressLabel;
int m_iFileProgress;
@@ -598,7 +434,6 @@ private:
int m_iIDMessageGen;
static int m_iIDGen;
static int m_iIDMax;
public:
PostInfo();
@@ -626,6 +461,8 @@ public:
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
bool GetRequestParCheck() { return m_bRequestParCheck; }
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
bool GetRequestParRename() { return m_bRequestParRename; }
void SetRequestParRename(bool bRequestParRename) { m_bRequestParRename = bRequestParRename; }
void AppendMessage(Message::EKind eKind, const char* szText);
Thread* GetPostThread() { return m_pPostThread; }
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
@@ -648,9 +485,7 @@ public:
aiRunning,
aiFinished,
aiFailed,
aiRetry,
aiScanSkipped,
aiScanFailed
aiRetry
};
private:
@@ -659,23 +494,17 @@ private:
char* m_szNZBFilename;
char* m_szCategory;
int m_iPriority;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
bool m_bAddTop;
bool m_bAddPaused;
bool m_bForce;
EStatus m_eStatus;
static int m_iIDGen;
static int m_iIDMax;
public:
UrlInfo();
~UrlInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
void SetURL(const char* szURL); // needs locking (for shared objects)
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
@@ -684,71 +513,18 @@ public:
void SetCategory(const char* szCategory); // needs locking (for shared objects)
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
const char* GetDupeKey() { return m_szDupeKey; }
void SetDupeKey(const char* szDupeKey);
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
bool GetAddTop() { return m_bAddTop; }
void SetAddTop(bool bAddTop) { m_bAddTop = bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
void SetAddPaused(bool bAddPaused) { m_bAddPaused = bAddPaused; }
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
static void MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize);
bool GetForce() { return m_bForce; }
void SetForce(bool bForce) { m_bForce = bForce; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
};
typedef std::deque<UrlInfo*> UrlQueue;
class DupInfo
{
public:
enum EStatus
{
dsUndefined,
dsSuccess,
dsFailed,
dsDeleted,
dsDupe,
dsBad,
dsGood
};
private:
char* m_szName;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
long long m_lSize;
unsigned int m_iFullContentHash;
unsigned int m_iFilteredContentHash;
EStatus m_eStatus;
public:
DupInfo();
~DupInfo();
const char* GetName() { return m_szName; } // needs locking (for shared objects)
void SetName(const char* szName); // needs locking (for shared objects)
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
unsigned int GetFullContentHash() { return m_iFullContentHash; }
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
};
class HistoryInfo
{
public:
@@ -756,8 +532,7 @@ public:
{
hkUnknown,
hkNZBInfo,
hkUrlInfo,
hkDupInfo
hkUrlInfo
};
private:
@@ -767,20 +542,16 @@ private:
time_t m_tTime;
static int m_iIDGen;
static int m_iIDMax;
public:
HistoryInfo(NZBInfo* pNZBInfo);
HistoryInfo(UrlInfo* pUrlInfo);
HistoryInfo(DupInfo* pDupInfo);
~HistoryInfo();
int GetID() { return m_iID; }
void SetID(int iID);
static void ResetGenID(bool bMax);
EKind GetKind() { return m_eKind; }
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }
DupInfo* GetDupInfo() { return (DupInfo*)m_pInfo; }
void DiscardUrlInfo() { m_pInfo = NULL; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }

View File

@@ -1,583 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifdef WIN32
#include <direct.h>
#else
#include <unistd.h>
#endif
#include <set>
#include <algorithm>
#include "nzbget.h"
#include "Options.h"
#include "Log.h"
#include "Util.h"
#include "DiskState.h"
#include "NZBFile.h"
#include "QueueCoordinator.h"
#include "DupeCoordinator.h"
extern QueueCoordinator* g_pQueueCoordinator;
extern Options* g_pOptions;
extern DiskState* g_pDiskState;
bool DupeCoordinator::IsDupeSuccess(NZBInfo* pNZBInfo)
{
bool bFailure =
pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone ||
pNZBInfo->GetMarkStatus() == NZBInfo::ksBad ||
pNZBInfo->GetParStatus() == NZBInfo::psFailure ||
pNZBInfo->GetUnpackStatus() == NZBInfo::usFailure ||
pNZBInfo->GetUnpackStatus() == NZBInfo::usPassword ||
(pNZBInfo->GetParStatus() == NZBInfo::psSkipped &&
pNZBInfo->GetUnpackStatus() == NZBInfo::usSkipped &&
pNZBInfo->CalcHealth() < pNZBInfo->CalcCriticalHealth());
return !bFailure;
}
bool DupeCoordinator::SameNameOrKey(const char* szName1, const char* szDupeKey1,
const char* szName2, const char* szDupeKey2)
{
bool bHasDupeKeys = !Util::EmptyStr(szDupeKey1) && !Util::EmptyStr(szDupeKey2);
return (bHasDupeKeys && !strcmp(szDupeKey1, szDupeKey2)) ||
(!bHasDupeKeys && !strcmp(szName1, szName2));
}
/**
Check if the title was already downloaded or is already queued:
- if there is a duplicate with exactly same content (via hash-check)
in queue or in history - the new item is skipped;
- if there is a duplicate marked as good in history - the new item is skipped;
- if there is a duplicate with success-status in dup-history but
there are no duplicates in recent history - the new item is skipped;
- if queue has a duplicate with the same or higher score - the new item
is moved to history as dupe-backup;
- if queue has a duplicate with lower score - the existing item is moved
to history as dupe-backup (unless it is in post-processing stage) and
the new item is added to queue;
- if queue doesn't have duplicates - the new item is added to queue.
*/
void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("Checking duplicates for %s", pNZBInfo->GetName());
GroupQueue groupQueue;
pDownloadQueue->BuildGroups(&groupQueue);
// find duplicates in download queue with exactly same content
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pGroupNZBInfo->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pGroupNZBInfo->GetFilteredContentHash());
// if there is a duplicate with exactly same content (via hash-check)
// in queue - the new item is skipped
if (pGroupNZBInfo != pNZBInfo && bSameContent)
{
if (!strcmp(pNZBInfo->GetName(), pGroupNZBInfo->GetName()))
{
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
}
else
{
warn("Skipping duplicate %s, already queued as %s",
pNZBInfo->GetName(), pGroupNZBInfo->GetName());
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
}
// find duplicates in post queue with exactly same content
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pPostInfo->GetNZBInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pPostInfo->GetNZBInfo()->GetFilteredContentHash());
// if there is a duplicate with exactly same content (via hash-check)
// in queue - the new item is skipped;
if (bSameContent)
{
if (!strcmp(pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName()))
{
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
}
else
{
warn("Skipping duplicate %s, already queued as %s",
pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
}
// find duplicates in history
bool bSkip = false;
bool bGood = false;
bool bSameContent = false;
const char* szDupeName = NULL;
// find duplicates in queue having exactly same content
// also: nzb-files having duplicates marked as good are skipped
// also (only in score mode): nzb-files having success-duplicates in dup-history but don't having duplicates in recent history are skipped
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
((pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetNZBInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetNZBInfo()->GetFilteredContentHash())))
{
bSkip = true;
bSameContent = true;
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
((pNZBInfo->GetFullContentHash() > 0 &&
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetDupInfo()->GetFullContentHash()) ||
(pNZBInfo->GetFilteredContentHash() > 0 &&
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetDupInfo()->GetFilteredContentHash())))
{
bSkip = true;
bSameContent = true;
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
bSkip = true;
bGood = true;
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
break;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood ||
(pNZBInfo->GetDupeMode() == dmScore &&
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess &&
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetDupInfo()->GetDupeScore())) &&
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
bSkip = true;
bGood = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
break;
}
}
if (!bSameContent && !bGood && pNZBInfo->GetDupeMode() == dmScore)
{
// nzb-files having success-duplicates in recent history (with different content) are added to history for backup
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()) &&
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetNZBInfo()->GetDupeScore() &&
IsDupeSuccess(pHistoryInfo->GetNZBInfo()))
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pHistoryInfo->GetNZBInfo()->GetName());
return;
}
}
}
if (bSkip)
{
if (!strcmp(pNZBInfo->GetName(), szDupeName))
{
warn("Skipping duplicate %s, found in history with %s", pNZBInfo->GetName(),
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
else
{
warn("Skipping duplicate %s, found in history %s with %s",
pNZBInfo->GetName(), szDupeName,
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
}
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
return;
}
// find duplicates in download queue and post-queue and handle both items according to their scores:
// only one item remains in queue and another one is moved to history as dupe-backup
if (pNZBInfo->GetDupeMode() == dmScore)
{
// find duplicates in download queue
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
if (pGroupNZBInfo != pNZBInfo &&
pGroupNZBInfo->GetDupeMode() != dmForce &&
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
// if queue has a duplicate with the same or higher score - the new item
// is moved to history as dupe-backup
if (pNZBInfo->GetDupeScore() <= pGroupNZBInfo->GetDupeScore())
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pGroupNZBInfo->GetName());
return;
}
// if queue has a duplicate with lower score - the existing item is moved
// to history as dupe-backup (unless it is in post-processing stage) and
// the new item is added to queue
else
{
// unless it is in post-processing stage
bool bPostProcess = false;
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
if (pPostInfo->GetNZBInfo() == pGroupNZBInfo)
{
bPostProcess = true;
break;
}
}
if (!bPostProcess)
{
// the existing queue item is moved to history as dupe-backup
info("Moving collection %s with lower duplicate score to history", pGroupNZBInfo->GetName());
pGroupNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pGroupInfo->GetLastID(), false, QueueEditor::eaGroupDelete, 0, NULL);
}
}
}
}
// find duplicates in post queue
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
// if queue has a duplicate with the same or higher score - the new item
// is moved to history as dupe-backup;
if (pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pNZBInfo->GetDupeScore() <= pPostInfo->GetNZBInfo()->GetDupeScore() &&
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(),
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
{
// Flag saying QueueCoordinator to skip nzb-file
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
return;
}
}
}
}
/**
- if download of an item fails and there are duplicates in history -
return the best duplicate from historyto queue for download;
- if download of an item completes successfully - nothing extra needs to be done;
*/
void DupeCoordinator::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
debug("Processing duplicates for %s", pNZBInfo->GetName());
if (pNZBInfo->GetDupeMode() == dmScore && !IsDupeSuccess(pNZBInfo))
{
ReturnBestDupe(pDownloadQueue, pNZBInfo, pNZBInfo->GetName(), pNZBInfo->GetDupeKey());
}
}
/**
Returns the best duplicate from history to download queue.
*/
void DupeCoordinator::ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey)
{
// check if history (recent or dup) has other success-duplicates or good-duplicates
bool bHistoryDupe = false;
int iHistoryScore = 0;
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
bool bGoodDupe = false;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
(IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood) &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
if (!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore)
{
iHistoryScore = pHistoryInfo->GetNZBInfo()->GetDupeScore();
}
bHistoryDupe = true;
bGoodDupe = pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood;
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess ||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood) &&
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
if (!bHistoryDupe || pHistoryInfo->GetDupInfo()->GetDupeScore() > iHistoryScore)
{
iHistoryScore = pHistoryInfo->GetDupInfo()->GetDupeScore();
}
bHistoryDupe = true;
bGoodDupe = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
}
if (bGoodDupe)
{
// another duplicate with good-status exists - exit without moving other dupes to queue
return;
}
}
// check if duplicates exist in post-processing queue
bool bPostDupe = false;
int iPostScore = 0;
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
{
PostInfo* pPostInfo = *it;
if (pPostInfo->GetNZBInfo() != pNZBInfo &&
pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey) &&
(!bPostDupe || pPostInfo->GetNZBInfo()->GetDupeScore() > iPostScore))
{
iPostScore = pPostInfo->GetNZBInfo()->GetDupeScore();
bPostDupe = true;
}
}
// check if duplicates exist in download queue
GroupQueue groupQueue;
pDownloadQueue->BuildGroups(&groupQueue);
bool bQueueDupe = false;
int iQueueScore = 0;
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
{
GroupInfo* pGroupInfo = *it;
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
if (pGroupNZBInfo != pNZBInfo &&
pGroupNZBInfo->GetDupeMode() != dmForce &&
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(), szNZBName, szDupeKey) &&
(!bQueueDupe || pGroupNZBInfo->GetDupeScore() > iQueueScore))
{
iQueueScore = pGroupNZBInfo->GetDupeScore();
bQueueDupe = true;
}
}
// find dupe-backup with highest score, whose score is also higher than other
// success-duplicates and higher than already queued items
HistoryInfo* pHistoryDupe = NULL;
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
pHistoryInfo->GetNZBInfo()->CalcHealth() >= pHistoryInfo->GetNZBInfo()->CalcCriticalHealth() &&
pHistoryInfo->GetNZBInfo()->GetMarkStatus() != NZBInfo::ksBad &&
(!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore) &&
(!bPostDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iPostScore) &&
(!bQueueDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iQueueScore) &&
(!pHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > pHistoryDupe->GetNZBInfo()->GetDupeScore()) &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
pHistoryDupe = pHistoryInfo;
}
}
// move that dupe-backup from history to download queue
if (pHistoryDupe)
{
info("Found duplicate %s for %s", pHistoryDupe->GetNZBInfo()->GetName(), szNZBName);
HistoryRedownload(pDownloadQueue, pHistoryDupe);
}
}
void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood)
{
char szNZBName[1024];
pHistoryInfo->GetName(szNZBName, 1024);
info("Marking %s as %s", szNZBName, (bGood ? "good" : "bad"));
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
pHistoryInfo->GetNZBInfo()->SetMarkStatus(bGood ? NZBInfo::ksGood : NZBInfo::ksBad);
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo)
{
pHistoryInfo->GetDupInfo()->SetStatus(bGood ? DupInfo::dsGood : DupInfo::dsBad);
}
else
{
error("Could not mark %s as bad: history item has wrong type", szNZBName);
return;
}
if (!g_pOptions->GetDupeCheck() ||
(pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() == dmForce) ||
(pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
pHistoryInfo->GetDupInfo()->GetDupeMode() == dmForce))
{
return;
}
if (bGood)
{
// mark as good
// moving all duplicates from history to dup-history
HistoryCleanup(pDownloadQueue, pHistoryInfo);
}
else
{
// mark as bad
const char* szDupeKey = pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pHistoryInfo->GetNZBInfo()->GetDupeKey() :
pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pHistoryInfo->GetDupInfo()->GetDupeKey() :
NULL;
ReturnBestDupe(pDownloadQueue, NULL, szNZBName, szDupeKey);
}
}
void DupeCoordinator::HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo)
{
const char* szDupeKey = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetDupeKey() :
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetDupeKey() :
NULL;
const char* szNZBName = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetName() :
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetName() :
NULL;
bool bChanged = false;
int index = 0;
// traversing in a reverse order to delete items in order they were added to history
// (just to produce the log-messages in a more logical order)
for (HistoryList::reverse_iterator it = pDownloadQueue->GetHistoryList()->rbegin(); it != pDownloadQueue->GetHistoryList()->rend(); )
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
pHistoryInfo != pMarkHistoryInfo &&
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
{
HistoryTransformToDup(pDownloadQueue, pHistoryInfo, index);
index++;
it = pDownloadQueue->GetHistoryList()->rbegin() + index;
bChanged = true;
}
else
{
it++;
index++;
}
}
if (bChanged && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
}
void DupeCoordinator::HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
// replace history element
DupInfo* pDupInfo = new DupInfo();
pDupInfo->SetName(pHistoryInfo->GetNZBInfo()->GetName());
pDupInfo->SetDupeKey(pHistoryInfo->GetNZBInfo()->GetDupeKey());
pDupInfo->SetDupeScore(pHistoryInfo->GetNZBInfo()->GetDupeScore());
pDupInfo->SetDupeMode(pHistoryInfo->GetNZBInfo()->GetDupeMode());
pDupInfo->SetSize(pHistoryInfo->GetNZBInfo()->GetSize());
pDupInfo->SetFullContentHash(pHistoryInfo->GetNZBInfo()->GetFullContentHash());
pDupInfo->SetFilteredContentHash(pHistoryInfo->GetNZBInfo()->GetFilteredContentHash());
pDupInfo->SetStatus(
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood ? DupInfo::dsGood :
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad ? DupInfo::dsBad :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe ? DupInfo::dsDupe :
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsManual ? DupInfo::dsDeleted :
IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ? DupInfo::dsSuccess :
DupInfo::dsFailed);
HistoryInfo* pNewHistoryInfo = new HistoryInfo(pDupInfo);
pNewHistoryInfo->SetTime(pHistoryInfo->GetTime());
(*pDownloadQueue->GetHistoryList())[pDownloadQueue->GetHistoryList()->size() - 1 - rindex] = pNewHistoryInfo;
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
delete pHistoryInfo;
info("Collection %s removed from history", szNiceName);
}

View File

@@ -1,53 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef DUPECOORDINATOR_H
#define DUPECOORDINATOR_H
#include <deque>
#include "DownloadInfo.h"
class DupeCoordinator
{
private:
bool IsDupeSuccess(NZBInfo* pNZBInfo);
void ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey);
void HistoryReturnDupe(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
void HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo);
bool SameNameOrKey(const char* szName1, const char* szDupeKey1, const char* szName2, const char* szDupeKey2);
protected:
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo) = 0;
virtual void DeleteQueuedFile(const char* szQueuedFile) = 0;
public:
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood);
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
};
#endif

View File

@@ -1,742 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#ifndef WIN32
#include <unistd.h>
#include <sys/time.h>
#endif
#include "nzbget.h"
#include "FeedCoordinator.h"
#include "Options.h"
#include "WebDownloader.h"
#include "Log.h"
#include "Util.h"
#include "FeedFile.h"
#include "FeedFilter.h"
#include "UrlCoordinator.h"
#include "DiskState.h"
extern Options* g_pOptions;
extern UrlCoordinator* g_pUrlCoordinator;
extern DiskState* g_pDiskState;
FeedCoordinator::FeedCacheItem::FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
time_t tLastUsage, FeedItemInfos* pFeedItemInfos)
{
m_szUrl = strdup(szUrl);
m_iCacheTimeSec = iCacheTimeSec;
m_szCacheId = strdup(szCacheId);
m_tLastUsage = tLastUsage;
m_pFeedItemInfos = pFeedItemInfos;
m_pFeedItemInfos->Retain();
}
FeedCoordinator::FeedCacheItem::~FeedCacheItem()
{
free(m_szUrl);
free(m_szCacheId);
m_pFeedItemInfos->Release();
}
FeedCoordinator::FeedCoordinator()
{
debug("Creating FeedCoordinator");
m_bForce = false;
m_bSave = false;
m_UrlCoordinatorObserver.m_pOwner = this;
g_pUrlCoordinator->Attach(&m_UrlCoordinatorObserver);
}
FeedCoordinator::~FeedCoordinator()
{
debug("Destroying FeedCoordinator");
// Cleanup
debug("Deleting FeedDownloaders");
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
delete *it;
}
m_ActiveDownloads.clear();
debug("Deleting Feeds");
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
delete *it;
}
m_Feeds.clear();
debug("Deleting FeedCache");
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
{
delete *it;
}
m_FeedCache.clear();
debug("FeedCoordinator destroyed");
}
void FeedCoordinator::AddFeed(FeedInfo* pFeedInfo)
{
m_Feeds.push_back(pFeedInfo);
}
void FeedCoordinator::Run()
{
debug("Entering FeedCoordinator-loop");
m_mutexDownloads.Lock();
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pOptions->GetReloadQueue())
{
g_pDiskState->LoadFeeds(&m_Feeds, &m_FeedHistory);
}
m_mutexDownloads.Unlock();
int iSleepInterval = 100;
int iUpdateCounter = 0;
int iCleanupCounter = 60000;
while (!IsStopped())
{
usleep(iSleepInterval * 1000);
iUpdateCounter += iSleepInterval;
if (iUpdateCounter >= 1000)
{
// this code should not be called too often, once per second is OK
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
{
m_mutexDownloads.Lock();
time_t tCurrent = time(NULL);
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
{
m_bForce = false;
// check feed list and update feeds
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (((pFeedInfo->GetInterval() > 0 &&
(tCurrent - pFeedInfo->GetLastUpdate() >= pFeedInfo->GetInterval() * 60 ||
tCurrent < pFeedInfo->GetLastUpdate())) ||
pFeedInfo->GetFetch()) &&
pFeedInfo->GetStatus() != FeedInfo::fsRunning)
{
StartFeedDownload(pFeedInfo, pFeedInfo->GetFetch());
}
else if (pFeedInfo->GetFetch())
{
m_bForce = true;
}
}
}
m_mutexDownloads.Unlock();
}
CheckSaveFeeds();
ResetHangingDownloads();
iUpdateCounter = 0;
}
iCleanupCounter += iSleepInterval;
if (iCleanupCounter >= 60000)
{
// clean up feed history once a minute
CleanupHistory();
CleanupCache();
CheckSaveFeeds();
iCleanupCounter = 0;
}
}
// waiting for downloads
debug("FeedCoordinator: waiting for Downloads to complete");
bool completed = false;
while (!completed)
{
m_mutexDownloads.Lock();
completed = m_ActiveDownloads.size() == 0;
m_mutexDownloads.Unlock();
CheckSaveFeeds();
usleep(100 * 1000);
ResetHangingDownloads();
}
debug("FeedCoordinator: Downloads are completed");
debug("Exiting FeedCoordinator-loop");
}
void FeedCoordinator::Stop()
{
Thread::Stop();
debug("Stopping UrlDownloads");
m_mutexDownloads.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
(*it)->Stop();
}
m_mutexDownloads.Unlock();
debug("UrlDownloads are notified");
}
void FeedCoordinator::ResetHangingDownloads()
{
const int TimeOut = g_pOptions->GetTerminateTimeout();
if (TimeOut == 0)
{
return;
}
m_mutexDownloads.Lock();
time_t tm = ::time(NULL);
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
{
FeedDownloader* pFeedDownloader = *it;
if (tm - pFeedDownloader->GetLastUpdateTime() > TimeOut &&
pFeedDownloader->GetStatus() == FeedDownloader::adRunning)
{
debug("Terminating hanging download %s", pFeedDownloader->GetInfoName());
if (pFeedDownloader->Terminate())
{
error("Terminated hanging download %s", pFeedDownloader->GetInfoName());
pFeedDownloader->GetFeedInfo()->SetStatus(FeedInfo::fsUndefined);
}
else
{
error("Could not terminate hanging download %s", pFeedDownloader->GetInfoName());
}
m_ActiveDownloads.erase(it);
// it's not safe to destroy pFeedDownloader, because the state of object is unknown
delete pFeedDownloader;
it = m_ActiveDownloads.begin();
continue;
}
it++;
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::LogDebugInfo()
{
debug(" FeedCoordinator");
debug(" ----------------");
m_mutexDownloads.Lock();
debug(" Active Downloads: %i", m_ActiveDownloads.size());
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
FeedDownloader* pFeedDownloader = *it;
pFeedDownloader->LogDebugInfo();
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::StartFeedDownload(FeedInfo* pFeedInfo, bool bForce)
{
debug("Starting new FeedDownloader for %", pFeedInfo->GetName());
FeedDownloader* pFeedDownloader = new FeedDownloader();
pFeedDownloader->SetAutoDestroy(true);
pFeedDownloader->Attach(this);
pFeedDownloader->SetFeedInfo(pFeedInfo);
pFeedDownloader->SetURL(pFeedInfo->GetUrl());
if (strlen(pFeedInfo->GetName()) > 0)
{
pFeedDownloader->SetInfoName(pFeedInfo->GetName());
}
else
{
char szUrlName[1024];
UrlInfo::MakeNiceName(pFeedInfo->GetUrl(), "", szUrlName, sizeof(szUrlName));
pFeedDownloader->SetInfoName(szUrlName);
}
pFeedDownloader->SetForce(bForce || g_pOptions->GetUrlForce());
char tmp[1024];
if (pFeedInfo->GetID() > 0)
{
snprintf(tmp, 1024, "%sfeed-%i.tmp", g_pOptions->GetTempDir(), pFeedInfo->GetID());
}
else
{
snprintf(tmp, 1024, "%sfeed-%i-%i.tmp", g_pOptions->GetTempDir(), (int)time(NULL), rand());
}
tmp[1024-1] = '\0';
pFeedDownloader->SetOutputFilename(tmp);
pFeedInfo->SetStatus(FeedInfo::fsRunning);
pFeedInfo->SetForce(bForce);
pFeedInfo->SetFetch(false);
m_ActiveDownloads.push_back(pFeedDownloader);
pFeedDownloader->Start();
}
void FeedCoordinator::Update(Subject* pCaller, void* pAspect)
{
debug("Notification from FeedDownloader received");
FeedDownloader* pFeedDownloader = (FeedDownloader*) pCaller;
if ((pFeedDownloader->GetStatus() == WebDownloader::adFinished) ||
(pFeedDownloader->GetStatus() == WebDownloader::adFailed) ||
(pFeedDownloader->GetStatus() == WebDownloader::adRetry))
{
FeedCompleted(pFeedDownloader);
}
}
void FeedCoordinator::FeedCompleted(FeedDownloader* pFeedDownloader)
{
debug("Feed downloaded");
FeedInfo* pFeedInfo = pFeedDownloader->GetFeedInfo();
bool bStatusOK = pFeedDownloader->GetStatus() == WebDownloader::adFinished;
if (bStatusOK)
{
pFeedInfo->SetOutputFilename(pFeedDownloader->GetOutputFilename());
}
// delete Download from Queue
m_mutexDownloads.Lock();
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
FeedDownloader* pa = *it;
if (pa == pFeedDownloader)
{
m_ActiveDownloads.erase(it);
break;
}
}
m_mutexDownloads.Unlock();
if (bStatusOK)
{
if (!pFeedInfo->GetPreview())
{
FeedFile* pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
remove(pFeedInfo->GetOutputFilename());
m_mutexDownloads.Lock();
if (pFeedFile)
{
ProcessFeed(pFeedInfo, pFeedFile->GetFeedItemInfos());
delete pFeedFile;
}
pFeedInfo->SetLastUpdate(time(NULL));
pFeedInfo->SetForce(false);
m_bSave = true;
m_mutexDownloads.Unlock();
}
pFeedInfo->SetStatus(FeedInfo::fsFinished);
}
else
{
pFeedInfo->SetStatus(FeedInfo::fsFailed);
}
}
void FeedCoordinator::FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
{
debug("Filtering feed %s", pFeedInfo->GetName());
FeedFilter* pFeedFilter = NULL;
if (pFeedInfo->GetFilter() && strlen(pFeedInfo->GetFilter()) > 0)
{
pFeedFilter = new FeedFilter(pFeedInfo->GetFilter());
}
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
pFeedItemInfo->SetMatchStatus(FeedItemInfo::msAccepted);
pFeedItemInfo->SetMatchRule(0);
pFeedItemInfo->SetPauseNzb(pFeedInfo->GetPauseNzb());
pFeedItemInfo->SetPriority(pFeedInfo->GetPriority());
pFeedItemInfo->SetAddCategory(pFeedInfo->GetCategory());
pFeedItemInfo->SetDupeScore(0);
pFeedItemInfo->SetDupeMode(dmScore);
pFeedItemInfo->BuildDupeKey(NULL, NULL);
if (pFeedFilter)
{
pFeedFilter->Match(pFeedItemInfo);
}
}
delete pFeedFilter;
}
void FeedCoordinator::ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
{
debug("Process feed %s", pFeedInfo->GetName());
FilterFeed(pFeedInfo, pFeedItemInfos);
bool bFirstFetch = pFeedInfo->GetLastUpdate() == 0;
int iAdded = 0;
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
if (pFeedItemInfo->GetMatchStatus() == FeedItemInfo::msAccepted)
{
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
FeedHistoryInfo::EStatus eStatus = FeedHistoryInfo::hsUnknown;
if (bFirstFetch)
{
eStatus = FeedHistoryInfo::hsBacklog;
}
else if (!pFeedHistoryInfo)
{
DownloadItem(pFeedInfo, pFeedItemInfo);
eStatus = FeedHistoryInfo::hsFetched;
iAdded++;
}
if (pFeedHistoryInfo)
{
pFeedHistoryInfo->SetLastSeen(time(NULL));
}
else
{
m_FeedHistory.Add(pFeedItemInfo->GetUrl(), eStatus, time(NULL));
}
}
}
if (iAdded)
{
info("%s has %i new item(s)", pFeedInfo->GetName(), iAdded);
}
else
{
detail("%s has no new items", pFeedInfo->GetName());
}
}
void FeedCoordinator::DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo)
{
debug("Download %s from %s", pFeedItemInfo->GetUrl(), pFeedInfo->GetName());
UrlInfo* pUrlInfo = new UrlInfo();
pUrlInfo->SetURL(pFeedItemInfo->GetUrl());
// add .nzb-extension if not present
char szNZBName[1024];
strncpy(szNZBName, pFeedItemInfo->GetFilename(), 1024);
szNZBName[1024-1] = '\0';
char* ext = strrchr(szNZBName, '.');
if (ext && !strcasecmp(ext, ".nzb"))
{
*ext = '\0';
}
char szNZBName2[1024];
snprintf(szNZBName2, 1024, "%s.nzb", szNZBName);
Util::MakeValidFilename(szNZBName2, '_', false);
if (strlen(szNZBName) > 0)
{
pUrlInfo->SetNZBFilename(szNZBName2);
}
pUrlInfo->SetCategory(pFeedItemInfo->GetAddCategory());
pUrlInfo->SetPriority(pFeedItemInfo->GetPriority());
pUrlInfo->SetAddPaused(pFeedItemInfo->GetPauseNzb());
pUrlInfo->SetDupeKey(pFeedItemInfo->GetDupeKey());
pUrlInfo->SetDupeScore(pFeedItemInfo->GetDupeScore());
pUrlInfo->SetDupeMode(pFeedItemInfo->GetDupeMode());
pUrlInfo->SetForce(pFeedInfo->GetForce() || g_pOptions->GetUrlForce());
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, false);
}
bool FeedCoordinator::ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos)
{
if (iID < 1 || iID > (int)m_Feeds.size())
{
return false;
}
FeedInfo* pFeedInfo = m_Feeds.at(iID - 1);
return PreviewFeed(pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(), pFeedInfo->GetPriority(),
0, NULL, ppFeedItemInfos);
}
bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos)
{
debug("Preview feed %s", szName);
FeedInfo* pFeedInfo = new FeedInfo(0, szName, szUrl, 0, szFilter, bPauseNzb, szCategory, iPriority);
pFeedInfo->SetPreview(true);
FeedItemInfos* pFeedItemInfos = NULL;
bool bHasCache = false;
if (iCacheTimeSec > 0 && *szCacheId != '\0')
{
m_mutexDownloads.Lock();
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
{
FeedCacheItem* pFeedCacheItem = *it;
if (!strcmp(pFeedCacheItem->GetCacheId(), szCacheId))
{
pFeedCacheItem->SetLastUsage(time(NULL));
pFeedItemInfos = pFeedCacheItem->GetFeedItemInfos();
pFeedItemInfos->Retain();
bHasCache = true;
break;
}
}
m_mutexDownloads.Unlock();
}
if (!bHasCache)
{
m_mutexDownloads.Lock();
bool bFirstFetch = true;
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo2 = *it;
if (!strcmp(pFeedInfo2->GetUrl(), pFeedInfo->GetUrl()) &&
!strcmp(pFeedInfo2->GetFilter(), pFeedInfo->GetFilter()) &&
pFeedInfo2->GetLastUpdate() > 0)
{
bFirstFetch = false;
break;
}
}
StartFeedDownload(pFeedInfo, true);
m_mutexDownloads.Unlock();
// wait until the download in a separate thread completes
while (pFeedInfo->GetStatus() == FeedInfo::fsRunning)
{
usleep(100 * 1000);
}
// now can process the feed
FeedFile* pFeedFile = NULL;
if (pFeedInfo->GetStatus() == FeedInfo::fsFinished)
{
pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
}
remove(pFeedInfo->GetOutputFilename());
if (!pFeedFile)
{
delete pFeedInfo;
return false;
}
pFeedItemInfos = pFeedFile->GetFeedItemInfos();
pFeedItemInfos->Retain();
delete pFeedFile;
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
{
FeedItemInfo* pFeedItemInfo = *it;
pFeedItemInfo->SetStatus(bFirstFetch ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
if (pFeedHistoryInfo)
{
pFeedItemInfo->SetStatus((FeedItemInfo::EStatus)pFeedHistoryInfo->GetStatus());
}
}
}
FilterFeed(pFeedInfo, pFeedItemInfos);
delete pFeedInfo;
if (iCacheTimeSec > 0 && *szCacheId != '\0' && !bHasCache)
{
FeedCacheItem* pFeedCacheItem = new FeedCacheItem(szUrl, iCacheTimeSec, szCacheId, time(NULL), pFeedItemInfos);
m_mutexDownloads.Lock();
m_FeedCache.push_back(pFeedCacheItem);
m_mutexDownloads.Unlock();
}
*ppFeedItemInfos = pFeedItemInfos;
return true;
}
void FeedCoordinator::FetchFeed(int iID)
{
debug("FetchFeeds");
m_mutexDownloads.Lock();
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (pFeedInfo->GetID() == iID || iID == 0)
{
pFeedInfo->SetFetch(true);
m_bForce = true;
}
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::UrlCoordinatorUpdate(Subject* pCaller, void* pAspect)
{
debug("Notification from URL-Coordinator received");
UrlCoordinator::Aspect* pUrlAspect = (UrlCoordinator::Aspect*)pAspect;
if (pUrlAspect->eAction == UrlCoordinator::eaUrlCompleted)
{
m_mutexDownloads.Lock();
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pUrlAspect->pUrlInfo->GetURL());
if (pFeedHistoryInfo)
{
pFeedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
}
else
{
m_FeedHistory.Add(pUrlAspect->pUrlInfo->GetURL(), FeedHistoryInfo::hsFetched, time(NULL));
}
m_bSave = true;
m_mutexDownloads.Unlock();
}
}
bool FeedCoordinator::HasActiveDownloads()
{
m_mutexDownloads.Lock();
bool bActive = !m_ActiveDownloads.empty();
m_mutexDownloads.Unlock();
return bActive;
}
void FeedCoordinator::CheckSaveFeeds()
{
debug("CheckSaveFeeds");
m_mutexDownloads.Lock();
if (m_bSave)
{
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveFeeds(&m_Feeds, &m_FeedHistory);
}
m_bSave = false;
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::CleanupHistory()
{
debug("CleanupHistory");
m_mutexDownloads.Lock();
time_t tOldestUpdate = time(NULL);
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
{
FeedInfo* pFeedInfo = *it;
if (pFeedInfo->GetLastUpdate() < tOldestUpdate)
{
tOldestUpdate = pFeedInfo->GetLastUpdate();
}
}
time_t tBorderDate = tOldestUpdate - g_pOptions->GetFeedHistory();
int i = 0;
for (FeedHistory::iterator it = m_FeedHistory.begin(); it != m_FeedHistory.end(); )
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (pFeedHistoryInfo->GetLastSeen() < tBorderDate)
{
detail("Deleting %s from feed history", pFeedHistoryInfo->GetUrl());
delete pFeedHistoryInfo;
m_FeedHistory.erase(it);
it = m_FeedHistory.begin() + i;
m_bSave = true;
}
else
{
it++;
i++;
}
}
m_mutexDownloads.Unlock();
}
void FeedCoordinator::CleanupCache()
{
debug("CleanupCache");
m_mutexDownloads.Lock();
time_t tCurTime = time(NULL);
int i = 0;
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); )
{
FeedCacheItem* pFeedCacheItem = *it;
if (pFeedCacheItem->GetLastUsage() + pFeedCacheItem->GetCacheTimeSec() < tCurTime ||
pFeedCacheItem->GetLastUsage() > tCurTime)
{
debug("Deleting %s from feed cache", pFeedCacheItem->GetUrl());
delete pFeedCacheItem;
m_FeedCache.erase(it);
it = m_FeedCache.begin() + i;
}
else
{
it++;
i++;
}
}
m_mutexDownloads.Unlock();
}

View File

@@ -1,127 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDCOORDINATOR_H
#define FEEDCOORDINATOR_H
#include <deque>
#include <list>
#include <time.h>
#include "Thread.h"
#include "WebDownloader.h"
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "Observer.h"
class FeedDownloader;
class FeedCoordinator : public Thread, public Observer, public Subject
{
public:
typedef std::list<FeedDownloader*> ActiveDownloads;
private:
class UrlCoordinatorObserver: public Observer
{
public:
FeedCoordinator* m_pOwner;
virtual void Update(Subject* pCaller, void* pAspect) { m_pOwner->UrlCoordinatorUpdate(pCaller, pAspect); }
};
class FeedCacheItem
{
private:
char* m_szUrl;
int m_iCacheTimeSec;
char* m_szCacheId;
time_t m_tLastUsage;
FeedItemInfos* m_pFeedItemInfos;
public:
FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
time_t tLastUsage, FeedItemInfos* pFeedItemInfos);
~FeedCacheItem();
const char* GetUrl() { return m_szUrl; }
int GetCacheTimeSec() { return m_iCacheTimeSec; }
const char* GetCacheId() { return m_szCacheId; }
time_t GetLastUsage() { return m_tLastUsage; }
void SetLastUsage(time_t tLastUsage) { m_tLastUsage = tLastUsage; }
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
};
typedef std::deque<FeedCacheItem*> FeedCache;
private:
Feeds m_Feeds;
ActiveDownloads m_ActiveDownloads;
FeedHistory m_FeedHistory;
Mutex m_mutexDownloads;
UrlCoordinatorObserver m_UrlCoordinatorObserver;
bool m_bForce;
bool m_bSave;
FeedCache m_FeedCache;
void StartFeedDownload(FeedInfo* pFeedInfo, bool bForce);
void FeedCompleted(FeedDownloader* pFeedDownloader);
void FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
void ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
void DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo);
void ResetHangingDownloads();
void UrlCoordinatorUpdate(Subject* pCaller, void* pAspect);
void CleanupHistory();
void CleanupCache();
void CheckSaveFeeds();
public:
FeedCoordinator();
virtual ~FeedCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* pCaller, void* pAspect);
void AddFeed(FeedInfo* pFeedInfo);
bool PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
bool bPauseNzb, const char* szCategory, int iPriority,
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos);
bool ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos);
void FetchFeed(int iID);
bool HasActiveDownloads();
Feeds* GetFeeds() { return &m_Feeds; }
void LogDebugInfo();
};
class FeedDownloader : public WebDownloader
{
private:
FeedInfo* m_pFeedInfo;
public:
void SetFeedInfo(FeedInfo* pFeedInfo) { m_pFeedInfo = pFeedInfo; }
FeedInfo* GetFeedInfo() { return m_pFeedInfo; }
};
#endif

View File

@@ -1,609 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <string.h>
#include <list>
#ifdef WIN32
#include <comutil.h>
#import <msxml.tlb> named_guids
using namespace MSXML;
#else
#include <libxml/parser.h>
#include <libxml/xmlreader.h>
#include <libxml/xmlerror.h>
#include <libxml/entities.h>
#endif
#include "nzbget.h"
#include "FeedFile.h"
#include "Log.h"
#include "DownloadInfo.h"
#include "Options.h"
#include "Util.h"
extern Options* g_pOptions;
FeedFile::FeedFile(const char* szFileName)
{
debug("Creating FeedFile");
m_szFileName = strdup(szFileName);
m_pFeedItemInfos = new FeedItemInfos();
m_pFeedItemInfos->Retain();
#ifndef WIN32
m_pFeedItemInfo = NULL;
m_szTagContent = NULL;
m_iTagContentLen = 0;
#endif
}
FeedFile::~FeedFile()
{
debug("Destroying FeedFile");
// Cleanup
free(m_szFileName);
m_pFeedItemInfos->Release();
#ifndef WIN32
delete m_pFeedItemInfo;
free(m_szTagContent);
#endif
}
void FeedFile::LogDebugInfo()
{
debug(" FeedFile %s", m_szFileName);
}
void FeedFile::AddItem(FeedItemInfo* pFeedItemInfo)
{
m_pFeedItemInfos->Add(pFeedItemInfo);
}
void FeedFile::ParseSubject(FeedItemInfo* pFeedItemInfo)
{
// if title has quatation marks we use only part within quatation marks
char* p = (char*)pFeedItemInfo->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)
{
char* filename = (char*)malloc(len + 1);
strncpy(filename, start, len);
filename[len] = '\0';
char* ext = strrchr(filename, '.');
if (ext && !strcasecmp(ext, ".par2"))
{
*ext = '\0';
}
pFeedItemInfo->SetFilename(filename);
free(filename);
return;
}
}
}
pFeedItemInfo->SetFilename(pFeedItemInfo->GetTitle());
}
#ifdef WIN32
FeedFile* FeedFile::Create(const char* szFileName)
{
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);
// filename needs to be properly encoded
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
EncodeURL(szFileName, szURL);
debug("url=\"%s\"", szURL);
_variant_t v(szURL);
free(szURL);
VARIANT_BOOL success = doc->load(v);
if (success == VARIANT_FALSE)
{
_bstr_t r(doc->GetparseError()->reason);
const char* szErrMsg = r;
error("Error parsing rss feed: %s", szErrMsg);
return NULL;
}
FeedFile* pFile = new FeedFile(szFileName);
if (!pFile->ParseFeed(doc))
{
delete pFile;
pFile = NULL;
}
return pFile;
}
void FeedFile::EncodeURL(const char* szFilename, char* szURL)
{
while (char ch = *szFilename++)
{
if (('0' <= ch && ch <= '9') ||
('a' <= ch && ch <= 'z') ||
('A' <= ch && ch <= 'Z') )
{
*szURL++ = ch;
}
else
{
*szURL++ = '%';
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
}
}
*szURL = NULL;
}
bool 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);
FeedItemInfo* pFeedItemInfo = new FeedItemInfo();
AddItem(pFeedItemInfo);
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());
pFeedItemInfo->SetTitle(title);
ParseSubject(pFeedItemInfo);
// <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)
{
pFeedItemInfo->SetTime(unixtime);
}
}
// <category>Movies &gt; HD</category>
tag = node->selectSingleNode("category");
if (tag)
{
_bstr_t category(tag->Gettext());
pFeedItemInfo->SetCategory(category);
}
// <description>long text</description>
tag = node->selectSingleNode("description");
if (tag)
{
_bstr_t description(tag->Gettext());
pFeedItemInfo->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());
pFeedItemInfo->SetUrl(url);
}
attr = tag->Getattributes()->getNamedItem("length");
if (attr)
{
_bstr_t size(attr->Gettext());
long long lSize = atoll(size);
pFeedItemInfo->SetSize(lSize);
}
}
if (!pFeedItemInfo->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());
pFeedItemInfo->SetUrl(link);
}
// newznab special
//<newznab:attr name="size" value="5423523453534" />
if (pFeedItemInfo->GetSize() == 0)
{
tag = node->selectSingleNode("newznab:attr[@name='size']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t size(attr->Gettext());
long long lSize = atoll(size);
pFeedItemInfo->SetSize(lSize);
}
}
}
//<newznab:attr name="imdb" value="1588173"/>
tag = node->selectSingleNode("newznab:attr[@name='imdb']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
int iVal = atoi(val);
pFeedItemInfo->SetImdbId(iVal);
}
}
//<newznab:attr name="rageid" value="33877"/>
tag = node->selectSingleNode("newznab:attr[@name='rageid']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
int iVal = atoi(val);
pFeedItemInfo->SetRageId(iVal);
}
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
tag = node->selectSingleNode("newznab:attr[@name='episode']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
pFeedItemInfo->SetEpisode(val);
}
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
tag = node->selectSingleNode("newznab:attr[@name='season']");
if (tag)
{
attr = tag->Getattributes()->getNamedItem("value");
if (attr)
{
_bstr_t val(attr->Gettext());
pFeedItemInfo->SetSeason(val);
}
}
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab: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 name(name->Gettext());
_bstr_t val(value->Gettext());
pFeedItemInfo->GetAttributes()->Add(name, val);
}
}
}
return true;
}
#else
FeedFile* FeedFile::Create(const char* szFileName)
{
FeedFile* pFile = new FeedFile(szFileName);
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);
pFile->m_bIgnoreNextError = false;
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
if (ret != 0)
{
error("Failed to parse rss feed");
delete pFile;
pFile = NULL;
}
return pFile;
}
void FeedFile::Parse_StartElement(const char *name, const char **atts)
{
ResetTagContent();
if (!strcmp("item", name))
{
delete m_pFeedItemInfo;
m_pFeedItemInfo = new FeedItemInfo();
}
else if (!strcmp("enclosure", name) && m_pFeedItemInfo)
{
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
for (; *atts; atts+=2)
{
if (!strcmp("url", atts[0]))
{
char* szUrl = strdup(atts[1]);
WebUtil::XmlDecode(szUrl);
m_pFeedItemInfo->SetUrl(szUrl);
free(szUrl);
}
else if (!strcmp("length", atts[0]))
{
long long lSize = atoll(atts[1]);
m_pFeedItemInfo->SetSize(lSize);
}
}
}
else if (m_pFeedItemInfo && !strcmp("newznab:attr", name) &&
atts[0] && atts[1] && atts[2] && atts[3] &&
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
{
m_pFeedItemInfo->GetAttributes()->Add(atts[1], atts[3]);
//<newznab:attr name="size" value="5423523453534" />
if (m_pFeedItemInfo->GetSize() == 0 &&
!strcmp("size", atts[1]))
{
long long lSize = atoll(atts[3]);
m_pFeedItemInfo->SetSize(lSize);
}
//<newznab:attr name="imdb" value="1588173"/>
else if (!strcmp("imdb", atts[1]))
{
m_pFeedItemInfo->SetImdbId(atoi(atts[3]));
}
//<newznab:attr name="rageid" value="33877"/>
else if (!strcmp("rageid", atts[1]))
{
m_pFeedItemInfo->SetRageId(atoi(atts[3]));
}
//<newznab:attr name="episode" value="E09"/>
//<newznab:attr name="episode" value="9"/>
else if (!strcmp("episode", atts[1]))
{
m_pFeedItemInfo->SetEpisode(atts[3]);
}
//<newznab:attr name="season" value="S03"/>
//<newznab:attr name="season" value="3"/>
else if (!strcmp("season", atts[1]))
{
m_pFeedItemInfo->SetSeason(atts[3]);
}
}
}
void FeedFile::Parse_EndElement(const char *name)
{
if (!strcmp("item", name))
{
// Close the file element, add the new file to file-list
AddItem(m_pFeedItemInfo);
m_pFeedItemInfo = NULL;
}
else if (!strcmp("title", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetTitle(m_szTagContent);
ResetTagContent();
ParseSubject(m_pFeedItemInfo);
}
else if (!strcmp("link", name) && m_pFeedItemInfo &&
(!m_pFeedItemInfo->GetUrl() || strlen(m_pFeedItemInfo->GetUrl()) == 0))
{
m_pFeedItemInfo->SetUrl(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("category", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetCategory(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("description", name) && m_pFeedItemInfo)
{
m_pFeedItemInfo->SetDescription(m_szTagContent);
ResetTagContent();
}
else if (!strcmp("pubDate", name) && m_pFeedItemInfo)
{
time_t unixtime = WebUtil::ParseRfc822DateTime(m_szTagContent);
if (unixtime > 0)
{
m_pFeedItemInfo->SetTime(unixtime);
}
ResetTagContent();
}
}
void FeedFile::Parse_Content(const char *buf, int len)
{
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
m_iTagContentLen += len;
m_szTagContent[m_iTagContentLen] = '\0';
}
void FeedFile::ResetTagContent()
{
free(m_szTagContent);
m_szTagContent = NULL;
m_iTagContentLen = 0;
}
void FeedFile::SAX_StartElement(FeedFile* pFile, const char *name, const char **atts)
{
pFile->Parse_StartElement(name, atts);
}
void FeedFile::SAX_EndElement(FeedFile* pFile, const char *name)
{
pFile->Parse_EndElement(name);
}
void FeedFile::SAX_characters(FeedFile* pFile, 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
pFile->Parse_Content(str + off, newlen);
}
}
void* FeedFile::SAX_getEntity(FeedFile* pFile, const char * name)
{
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
if (!e)
{
warn("entity not found");
pFile->m_bIgnoreNextError = true;
}
return e;
}
void FeedFile::SAX_error(FeedFile* pFile, const char *msg, ...)
{
if (pFile->m_bIgnoreNextError)
{
pFile->m_bIgnoreNextError = false;
return;
}
va_list argp;
va_start(argp, msg);
char szErrMsg[1024];
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
szErrMsg[1024-1] = '\0';
va_end(argp);
// remove trailing CRLF
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
error("Error parsing rss feed: %s", szErrMsg);
}
#endif

View File

@@ -1,70 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDFILE_H
#define FEEDFILE_H
#include <vector>
#include "FeedInfo.h"
class FeedFile
{
private:
FeedItemInfos* m_pFeedItemInfos;
char* m_szFileName;
FeedFile(const char* szFileName);
void AddItem(FeedItemInfo* pFeedItemInfo);
void ParseSubject(FeedItemInfo* pFeedItemInfo);
#ifdef WIN32
bool ParseFeed(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
#else
FeedItemInfo* m_pFeedItemInfo;
char* m_szTagContent;
int m_iTagContentLen;
bool m_bIgnoreNextError;
static void SAX_StartElement(FeedFile* pFile, const char *name, const char **atts);
static void SAX_EndElement(FeedFile* pFile, const char *name);
static void SAX_characters(FeedFile* pFile, const char * xmlstr, int len);
static void* SAX_getEntity(FeedFile* pFile, const char * name);
static void SAX_error(FeedFile* pFile, 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
public:
virtual ~FeedFile();
static FeedFile* Create(const char* szFileName);
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
void LogDebugInfo();
};
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,186 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef FEEDFILTER_H
#define FEEDFILTER_H
#include "DownloadInfo.h"
#include "FeedInfo.h"
#include "Util.h"
class FeedFilter
{
private:
typedef std::deque<char*> RefValues;
enum ETermCommand
{
fcText,
fcRegex,
fcEqual,
fcLess,
fcLessEqual,
fcGreater,
fcGreaterEqual,
fcOpeningBrace,
fcClosingBrace,
fcOrOperator
};
class Term
{
private:
bool m_bPositive;
char* m_szField;
ETermCommand m_eCommand;
char* m_szParam;
long long m_iIntParam;
double m_fFloatParam;
bool m_bFloat;
RegEx* m_pRegEx;
RefValues* m_pRefValues;
bool GetFieldData(const char* szField, FeedItemInfo* pFeedItemInfo,
const char** StrValue, long long* IntValue);
bool ParseParam(const char* szField, const char* szParam);
bool ParseSizeParam(const char* szParam);
bool ParseAgeParam(const char* szParam);
bool ParseNumericParam(const char* szParam);
bool MatchValue(const char* szStrValue, long long iIntValue);
bool MatchText(const char* szStrValue);
bool MatchRegex(const char* szStrValue);
void FillWildMaskRefValues(const char* szStrValue, WildMask* pMask, int iRefOffset);
void FillRegExRefValues(const char* szStrValue, RegEx* pRegEx);
public:
Term();
~Term();
void SetRefValues(RefValues* pRefValues) { m_pRefValues = pRefValues; }
bool Compile(char* szToken);
bool Match(FeedItemInfo* pFeedItemInfo);
ETermCommand GetCommand() { return m_eCommand; }
};
typedef std::deque<Term*> TermList;
enum ERuleCommand
{
frAccept,
frReject,
frRequire,
frOptions,
frComment
};
class Rule
{
private:
bool m_bIsValid;
ERuleCommand m_eCommand;
char* m_szCategory;
int m_iPriority;
int m_iAddPriority;
bool m_bPause;
int m_iDupeScore;
int m_iAddDupeScore;
char* m_szDupeKey;
char* m_szAddDupeKey;
EDupeMode m_eDupeMode;
char* m_szSeries;
char* m_szRageId;
bool m_bHasCategory;
bool m_bHasPriority;
bool m_bHasAddPriority;
bool m_bHasPause;
bool m_bHasDupeScore;
bool m_bHasAddDupeScore;
bool m_bHasDupeKey;
bool m_bHasAddDupeKey;
bool m_bHasDupeMode;
bool m_bPatCategory;
bool m_bPatDupeKey;
bool m_bPatAddDupeKey;
bool m_bHasSeries;
bool m_bHasRageId;
char* m_szPatCategory;
char* m_szPatDupeKey;
char* m_szPatAddDupeKey;
TermList m_Terms;
RefValues m_RefValues;
char* CompileCommand(char* szRule);
char* CompileOptions(char* szRule);
bool CompileTerm(char* szTerm);
bool MatchExpression(FeedItemInfo* pFeedItemInfo);
public:
Rule();
~Rule();
void Compile(char* szRule);
bool IsValid() { return m_bIsValid; }
ERuleCommand GetCommand() { return m_eCommand; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
int GetAddPriority() { return m_iAddPriority; }
bool GetPause() { return m_bPause; }
const char* GetDupeKey() { return m_szDupeKey; }
const char* GetAddDupeKey() { return m_szAddDupeKey; }
int GetDupeScore() { return m_iDupeScore; }
int GetAddDupeScore() { return m_iAddDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
const char* GetRageId() { return m_szRageId; }
const char* GetSeries() { return m_szSeries; }
bool HasCategory() { return m_bHasCategory; }
bool HasPriority() { return m_bHasPriority; }
bool HasAddPriority() { return m_bHasAddPriority; }
bool HasPause() { return m_bHasPause; }
bool HasDupeScore() { return m_bHasDupeScore; }
bool HasAddDupeScore() { return m_bHasAddDupeScore; }
bool HasDupeKey() { return m_bHasDupeKey; }
bool HasAddDupeKey() { return m_bHasAddDupeKey; }
bool HasDupeMode() { return m_bHasDupeMode; }
bool HasRageId() { return m_bHasRageId; }
bool HasSeries() { return m_bHasSeries; }
bool Match(FeedItemInfo* pFeedItemInfo);
void ExpandRefValues(FeedItemInfo* pFeedItemInfo, char** pDestStr, char* pPatStr);
const char* GetRefValue(FeedItemInfo* pFeedItemInfo, const char* szVarName);
};
typedef std::deque<Rule*> RuleList;
private:
RuleList m_Rules;
void Compile(const char* szFilter);
void CompileRule(char* szRule);
void ApplyOptions(Rule* pRule, FeedItemInfo* pFeedItemInfo);
public:
FeedFilter(const char* szFilter);
~FeedFilter();
void Match(FeedItemInfo* pFeedItemInfo);
};
#endif

View File

@@ -1,440 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision: 0 $
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/stat.h>
#include "nzbget.h"
#include "FeedInfo.h"
#include "Util.h"
FeedInfo::FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority)
{
m_iID = iID;
m_szName = strdup(szName ? szName : "");
m_szUrl = strdup(szUrl ? szUrl : "");
m_szFilter = strdup(szFilter ? szFilter : "");
m_iFilterHash = Util::HashBJ96(szFilter, strlen(szFilter), 0);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iInterval = iInterval;
m_bPauseNzb = bPauseNzb;
m_iPriority = iPriority;
m_tLastUpdate = 0;
m_bPreview = false;
m_eStatus = fsUndefined;
m_szOutputFilename = NULL;
m_bFetch = false;
m_bForce = false;
}
FeedInfo::~FeedInfo()
{
free(m_szName);
free(m_szUrl);
free(m_szFilter);
free(m_szCategory);
free(m_szOutputFilename);
}
void FeedInfo::SetOutputFilename(const char* szOutputFilename)
{
free(m_szOutputFilename);
m_szOutputFilename = strdup(szOutputFilename);
}
FeedItemInfo::Attr::Attr(const char* szName, const char* szValue)
{
m_szName = strdup(szName ? szName : "");
m_szValue = strdup(szValue ? szValue : "");
}
FeedItemInfo::Attr::~Attr()
{
free(m_szName);
free(m_szValue);
}
FeedItemInfo::Attributes::~Attributes()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
void FeedItemInfo::Attributes::Add(const char* szName, const char* szValue)
{
push_back(new Attr(szName, szValue));
}
FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* szName)
{
for (iterator it = begin(); it != end(); it++)
{
Attr* pAttr = *it;
if (!strcasecmp(pAttr->GetName(), szName))
{
return pAttr;
}
}
return NULL;
}
FeedItemInfo::FeedItemInfo()
{
m_pSharedFeedData = NULL;
m_szTitle = NULL;
m_szFilename = NULL;
m_szUrl = NULL;
m_szCategory = strdup("");
m_lSize = 0;
m_tTime = 0;
m_iImdbId = 0;
m_iRageId = 0;
m_szDescription = strdup("");
m_szSeason = NULL;
m_szEpisode = NULL;
m_iSeasonNum = 0;
m_iEpisodeNum = 0;
m_bSeasonEpisodeParsed = false;
m_szAddCategory = strdup("");
m_bPauseNzb = false;
m_iPriority = 0;
m_eStatus = isUnknown;
m_eMatchStatus = msIgnored;
m_iMatchRule = 0;
m_szDupeKey = NULL;
m_iDupeScore = 0;
m_eDupeMode = dmScore;
}
FeedItemInfo::~FeedItemInfo()
{
free(m_szTitle);
free(m_szFilename);
free(m_szUrl);
free(m_szCategory);
free(m_szDescription);
free(m_szSeason);
free(m_szEpisode);
free(m_szAddCategory);
free(m_szDupeKey);
}
void FeedItemInfo::SetTitle(const char* szTitle)
{
free(m_szTitle);
m_szTitle = szTitle ? strdup(szTitle) : NULL;
}
void FeedItemInfo::SetFilename(const char* szFilename)
{
free(m_szFilename);
m_szFilename = szFilename ? strdup(szFilename) : NULL;
}
void FeedItemInfo::SetUrl(const char* szUrl)
{
free(m_szUrl);
m_szUrl = szUrl ? strdup(szUrl) : NULL;
}
void FeedItemInfo::SetCategory(const char* szCategory)
{
free(m_szCategory);
m_szCategory = strdup(szCategory ? szCategory: "");
}
void FeedItemInfo::SetDescription(const char* szDescription)
{
free(m_szDescription);
m_szDescription = strdup(szDescription ? szDescription: "");
}
void FeedItemInfo::SetSeason(const char* szSeason)
{
free(m_szSeason);
m_szSeason = szSeason ? strdup(szSeason) : NULL;
m_iSeasonNum = szSeason ? ParsePrefixedInt(szSeason) : 0;
}
void FeedItemInfo::SetEpisode(const char* szEpisode)
{
free(m_szEpisode);
m_szEpisode = szEpisode ? strdup(szEpisode) : NULL;
m_iEpisodeNum = szEpisode ? ParsePrefixedInt(szEpisode) : 0;
}
int FeedItemInfo::ParsePrefixedInt(const char *szValue)
{
const char* szVal = szValue;
if (!strchr("0123456789", *szVal))
{
szVal++;
}
return atoi(szVal);
}
void FeedItemInfo::SetAddCategory(const char* szAddCategory)
{
free(m_szAddCategory);
m_szAddCategory = strdup(szAddCategory ? szAddCategory : "");
}
void FeedItemInfo::SetDupeKey(const char* szDupeKey)
{
free(m_szDupeKey);
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
}
void FeedItemInfo::AppendDupeKey(const char* szExtraDupeKey)
{
if (!m_szDupeKey || *m_szDupeKey == '\0' || !szExtraDupeKey || *szExtraDupeKey == '\0')
{
return;
}
int iLen = (m_szDupeKey ? strlen(m_szDupeKey) : 0) + 1 + strlen(szExtraDupeKey) + 1;
char* szNewKey = (char*)malloc(iLen);
snprintf(szNewKey, iLen, "%s-%s", m_szDupeKey, szExtraDupeKey);
szNewKey[iLen - 1] = '\0';
free(m_szDupeKey);
m_szDupeKey = szNewKey;
}
void FeedItemInfo::BuildDupeKey(const char* szRageId, const char* szSeries)
{
int iRageId = szRageId && *szRageId ? atoi(szRageId) : m_iRageId;
free(m_szDupeKey);
if (m_iImdbId != 0)
{
m_szDupeKey = (char*)malloc(20);
snprintf(m_szDupeKey, 20, "imdb=%i", m_iImdbId);
}
else if (szSeries && *szSeries && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
{
int iLen = strlen(szSeries) + 50;
m_szDupeKey = (char*)malloc(iLen);
snprintf(m_szDupeKey, iLen, "series=%s-%s-%s", szSeries, m_szSeason, m_szEpisode);
m_szDupeKey[iLen-1] = '\0';
}
else if (iRageId != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
{
m_szDupeKey = (char*)malloc(100);
snprintf(m_szDupeKey, 100, "rageid=%i-%s-%s", iRageId, m_szSeason, m_szEpisode);
m_szDupeKey[100-1] = '\0';
}
else
{
m_szDupeKey = strdup("");
}
}
int FeedItemInfo::GetSeasonNum()
{
if (!m_szSeason && !m_bSeasonEpisodeParsed)
{
ParseSeasonEpisode();
}
return m_iSeasonNum;
}
int FeedItemInfo::GetEpisodeNum()
{
if (!m_szEpisode && !m_bSeasonEpisodeParsed)
{
ParseSeasonEpisode();
}
return m_iEpisodeNum;
}
void FeedItemInfo::ParseSeasonEpisode()
{
m_bSeasonEpisodeParsed = true;
RegEx* pRegEx = m_pSharedFeedData->GetSeasonEpisodeRegEx();
if (pRegEx->Match(m_szTitle))
{
char szRegValue[100];
char szValue[100];
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + pRegEx->GetMatchStart(1)));
szValue[100-1] = '\0';
SetSeason(szValue);
int iLen = pRegEx->GetMatchLen(2);
iLen = iLen < 99 ? iLen : 99;
strncpy(szRegValue, m_szTitle + pRegEx->GetMatchStart(2), pRegEx->GetMatchLen(2));
szRegValue[iLen] = '\0';
snprintf(szValue, 100, "E%s", szRegValue);
szValue[100-1] = '\0';
Util::ReduceStr(szValue, "-", "");
for (char* p = szValue; *p; p++) *p = toupper(*p); // convert string to uppercase e02 -> E02
SetEpisode(szValue);
}
}
FeedHistoryInfo::FeedHistoryInfo(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
{
m_szUrl = szUrl ? strdup(szUrl) : NULL;
m_eStatus = eStatus;
m_tLastSeen = tLastSeen;
}
FeedHistoryInfo::~FeedHistoryInfo()
{
free(m_szUrl);
}
FeedHistory::~FeedHistory()
{
Clear();
}
void FeedHistory::Clear()
{
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
clear();
}
void FeedHistory::Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
{
push_back(new FeedHistoryInfo(szUrl, eStatus, tLastSeen));
}
void FeedHistory::Remove(const char* szUrl)
{
for (iterator it = begin(); it != end(); it++)
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
{
delete pFeedHistoryInfo;
erase(it);
break;
}
}
}
FeedHistoryInfo* FeedHistory::Find(const char* szUrl)
{
for (iterator it = begin(); it != end(); it++)
{
FeedHistoryInfo* pFeedHistoryInfo = *it;
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
{
return pFeedHistoryInfo;
}
}
return NULL;
}
FeedItemInfos::FeedItemInfos()
{
debug("Creating FeedItemInfos");
m_iRefCount = 0;
}
FeedItemInfos::~FeedItemInfos()
{
debug("Destroing FeedItemInfos");
for (iterator it = begin(); it != end(); it++)
{
delete *it;
}
}
void FeedItemInfos::Retain()
{
m_iRefCount++;
}
void FeedItemInfos::Release()
{
m_iRefCount--;
if (m_iRefCount <= 0)
{
delete this;
}
}
void FeedItemInfos::Add(FeedItemInfo* pFeedItemInfo)
{
push_back(pFeedItemInfo);
pFeedItemInfo->SetSharedFeedData(&m_SharedFeedData);
}
SharedFeedData::SharedFeedData()
{
m_pSeasonEpisodeRegEx = NULL;
}
SharedFeedData::~SharedFeedData()
{
delete m_pSeasonEpisodeRegEx;
}
RegEx* SharedFeedData::GetSeasonEpisodeRegEx()
{
if (!m_pSeasonEpisodeRegEx)
{
m_pSeasonEpisodeRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
}
return m_pSeasonEpisodeRegEx;
}

View File

@@ -1,278 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision: 0 $
* $Date$
*
*/
#ifndef FEEDINFO_H
#define FEEDINFO_H
#include <deque>
#include <time.h>
#include "Util.h"
#include "DownloadInfo.h"
class FeedInfo
{
public:
enum EStatus
{
fsUndefined,
fsRunning,
fsFinished,
fsFailed
};
private:
int m_iID;
char* m_szName;
char* m_szUrl;
int m_iInterval;
char* m_szFilter;
unsigned int m_iFilterHash;
bool m_bPauseNzb;
char* m_szCategory;
int m_iPriority;
time_t m_tLastUpdate;
bool m_bPreview;
EStatus m_eStatus;
char* m_szOutputFilename;
bool m_bFetch;
bool m_bForce;
public:
FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority);
~FeedInfo();
int GetID() { return m_iID; }
const char* GetName() { return m_szName; }
const char* GetUrl() { return m_szUrl; }
int GetInterval() { return m_iInterval; }
const char* GetFilter() { return m_szFilter; }
unsigned int GetFilterHash() { return m_iFilterHash; }
bool GetPauseNzb() { return m_bPauseNzb; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
time_t GetLastUpdate() { return m_tLastUpdate; }
void SetLastUpdate(time_t tLastUpdate) { m_tLastUpdate = tLastUpdate; }
bool GetPreview() { return m_bPreview; }
void SetPreview(bool bPreview) { m_bPreview = bPreview; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
const char* GetOutputFilename() { return m_szOutputFilename; }
void SetOutputFilename(const char* szOutputFilename);
bool GetFetch() { return m_bFetch; }
void SetFetch(bool bFetch) { m_bFetch = bFetch; }
bool GetForce() { return m_bForce; }
void SetForce(bool bForce) { m_bForce = bForce; }
};
typedef std::deque<FeedInfo*> Feeds;
class SharedFeedData
{
private:
RegEx* m_pSeasonEpisodeRegEx;
public:
SharedFeedData();
~SharedFeedData();
RegEx* GetSeasonEpisodeRegEx();
};
class FeedItemInfo
{
public:
enum EStatus
{
isUnknown,
isBacklog,
isFetched,
isNew
};
enum EMatchStatus
{
msIgnored,
msAccepted,
msRejected
};
class Attr
{
private:
char* m_szName;
char* m_szValue;
public:
Attr(const char* szName, const char* szValue);
~Attr();
const char* GetName() { return m_szName; }
const char* GetValue() { return m_szValue; }
};
typedef std::deque<Attr*> AttributesBase;
class Attributes: public AttributesBase
{
public:
~Attributes();
void Add(const char* szName, const char* szValue);
Attr* Find(const char* szName);
};
private:
char* m_szTitle;
char* m_szFilename;
char* m_szUrl;
time_t m_tTime;
long long m_lSize;
char* m_szCategory;
int m_iImdbId;
int m_iRageId;
char* m_szDescription;
char* m_szSeason;
char* m_szEpisode;
int m_iSeasonNum;
int m_iEpisodeNum;
bool m_bSeasonEpisodeParsed;
char* m_szAddCategory;
bool m_bPauseNzb;
int m_iPriority;
EStatus m_eStatus;
EMatchStatus m_eMatchStatus;
int m_iMatchRule;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
SharedFeedData* m_pSharedFeedData;
Attributes m_Attributes;
int ParsePrefixedInt(const char *szValue);
void ParseSeasonEpisode();
public:
FeedItemInfo();
~FeedItemInfo();
void SetSharedFeedData(SharedFeedData* pSharedFeedData) { m_pSharedFeedData = pSharedFeedData; }
const char* GetTitle() { return m_szTitle; }
void SetTitle(const char* szTitle);
const char* GetFilename() { return m_szFilename; }
void SetFilename(const char* szFilename);
const char* GetUrl() { return m_szUrl; }
void SetUrl(const char* szUrl);
long long GetSize() { return m_lSize; }
void SetSize(long long lSize) { m_lSize = lSize; }
const char* GetCategory() { return m_szCategory; }
void SetCategory(const char* szCategory);
int GetImdbId() { return m_iImdbId; }
void SetImdbId(int iImdbId) { m_iImdbId = iImdbId; }
int GetRageId() { return m_iRageId; }
void SetRageId(int iRageId) { m_iRageId = iRageId; }
const char* GetDescription() { return m_szDescription; }
void SetDescription(const char* szDescription);
const char* GetSeason() { return m_szSeason; }
void SetSeason(const char* szSeason);
const char* GetEpisode() { return m_szEpisode; }
void SetEpisode(const char* szEpisode);
int GetSeasonNum();
int GetEpisodeNum();
const char* GetAddCategory() { return m_szAddCategory; }
void SetAddCategory(const char* szAddCategory);
bool GetPauseNzb() { return m_bPauseNzb; }
void SetPauseNzb(bool bPauseNzb) { m_bPauseNzb = bPauseNzb; }
int GetPriority() { return m_iPriority; }
void SetPriority(int iPriority) { m_iPriority = iPriority; }
time_t GetTime() { return m_tTime; }
void SetTime(time_t tTime) { m_tTime = tTime; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
EMatchStatus GetMatchStatus() { return m_eMatchStatus; }
void SetMatchStatus(EMatchStatus eMatchStatus) { m_eMatchStatus = eMatchStatus; }
int GetMatchRule() { return m_iMatchRule; }
void SetMatchRule(int iMatchRule) { m_iMatchRule = iMatchRule; }
const char* GetDupeKey() { return m_szDupeKey; }
void SetDupeKey(const char* szDupeKey);
void AppendDupeKey(const char* szExtraDupeKey);
void BuildDupeKey(const char* szRageId, const char* szSeries);
int GetDupeScore() { return m_iDupeScore; }
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
Attributes* GetAttributes() { return &m_Attributes; }
};
typedef std::deque<FeedItemInfo*> FeedItemInfosBase;
class FeedItemInfos : public FeedItemInfosBase
{
private:
int m_iRefCount;
SharedFeedData m_SharedFeedData;
public:
FeedItemInfos();
~FeedItemInfos();
void Retain();
void Release();
void Add(FeedItemInfo* pFeedItemInfo);
};
class FeedHistoryInfo
{
public:
enum EStatus
{
hsUnknown,
hsBacklog,
hsFetched
};
private:
char* m_szUrl;
EStatus m_eStatus;
time_t m_tLastSeen;
public:
FeedHistoryInfo(const char* szUrl, EStatus eStatus, time_t tLastSeen);
~FeedHistoryInfo();
const char* GetUrl() { return m_szUrl; }
EStatus GetStatus() { return m_eStatus; }
void SetStatus(EStatus Status) { m_eStatus = Status; }
time_t GetLastSeen() { return m_tLastSeen; }
void SetLastSeen(time_t tLastSeen) { m_tLastSeen = tLastSeen; }
};
typedef std::deque<FeedHistoryInfo*> FeedHistoryBase;
class FeedHistory : public FeedHistoryBase
{
public:
~FeedHistory();
void Clear();
void Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen);
void Remove(const char* szUrl);
FeedHistoryInfo* Find(const char* szUrl);
};
#endif

14
Log.cpp
View File

@@ -60,7 +60,10 @@ Log::Log()
Log::~Log()
{
Clear();
free(m_szLogFilename);
if (m_szLogFilename)
{
free(m_szLogFilename);
}
}
void Log::Filelog(const char* msg, ...)
@@ -77,7 +80,6 @@ void Log::Filelog(const char* msg, ...)
time_t rawtime;
time(&rawtime);
rawtime += g_pOptions->GetTimeCorrection();
char szTime[50];
#ifdef HAVE_CTIME_R_3
@@ -303,7 +305,10 @@ Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText
Message::~ Message()
{
free(m_szText);
if (m_szText)
{
free(m_szText);
}
}
void Log::Clear()
@@ -365,9 +370,6 @@ void Log::InitOptions()
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
{
m_szLogFilename = strdup(g_pOptions->GetLogFile());
#ifdef WIN32
WebUtil::Utf8ToAnsi(m_szLogFilename, strlen(m_szLogFilename) + 1);
#endif
}
m_iIDGen = 0;

View File

@@ -1,325 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef WIN32
#include "win32.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <errno.h>
#include "nzbget.h"
#include "Log.h"
#include "Util.h"
#include "Maintenance.h"
extern Options* g_pOptions;
extern Maintenance* g_pMaintenance;
Maintenance::Maintenance()
{
m_iIDMessageGen = 0;
m_UpdateScriptController = NULL;
m_szUpdateScript = NULL;
}
Maintenance::~Maintenance()
{
m_mutexController.Lock();
if (m_UpdateScriptController)
{
m_UpdateScriptController->Detach();
m_mutexController.Unlock();
while (m_UpdateScriptController)
{
usleep(20*1000);
}
}
ClearMessages();
free(m_szUpdateScript);
}
void Maintenance::ResetUpdateController()
{
m_mutexController.Lock();
m_UpdateScriptController = NULL;
m_mutexController.Unlock();
}
void Maintenance::ClearMessages()
{
for (Log::Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
{
delete *it;
}
m_Messages.clear();
}
Log::Messages* Maintenance::LockMessages()
{
m_mutexLog.Lock();
return &m_Messages;
}
void Maintenance::UnlockMessages()
{
m_mutexLog.Unlock();
}
void Maintenance::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
{
if (tTime == 0)
{
tTime = time(NULL);
}
m_mutexLog.Lock();
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
m_Messages.push_back(pMessage);
m_mutexLog.Unlock();
}
bool Maintenance::StartUpdate(EBranch eBranch)
{
m_mutexController.Lock();
bool bAlreadyUpdating = m_UpdateScriptController != NULL;
m_mutexController.Unlock();
if (bAlreadyUpdating)
{
error("Could not start update-script: update-script is already running");
return false;
}
if (m_szUpdateScript)
{
free(m_szUpdateScript);
m_szUpdateScript = NULL;
}
if (!ReadPackageInfoStr("install-script", &m_szUpdateScript))
{
return false;
}
ClearMessages();
m_UpdateScriptController = new UpdateScriptController();
m_UpdateScriptController->SetScript(m_szUpdateScript);
m_UpdateScriptController->SetBranch(eBranch);
m_UpdateScriptController->SetAutoDestroy(true);
m_UpdateScriptController->Start();
return true;
}
bool Maintenance::CheckUpdates(char** pUpdateInfo)
{
char* szUpdateInfoScript;
if (!ReadPackageInfoStr("update-info-script", &szUpdateInfoScript))
{
return false;
}
*pUpdateInfo = NULL;
UpdateInfoScriptController::ExecuteScript(szUpdateInfoScript, pUpdateInfo);
free(szUpdateInfoScript);
return *pUpdateInfo;
}
bool Maintenance::ReadPackageInfoStr(const char* szKey, char** pValue)
{
char szFileName[1024];
snprintf(szFileName, 1024, "%s%cpackage-info.json", g_pOptions->GetWebDir(), PATH_SEPARATOR);
szFileName[1024-1] = '\0';
char* szPackageInfo;
int iPackageInfoLen;
if (!Util::LoadFileIntoBuffer(szFileName, &szPackageInfo, &iPackageInfoLen))
{
error("Could not load file %s", szFileName);
return false;
}
char szKeyStr[100];
snprintf(szKeyStr, 100, "\"%s\"", szKey);
szKeyStr[100-1] = '\0';
char* p = strstr(szPackageInfo, szKeyStr);
if (!p)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
p = strchr(p + strlen(szKeyStr), '"');
if (!p)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
p++;
char* pend = strchr(p, '"');
if (!pend)
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
int iLen = pend - p;
if (iLen >= sizeof(szFileName))
{
error("Could not parse file %s", szFileName);
free(szPackageInfo);
return false;
}
*pValue = (char*)malloc(iLen+1);
strncpy(*pValue, p, iLen);
(*pValue)[iLen] = '\0';
WebUtil::JsonDecode(*pValue);
free(szPackageInfo);
return true;
}
void UpdateScriptController::Run()
{
m_iPrefixLen = 0;
PrintMessage(Message::mkInfo, "Executing update-script %s", GetScript());
char szInfoName[1024];
snprintf(szInfoName, 1024, "update-script %s", Util::BaseFileName(GetScript()));
szInfoName[1024-1] = '\0';
SetInfoName(szInfoName);
const char* szBranchName[] = { "STABLE", "TESTING", "DEVEL" };
SetEnvVar("NZBUP_BRANCH", szBranchName[m_eBranch]);
char szProcessID[20];
#ifdef WIN32
int pid = (int)GetCurrentProcessId();
#else
int pid = (int)getppid();
#endif
snprintf(szProcessID, 20, "%i", pid);
szProcessID[20-1] = '\0';
SetEnvVar("NZBUP_PROCESSID", szProcessID);
char szLogPrefix[100];
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 100);
szLogPrefix[100-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
SetLogPrefix(szLogPrefix);
m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
Execute();
g_pMaintenance->ResetUpdateController();
}
void UpdateScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
szText = szText + m_iPrefixLen;
g_pMaintenance->AppendMessage(eKind, time(NULL), szText);
ScriptController::AddMessage(eKind, szText);
}
void UpdateInfoScriptController::ExecuteScript(const char* szScript, char** pUpdateInfo)
{
detail("Executing update-info-script %s", Util::BaseFileName(szScript));
UpdateInfoScriptController* pScriptController = new UpdateInfoScriptController();
pScriptController->SetScript(szScript);
char szInfoName[1024];
snprintf(szInfoName, 1024, "update-info-script %s", Util::BaseFileName(szScript));
szInfoName[1024-1] = '\0';
pScriptController->SetInfoName(szInfoName);
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
szLogPrefix[1024-1] = '\0';
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
pScriptController->SetLogPrefix(szLogPrefix);
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
pScriptController->Execute();
if (pScriptController->m_UpdateInfo.GetBuffer())
{
int iLen = strlen(pScriptController->m_UpdateInfo.GetBuffer());
*pUpdateInfo = (char*)malloc(iLen + 1);
strncpy(*pUpdateInfo, pScriptController->m_UpdateInfo.GetBuffer(), iLen);
(*pUpdateInfo)[iLen] = '\0';
}
delete pScriptController;
}
void UpdateInfoScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
szText = szText + m_iPrefixLen;
if (!strncmp(szText, "[NZB] ", 6))
{
debug("Command %s detected", szText + 6);
if (!strncmp(szText + 6, "[UPDATEINFO]", 12))
{
m_UpdateInfo.Append(szText + 6 + 12);
}
else
{
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
}
}
else
{
ScriptController::AddMessage(eKind, szText);
}
}

View File

@@ -1,94 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifndef MAINTENANCE_H
#define MAINTENANCE_H
#include "Thread.h"
#include "ScriptController.h"
#include "Log.h"
#include "Util.h"
class UpdateScriptController;
class Maintenance
{
private:
Log::Messages m_Messages;
Mutex m_mutexLog;
Mutex m_mutexController;
int m_iIDMessageGen;
UpdateScriptController* m_UpdateScriptController;
char* m_szUpdateScript;
bool ReadPackageInfoStr(const char* szKey, char** pValue);
public:
enum EBranch
{
brStable,
brTesting,
brDevel
};
Maintenance();
~Maintenance();
void ClearMessages();
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
Log::Messages* LockMessages();
void UnlockMessages();
bool StartUpdate(EBranch eBranch);
void ResetUpdateController();
bool CheckUpdates(char** pUpdateInfo);
};
class UpdateScriptController : public Thread, public ScriptController
{
private:
Maintenance::EBranch m_eBranch;
int m_iPrefixLen;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
virtual void Run();
void SetBranch(Maintenance::EBranch eBranch) { m_eBranch = eBranch; }
};
class UpdateInfoScriptController : public ScriptController
{
private:
int m_iPrefixLen;
StringBuilder m_UpdateInfo;
protected:
virtual void AddMessage(Message::EKind eKind, const char* szText);
public:
static void ExecuteScript(const char* szScript, char** pUpdateInfo);
};
#endif

View File

@@ -24,12 +24,10 @@ bin_PROGRAMS = nzbget
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
NCursesFrontend.h NNTPConnection.cpp \
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
@@ -40,7 +38,7 @@ nzbget_SOURCES = \
EXTRA_DIST = \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES) $(osx_FILES)
$(patches_FILES) $(windows_FILES)
patches_FILES = \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
@@ -49,21 +47,6 @@ patches_FILES = \
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
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/Images/statusicon-inv.png \
osx/Resources/Images/statusicon-inv@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
doc_FILES = \
README ChangeLog COPYING
@@ -73,7 +56,7 @@ exampleconf_FILES = \
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/util.js webui/config.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \

View File

@@ -85,10 +85,7 @@ PROGRAMS = $(bin_PROGRAMS)
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
DupeCoordinator.$(OBJEXT) Frontend.$(OBJEXT) \
FeedCoordinator.$(OBJEXT) FeedFile.$(OBJEXT) \
FeedFilter.$(OBJEXT) FeedInfo.$(OBJEXT) Log.$(OBJEXT) \
LoggableFrontend.$(OBJEXT) Maintenance.$(OBJEXT) \
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
Options.$(OBJEXT) ParChecker.$(OBJEXT) ParRenamer.$(OBJEXT) \
@@ -245,12 +242,10 @@ target_vendor = @target_vendor@
nzbget_SOURCES = \
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
NCursesFrontend.h NNTPConnection.cpp \
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h \
Log.cpp Log.h LoggableFrontend.cpp LoggableFrontend.h MessageBase.h \
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h NZBFile.cpp \
NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
@@ -261,7 +256,7 @@ nzbget_SOURCES = \
EXTRA_DIST = \
Makefile.cvs nzbgetd \
$(patches_FILES) $(windows_FILES) $(osx_FILES)
$(patches_FILES) $(windows_FILES)
patches_FILES = \
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
@@ -270,21 +265,6 @@ patches_FILES = \
windows_FILES = \
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
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/Images/statusicon-inv.png \
osx/Resources/Images/statusicon-inv@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
doc_FILES = \
README ChangeLog COPYING
@@ -294,7 +274,7 @@ exampleconf_FILES = \
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/util.js webui/config.js \
webui/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
webui/lib/jquery.js webui/lib/jquery.min.js \
webui/img/icons.png webui/img/icons-2x.png \
@@ -460,15 +440,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DiskState.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadInfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DupeCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedCoordinator.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFile.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFilter.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedInfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Frontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Log.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoggableFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Maintenance.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NNTPConnection.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NZBFile.Po@am__quote@
@@ -617,7 +591,7 @@ distclean-tags:
distdir: $(DISTFILES)
$(am__remove_distdir)
mkdir $(distdir)
$(mkdir_p) $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
$(mkdir_p) $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
list='$(DISTFILES)'; for file in $$list; do \

View File

@@ -27,7 +27,7 @@
#ifndef MESSAGEBASE_H
#define MESSAGEBASE_H
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A621B; // = "nzb-XX" (protocol version)
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6217; // = "nzb-XX" (protocol version)
static const int NZBREQUESTFILENAMESIZE = 512;
static const int NZBREQUESTPASSWORDSIZE = 32;
@@ -88,8 +88,6 @@ enum eRemoteEditAction
eRemoteEditActionGroupPause, // pause group
eRemoteEditActionGroupResume, // resume (unpause) group
eRemoteEditActionGroupDelete, // delete group
eRemoteEditActionGroupDupeDelete, // delete group
eRemoteEditActionGroupFinalDelete, // delete group
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files) in group
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars in group, except main par-file (does not affect other files)
eRemoteEditActionGroupSetPriority, // set priority for groups
@@ -97,25 +95,14 @@ enum eRemoteEditAction
eRemoteEditActionGroupMerge, // merge group
eRemoteEditActionGroupSetParameter, // set post-process parameter for group
eRemoteEditActionGroupSetName, // set group name (rename group)
eRemoteEditActionGroupSetDupeKey, // (reserved)
eRemoteEditActionGroupSetDupeScore, // (reserved)
eRemoteEditActionGroupSetDupeMode, // (reserved)
eRemoteEditActionPostMoveOffset = 51, // move post-job to m_iOffset relative to the current position in post-queue
eRemoteEditActionPostMoveTop, // move post-job to the top of post-queue
eRemoteEditActionPostMoveBottom, // move post-job to the bottom of post-queue
eRemoteEditActionPostDelete, // delete post-job
eRemoteEditActionHistoryDelete, // hide history-item
eRemoteEditActionHistoryFinalDelete, // delete history-item
eRemoteEditActionHistoryDelete, // delete history-item
eRemoteEditActionHistoryReturn, // move history-item back to download queue
eRemoteEditActionHistoryProcess, // move history-item back to download queue and start postprocessing
eRemoteEditActionHistoryRedownload, // move history-item back to download queue for redownload
eRemoteEditActionHistorySetParameter, // set post-process parameter for history-item
eRemoteEditActionHistorySetDupeKey, // (reserved)
eRemoteEditActionHistorySetDupeScore, // (reserved)
eRemoteEditActionHistorySetDupeMode, // (reserved)
eRemoteEditActionHistorySetDupeBackup, // (reserved)
eRemoteEditActionHistoryMarkBad, // mark history-item as bad (and download other duplicate)
eRemoteEditActionHistoryMarkGood // mark history-item as good (and push it into dup-history)
eRemoteEditActionHistorySetParameter // set post-process parameter for history-item
};
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":

View File

@@ -202,9 +202,14 @@ NCursesFrontend::NCursesFrontend()
NCursesFrontend::~NCursesFrontend()
{
#ifdef WIN32
free(m_pScreenBuffer);
free(m_pOldScreenBuffer);
if (m_pScreenBuffer)
{
free(m_pScreenBuffer);
}
if (m_pOldScreenBuffer)
{
free(m_pOldScreenBuffer);
}
m_ColorAttr.clear();
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -545,8 +550,6 @@ int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
szText = (char*)malloc(iLen);
time_t rawtime = Msg->GetTime();
rawtime += g_pOptions->GetTimeCorrection();
char szTime[50];
#ifdef HAVE_CTIME_R_3
ctime_r(&rawtime, szTime, 50);
@@ -720,8 +723,11 @@ void NCursesFrontend::PrintKeyInputBar()
void NCursesFrontend::SetHint(const char* szHint)
{
free(m_szHint);
m_szHint = NULL;
if (m_szHint)
{
free(m_szHint);
m_szHint = NULL;
}
if (szHint)
{
m_szHint = strdup(szHint);
@@ -786,8 +792,8 @@ void NCursesFrontend::PrintFileQueue()
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
m_bUseColor ? "" : "*** ", (int)pDownloadQueue->GetFileQueue()->size(),
(int)pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
m_bUseColor ? "" : "*** ", pDownloadQueue->GetFileQueue()->size(),
pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
}
@@ -971,7 +977,7 @@ void NCursesFrontend::PrintGroupQueue()
char szBuffer[MAX_SCREEN_WIDTH];
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
m_bUseColor ? "" : "*** ", (int)pGroupQueue->size(), szRemaining, szUnpaused);
m_bUseColor ? "" : "*** ", pGroupQueue->size(), szRemaining, szUnpaused);
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
PrintTopHeader(szBuffer, m_iQueueWinTop, false);
}
@@ -1122,7 +1128,11 @@ void NCursesFrontend::PrepareGroupQueue()
void NCursesFrontend::ClearGroupQueue()
{
m_groupQueue.Clear();
for (GroupQueue::iterator it = m_groupQueue.begin(); it != m_groupQueue.end(); it++)
{
delete *it;
}
m_groupQueue.clear();
}
bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,7 +35,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include "nzbget.h"
#include "Log.h"
@@ -56,7 +55,11 @@ NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer
NNTPConnection::~NNTPConnection()
{
free(m_szActiveGroup);
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
free(m_szLineBuf);
}
@@ -97,10 +100,10 @@ const char* NNTPConnection::Request(const char* req)
bool NNTPConnection::Authenticate()
{
if (strlen(m_pNewsServer->GetUser()) == 0 || strlen(m_pNewsServer->GetPassword()) == 0)
if (!m_pNewsServer->GetUser() || strlen(m_pNewsServer->GetUser()) == 0 ||
!m_pNewsServer->GetPassword() || strlen(m_pNewsServer->GetPassword()) == 0)
{
error("%c%s (%s) requested authorization but username/password are not set in settings",
toupper(m_pNewsServer->GetName()[0]), m_pNewsServer->GetName() + 1, m_pNewsServer->GetHost());
error("Server%i (%s) requested authorization but username/password are not set in settings", m_pNewsServer->GetID(), m_pNewsServer->GetHost());
m_bAuthError = true;
return false;
}
@@ -208,7 +211,11 @@ const char* NNTPConnection::JoinGroup(const char* grp)
if (answer && !strncmp(answer, "2", 1))
{
debug("Changed group to %s on %s", grp, GetHost());
free(m_szActiveGroup);
if (m_szActiveGroup)
{
free(m_szActiveGroup);
}
m_szActiveGroup = strdup(grp);
}
else
@@ -249,7 +256,8 @@ bool NNTPConnection::Connect()
return false;
}
if ((strlen(m_pNewsServer->GetUser()) > 0 && strlen(m_pNewsServer->GetPassword()) > 0) &&
if ((m_pNewsServer->GetUser() && strlen(m_pNewsServer->GetUser()) > 0 &&
m_pNewsServer->GetPassword() && strlen(m_pNewsServer->GetPassword()) > 0) &&
!Authenticate())
{
return false;
@@ -265,8 +273,11 @@ bool NNTPConnection::Disconnect()
if (m_eStatus == csConnected)
{
Request("quit\r\n");
free(m_szActiveGroup);
m_szActiveGroup = NULL;
if (m_szActiveGroup)
{
free(m_szActiveGroup);
m_szActiveGroup = NULL;
}
}
return Connection::Disconnect();
}

View File

@@ -34,7 +34,6 @@
#include <string.h>
#include <list>
#include <ctype.h>
#ifdef WIN32
#include <comutil.h>
#import <msxml.tlb> named_guids
@@ -62,15 +61,13 @@ NZBFile::NZBFile(const char* szFileName, const char* szCategory)
debug("Creating NZBFile");
m_szFileName = strdup(szFileName);
m_szPassword = NULL;
m_pNZBInfo = new NZBInfo();
m_pNZBInfo->Retain();
m_pNZBInfo->AddReference();
m_pNZBInfo->SetFilename(szFileName);
m_pNZBInfo->SetCategory(szCategory);
m_pNZBInfo->BuildDestDirName();
#ifndef WIN32
m_bPassword = false;
m_pFileInfo = NULL;
m_pArticle = NULL;
m_szTagContent = NULL;
@@ -85,8 +82,10 @@ NZBFile::~NZBFile()
debug("Destroying NZBFile");
// Cleanup
free(m_szFileName);
free(m_szPassword);
if (m_szFileName)
{
free(m_szFileName);
}
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
@@ -100,8 +99,15 @@ NZBFile::~NZBFile()
}
#ifndef WIN32
delete m_pFileInfo;
free(m_szTagContent);
if (m_pFileInfo)
{
delete m_pFileInfo;
}
if (m_szTagContent)
{
free(m_szTagContent);
}
#endif
}
@@ -121,70 +127,39 @@ void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
pFileInfo->GetArticles()->push_back(NULL);
int index = pArticleInfo->GetPartNumber() - 1;
if ((*pFileInfo->GetArticles())[index])
{
delete (*pFileInfo->GetArticles())[index];
}
(*pFileInfo->GetArticles())[index] = pArticleInfo;
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
}
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
{
// calculate file size and delete empty articles
long long lSize = 0;
long long lMissedSize = 0;
long long lOneSize = 0;
int iUncountedArticles = 0;
int iMissedArticles = 0;
// deleting empty articles
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
int iTotalArticles = (int)pArticles->size();
int i = 0;
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end(); )
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
{
ArticleInfo* pArticle = *it;
if (!pArticle)
if (*it == NULL)
{
pArticles->erase(it);
it = pArticles->begin() + i;
iMissedArticles++;
if (lOneSize > 0)
{
lMissedSize += lOneSize;
}
else
{
iUncountedArticles++;
}
}
else
{
lSize += pArticle->GetSize();
if (lOneSize == 0)
{
lOneSize = pArticle->GetSize();
}
it++;
i++;
}
}
if (pArticles->empty())
if (!pArticles->empty())
{
delete pFileInfo;
return;
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
}
else
{
delete pFileInfo;
}
lMissedSize += iUncountedArticles * lOneSize;
lSize += lMissedSize;
m_FileInfos.push_back(pFileInfo);
pFileInfo->SetNZBInfo(m_pNZBInfo);
pFileInfo->SetSize(lSize);
pFileInfo->SetRemainingSize(lSize - lMissedSize);
pFileInfo->SetMissedSize(lMissedSize);
pFileInfo->SetTotalArticles(iTotalArticles);
pFileInfo->SetMissedArticles(iMissedArticles);
}
void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
@@ -333,7 +308,7 @@ bool NZBFile::HasDuplicateFilenames()
/**
* Generate filenames from subjects and check if the parsing of subject was correct
*/
void NZBFile::BuildFilenames()
void NZBFile::ProcessFilenames()
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
@@ -352,185 +327,23 @@ void NZBFile::BuildFilenames()
if (HasDuplicateFilenames())
{
m_pNZBInfo->SetManyDupeFiles(true);
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetFilename(pFileInfo->GetSubject());
}
}
}
bool CompareFileInfo(FileInfo* pFirst, FileInfo* pSecond)
{
return strcmp(pFirst->GetFilename(), pSecond->GetFilename()) > 0;
}
void NZBFile::CalcHashes()
{
FileInfoList fileList;
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
fileList.push_back(*it);
}
fileList.sort(CompareFileInfo);
// split ExtCleanupDisk into tokens and create a list
ExtList extList;
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
char* saveptr;
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
while (szExt)
{
extList.push_back(szExt);
szExt = strtok_r(NULL, ",; ", &saveptr);
}
unsigned int iFullContentHash = 0;
unsigned int iFilteredContentHash = 0;
int iUseForFilteredCount = 0;
for (FileInfoList::iterator it = fileList.begin(); it != fileList.end(); it++)
{
FileInfo* pFileInfo = *it;
// check file extension
int iFilenameLen = strlen(pFileInfo->GetFilename());
bool bSkip = false;
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
{
const char* szExt = *it;
int iExtLen = strlen(szExt);
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, pFileInfo->GetFilename() + iFilenameLen - iExtLen))
{
bSkip = true;
break;
}
}
bSkip = bSkip && !pFileInfo->GetParFile();
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
{
ArticleInfo* pArticle = *it;
int iLen = strlen(pArticle->GetMessageID());
iFullContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFullContentHash);
if (!bSkip)
{
iFilteredContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFilteredContentHash);
iUseForFilteredCount++;
}
}
}
free(szExtCleanupDisk);
// if filtered hash is based on less than a half of files - do not use filtered hash at all
if (iUseForFilteredCount < (int)fileList.size() / 2)
{
iFilteredContentHash = 0;
}
m_pNZBInfo->SetFullContentHash(iFullContentHash);
m_pNZBInfo->SetFilteredContentHash(iFilteredContentHash);
}
void NZBFile::ProcessFiles()
{
BuildFilenames();
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
{
FileInfo* pFileInfo = *it;
pFileInfo->MakeValidFilename();
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
bool bParFile = strstr(szLoFileName, ".par2");
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
m_pNZBInfo->SetTotalArticles(m_pNZBInfo->GetTotalArticles() + pFileInfo->GetTotalArticles());
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
m_pNZBInfo->SetFailedSize(m_pNZBInfo->GetFailedSize() + pFileInfo->GetMissedSize());
m_pNZBInfo->SetCurrentFailedSize(m_pNZBInfo->GetFailedSize());
pFileInfo->SetParFile(bParFile);
if (bParFile)
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
m_pNZBInfo->SetParSize(m_pNZBInfo->GetParSize() + pFileInfo->GetSize());
m_pNZBInfo->SetParFailedSize(m_pNZBInfo->GetParFailedSize() + pFileInfo->GetMissedSize());
m_pNZBInfo->SetParCurrentFailedSize(m_pNZBInfo->GetParFailedSize());
}
}
CalcHashes();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
{
FileInfo* pFileInfo = *it;
g_pDiskState->SaveFile(pFileInfo);
pFileInfo->ClearArticles();
}
}
if (m_szPassword)
{
ReadPassword();
}
}
/**
* Password read using XML-parser may have special characters (such as TAB) stripped.
* This function rereads password directly from file to keep all characters intact.
*/
void NZBFile::ReadPassword()
{
FILE* pFile = fopen(m_szFileName, "rb");
if (!pFile)
{
return;
}
// obtain file size.
fseek(pFile , 0 , SEEK_END);
int iSize = ftell(pFile);
rewind(pFile);
// reading first 4KB of the file
// allocate memory to contain the whole file.
char* buf = (char*)malloc(4096);
iSize = iSize < 4096 ? iSize : 4096;
// copy the file into the buffer.
fread(buf, 1, iSize, pFile);
fclose(pFile);
buf[iSize-1] = '\0';
char* szMetaPassword = strstr(buf, "<meta type=\"password\">");
if (szMetaPassword)
{
szMetaPassword += 22; // length of '<meta type="password">'
char* szEnd = strstr(szMetaPassword, "</meta>");
if (szEnd)
{
*szEnd = '\0';
WebUtil::XmlDecode(szMetaPassword);
free(m_szPassword);
m_szPassword = strdup(szMetaPassword);
}
}
free(buf);
}
#ifdef WIN32
@@ -571,7 +384,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
NZBFile* pFile = new NZBFile(szFileName, szCategory);
if (pFile->ParseNZB(doc))
{
pFile->ProcessFiles();
pFile->ProcessFilenames();
}
else
{
@@ -595,7 +408,7 @@ void NZBFile::EncodeURL(const char* szFilename, char* szURL)
else
{
*szURL++ = '%';
int a = (unsigned char)ch >> 4;
int a = ch >> 4;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
a = ch & 0xF;
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
@@ -609,17 +422,10 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
MSXML::IXMLDOMDocumentPtr doc = nzb;
MSXML::IXMLDOMNodePtr root = doc->documentElement;
MSXML::IXMLDOMNodePtr node = root->selectSingleNode("/nzb/head/meta[@type='password']");
if (node)
{
_bstr_t password(node->Gettext());
m_szPassword = strdup(password);
}
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
for (int i = 0; i < fileList->Getlength(); i++)
{
node = fileList->Getitem(i);
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
if (!attribute) return false;
_bstr_t subject(attribute->Gettext());
@@ -668,6 +474,11 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
pArticle->SetSize(lsize);
AddArticle(pFileInfo, pArticle);
}
if (lsize > 0)
{
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
}
}
AddFileInfo(pFileInfo);
@@ -694,7 +505,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
if (ret == 0)
{
pFile->ProcessFiles();
pFile->ProcessFilenames();
}
else
{
@@ -758,6 +569,10 @@ void NZBFile::Parse_StartElement(const char *name, const char **atts)
partNumber = atol(attrvalue);
}
}
if (lsize > 0)
{
m_pFileInfo->SetSize(m_pFileInfo->GetSize() + lsize);
}
if (partNumber > 0)
{
@@ -768,10 +583,6 @@ void NZBFile::Parse_StartElement(const char *name, const char **atts)
AddArticle(m_pFileInfo, m_pArticle);
}
}
else if (!strcmp("meta", name))
{
m_bPassword = atts[0] && atts[1] && !strcmp("type", atts[0]) && !strcmp("password", atts[1]);
}
}
void NZBFile::Parse_EndElement(const char *name)
@@ -809,10 +620,6 @@ void NZBFile::Parse_EndElement(const char *name)
m_pArticle->SetMessageID(ID);
m_pArticle = NULL;
}
else if (!strcmp("meta", name) && m_bPassword)
{
m_szPassword = strdup(m_szTagContent);
}
}
void NZBFile::Parse_Content(const char *buf, int len)

View File

@@ -28,7 +28,6 @@
#define NZBFILE_H
#include <vector>
#include <list>
#include "DownloadInfo.h"
@@ -36,24 +35,18 @@ class NZBFile
{
public:
typedef std::vector<FileInfo*> FileInfos;
typedef std::list<FileInfo*> FileInfoList;
typedef std::list<char*> ExtList;
private:
FileInfos m_FileInfos;
NZBInfo* m_pNZBInfo;
char* m_szFileName;
char* m_szPassword;
NZBFile(const char* szFileName, const char* szCategory);
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
void AddFileInfo(FileInfo* pFileInfo);
void ParseSubject(FileInfo* pFileInfo, bool TryQuotes);
void BuildFilenames();
void ProcessFiles();
void CalcHashes();
void ProcessFilenames();
bool HasDuplicateFilenames();
void ReadPassword();
#ifdef WIN32
bool ParseNZB(IUnknown* nzb);
static void EncodeURL(const char* szFilename, char* szURL);
@@ -63,7 +56,6 @@ private:
char* m_szTagContent;
int m_iTagContentLen;
bool m_bIgnoreNextError;
bool m_bPassword;
static void SAX_StartElement(NZBFile* pFile, const char *name, const char **atts);
static void SAX_EndElement(NZBFile* pFile, const char *name);
@@ -81,7 +73,6 @@ public:
const char* GetFileName() const { return m_szFileName; }
FileInfos* GetFileInfos() { return &m_FileInfos; }
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
const char* GetPassword() { return m_szPassword; }
void DetachFileInfos();
void LogDebugInfo();

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -34,47 +34,45 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "nzbget.h"
#include "NewsServer.h"
NewsServer::NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS,
const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
NewsServer::NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
{
m_iID = iID;
m_iStateID = 0;
m_bActive = bActive;
m_szHost = NULL;
m_iPort = iPort;
m_szUser = NULL;
m_szPassword = NULL;
m_iLevel = iLevel;
m_iNormLevel = iLevel;
m_iGroup = iGroup;
m_iMaxConnections = iMaxConnections;
m_bJoinGroup = bJoinGroup;
m_bTLS = bTLS;
m_szHost = strdup(szHost ? szHost : "");
m_szUser = strdup(szUser ? szUser : "");
m_szPassword = strdup(szPass ? szPass : "");
m_szCipher = strdup(szCipher ? szCipher : "");
if (szName && strlen(szName) > 0)
{
m_szName = strdup(szName);
}
else
{
m_szName = (char*)malloc(20);
snprintf(m_szName, 20, "server%i", iID);
m_szName[20-1] = '\0';
}
m_szHost = szHost ? strdup(szHost) : NULL;
m_szUser = szUser ? strdup(szUser) : NULL;
m_szPassword = szPass ? strdup(szPass) : NULL;
m_szCipher = szCipher ? strdup(szCipher) : NULL;
}
NewsServer::~NewsServer()
{
free(m_szName);
free(m_szHost);
free(m_szUser);
free(m_szPassword);
free(m_szCipher);
if (m_szHost)
{
free(m_szHost);
}
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
if (m_szCipher)
{
free(m_szCipher);
}
}

View File

@@ -2,7 +2,7 @@
* This file if part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,15 +27,10 @@
#ifndef NEWSSERVER_H
#define NEWSSERVER_H
#include <vector>
class NewsServer
{
private:
int m_iID;
int m_iStateID;
bool m_bActive;
char* m_szName;
int m_iGroup;
char* m_szHost;
int m_iPort;
@@ -43,22 +38,15 @@ private:
char* m_szPassword;
int m_iMaxConnections;
int m_iLevel;
int m_iNormLevel;
bool m_bJoinGroup;
bool m_bTLS;
char* m_szCipher;
public:
NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
const char* szUser, const char* szPass, bool bJoinGroup,
NewsServer(int iID, const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup,
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
~NewsServer();
int GetID() { return m_iID; }
int GetStateID() { return m_iStateID; }
void SetStateID(int iStateID) { m_iStateID = iStateID; }
bool GetActive() { return m_bActive; }
void SetActive(bool bActive) { m_bActive = bActive; }
const char* GetName() { return m_szName; }
int GetGroup() { return m_iGroup; }
const char* GetHost() { return m_szHost; }
int GetPort() { return m_iPort; }
@@ -66,13 +54,10 @@ public:
const char* GetPassword() { return m_szPassword; }
int GetMaxConnections() { return m_iMaxConnections; }
int GetLevel() { return m_iLevel; }
int GetNormLevel() { return m_iNormLevel; }
void SetNormLevel(int iLevel) { m_iNormLevel = iLevel; }
void SetLevel(int iLevel) { m_iLevel = iLevel; }
int GetJoinGroup() { return m_bJoinGroup; }
bool GetTLS() { return m_bTLS; }
const char* GetCipher() { return m_szCipher; }
};
typedef std::vector<NewsServer*> Servers;
#endif

View File

File diff suppressed because it is too large Load Diff

View File

@@ -92,12 +92,6 @@ public:
psFull,
psAuto
};
enum EHealthCheck
{
hcPause,
hcDelete,
hcNone
};
enum EScriptLogKind
{
slNone,
@@ -107,6 +101,7 @@ public:
slError,
slDebug
};
enum EMatchMode
{
mmID = 1,
@@ -179,18 +174,14 @@ public:
private:
char* m_szName;
char* m_szDestDir;
bool m_bUnpack;
char* m_szDefScript;
NameList m_Aliases;
public:
Category(const char* szName, const char* szDestDir, bool bUnpack, const char* szDefScript);
Category(const char* szName, const char* szDestDir, const char* szDefScript);
~Category();
const char* GetName() { return m_szName; }
const char* GetDestDir() { return m_szDestDir; }
bool GetUnpack() { return m_bUnpack; }
const char* GetDefScript() { return m_szDefScript; }
NameList* GetAliases() { return &m_Aliases; }
};
typedef std::vector<Category*> CategoriesBase;
@@ -199,7 +190,7 @@ public:
{
public:
~Categories();
Category* FindCategory(const char* szName, bool bSearchAliases);
Category* FindCategory(const char* szName);
};
class Script
@@ -269,7 +260,6 @@ private:
int m_iSecurePort;
char* m_szSecureCert;
char* m_szSecureKey;
char* m_szAuthorizedIP;
char* m_szLockFile;
char* m_szDaemonUsername;
EOutputMode m_eOutputMode;
@@ -283,12 +273,11 @@ private:
EParCheck m_eParCheck;
bool m_bParRepair;
EParScan m_eParScan;
bool m_bParRename;
EHealthCheck m_eHealthCheck;
char* m_szDefScript;
char* m_szScriptOrder;
char* m_szNZBProcess;
char* m_szNZBAddedProcess;
bool m_bStrictParName;
bool m_bNoConfig;
int m_iUMask;
int m_iUpdateInterval;
@@ -308,6 +297,7 @@ private:
bool m_bScriptPauseQueue;
bool m_bNzbCleanupDisk;
bool m_bDeleteCleanupDisk;
bool m_bMergeNzb;
int m_iParTimeLimit;
int m_iKeepHistory;
bool m_bAccurateRate;
@@ -317,9 +307,6 @@ private:
char* m_szSevenZipCmd;
bool m_bUnpackPauseQueue;
char* m_szExtCleanupDisk;
int m_iFeedHistory;
bool m_bUrlForce;
int m_iTimeCorrection;
// Parsed command-line parameters
bool m_bServerMode;
@@ -362,7 +349,6 @@ private:
void InitServers();
void InitCategories();
void InitScheduler();
void InitFeeds();
void CheckOptions();
void PrintUsage(char* com);
void Dump();
@@ -373,7 +359,6 @@ private:
const char* GetOption(const char* optname);
void SetOption(const char* optname, const char* value);
bool SetOptionString(const char* option);
bool SplitOptionString(const char* option, char** pOptName, char** pOptValue);
bool ValidateOptionName(const char* optname);
void LoadConfigFile();
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
@@ -434,7 +419,6 @@ public:
int GetSecurePort() { return m_iSecurePort; }
const char* GetSecureCert() { return m_szSecureCert; }
const char* GetSecureKey() { return m_szSecureKey; }
const char* GetAuthorizedIP() { return m_szAuthorizedIP; }
const char* GetLockFile() { return m_szLockFile; }
const char* GetDaemonUsername() { return m_szDaemonUsername; }
EOutputMode GetOutputMode() { return m_eOutputMode; }
@@ -448,12 +432,11 @@ public:
EParCheck GetParCheck() { return m_eParCheck; }
bool GetParRepair() { return m_bParRepair; }
EParScan GetParScan() { return m_eParScan; }
bool GetParRename() { return m_bParRename; }
EHealthCheck GetHealthCheck() { return m_eHealthCheck; }
const char* GetScriptOrder() { return m_szScriptOrder; }
const char* GetDefScript() { return m_szDefScript; }
const char* GetNZBProcess() { return m_szNZBProcess; }
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
bool GetStrictParName() { return m_bStrictParName; }
int GetUMask() { return m_iUMask; }
int GetUpdateInterval() {return m_iUpdateInterval; }
bool GetCursesNZBName() { return m_bCursesNZBName; }
@@ -472,6 +455,7 @@ public:
bool GetScriptPauseQueue() { return m_bScriptPauseQueue; }
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
bool GetMergeNzb() { return m_bMergeNzb; }
int GetParTimeLimit() { return m_iParTimeLimit; }
int GetKeepHistory() { return m_iKeepHistory; }
bool GetAccurateRate() { return m_bAccurateRate; }
@@ -481,11 +465,8 @@ public:
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
int GetFeedHistory() { return m_iFeedHistory; }
bool GetUrlForce() { return m_bUrlForce; }
int GetTimeCorrection() { return m_iTimeCorrection; }
Category* FindCategory(const char* szName, bool bSearchAliases) { return m_Categories.FindCategory(szName, bSearchAliases); }
Category* FindCategory(const char* szName) { return m_Categories.FindCategory(szName); }
// Parsed command-line parameters
bool GetServerMode() { return m_bServerMode; }

View File

@@ -163,7 +163,6 @@ ParChecker::ParChecker()
m_szInfoName = NULL;
m_szErrMsg = NULL;
m_szProgressLabel = (char*)malloc(1024);
m_pRepairer = NULL;
m_iFileProgress = 0;
m_iStageProgress = 0;
m_iExtraFiles = 0;
@@ -176,9 +175,22 @@ ParChecker::~ParChecker()
{
debug("Destroying ParChecker");
free(m_szDestDir);
free(m_szNZBName);
free(m_szInfoName);
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szNZBName)
{
free(m_szNZBName);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szErrMsg)
{
free(m_szErrMsg);
}
free(m_szProgressLabel);
Cleanup();
@@ -186,9 +198,6 @@ ParChecker::~ParChecker()
void ParChecker::Cleanup()
{
delete (Repairer*)m_pRepairer;
m_pRepairer = NULL;
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
@@ -202,26 +211,32 @@ void ParChecker::Cleanup()
m_ProcessedFiles.clear();
m_sourceFiles.clear();
free(m_szErrMsg);
m_szErrMsg = NULL;
}
void ParChecker::SetDestDir(const char * szDestDir)
{
free(m_szDestDir);
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void ParChecker::SetNZBName(const char * szNZBName)
{
free(m_szNZBName);
if (m_szNZBName)
{
free(m_szNZBName);
}
m_szNZBName = strdup(szNZBName);
}
void ParChecker::SetInfoName(const char * szInfoName)
{
free(m_szInfoName);
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
@@ -283,6 +298,49 @@ void ParChecker::Run()
Completed();
}
void ParChecker::WriteBrokenLog(EStatus eStatus)
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_szDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (eStatus != psRepairNotNeeded || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (eStatus == psFailed)
{
if (m_bCancelled)
{
fprintf(file, "Repair cancelled for %s\n", m_szInfoName);
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
}
else if (eStatus == psRepairPossible)
{
fprintf(file, "Repair possible for %s\n", m_szInfoName);
}
else if (eStatus == psRepaired)
{
fprintf(file, "Successfully repaired %s\n", m_szInfoName);
}
else if (eStatus == psRepairNotNeeded)
{
fprintf(file, "Repair not needed for %s\n", m_szInfoName);
}
fclose(file);
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
}
ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
{
Cleanup();
@@ -297,22 +355,51 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
debug("par: %s", m_szParFilename);
Result res;
Repairer* pRepairer = new Repairer();
m_pRepairer = pRepairer;
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
m_iStageProgress = 0;
UpdateProgress();
Result res = (Result)PreProcessPar();
if (IsStopped() || res != eSuccess)
res = pRepairer->PreProcess(m_szParFilename);
debug("ParChecker: PreProcess-result=%i", res);
if (res != eSuccess || IsStopped())
{
if (res == eInvalidCommandLineArguments)
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
}
else
{
PrintMessage(Message::mkError, "Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
m_szErrMsg = strdup("par2-file could not be processed");
}
delete pRepairer;
Cleanup();
return psFailed;
}
char BufReason[1024];
BufReason[0] = '\0';
if (m_szErrMsg)
{
free(m_szErrMsg);
}
m_szErrMsg = NULL;
m_eStage = ptVerifyingSources;
Repairer* pRepairer = (Repairer*)m_pRepairer;
res = pRepairer->Process(false);
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
if (!IsStopped() && pRepairer->missingfilecount > 0 && g_pOptions->GetParScan() == Options::psAuto && AddMissingFiles())
@@ -328,13 +415,75 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
debug("ParChecker: Process-result=%i", res);
}
if (!IsStopped() && res == eRepairNotPossible)
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
res = (Result)ProcessMorePars();
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
PrintMessage(Message::mkInfo, "Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
m_mutexQueuedParFiles.Unlock();
if (!hasMorePars)
{
int iBlockFound = 0;
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_bQueuedParFilesChanged = false;
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)
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool bQueuedParFilesChanged = false;
while (!bQueuedParFilesChanged && !IsStopped())
{
m_mutexQueuedParFiles.Lock();
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
m_mutexQueuedParFiles.Unlock();
usleep(100 * 1000);
}
}
}
if (IsStopped())
{
break;
}
bMoreFilesLoaded = LoadMorePars();
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
}
if (IsStopped())
{
delete pRepairer;
Cleanup();
return psFailed;
}
@@ -402,192 +551,11 @@ ParChecker::EStatus ParChecker::RunParCheck(const char* szParFilename)
PrintMessage(Message::mkError, "Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
delete pRepairer;
Cleanup();
return eStatus;
}
int ParChecker::PreProcessPar()
{
Result res = eRepairFailed;
while (!IsStopped() && res != eSuccess)
{
Cleanup();
Repairer* pRepairer = new Repairer();
m_pRepairer = pRepairer;
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
res = pRepairer->PreProcess(m_szParFilename);
debug("ParChecker: PreProcess-result=%i", res);
if (IsStopped())
{
PrintMessage(Message::mkError, "Could not verify %s: stopping", m_szInfoName);
m_szErrMsg = strdup("par-check was stopped");
return eRepairFailed;
}
if (res == eInvalidCommandLineArguments)
{
PrintMessage(Message::mkError, "Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
m_szErrMsg = strdup("Command line could not be parsed");
return res;
}
if (res != eSuccess)
{
PrintMessage(Message::mkWarning, "Could not verify %s: par2-file could not be processed", m_szInfoName);
PrintMessage(Message::mkInfo, "Requesting more par2-files for %s", m_szInfoName);
bool bHasMorePars = LoadMainParBak();
if (!bHasMorePars)
{
PrintMessage(Message::mkWarning, "No more par2-files found");
break;
}
}
}
if (res != eSuccess)
{
PrintMessage(Message::mkError, "Could not verify %s: par2-file could not be processed", m_szInfoName);
m_szErrMsg = strdup("par2-file could not be processed");
return res;
}
return res;
}
bool ParChecker::LoadMainParBak()
{
while (!IsStopped())
{
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
for (FileList::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
{
free(*it);
}
m_QueuedParFiles.clear();
m_mutexQueuedParFiles.Unlock();
if (hasMorePars)
{
return true;
}
int iBlockFound = 0;
bool requested = RequestMorePars(1, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_bQueuedParFilesChanged = false;
m_mutexQueuedParFiles.Unlock();
if (!requested && !hasMorePars)
{
return false;
}
if (!hasMorePars)
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool bQueuedParFilesChanged = false;
while (!bQueuedParFilesChanged && !IsStopped())
{
m_mutexQueuedParFiles.Lock();
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
m_mutexQueuedParFiles.Unlock();
usleep(100 * 1000);
}
}
}
return false;
}
int ParChecker::ProcessMorePars()
{
Result res = eRepairNotPossible;
Repairer* pRepairer = (Repairer*)m_pRepairer;
bool bMoreFilesLoaded = true;
while (!IsStopped() && res == eRepairNotPossible)
{
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
if (bMoreFilesLoaded)
{
PrintMessage(Message::mkInfo, "Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
}
m_mutexQueuedParFiles.Lock();
bool hasMorePars = !m_QueuedParFiles.empty();
m_mutexQueuedParFiles.Unlock();
if (!hasMorePars)
{
int iBlockFound = 0;
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
if (requested)
{
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
m_szProgressLabel[1024-1] = '\0';
m_iFileProgress = 0;
UpdateProgress();
}
m_mutexQueuedParFiles.Lock();
hasMorePars = !m_QueuedParFiles.empty();
m_bQueuedParFilesChanged = false;
m_mutexQueuedParFiles.Unlock();
if (!requested && !hasMorePars)
{
m_szErrMsg = (char*)malloc(1024);
snprintf(m_szErrMsg, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
m_szErrMsg[1024-1] = '\0';
break;
}
if (!hasMorePars)
{
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
bool bQueuedParFilesChanged = false;
while (!bQueuedParFilesChanged && !IsStopped())
{
m_mutexQueuedParFiles.Lock();
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
m_mutexQueuedParFiles.Unlock();
usleep(100 * 1000);
}
}
}
if (IsStopped())
{
break;
}
bMoreFilesLoaded = LoadMorePars();
if (bMoreFilesLoaded)
{
pRepairer->UpdateVerificationResults();
res = pRepairer->Process(false);
debug("ParChecker: Process-result=%i", res);
}
}
return res;
}
bool ParChecker::LoadMorePars()
{
m_mutexQueuedParFiles.Lock();
@@ -899,49 +867,6 @@ void ParChecker::Cancel()
#endif
}
void ParChecker::WriteBrokenLog(EStatus eStatus)
{
char szBrokenLogName[1024];
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_szDestDir, (int)PATH_SEPARATOR);
szBrokenLogName[1024-1] = '\0';
if (eStatus != psRepairNotNeeded || Util::FileExists(szBrokenLogName))
{
FILE* file = fopen(szBrokenLogName, "ab");
if (file)
{
if (eStatus == psFailed)
{
if (m_bCancelled)
{
fprintf(file, "Repair cancelled for %s\n", m_szInfoName);
}
else
{
fprintf(file, "Repair failed for %s: %s\n", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
}
}
else if (eStatus == psRepairPossible)
{
fprintf(file, "Repair possible for %s\n", m_szInfoName);
}
else if (eStatus == psRepaired)
{
fprintf(file, "Successfully repaired %s\n", m_szInfoName);
}
else if (eStatus == psRepairNotNeeded)
{
fprintf(file, "Repair not needed for %s\n", m_szInfoName);
}
fclose(file);
}
else
{
PrintMessage(Message::mkError, "Could not open file %s", szBrokenLogName);
}
}
}
void ParChecker::SaveSourceList()
{
// Buliding a list of DiskFile-objects, marked as source-files

View File

@@ -79,16 +79,13 @@ private:
bool m_bCancelled;
SourceList m_sourceFiles;
void Cleanup();
EStatus RunParCheck(const char* szParFilename);
int PreProcessPar();
bool LoadMainParBak();
int ProcessMorePars();
void WriteBrokenLog(EStatus eStatus);
void Cleanup();
bool LoadMorePars();
bool CheckSplittedFragments();
bool AddSplittedFragments(const char* szFilename);
bool AddMissingFiles();
void WriteBrokenLog(EStatus eStatus);
void SaveSourceList();
void DeleteLeftovers();
void signal_filename(std::string str);

View File

@@ -35,7 +35,6 @@
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <ctype.h>
#ifdef WIN32
#include <direct.h>
#else
@@ -216,13 +215,8 @@ bool ParCoordinator::ParseParFilename(const char* szParFilename, int* iBaseNameL
char* szEnd = szFilename;
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
*szEnd = '\0';
iLen = strlen(szFilename);
if (iLen < 6)
{
return false;
}
if (strcasecmp(szFilename + iLen - 5, ".par2"))
{
return false;
@@ -278,19 +272,9 @@ void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
*/
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
{
const char* szDestDir = pPostInfo->GetNZBInfo()->GetDestDir();
char szFinalDir[1024];
if (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess)
{
pPostInfo->GetNZBInfo()->BuildFinalDirName(szFinalDir, 1024);
szFinalDir[1024-1] = '\0';
szDestDir = szFinalDir;
}
m_eCurrentJob = jkParRename;
m_ParRenamer.SetPostInfo(pPostInfo);
m_ParRenamer.SetDestDir(szDestDir);
m_ParRenamer.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
m_ParRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
pPostInfo->SetWorking(true);
@@ -406,7 +390,7 @@ bool ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilenam
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;
}
if (iBlockFound < iBlockNeeded)
if (iBlockFound < iBlockNeeded && !g_pOptions->GetStrictParName())
{
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, false, false, &iCurBlockFound);
iBlockFound += iCurBlockFound;

View File

@@ -91,8 +91,14 @@ ParRenamer::~ParRenamer()
{
debug("Destroying ParRenamer");
free(m_szDestDir);
free(m_szInfoName);
if (m_szDestDir)
{
free(m_szDestDir);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
free(m_szProgressLabel);
Cleanup();
@@ -100,33 +106,28 @@ ParRenamer::~ParRenamer()
void ParRenamer::Cleanup()
{
ClearHashList();
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
{
free(*it);
}
m_DirList.clear();
}
void ParRenamer::ClearHashList()
{
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
delete *it;
}
m_FileHashList.clear();
m_fileHashList.clear();
}
void ParRenamer::SetDestDir(const char * szDestDir)
{
free(m_szDestDir);
if (m_szDestDir)
{
free(m_szDestDir);
}
m_szDestDir = strdup(szDestDir);
}
void ParRenamer::SetInfoName(const char * szInfoName)
{
free(m_szInfoName);
if (m_szInfoName)
{
free(m_szInfoName);
}
m_szInfoName = strdup(szInfoName);
}
@@ -139,8 +140,6 @@ void ParRenamer::Run()
{
Cleanup();
m_bCancelled = false;
m_iFileCount = 0;
m_iCurFile = 0;
m_iRenamedCount = 0;
m_eStatus = psFailed;
@@ -149,16 +148,8 @@ void ParRenamer::Run()
m_iStageProgress = 0;
UpdateProgress();
BuildDirList(m_szDestDir);
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
{
char* szDestDir = *it;
debug("Checking %s", szDestDir);
ClearHashList();
LoadParFiles(szDestDir);
CheckFiles(szDestDir);
}
LoadParFiles();
CheckFiles();
if (m_bCancelled)
{
@@ -178,46 +169,17 @@ void ParRenamer::Run()
Completed();
}
void ParRenamer::BuildDirList(const char* szDestDir)
{
m_DirList.push_back(strdup(szDestDir));
char* szFullFilename = (char*)malloc(1024);
DirBrowser* pDirBrowser = new DirBrowser(szDestDir);
while (const char* filename = pDirBrowser->Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (Util::DirectoryExists(szFullFilename))
{
BuildDirList(szFullFilename);
}
else
{
m_iFileCount++;
}
}
}
free(szFullFilename);
delete pDirBrowser;
}
void ParRenamer::LoadParFiles(const char* szDestDir)
void ParRenamer::LoadParFiles()
{
ParCoordinator::FileList parFileList;
ParCoordinator::FindMainPars(szDestDir, &parFileList);
ParCoordinator::FindMainPars(m_szDestDir, &parFileList);
for (ParCoordinator::FileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
{
char* szParFilename = *it;
char szFullParFilename[1024];
snprintf(szFullParFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, szParFilename);
snprintf(szFullParFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szParFilename);
szFullParFilename[1024-1] = '\0';
LoadParFile(szFullParFilename);
@@ -245,39 +207,47 @@ void ParRenamer::LoadParFile(const char* szParFilename)
}
Par2RepairerSourceFile* sourceFile = (*it).second;
m_FileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
m_fileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
sourceFile->GetDescriptionPacket()->Hash16k().print().c_str()));
}
delete pRepairer;
}
void ParRenamer::CheckFiles(const char* szDestDir)
void ParRenamer::CheckFiles()
{
DirBrowser dir(szDestDir);
int iFileCount = 0;
DirBrowser dir2(m_szDestDir);
while (const char* filename = dir2.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
iFileCount++;
}
}
int iCurFile = 0;
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (!Util::DirectoryExists(szFullFilename))
{
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = m_iCurFile * 1000 / m_iFileCount;
UpdateProgress();
m_iCurFile++;
CheckFile(szDestDir, szFullFilename);
}
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
m_szProgressLabel[1024-1] = '\0';
m_iStageProgress = iCurFile * 1000 / iFileCount;
UpdateProgress();
iCurFile++;
CheckFile(szFullFilename);
}
}
}
void ParRenamer::CheckFile(const char* szDestDir, const char* szFilename)
void ParRenamer::CheckFile(const char* szFilename)
{
debug("Computing hash for %s", szFilename);
@@ -313,7 +283,7 @@ void ParRenamer::CheckFile(const char* szDestDir, const char* szFilename)
debug("file: %s; hash16k: %s", Util::BaseFileName(szFilename), hash16k.print().c_str());
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
for (FileHashList::iterator it = m_fileHashList.begin(); it != m_fileHashList.end(); it++)
{
FileHash* pFileHash = *it;
if (!strcmp(pFileHash->GetHash(), hash16k.print().c_str()))
@@ -321,7 +291,7 @@ void ParRenamer::CheckFile(const char* szDestDir, const char* szFilename)
debug("Found correct filename: %s", pFileHash->GetFilename());
char szDstFilename[1024];
snprintf(szDstFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
snprintf(szDstFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
szDstFilename[1024-1] = '\0';
if (!Util::FileExists(szDstFilename))

View File

@@ -56,8 +56,6 @@ public:
};
typedef std::deque<FileHash*> FileHashList;
typedef std::deque<char*> DirList;
private:
char* m_szInfoName;
@@ -66,20 +64,14 @@ private:
char* m_szProgressLabel;
int m_iStageProgress;
bool m_bCancelled;
DirList m_DirList;
FileHashList m_FileHashList;
int m_iFileCount;
int m_iCurFile;
FileHashList m_fileHashList;
int m_iRenamedCount;
void Cleanup();
void ClearHashList();
void BuildDirList(const char* szDestDir);
void CheckDir(const char* szDestDir);
void LoadParFiles(const char* szDestDir);
void LoadParFiles();
void LoadParFile(const char* szParFilename);
void CheckFiles(const char* szDestDir);
void CheckFile(const char* szDestDir, const char* szFilename);
void CheckFiles();
void CheckFile(const char* szFilename);
protected:
virtual void UpdateProgress() {}

View File

@@ -39,8 +39,6 @@
#else
#include <unistd.h>
#endif
#include <set>
#include <algorithm>
#include "nzbget.h"
#include "PrePostProcessor.h"
@@ -61,13 +59,6 @@ extern DiskState* g_pDiskState;
extern Scheduler* g_pScheduler;
extern Scanner* g_pScanner;
void PrePostProcessor::PostDupeCoordinator::HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo)
{
HistoryList::iterator it = std::find(pDownloadQueue->GetHistoryList()->begin(),
pDownloadQueue->GetHistoryList()->end(), pHistoryInfo);
m_pOwner->HistoryRedownload(pDownloadQueue, it, pHistoryInfo, true);
}
PrePostProcessor::PrePostProcessor()
{
debug("Creating PrePostProcessor");
@@ -77,10 +68,10 @@ PrePostProcessor::PrePostProcessor()
m_QueueCoordinatorObserver.m_pOwner = this;
g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver);
#ifndef DISABLE_PARCHECK
m_ParCoordinator.m_pOwner = this;
#endif
m_DupeCoordinator.m_pOwner = this;
}
PrePostProcessor::~PrePostProcessor()
@@ -132,7 +123,7 @@ void PrePostProcessor::Run()
int iDiskSpaceInterval = 1000;
int iSchedulerInterval = 1000;
int iHistoryInterval = 600000;
int iHistoryInterval = 60000;
const int iStepMSec = 200;
while (!IsStopped())
@@ -163,9 +154,9 @@ void PrePostProcessor::Run()
}
iSchedulerInterval += iStepMSec;
if (iHistoryInterval >= 600000)
if (iHistoryInterval >= 60000)
{
// check history (remove old entries) every 10 minutes
// check history (remove old entries) every 1 minute
CheckHistory();
iHistoryInterval = 0;
}
@@ -213,11 +204,7 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
}
QueueCoordinator::Aspect* pAspect = (QueueCoordinator::Aspect*)Aspect;
if (pAspect->eAction == QueueCoordinator::eaNZBFileFound)
{
NZBFound(pAspect->pDownloadQueue, pAspect->pNZBInfo);
}
else if (pAspect->eAction == QueueCoordinator::eaNZBFileAdded)
if (pAspect->eAction == QueueCoordinator::eaNZBFileAdded)
{
NZBAdded(pAspect->pDownloadQueue, pAspect->pNZBInfo);
}
@@ -231,18 +218,15 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, true, false) &&
(!pAspect->pFileInfo->GetPaused() || IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, false)))
{
if ((pAspect->eAction == QueueCoordinator::eaFileCompleted ||
if (pAspect->eAction == QueueCoordinator::eaFileCompleted ||
(pAspect->pFileInfo->GetAutoDeleted() &&
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true))) &&
pAspect->pFileInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsHealth)
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true)))
{
info("Collection %s completely downloaded", pAspect->pNZBInfo->GetName());
NZBDownloaded(pAspect->pDownloadQueue, pAspect->pNZBInfo);
}
else if ((pAspect->eAction == QueueCoordinator::eaFileDeleted ||
(pAspect->eAction == QueueCoordinator::eaFileCompleted &&
pAspect->pFileInfo->GetNZBInfo()->GetDeleteStatus() > NZBInfo::dsNone)) &&
!pAspect->pNZBInfo->GetParCleanup() && !pAspect->pNZBInfo->GetPostProcess() &&
else if (pAspect->eAction == QueueCoordinator::eaFileDeleted &&
!pAspect->pNZBInfo->GetParCleanup() &&
IsNZBFileCompleted(pAspect->pDownloadQueue, pAspect->pNZBInfo, false, true))
{
info("Collection %s deleted from queue", pAspect->pNZBInfo->GetName());
@@ -252,27 +236,20 @@ void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
}
}
void PrePostProcessor::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce)
{
m_DupeCoordinator.NZBFound(pDownloadQueue, pNZBInfo);
}
}
void PrePostProcessor::NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
PostScriptController::InitParamsForNewNZB(pNZBInfo);
if (g_pOptions->GetMergeNzb())
{
pNZBInfo = MergeGroups(pDownloadQueue, pNZBInfo);
}
if (g_pOptions->GetParCheck() != Options::pcForce)
{
m_ParCoordinator.PausePars(pDownloadQueue, pNZBInfo);
}
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce &&
pNZBInfo->GetDeleteStatus() == NZBInfo::dsDupe)
{
NZBCompleted(pDownloadQueue, pNZBInfo, false);
}
if (strlen(g_pOptions->GetNZBAddedProcess()) > 0)
{
NZBAddedScriptController::StartScript(pDownloadQueue, pNZBInfo, g_pOptions->GetNZBAddedProcess());
@@ -294,19 +271,11 @@ void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
pNZBInfo->SetParStatus(NZBInfo::psSkipped);
}
if (pNZBInfo->GetRenameStatus() == NZBInfo::rsNone && !g_pOptions->GetParRename())
if (pNZBInfo->GetRenameStatus() == NZBInfo::rsNone)
{
pNZBInfo->SetRenameStatus(NZBInfo::rsSkipped);
}
if (pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone)
{
pNZBInfo->SetParStatus(NZBInfo::psFailure);
pNZBInfo->SetUnpackStatus(NZBInfo::usFailure);
pNZBInfo->SetRenameStatus(NZBInfo::rsFailure);
pNZBInfo->SetMoveStatus(NZBInfo::msFailure);
}
pNZBInfo->SetPostProcess(true);
pDownloadQueue->GetPostQueue()->push_back(pPostInfo);
SaveQueue(pDownloadQueue);
@@ -320,14 +289,7 @@ void PrePostProcessor::NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZ
void PrePostProcessor::NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsNone)
{
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
}
pNZBInfo->SetDeleting(false);
if ((g_pOptions->GetDeleteCleanupDisk() && pNZBInfo->GetCleanupDisk()) ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsDupe)
if (g_pOptions->GetDeleteCleanupDisk() && pNZBInfo->GetCleanupDisk())
{
// download was cancelled, deleting already downloaded files from disk
for (NZBInfo::Files::reverse_iterator it = pNZBInfo->GetCompletedFiles()->rbegin(); it != pNZBInfo->GetCompletedFiles()->rend(); it++)
@@ -361,23 +323,19 @@ void PrePostProcessor::NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBIn
{
rmdir(pNZBInfo->GetDestDir());
}
if (g_pOptions->GetNzbCleanupDisk())
{
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
}
}
if (pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth)
{
NZBDownloaded(pDownloadQueue, pNZBInfo);
}
else
{
NZBCompleted(pDownloadQueue, pNZBInfo, true);
}
NZBCompleted(pDownloadQueue, pNZBInfo, true);
}
void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue)
{
bool bNeedSave = false;
if (g_pOptions->GetKeepHistory() > 0 && !pNZBInfo->GetAvoidHistory())
if (g_pOptions->GetKeepHistory() > 0)
{
//remove old item for the same NZB
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
@@ -418,34 +376,23 @@ void PrePostProcessor::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZB
}
pNZBInfo->SetParkedFileCount(iParkedFiles);
if (bSaveQueue)
{
SaveQueue(pDownloadQueue);
}
info("Collection %s added to history", pNZBInfo->GetName());
bNeedSave = true;
}
pNZBInfo->SetAvoidHistory(false);
if (g_pOptions->GetDupeCheck() && pNZBInfo->GetDupeMode() != dmForce &&
(pNZBInfo->GetDeleteStatus() == NZBInfo::dsNone ||
pNZBInfo->GetDeleteStatus() == NZBInfo::dsHealth))
{
m_DupeCoordinator.NZBCompleted(pDownloadQueue, pNZBInfo);
bNeedSave = true;
}
if (bSaveQueue && bNeedSave)
{
SaveQueue(pDownloadQueue);
}
}
/**
* Removes old entries from (recent) history
* Removes old entries from history
*/
void PrePostProcessor::CheckHistory()
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
time_t tMinTime = time(NULL) - g_pOptions->GetKeepHistory() * 60*60*24;
time_t tMinTime = time(NULL) - g_pOptions->GetKeepHistory() * 60000;
bool bChanged = false;
int index = 0;
@@ -454,29 +401,13 @@ void PrePostProcessor::CheckHistory()
for (HistoryList::reverse_iterator it = pDownloadQueue->GetHistoryList()->rbegin(); it != pDownloadQueue->GetHistoryList()->rend(); )
{
HistoryInfo* pHistoryInfo = *it;
if (pHistoryInfo->GetKind() != HistoryInfo::hkDupInfo && pHistoryInfo->GetTime() < tMinTime)
if (pHistoryInfo->GetTime() < tMinTime)
{
if (g_pOptions->GetDupeCheck() && pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
// replace history element
m_DupeCoordinator.HistoryTransformToDup(pDownloadQueue, pHistoryInfo, index);
index++;
}
else
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
pDownloadQueue->GetHistoryList()->erase(pDownloadQueue->GetHistoryList()->end() - 1 - index);
delete pHistoryInfo;
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
}
info("Collection %s removed from history", szNiceName);
}
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
pDownloadQueue->GetHistoryList()->erase(pDownloadQueue->GetHistoryList()->end() - 1 - index);
delete pHistoryInfo;
info("Collection %s removed from history", szNiceName);
it = pDownloadQueue->GetHistoryList()->rbegin() + index;
bChanged = true;
}
@@ -497,12 +428,7 @@ void PrePostProcessor::CheckHistory()
void PrePostProcessor::DeleteQueuedFile(const char* szQueuedFile)
{
if (!g_pOptions->GetNzbCleanupDisk())
{
return;
}
// szQueuedFile may contain one filename or several filenames separated
// szQueuedFile may contain one filename or several filenames separated
// with "|"-character (for merged groups)
char* szFilename = strdup(szQueuedFile);
char* szEnd = szFilename - 1;
@@ -523,21 +449,44 @@ void PrePostProcessor::DeleteQueuedFile(const char* szQueuedFile)
free(szFilename);
}
/**
* Find ID of any file in the nzb-file
*/
int PrePostProcessor::FindGroupID(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
NZBInfo* PrePostProcessor::MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
{
int iAddedGroupID = 0;
// merge(1): find ID of any file in new nzb-file
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
return pFileInfo->GetID();
iAddedGroupID = pFileInfo->GetID();
break;
}
}
return 0;
// merge(2): check if queue has other nzb-files with the same name
if (iAddedGroupID > 0)
{
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() != pNZBInfo &&
!strcmp(pFileInfo->GetNZBInfo()->GetName(), pNZBInfo->GetName()))
{
// file found, do merging
IDList cIDList;
cIDList.push_back(pFileInfo->GetID());
cIDList.push_back(iAddedGroupID);
g_pQueueCoordinator->GetQueueEditor()->LockedEditList(pDownloadQueue, &cIDList, false, QueueEditor::eaGroupMerge, 0, NULL);
return pFileInfo->GetNZBInfo();
}
}
}
return pNZBInfo;
}
void PrePostProcessor::CheckDiskSpace()
@@ -545,19 +494,9 @@ void PrePostProcessor::CheckDiskSpace()
long long lFreeSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetDestDir());
warn("Low disk space. Pausing download");
g_pOptions->SetPauseDownload(true);
}
if (!Util::EmptyStr(g_pOptions->GetInterDir()))
{
lFreeSpace = Util::FreeDiskSize(g_pOptions->GetInterDir());
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
{
warn("Low disk space on %s. Pausing download", g_pOptions->GetInterDir());
g_pOptions->SetPauseDownload(true);
}
}
}
void PrePostProcessor::CheckPostQueue()
@@ -600,7 +539,14 @@ void PrePostProcessor::CheckPostQueue()
pPostInfo->SetStage(PostInfo::ptQueued);
}
}
else if (pPostInfo->GetRequestParRename())
{
pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsNone);
pPostInfo->SetRequestParRename(false);
pPostInfo->SetStage(PostInfo::ptQueued);
DeletePostThread(pPostInfo);
}
#endif
if (pPostInfo->GetDeleted())
{
@@ -658,8 +604,11 @@ void PrePostProcessor::SanitisePostQueue(PostQueue* pPostQueue)
void PrePostProcessor::DeletePostThread(PostInfo* pPostInfo)
{
delete pPostInfo->GetPostThread();
pPostInfo->SetPostThread(NULL);
if (pPostInfo->GetPostThread())
{
delete pPostInfo->GetPostThread();
pPostInfo->SetPostThread(NULL);
}
}
void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo)
@@ -687,29 +636,9 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
}
return;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSkipped &&
pPostInfo->GetNZBInfo()->CalcHealth() < pPostInfo->GetNZBInfo()->CalcCriticalHealth() &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
warn("Skipping par-check for %s due to health %.1f%% below critical %.1f%%", pPostInfo->GetInfoName(),
pPostInfo->GetNZBInfo()->CalcHealth() / 10.0, pPostInfo->GetNZBInfo()->CalcCriticalHealth() / 10.0);
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
return;
}
else if (pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSkipped &&
pPostInfo->GetNZBInfo()->GetFailedSize() - pPostInfo->GetNZBInfo()->GetParFailedSize() > 0 &&
m_ParCoordinator.FindMainPars(pPostInfo->GetNZBInfo()->GetDestDir(), NULL))
{
info("Collection %s with health %.1f%% needs par-check",
pPostInfo->GetInfoName(), pPostInfo->GetNZBInfo()->CalcHealth() / 10.0);
pPostInfo->SetRequestParCheck(true);
return;
}
#endif
NZBParameter* pUnpackParameter = pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
bool bUnpackParam = !(pUnpackParameter && !strcasecmp(pUnpackParameter->GetValue(), "no"));
bool bUnpack = bUnpackParam && (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone);
bool bUnpack = g_pOptions->GetUnpack() && (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone);
bool bParFailed = pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psRepairPossible ||
@@ -725,8 +654,6 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
bool bMoveInter = !bUnpack &&
pPostInfo->GetNZBInfo()->GetMoveStatus() == NZBInfo::msNone &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usFailure &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usSpace &&
pPostInfo->GetNZBInfo()->GetUnpackStatus() != NZBInfo::usPassword &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure &&
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psManual &&
strlen(g_pOptions->GetInterDir()) > 0 &&
@@ -737,8 +664,9 @@ void PrePostProcessor::StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostIn
if (bUnpack && bParFailed)
{
warn("Skipping unpack for %s due to %s", pPostInfo->GetInfoName(),
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psManual ? "required par-repair" : "par-failure");
warn("Skipping unpack due to %s for %s",
pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psManual ? "required par-repair" : "par-failure",
pPostInfo->GetInfoName());
pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
bUnpack = false;
}
@@ -801,15 +729,22 @@ void PrePostProcessor::JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPo
pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess ||
(pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usNone &&
pPostInfo->GetNZBInfo()->GetScriptStatuses()->CalcTotalStatus() == ScriptStatus::srSuccess);
if (g_pOptions->GetParCleanupQueue() && bCanCleanupQueue)
if ((g_pOptions->GetParCleanupQueue() || g_pOptions->GetNzbCleanupDisk()) && bCanCleanupQueue)
{
FileInfo* pFileInfo = GetQueueGroup(pDownloadQueue, pPostInfo->GetNZBInfo());
if (pFileInfo)
if (g_pOptions->GetParCleanupQueue())
{
info("Cleaning up download queue for %s", pPostInfo->GetNZBInfo()->GetName());
pFileInfo->GetNZBInfo()->ClearCompletedFiles();
pFileInfo->GetNZBInfo()->SetParCleanup(true);
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupDelete, 0, NULL);
FileInfo* pFileInfo = GetQueueGroup(pDownloadQueue, pPostInfo->GetNZBInfo());
if (pFileInfo)
{
info("Cleaning up download queue for %s", pPostInfo->GetNZBInfo()->GetName());
pFileInfo->GetNZBInfo()->ClearCompletedFiles();
pFileInfo->GetNZBInfo()->SetParCleanup(true);
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupDelete, 0, NULL);
}
}
if (g_pOptions->GetNzbCleanupDisk())
{
DeleteQueuedFile(pPostInfo->GetNZBInfo()->GetQueuedFilename());
}
}
@@ -879,9 +814,15 @@ FileInfo* PrePostProcessor::GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo
void PrePostProcessor::ApplySchedulerState()
{
if (g_pScheduler->GetDownloadRateChanged())
{
info("Scheduler: set download rate to %i KB/s", g_pScheduler->GetDownloadRate() / 1024);
g_pOptions->SetDownloadRate(g_pScheduler->GetDownloadRate());
}
if (g_pScheduler->GetPauseDownloadChanged())
{
info("Scheduler: %s download queue", g_pScheduler->GetPauseDownload() ? "pausing" : "unpausing");
info("Scheduler: %s download queue", g_pScheduler->GetPauseDownload() ? "pause" : "unpause");
m_bSchedulerPauseChanged = true;
m_bSchedulerPause = g_pScheduler->GetPauseDownload();
if (!m_bPostPause)
@@ -889,6 +830,12 @@ void PrePostProcessor::ApplySchedulerState()
g_pOptions->SetPauseDownload(m_bSchedulerPause);
}
}
if (g_pScheduler->GetPauseScanChanged())
{
info("Scheduler: %s scan", g_pScheduler->GetPauseScan() ? "pause" : "unpause");
g_pOptions->SetPauseScan(g_pScheduler->GetPauseScan());
}
}
void PrePostProcessor::UpdatePauseState(bool bNeedPause, const char* szReason)
@@ -968,17 +915,9 @@ bool PrePostProcessor::QueueEditList(IDList* pIDList, EEditAction eAction, int i
return PostQueueDelete(pIDList);
case eaHistoryDelete:
case eaHistoryFinalDelete:
case eaHistoryReturn:
case eaHistoryProcess:
case eaHistoryRedownload:
case eaHistorySetParameter:
case eaHistorySetDupeKey:
case eaHistorySetDupeScore:
case eaHistorySetDupeMode:
case eaHistorySetDupeBackup:
case eaHistoryMarkBad:
case eaHistoryMarkGood:
return HistoryEdit(pIDList, eAction, iOffset, szText);
default:
@@ -1130,8 +1069,7 @@ bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOf
switch (eAction)
{
case eaHistoryDelete:
case eaHistoryFinalDelete:
HistoryDelete(pDownloadQueue, itHistory, pHistoryInfo, eAction == eaHistoryFinalDelete);
HistoryDelete(pDownloadQueue, itHistory, pHistoryInfo);
break;
case eaHistoryReturn:
@@ -1139,26 +1077,10 @@ bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOf
HistoryReturn(pDownloadQueue, itHistory, pHistoryInfo, eAction == eaHistoryProcess);
break;
case eaHistoryRedownload:
HistoryRedownload(pDownloadQueue, itHistory, pHistoryInfo, false);
break;
case eaHistorySetParameter:
HistorySetParameter(pHistoryInfo, szText);
break;
case eaHistorySetDupeKey:
case eaHistorySetDupeScore:
case eaHistorySetDupeMode:
case eaHistorySetDupeBackup:
HistorySetDupeParam(pHistoryInfo, eAction, szText);
break;
case eaHistoryMarkBad:
case eaHistoryMarkGood:
m_DupeCoordinator.HistoryMark(pDownloadQueue, pHistoryInfo, eAction == eaHistoryMarkGood);
break;
default:
// nothing, just to avoid compiler warning
break;
@@ -1180,8 +1102,7 @@ bool PrePostProcessor::HistoryEdit(IDList* pIDList, EEditAction eAction, int iOf
return bOK;
}
void PrePostProcessor::HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory,
HistoryInfo* pHistoryInfo, bool bFinal)
void PrePostProcessor::HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
@@ -1212,36 +1133,10 @@ void PrePostProcessor::HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList:
index++;
}
}
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
g_pOptions->GetDeleteCleanupDisk() &&
(pHistoryInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsNone ||
pHistoryInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psFailure ||
pHistoryInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usFailure ||
pHistoryInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usPassword) &&
Util::DirectoryExists(pHistoryInfo->GetNZBInfo()->GetDestDir()))
{
info("Deleting %s", pHistoryInfo->GetNZBInfo()->GetDestDir());
Util::DeleteDirectoryWithContent(pHistoryInfo->GetNZBInfo()->GetDestDir());
}
if (bFinal || !g_pOptions->GetDupeCheck() || pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo)
{
pDownloadQueue->GetHistoryList()->erase(itHistory);
delete pHistoryInfo;
}
else
{
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
// replace history element
int rindex = pDownloadQueue->GetHistoryList()->size() - 1 - (itHistory - pDownloadQueue->GetHistoryList()->begin());
m_DupeCoordinator.HistoryTransformToDup(pDownloadQueue, pHistoryInfo, rindex);
}
}
pDownloadQueue->GetHistoryList()->erase(itHistory);
delete pHistoryInfo;
}
void PrePostProcessor::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess)
@@ -1286,18 +1181,11 @@ void PrePostProcessor::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList:
pNZBInfo->SetParCleanup(false);
if (!pNZBInfo->GetUnpackCleanedUpDisk())
{
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->SetUnpackStatus(NZBInfo::usNone);
pNZBInfo->SetCleanupStatus(NZBInfo::csNone);
if (m_ParCoordinator.FindMainPars(pNZBInfo->GetDestDir(), NULL))
{
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
}
}
pNZBInfo->SetDeleteStatus(NZBInfo::dsNone);
pNZBInfo->SetDeletePaused(false);
pNZBInfo->SetMarkStatus(NZBInfo::ksNone);
pNZBInfo->GetScriptStatuses()->Clear();
pNZBInfo->SetParkedFileCount(0);
}
@@ -1335,88 +1223,6 @@ void PrePostProcessor::HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList:
}
}
void PrePostProcessor::HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory,
HistoryInfo* pHistoryInfo, bool bRestorePauseState)
{
NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo();
bool bPaused = bRestorePauseState && pNZBInfo->GetDeletePaused();
int iGroupdID = -1;
if (!Util::FileExists(pNZBInfo->GetQueuedFilename()))
{
error("Could not return collection %s from history back to queue: could not find source nzb-file %s",
pNZBInfo->GetName(), pNZBInfo->GetQueuedFilename());
return;
}
NZBFile* pNZBFile = NZBFile::Create(pNZBInfo->GetQueuedFilename(), "");
if (pNZBFile == NULL)
{
error("Could not return collection %s from history back to queue: could not parse nzb-file",
pNZBInfo->GetName());
return;
}
info("Returning collection %s from history back to queue", pNZBInfo->GetName());
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
pFileInfo->SetNZBInfo(pNZBInfo);
pFileInfo->SetPaused(bPaused);
iGroupdID = pFileInfo->GetID();
}
if (Util::DirectoryExists(pNZBInfo->GetDestDir()))
{
detail("Deleting %s", pNZBInfo->GetDestDir());
Util::DeleteDirectoryWithContent(pNZBInfo->GetDestDir());
}
pNZBInfo->BuildDestDirName();
if (Util::DirectoryExists(pNZBInfo->GetDestDir()))
{
detail("Deleting %s", pNZBInfo->GetDestDir());
Util::DeleteDirectoryWithContent(pNZBInfo->GetDestDir());
}
// reset status fields (which are not reset by "HistoryReturn")
pNZBInfo->SetMoveStatus(NZBInfo::msNone);
pNZBInfo->SetUnpackCleanedUpDisk(false);
pNZBInfo->SetParStatus(NZBInfo::psNone);
pNZBInfo->SetRenameStatus(NZBInfo::rsNone);
pNZBInfo->ClearCompletedFiles();
pNZBInfo->GetServerStats()->Clear();
// take file stats from newly read nzb-file
pNZBInfo->SetFileCount(pNZBFile->GetNZBInfo()->GetFileCount());
pNZBInfo->SetFullContentHash(pNZBFile->GetNZBInfo()->GetFullContentHash());
pNZBInfo->SetFilteredContentHash(pNZBFile->GetNZBInfo()->GetFilteredContentHash());
pNZBInfo->SetSize(pNZBFile->GetNZBInfo()->GetSize());
pNZBInfo->SetSuccessSize(pNZBFile->GetNZBInfo()->GetSuccessSize());
pNZBInfo->SetCurrentSuccessSize(pNZBFile->GetNZBInfo()->GetCurrentSuccessSize());
pNZBInfo->SetFailedSize(pNZBFile->GetNZBInfo()->GetFailedSize());
pNZBInfo->SetCurrentFailedSize(pNZBFile->GetNZBInfo()->GetCurrentFailedSize());
pNZBInfo->SetParSize(pNZBFile->GetNZBInfo()->GetParSize());
pNZBInfo->SetParSuccessSize(pNZBFile->GetNZBInfo()->GetParSuccessSize());
pNZBInfo->SetParCurrentSuccessSize(pNZBFile->GetNZBInfo()->GetParCurrentSuccessSize());
pNZBInfo->SetParFailedSize(pNZBFile->GetNZBInfo()->GetParFailedSize());
pNZBInfo->SetParCurrentFailedSize(pNZBFile->GetNZBInfo()->GetParCurrentFailedSize());
pNZBInfo->SetSuccessArticles(pNZBFile->GetNZBInfo()->GetSuccessArticles());
pNZBInfo->SetFailedArticles(pNZBFile->GetNZBInfo()->GetFailedArticles());
g_pQueueCoordinator->AddFileInfosToFileQueue(pNZBFile, pDownloadQueue->GetParkedFiles(), false);
delete pNZBFile;
HistoryReturn(pDownloadQueue, itHistory, pHistoryInfo, false);
if (!bPaused && g_pOptions->GetParCheck() != Options::pcForce)
{
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, iGroupdID, false, QueueEditor::eaGroupPauseExtraPars, 0, NULL);
}
}
void PrePostProcessor::HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText)
{
char szNiceName[1024];
@@ -1436,7 +1242,7 @@ void PrePostProcessor::HistorySetParameter(HistoryInfo* pHistoryInfo, const char
{
*szValue = '\0';
szValue++;
pHistoryInfo->GetNZBInfo()->GetParameters()->SetParameter(szStr, szValue);
pHistoryInfo->GetNZBInfo()->SetParameter(szStr, szValue);
}
else
{
@@ -1445,97 +1251,3 @@ void PrePostProcessor::HistorySetParameter(HistoryInfo* pHistoryInfo, const char
free(szStr);
}
void PrePostProcessor::HistorySetDupeParam(HistoryInfo* pHistoryInfo, EEditAction eAction, const char* szText)
{
char szNiceName[1024];
pHistoryInfo->GetName(szNiceName, 1024);
debug("Setting dupe-parameter '%i'='%s' for '%s'", (int)eAction, szText, szNiceName);
if (!(pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ||
pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo))
{
error("Could not set duplicate parameter for %s: history item has wrong type", szNiceName);
return;
}
EDupeMode eMode = dmScore;
if (eAction == eaHistorySetDupeMode)
{
if (!strcasecmp(szText, "SCORE"))
{
eMode = dmScore;
}
else if (!strcasecmp(szText, "ALL"))
{
eMode = dmAll;
}
else if (!strcasecmp(szText, "FORCE"))
{
eMode = dmForce;
}
else
{
error("Could not set duplicate mode for %s: incorrect mode (%s)", szNiceName, szText);
return;
}
}
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
{
switch (eAction)
{
case eaHistorySetDupeKey:
pHistoryInfo->GetNZBInfo()->SetDupeKey(szText);
break;
case eaHistorySetDupeScore:
pHistoryInfo->GetNZBInfo()->SetDupeScore(atoi(szText));
break;
case eaHistorySetDupeMode:
pHistoryInfo->GetNZBInfo()->SetDupeMode(eMode);
break;
case eaHistorySetDupeBackup:
if (pHistoryInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsDupe &&
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsManual)
{
error("Could not set duplicate parameter for %s: history item has wrong delete status", szNiceName);
return;
}
pHistoryInfo->GetNZBInfo()->SetDeleteStatus(!strcasecmp(szText, "YES") ||
!strcasecmp(szText, "TRUE") || !strcasecmp(szText, "1") ? NZBInfo::dsDupe : NZBInfo::dsManual);
break;
default:
// suppress compiler warning
break;
}
}
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo)
{
switch (eAction)
{
case eaHistorySetDupeKey:
pHistoryInfo->GetDupInfo()->SetDupeKey(szText);
break;
case eaHistorySetDupeScore:
pHistoryInfo->GetDupInfo()->SetDupeScore(atoi(szText));
break;
case eaHistorySetDupeMode:
pHistoryInfo->GetDupInfo()->SetDupeMode(eMode);
break;
case eaHistorySetDupeBackup:
error("Could not set duplicate parameter for %s: history item has wrong type", szNiceName);
return;
default:
// suppress compiler warning
break;
}
}
}

View File

@@ -32,12 +32,10 @@
#include "Observer.h"
#include "DownloadInfo.h"
#include "ParCoordinator.h"
#include "DupeCoordinator.h"
class PrePostProcessor : public Thread
{
public:
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
enum EEditAction
{
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
@@ -45,17 +43,9 @@ public:
eaPostMoveBottom,
eaPostDelete,
eaHistoryDelete,
eaHistoryFinalDelete,
eaHistoryReturn,
eaHistoryProcess,
eaHistoryRedownload,
eaHistorySetParameter,
eaHistorySetDupeKey,
eaHistorySetDupeScore,
eaHistorySetDupeMode,
eaHistorySetDupeBackup,
eaHistoryMarkBad,
eaHistoryMarkGood
eaHistorySetParameter
};
private:
@@ -73,22 +63,12 @@ private:
protected:
virtual bool PauseDownload() { return m_pOwner->PauseDownload(); }
virtual bool UnpauseDownload() { return m_pOwner->UnpauseDownload(); }
friend class PrePostProcessor;
};
class PostDupeCoordinator: public DupeCoordinator
{
private:
PrePostProcessor* m_pOwner;
protected:
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
virtual void DeleteQueuedFile(const char* szQueuedFile) { m_pOwner->DeleteQueuedFile(szQueuedFile); }
friend class PrePostProcessor;
};
private:
PostParCoordinator m_ParCoordinator;
PostDupeCoordinator m_DupeCoordinator;
QueueCoordinatorObserver m_QueueCoordinatorObserver;
bool m_bHasMoreJobs;
bool m_bSchedulerPauseChanged;
@@ -109,25 +89,21 @@ private:
void UpdatePauseState(bool bNeedPause, const char* szReason);
bool PauseDownload();
bool UnpauseDownload();
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
void DeleteQueuedFile(const char* szQueuedFile);
int FindGroupID(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
bool PostQueueDelete(IDList* pIDList);
bool HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bFinal);
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo);
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bRestorePauseState);
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
void HistorySetDupeParam(HistoryInfo* pHistoryInfo, EEditAction eAction, const char* szText);
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
void CheckHistory();
void Cleanup();
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
void CheckHistory();
void DeletePostThread(PostInfo* pPostInfo);
public:

View File

@@ -40,7 +40,6 @@
#include <unistd.h>
#include <sys/time.h>
#endif
#include <algorithm>
#include "nzbget.h"
#include "QueueCoordinator.h"
@@ -68,7 +67,6 @@ QueueCoordinator::QueueCoordinator()
m_tStartDownload = 0;
m_tPausedFrom = 0;
m_bStandBy = true;
m_iServerConfigGeneration = 0;
YDecoder::Init();
}
@@ -103,13 +101,6 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Lock();
if (g_pOptions->GetServerMode())
{
g_pDiskState->LoadStats(g_pServerPool->GetServers());
// currently there are no any stats but we need to save current server list into diskstate
g_pDiskState->SaveStats(g_pServerPool->GetServers());
}
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
{
if (g_pOptions->GetReloadQueue())
@@ -126,7 +117,17 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Unlock();
AdjustDownloadsLimit();
// compute maximum number of allowed download threads
m_iDownloadsLimit = 2; // two extra threads for completing files (when connections are not needed)
for (ServerPool::Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() == 0)
{
m_iDownloadsLimit += pNewsServer->GetMaxConnections();
}
}
m_tStartServer = time(NULL);
m_tLastCheck = m_tStartServer;
bool bWasStandBy = true;
@@ -135,7 +136,6 @@ void QueueCoordinator::Run()
while (!IsStopped())
{
bool bDownloadsChecked = false;
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, NULL, NULL);
@@ -149,7 +149,6 @@ void QueueCoordinator::Run()
m_mutexDownloadQueue.Lock();
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
bDownloadsChecked = true;
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
if (bHasMoreArticles && !IsStopped() && (int)m_ActiveDownloads.size() < m_iDownloadsLimit)
{
@@ -168,8 +167,7 @@ void QueueCoordinator::Run()
}
}
}
if (!bDownloadsChecked)
else
{
m_mutexDownloadQueue.Lock();
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
@@ -200,7 +198,6 @@ void QueueCoordinator::Run()
ResetHangingDownloads();
iResetCounter = 0;
AdjustStartTime();
AdjustDownloadsLimit();
}
}
@@ -220,94 +217,16 @@ void QueueCoordinator::Run()
debug("Exiting QueueCoordinator-loop");
}
/*
* Compute maximum number of allowed download threads
**/
void QueueCoordinator::AdjustDownloadsLimit()
{
if (m_iServerConfigGeneration == g_pServerPool->GetGeneration())
{
return;
}
// two extra threads for completing files (when connections are not needed)
int iDownloadsLimit = 2;
// allow one thread per 0-level (main) and 1-level (backup) server connection
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pNewsServer = *it;
if ((pNewsServer->GetNormLevel() == 0 || pNewsServer->GetNormLevel() == 1) && pNewsServer->GetActive())
{
iDownloadsLimit += pNewsServer->GetMaxConnections();
}
}
m_iDownloadsLimit = iDownloadsLimit;
}
void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
{
debug("Adding NZBFile to queue");
m_mutexDownloadQueue.Lock();
Aspect foundAspect = { eaNZBFileFound, &m_DownloadQueue, pNZBFile->GetNZBInfo(), NULL };
Notify(&foundAspect);
if (pNZBFile->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsNone)
{
bool bAllPaused = !pNZBFile->GetFileInfos()->empty();
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
bAllPaused &= pFileInfo->GetPaused();
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->DiscardFile(pFileInfo);
}
}
pNZBFile->GetNZBInfo()->SetDeletePaused(bAllPaused);
}
if (pNZBFile->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsManual)
{
m_mutexDownloadQueue.Unlock(); // UNLOCK
return;
}
m_DownloadQueue.GetNZBInfoList()->Add(pNZBFile->GetNZBInfo());
if (pNZBFile->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone)
{
AddFileInfosToFileQueue(pNZBFile, m_DownloadQueue.GetFileQueue(), bAddFirst);
}
char szNZBName[1024];
strncpy(szNZBName, pNZBFile->GetNZBInfo()->GetName(), sizeof(szNZBName)-1);
Aspect aspect = { eaNZBFileAdded, &m_DownloadQueue, pNZBFile->GetNZBInfo(), NULL };
Notify(&aspect);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
if (pNZBFile->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsNone)
{
info("Collection %s added to queue", szNZBName);
}
}
void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFileQueue, bool bAddFirst)
{
debug("Adding NZBFile to queue");
FileQueue tmpFileQueue;
tmpFileQueue.clear();
FileQueue DupeList;
DupeList.clear();
int index1 = 0;
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
@@ -315,9 +234,14 @@ void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFi
index1++;
FileInfo* pFileInfo = *it;
if (g_pOptions->GetDupeCheck() && !pNZBFile->GetNZBInfo()->GetDupeMode())
if (g_pOptions->GetDupeCheck())
{
bool dupe = false;
if (IsDupe(pFileInfo))
{
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
dupe = true;
}
int index2 = 0;
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
{
@@ -328,7 +252,7 @@ void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFi
(pFileInfo->GetSize() < pFileInfo2->GetSize() ||
(pFileInfo->GetSize() == pFileInfo2->GetSize() && index2 < index1)))
{
warn("File \"%s\" appears twice in collection, adding only the biggest file", pFileInfo->GetFilename());
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
dupe = true;
break;
}
@@ -336,7 +260,6 @@ void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFi
if (dupe)
{
DupeList.push_back(pFileInfo);
StatFileInfo(pFileInfo, false);
continue;
}
}
@@ -355,11 +278,11 @@ void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFi
{
if (bAddFirst)
{
pFileQueue->push_front(*it);
m_DownloadQueue.GetFileQueue()->push_front(*it);
}
else
{
pFileQueue->push_back(*it);
m_DownloadQueue.GetFileQueue()->push_back(*it);
}
}
@@ -373,7 +296,19 @@ void QueueCoordinator::AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFi
delete pFileInfo;
}
m_DownloadQueue.GetNZBInfoList()->Add(pNZBFile->GetNZBInfo());
pNZBFile->DetachFileInfos();
Aspect aspect = { eaNZBFileAdded, &m_DownloadQueue, pNZBFile->GetNZBInfo(), NULL };
Notify(&aspect);
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
{
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
}
m_mutexDownloadQueue.Unlock();
}
/*
@@ -512,8 +447,6 @@ bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
}
if (!hasDownloads)
{
StatFileInfo(pFileInfo, false);
Aspect aspect = { eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
@@ -614,7 +547,10 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
}
}
free(pCheckedFiles);
if (pCheckedFiles)
{
free(pCheckedFiles);
}
return bOK;
}
@@ -631,7 +567,7 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
pArticleDownloader->SetConnection(pConnection);
char szInfoName[1024];
snprintf(szInfoName, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), (int)pFileInfo->GetArticles()->size());
snprintf(szInfoName, 1024, "%s%c%s [%i/%i]", pFileInfo->GetNZBInfo()->GetName(), (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
szInfoName[1024-1] = '\0';
pArticleDownloader->SetInfoName(szInfoName);
@@ -680,18 +616,10 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
{
pArticleInfo->SetStatus(ArticleInfo::aiFinished);
pFileInfo->SetSuccessSize(pFileInfo->GetSuccessSize() + pArticleInfo->GetSize());
pFileInfo->GetNZBInfo()->SetCurrentSuccessSize(pFileInfo->GetNZBInfo()->GetCurrentSuccessSize() + pArticleInfo->GetSize());
pFileInfo->GetNZBInfo()->SetParCurrentSuccessSize(pFileInfo->GetNZBInfo()->GetParCurrentSuccessSize() + (pFileInfo->GetParFile() ? pArticleInfo->GetSize() : 0));
pFileInfo->SetSuccessArticles(pFileInfo->GetSuccessArticles() + 1);
}
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
{
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
pFileInfo->SetFailedSize(pFileInfo->GetFailedSize() + pArticleInfo->GetSize());
pFileInfo->GetNZBInfo()->SetCurrentFailedSize(pFileInfo->GetNZBInfo()->GetCurrentFailedSize() + pArticleInfo->GetSize());
pFileInfo->GetNZBInfo()->SetParCurrentFailedSize(pFileInfo->GetNZBInfo()->GetParCurrentFailedSize() + (pFileInfo->GetParFile() ? pArticleInfo->GetSize() : 0));
pFileInfo->SetFailedArticles(pFileInfo->GetFailedArticles() + 1);
}
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adRetry)
{
@@ -704,7 +632,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
pFileInfo->GetNZBInfo()->GetServerStats()->Add(pArticleDownloader->GetServerStats());
}
if (!pFileInfo->GetFilenameConfirmed() &&
@@ -713,10 +640,7 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
{
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
pFileInfo->SetFilenameConfirmed(true);
if (g_pOptions->GetDupeCheck() &&
pFileInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
!pFileInfo->GetNZBInfo()->GetManyDupeFiles() &&
Util::FileExists(pFileInfo->GetNZBInfo()->GetDestDir(), pFileInfo->GetFilename()))
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
{
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
fileCompleted = false;
@@ -727,6 +651,23 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
bool deleteFileObj = false;
if (pFileInfo->GetDeleted())
{
int cnt = 0;
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
if ((*it)->GetFileInfo() == pFileInfo)
{
cnt++;
}
}
if (cnt == 1)
{
// this was the last Download for a file deleted from queue
deleteFileObj = true;
}
}
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
{
// all jobs done
@@ -736,22 +677,16 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
deleteFileObj = true;
}
CheckHealth(pFileInfo);
bool hasOtherDownloaders = false;
// delete Download from Queue
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
ArticleDownloader* pDownloader = *it;
if (pDownloader != pArticleDownloader && pDownloader->GetFileInfo() == pFileInfo)
ArticleDownloader* pa = *it;
if (pa == pArticleDownloader)
{
hasOtherDownloaders = true;
m_ActiveDownloads.erase(it);
break;
}
}
deleteFileObj |= pFileInfo->GetDeleted() && !hasOtherDownloaders;
// remove downloader from downloader list
m_ActiveDownloads.erase(std::find(m_ActiveDownloads.begin(), m_ActiveDownloads.end(), pArticleDownloader));
pFileInfo->SetActiveDownloads(pFileInfo->GetActiveDownloads() - 1);
@@ -761,8 +696,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
// delete File from Queue
pFileInfo->SetDeleted(true);
StatFileInfo(pFileInfo, fileCompleted);
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, &m_DownloadQueue, pFileInfo->GetNZBInfo(), pFileInfo };
Notify(&aspect);
@@ -776,40 +709,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
m_mutexDownloadQueue.Unlock();
}
void QueueCoordinator::StatFileInfo(FileInfo* pFileInfo, bool bCompleted)
{
NZBInfo* pNZBInfo = pFileInfo->GetNZBInfo();
if (bCompleted || pNZBInfo->GetDeleting())
{
pNZBInfo->SetSuccessSize(pNZBInfo->GetSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetFailedSize(pNZBInfo->GetFailedSize() + pFileInfo->GetFailedSize());
pNZBInfo->SetFailedArticles(pNZBInfo->GetFailedArticles() + pFileInfo->GetFailedArticles() + pFileInfo->GetMissedArticles());
pNZBInfo->SetSuccessArticles(pNZBInfo->GetSuccessArticles() + pFileInfo->GetSuccessArticles());
if (pFileInfo->GetParFile())
{
pNZBInfo->SetParSuccessSize(pNZBInfo->GetParSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetParFailedSize(pNZBInfo->GetParFailedSize() + pFileInfo->GetFailedSize());
}
}
else if (!pNZBInfo->GetDeleting() && !pNZBInfo->GetParCleanup())
{
// file deleted but not the whole nzb and not par-cleanup
pNZBInfo->SetFileCount(pNZBInfo->GetFileCount() - 1);
pNZBInfo->SetSize(pNZBInfo->GetSize() - pFileInfo->GetSize());
pNZBInfo->SetCurrentSuccessSize(pNZBInfo->GetCurrentSuccessSize() - pFileInfo->GetSuccessSize());
pNZBInfo->SetFailedSize(pNZBInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pNZBInfo->SetCurrentFailedSize(pNZBInfo->GetCurrentFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pNZBInfo->SetTotalArticles(pNZBInfo->GetTotalArticles() - pFileInfo->GetTotalArticles());
if (pFileInfo->GetParFile())
{
pNZBInfo->SetParSize(pNZBInfo->GetParSize() - pFileInfo->GetSize());
pNZBInfo->SetParCurrentSuccessSize(pNZBInfo->GetParCurrentSuccessSize() - pFileInfo->GetSuccessSize());
pNZBInfo->SetParFailedSize(pNZBInfo->GetParFailedSize() - pFileInfo->GetMissedSize());
pNZBInfo->SetParCurrentFailedSize(pNZBInfo->GetParCurrentFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
}
}
}
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
{
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
@@ -857,30 +756,29 @@ void QueueCoordinator::DiscardDiskFile(FileInfo* pFileInfo)
}
}
void QueueCoordinator::CheckHealth(FileInfo* pFileInfo)
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
{
if (g_pOptions->GetHealthCheck() == Options::hcNone ||
pFileInfo->GetNZBInfo()->GetHealthPaused() ||
pFileInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsHealth ||
pFileInfo->GetNZBInfo()->CalcHealth() >= pFileInfo->GetNZBInfo()->CalcCriticalHealth())
debug("Checking if the file is already queued");
// checking on disk
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
{
return;
return true;
}
if (g_pOptions->GetHealthCheck() == Options::hcPause)
// checking in queue
for (FileQueue::iterator it = m_DownloadQueue.GetFileQueue()->begin(); it != m_DownloadQueue.GetFileQueue()->end(); it++)
{
warn("Pausing %s due to health %.1f%% below critical %.1f%%", pFileInfo->GetNZBInfo()->GetName(),
pFileInfo->GetNZBInfo()->CalcHealth() / 10.0, pFileInfo->GetNZBInfo()->CalcCriticalHealth() / 10.0);
pFileInfo->GetNZBInfo()->SetHealthPaused(true);
m_QueueEditor.LockedEditEntry(&m_DownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupPause, 0, NULL);
}
else if (g_pOptions->GetHealthCheck() == Options::hcDelete)
{
warn("Cancelling download and deleting %s due to health %.1f%% below critical %.1f%%", pFileInfo->GetNZBInfo()->GetName(),
pFileInfo->GetNZBInfo()->CalcHealth() / 10.0, pFileInfo->GetNZBInfo()->CalcCriticalHealth() / 10.0);
pFileInfo->GetNZBInfo()->SetDeleteStatus(NZBInfo::dsHealth);
m_QueueEditor.LockedEditEntry(&m_DownloadQueue, pFileInfo->GetID(), false, QueueEditor::eaGroupDelete, 0, NULL);
FileInfo* pQueueEntry = *it;
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
pFileInfo != pQueueEntry)
{
return true;
}
}
return false;
}
void QueueCoordinator::LogDebugInfo()
@@ -1113,20 +1011,7 @@ bool QueueCoordinator::MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZB
}
pDestNZBInfo->SetFileCount(pDestNZBInfo->GetFileCount() + pSrcNZBInfo->GetFileCount());
pDestNZBInfo->SetFullContentHash(0);
pDestNZBInfo->SetFilteredContentHash(0);
pDestNZBInfo->SetSize(pDestNZBInfo->GetSize() + pSrcNZBInfo->GetSize());
pDestNZBInfo->SetSuccessSize(pDestNZBInfo->GetSuccessSize() + pSrcNZBInfo->GetSuccessSize());
pDestNZBInfo->SetCurrentSuccessSize(pDestNZBInfo->GetCurrentSuccessSize() + pSrcNZBInfo->GetCurrentSuccessSize());
pDestNZBInfo->SetFailedSize(pDestNZBInfo->GetFailedSize() + pSrcNZBInfo->GetFailedSize());
pDestNZBInfo->SetCurrentFailedSize(pDestNZBInfo->GetCurrentFailedSize() + pSrcNZBInfo->GetCurrentFailedSize());
pDestNZBInfo->SetParSize(pDestNZBInfo->GetParSize() + pSrcNZBInfo->GetParSize());
pDestNZBInfo->SetParSuccessSize(pDestNZBInfo->GetParSuccessSize() + pSrcNZBInfo->GetParSuccessSize());
pDestNZBInfo->SetParCurrentSuccessSize(pDestNZBInfo->GetParCurrentSuccessSize() + pSrcNZBInfo->GetParCurrentSuccessSize());
pDestNZBInfo->SetParFailedSize(pDestNZBInfo->GetParFailedSize() + pSrcNZBInfo->GetParFailedSize());
pDestNZBInfo->SetParCurrentFailedSize(pDestNZBInfo->GetParCurrentFailedSize() + pSrcNZBInfo->GetParCurrentFailedSize());
// reattach completed file items to new NZBInfo-object
for (NZBInfo::Files::iterator it = pSrcNZBInfo->GetCompletedFiles()->begin(); it != pSrcNZBInfo->GetCompletedFiles()->end(); it++)
@@ -1183,20 +1068,20 @@ bool QueueCoordinator::SplitQueueEntries(FileQueue* pFileList, const char* szNam
}
NZBInfo* pNZBInfo = new NZBInfo();
pNZBInfo->Retain();
pNZBInfo->AddReference();
m_DownloadQueue.GetNZBInfoList()->Add(pNZBInfo);
pNZBInfo->SetFilename(pSrcNZBInfo->GetFilename());
pNZBInfo->SetName(szName);
pNZBInfo->SetCategory(pSrcNZBInfo->GetCategory());
pNZBInfo->SetFullContentHash(0);
pNZBInfo->SetFilteredContentHash(0);
pNZBInfo->BuildDestDirName();
pNZBInfo->SetQueuedFilename(pSrcNZBInfo->GetQueuedFilename());
pNZBInfo->GetParameters()->CopyFrom(pSrcNZBInfo->GetParameters());
pSrcNZBInfo->SetFullContentHash(0);
pSrcNZBInfo->SetFilteredContentHash(0);
for (NZBParameterList::iterator it = pSrcNZBInfo->GetParameters()->begin(); it != pSrcNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pNZBParameter = *it;
pNZBInfo->SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
for (FileQueue::iterator it = pFileList->begin(); it != pFileList->end(); it++)
{
@@ -1209,32 +1094,9 @@ bool QueueCoordinator::SplitQueueEntries(FileQueue* pFileList, const char* szNam
pSrcNZBInfo->SetFileCount(pSrcNZBInfo->GetFileCount() - 1);
pSrcNZBInfo->SetSize(pSrcNZBInfo->GetSize() - pFileInfo->GetSize());
pSrcNZBInfo->SetSuccessSize(pSrcNZBInfo->GetSuccessSize() - pFileInfo->GetSuccessSize());
pSrcNZBInfo->SetCurrentSuccessSize(pSrcNZBInfo->GetCurrentSuccessSize() - pFileInfo->GetSuccessSize());
pSrcNZBInfo->SetFailedSize(pSrcNZBInfo->GetFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pSrcNZBInfo->SetCurrentFailedSize(pSrcNZBInfo->GetCurrentFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pNZBInfo->SetFileCount(pNZBInfo->GetFileCount() + 1);
pNZBInfo->SetSize(pNZBInfo->GetSize() + pFileInfo->GetSize());
pNZBInfo->SetSuccessSize(pNZBInfo->GetSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetCurrentSuccessSize(pNZBInfo->GetCurrentSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetFailedSize(pNZBInfo->GetFailedSize() + pFileInfo->GetFailedSize() + pFileInfo->GetMissedSize());
pNZBInfo->SetCurrentFailedSize(pNZBInfo->GetCurrentFailedSize() + pFileInfo->GetFailedSize() + pFileInfo->GetMissedSize());
if (pFileInfo->GetParFile())
{
pSrcNZBInfo->SetParSize(pSrcNZBInfo->GetParSize() - pFileInfo->GetSize());
pSrcNZBInfo->SetParSuccessSize(pSrcNZBInfo->GetParSuccessSize() - pFileInfo->GetSuccessSize());
pSrcNZBInfo->SetParCurrentSuccessSize(pSrcNZBInfo->GetParCurrentSuccessSize() - pFileInfo->GetSuccessSize());
pSrcNZBInfo->SetParFailedSize(pSrcNZBInfo->GetParFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pSrcNZBInfo->SetParCurrentFailedSize(pSrcNZBInfo->GetParCurrentFailedSize() - pFileInfo->GetFailedSize() - pFileInfo->GetMissedSize());
pNZBInfo->SetParSize(pNZBInfo->GetParSize() + pFileInfo->GetSize());
pNZBInfo->SetParSuccessSize(pNZBInfo->GetParSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetParCurrentSuccessSize(pNZBInfo->GetParCurrentSuccessSize() + pFileInfo->GetSuccessSize());
pNZBInfo->SetParFailedSize(pNZBInfo->GetParFailedSize() + pFileInfo->GetFailedSize() + pFileInfo->GetMissedSize());
pNZBInfo->SetParCurrentFailedSize(pNZBInfo->GetParCurrentFailedSize() + pFileInfo->GetFailedSize() + pFileInfo->GetMissedSize());
}
}
pNZBInfo->Release();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,15 +43,12 @@ class QueueCoordinator : public Thread, public Observer, public Subject, public
{
public:
typedef std::list<ArticleDownloader*> ActiveDownloads;
enum EAspectAction
{
eaNZBFileFound,
eaNZBFileAdded,
eaFileCompleted,
eaFileDeleted
};
struct Aspect
{
EAspectAction eAction;
@@ -67,7 +64,6 @@ private:
Mutex m_mutexDownloadQueue;
bool m_bHasMoreJobs;
int m_iDownloadsLimit;
int m_iServerConfigGeneration;
// statistics
static const int SPEEDMETER_SLOTS = 30;
@@ -94,15 +90,13 @@ private:
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
bool IsDupe(FileInfo* pFileInfo);
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
void StatFileInfo(FileInfo* pFileInfo, bool bCompleted);
void CheckHealth(FileInfo* pFileInfo);
void ResetHangingDownloads();
void ResetSpeedStat();
void EnterLeaveStandBy(bool bEnter);
void AdjustStartTime();
void AdjustDownloadsLimit();
public:
QueueCoordinator();
@@ -121,7 +115,6 @@ public:
DownloadQueue* LockQueue();
void UnlockQueue() ;
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
void AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFileQueue, bool bAddFirst);
bool HasMoreJobs() { return m_bHasMoreJobs; }
bool GetStandBy() { return m_bStandBy; }
bool DeleteQueueEntry(FileInfo* pFileInfo);

View File

@@ -116,14 +116,7 @@ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
*/
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
{
if (pFileInfo->GetNZBInfo()->GetDeleting())
{
detail("Deleting file %s from download queue", pFileInfo->GetFilename());
}
else
{
info("Deleting file %s from download queue", pFileInfo->GetFilename());
}
info("Deleting file %s from download queue", pFileInfo->GetFilename());
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
}
@@ -228,93 +221,85 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
ItemList cItemList;
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
switch (eAction)
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
{
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
break;
case eaGroupMerge:
return MergeGroups(pDownloadQueue, &cItemList);
case eaFileSplit:
return SplitGroup(pDownloadQueue, &cItemList, szText);
case eaFileReorder:
ReorderFiles(pDownloadQueue, &cItemList);
break;
default:
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
}
else if (eAction == eaGroupMerge)
{
return MergeGroups(pDownloadQueue, &cItemList);
}
else if (eAction == eaFileSplit)
{
return SplitGroup(pDownloadQueue, &cItemList, szText);
}
else if (eAction == eaFileReorder)
{
ReorderFiles(pDownloadQueue, &cItemList);
}
else
{
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
{
EditItem* pItem = *it;
switch (eAction)
{
EditItem* pItem = *it;
switch (eAction)
{
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFilePause:
PauseUnpauseEntry(pItem->m_pFileInfo, true);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileResume:
PauseUnpauseEntry(pItem->m_pFileInfo, false);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileMoveOffset:
case eaFileMoveTop:
case eaFileMoveBottom:
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaFileDelete:
DeleteEntry(pItem->m_pFileInfo);
break;
case eaFileSetPriority:
SetPriorityEntry(pItem->m_pFileInfo, szText);
break;
case eaFileSetPriority:
SetPriorityEntry(pItem->m_pFileInfo, szText);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetCategory:
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetName:
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetName:
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetDupeKey:
case eaGroupSetDupeScore:
case eaGroupSetDupeMode:
SetNZBDupeParam(pItem->m_pFileInfo->GetNZBInfo(), eAction, szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupSetParameter:
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
case eaGroupSetPriority:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
break;
case eaGroupPause:
case eaGroupResume:
case eaGroupDelete:
case eaGroupDupeDelete:
case eaGroupFinalDelete:
case eaGroupMoveTop:
case eaGroupMoveBottom:
case eaGroupMoveOffset:
case eaGroupPauseAllPars:
case eaGroupPauseExtraPars:
case eaGroupSetPriority:
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
break;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
case eaFileSplit:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
case eaFilePauseAllPars:
case eaFilePauseExtraPars:
case eaGroupMerge:
case eaFileReorder:
case eaFileSplit:
// remove compiler warning "enumeration not handled in switch"
break;
}
delete pItem;
}
}
return cItemList.size() > 0;
@@ -483,7 +468,10 @@ bool QueueEditor::BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList*
}
}
delete pRegEx;
if (pRegEx)
{
delete pRegEx;
}
if (!bFound && (eMatchMode == mmName))
{
@@ -498,7 +486,6 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
{
IDList cIDList;
cIDList.clear();
bool bAllPaused = true;
// collecting files belonging to group
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
@@ -507,7 +494,6 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
{
cIDList.push_back(pFileInfo2->GetID());
bAllPaused &= pFileInfo2->GetPaused();
}
}
@@ -556,21 +542,15 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
}
iOffset = iFileOffset;
}
else if (eAction == eaGroupDelete || eAction == eaGroupDupeDelete || eAction == eaGroupFinalDelete)
else if (eAction == eaGroupDelete)
{
pFileInfo->GetNZBInfo()->SetDeleting(true);
pFileInfo->GetNZBInfo()->SetAvoidHistory(eAction == eaGroupFinalDelete);
pFileInfo->GetNZBInfo()->SetDeletePaused(bAllPaused);
if (eAction == eaGroupDupeDelete)
{
pFileInfo->GetNZBInfo()->SetDeleteStatus(NZBInfo::dsDupe);
}
pFileInfo->GetNZBInfo()->SetDeleted(true);
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
}
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause,
eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder, eaFileSplit,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFileDelete, eaFileDelete,
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
@@ -649,7 +629,7 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
}
if (iOffset > 0)
{
for (int i = iNum + 1; i <= (int)cGroupList.size() - iOffset; i++)
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
{
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
{
@@ -835,7 +815,7 @@ void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName)
}
/**
* Check if deletion of already downloaded files is possible (when nzb is deleted from queue).
* Check if deletion of already downloaded files is possible (when nzb id deleted from queue).
* The deletion is most always possible, except the case if all remaining files in queue
* (belonging to this nzb-file) are PARS.
*/
@@ -844,18 +824,15 @@ bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInf
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
{
FileInfo* pFileInfo = *it;
if (pFileInfo->GetNZBInfo() == pNZBInfo)
{
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 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"))
{
// non-par file found
return true;
}
if (!strstr(szLoFileName, ".par2"))
{
// non-par file found
return true;
}
}
@@ -976,7 +953,7 @@ void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
{
*szValue = '\0';
szValue++;
pNZBInfo->GetParameters()->SetParameter(szStr, szValue);
pNZBInfo->SetParameter(szStr, szValue);
}
else
{
@@ -985,47 +962,3 @@ void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
free(szStr);
}
void QueueEditor::SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText)
{
debug("QueueEditor: setting dupe parameter %i='%s' for '%s'", (int)eAction, szText, pNZBInfo->GetName());
switch (eAction)
{
case eaGroupSetDupeKey:
pNZBInfo->SetDupeKey(szText);
break;
case eaGroupSetDupeScore:
pNZBInfo->SetDupeScore(atoi(szText));
break;
case eaGroupSetDupeMode:
{
EDupeMode eMode = dmScore;
if (!strcasecmp(szText, "SCORE"))
{
eMode = dmScore;
}
else if (!strcasecmp(szText, "ALL"))
{
eMode = dmAll;
}
else if (!strcasecmp(szText, "FORCE"))
{
eMode = dmForce;
}
else
{
error("Could not set duplicate mode for %s: incorrect mode (%s)", pNZBInfo->GetName(), szText);
return;
}
pNZBInfo->SetDupeMode(eMode);
break;
}
default:
// suppress compiler warning
break;
}
}

View File

@@ -1,5 +1,5 @@
/*
* This file is part of nzbget
* This file if part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
@@ -33,7 +33,6 @@
class QueueEditor
{
public:
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
enum EEditAction
{
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
@@ -53,18 +52,13 @@ public:
eaGroupPause,
eaGroupResume,
eaGroupDelete,
eaGroupDupeDelete,
eaGroupFinalDelete,
eaGroupPauseAllPars,
eaGroupPauseExtraPars,
eaGroupSetPriority,
eaGroupSetCategory,
eaGroupMerge,
eaGroupSetParameter,
eaGroupSetName,
eaGroupSetDupeKey,
eaGroupSetDupeScore,
eaGroupSetDupeMode
eaGroupSetName
};
enum EMatchMode
@@ -107,7 +101,6 @@ private:
bool SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName);
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
void SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText);
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
void DeleteEntry(FileInfo* pFileInfo);

View File

@@ -76,7 +76,10 @@ RemoteClient::RemoteClient()
RemoteClient::~RemoteClient()
{
delete m_pConnection;
if (m_pConnection)
{
delete m_pConnection;
}
}
void RemoteClient::printf(const char * msg,...)
@@ -210,7 +213,10 @@ bool RemoteClient::RequestServerDownload(const char* szFilename, const char* szC
}
// Cleanup
free(szBuffer);
if (szBuffer)
{
free(szBuffer);
}
return OK;
}
@@ -244,7 +250,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
pNZBInfo->SetQueuedFilename(m_szQueuedFilename);
pNZBInfo->m_bMatch = ntohl(pListAnswer->m_bMatch);
pNZBInfo->Retain();
pNZBInfo->AddReference();
pDownloadQueue->GetNZBInfoList()->Add(pNZBInfo);
pBufPtr += sizeof(SNZBListResponseNZBEntry) + ntohl(pListAnswer->m_iFilenameLen) +
@@ -261,7 +267,7 @@ void RemoteClient::BuildFileList(SNZBListResponse* pListResponse, const char* pT
const char* szValue = pBufPtr + sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen);
NZBInfo* pNZBInfo = pDownloadQueue->GetNZBInfoList()->at(ntohl(pListAnswer->m_iNZBIndex) - 1);
pNZBInfo->GetParameters()->SetParameter(szName, szValue);
pNZBInfo->SetParameter(szName, szValue);
pBufPtr += sizeof(SNZBListResponsePPPEntry) + ntohl(pListAnswer->m_iNameLen) +
ntohl(pListAnswer->m_iValueLen);
@@ -521,20 +527,20 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
{
if (szParameters[0] == '\0')
{
strncat(szParameters, " (", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, " (", 1024);
}
else
{
strncat(szParameters, ", ", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, ", ", 1024);
}
NZBParameter* pNZBParameter = *it;
strncat(szParameters, pNZBParameter->GetName(), sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, "=", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, pNZBParameter->GetValue(), sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, pNZBParameter->GetName(), 1024);
strncat(szParameters, "=", 1024);
strncat(szParameters, pNZBParameter->GetValue(), 1024);
}
if (szParameters[0] != '\0')
{
strncat(szParameters, ")", sizeof(szParameters) - strlen(szParameters) - 1);
strncat(szParameters, ")", 1024);
}
if (!szPattern || ((MatchedNZBInfo*)pGroupInfo->GetNZBInfo())->m_bMatch)
@@ -545,6 +551,8 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
szPaused, szThreads, szCategory, szParameters);
iMatches++;
}
delete pGroupInfo;
}
for (FileQueue::iterator it = cRemoteQueue.GetFileQueue()->begin(); it != cRemoteQueue.GetFileQueue()->end(); it++)
@@ -650,10 +658,10 @@ bool RemoteClient::RequestServerList(bool bFiles, bool bGroups, const char* szPa
if (ntohl(ListResponse.m_iPostJobCount) > 0 || ntohl(ListResponse.m_bPostPaused))
{
strncat(szServerState, strlen(szServerState) > 0 ? ", Post-Processing" : "Post-Processing", sizeof(szServerState) - strlen(szServerState) - 1);
strncat(szServerState, strlen(szServerState) > 0 ? ", Post-Processing" : "Post-Processing", sizeof(szServerState));
if (ntohl(ListResponse.m_bPostPaused))
{
strncat(szServerState, " paused", sizeof(szServerState) - strlen(szServerState) - 1);
strncat(szServerState, " paused", sizeof(szServerState));
}
}

View File

@@ -66,7 +66,10 @@ RemoteServer::~RemoteServer()
{
debug("Destroying RemoteServer");
delete m_pConnection;
if (m_pConnection)
{
delete m_pConnection;
}
}
void RemoteServer::Run()

View File

@@ -67,24 +67,23 @@ Scanner::FileData::~FileData()
}
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus)
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
{
m_szFilename = strdup(szFilename);
m_szNZBName = strdup(szNZBName);
m_szCategory = strdup(szCategory ? szCategory : "");
m_iPriority = iPriority;
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
m_iDupeScore = iDupeScore;
m_eDupeMode = eDupeMode;
m_bAddTop = bAddTop;
m_bAddPaused = bAddPaused;
m_pAddStatus = pAddStatus;
if (pParameters)
{
m_Parameters.CopyFrom(pParameters);
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
NZBParameter* pNZBParameter = *it;
m_Parameters.SetParameter(pNZBParameter->GetName(), pNZBParameter->GetValue());
}
}
}
@@ -93,15 +92,12 @@ Scanner::QueueData::~QueueData()
free(m_szFilename);
free(m_szNZBName);
free(m_szCategory);
free(m_szDupeKey);
}
void Scanner::QueueData::SetAddStatus(EAddStatus eAddStatus)
{
if (m_pAddStatus)
for (NZBParameterList::iterator it = m_Parameters.begin(); it != m_Parameters.end(); it++)
{
*m_pAddStatus = eAddStatus;
delete *it;
}
m_Parameters.clear();
}
@@ -182,10 +178,10 @@ void Scanner::Check()
}
DropOldFiles();
ClearQueueList();
}
m_iNZBDirInterval += 200;
ClearQueueList();
m_mutexScan.Unlock();
}
@@ -323,8 +319,7 @@ void Scanner::DropOldFiles()
}
}
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
const char* szFullFilename, const char* szCategory)
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory)
{
const char* szExtension = strrchr(szBaseFilename, '.');
if (!szExtension)
@@ -338,35 +333,22 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
int iPriority = 0;
bool bAddTop = false;
bool bAddPaused = false;
const char* szDupeKey = NULL;
int iDupeScore = 0;
EDupeMode eDupeMode = dmScore;
EAddStatus eAddStatus = asSkipped;
bool bAdded = false;
QueueData* pQueueData = NULL;
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
{
QueueData* pQueueData1 = *it;
if (Util::SameFilename(pQueueData1->GetFilename(), szFullFilename))
QueueData* pQueueData = *it;
if (Util::SameFilename(pQueueData->GetFilename(), szFullFilename))
{
pQueueData = pQueueData1;
free(szNZBName);
szNZBName = strdup(pQueueData->GetNZBName());
free(szNZBCategory);
szNZBCategory = strdup(pQueueData->GetCategory());
iPriority = pQueueData->GetPriority();
szDupeKey = pQueueData->GetDupeKey();
iDupeScore = pQueueData->GetDupeScore();
eDupeMode = pQueueData->GetDupeMode();
bAddTop = pQueueData->GetAddTop();
bAddPaused = pQueueData->GetAddPaused();
pParameters->CopyFrom(pQueueData->GetParameters());
}
}
InitPPParameters(szNZBCategory, pParameters);
bool bExists = true;
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
@@ -392,77 +374,31 @@ void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFil
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
if (bRenameOK)
{
bAdded = AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority,
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
}
else
{
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFullFilename, szRenamedName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
eAddStatus = asFailed;
}
}
else if (bExists && !strcasecmp(szExtension, ".nzb"))
{
bAdded = AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority,
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority, pParameters, bAddTop, bAddPaused);
}
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
delete *it;
}
pParameters->clear();
delete pParameters;
free(szNZBName);
free(szNZBCategory);
if (pQueueData)
{
pQueueData->SetAddStatus(eAddStatus == asFailed ? asFailed : bAdded ? asSuccess : asSkipped);
}
}
void Scanner::InitPPParameters(const char* szCategory, NZBParameterList* pParameters)
{
bool bUnpack = g_pOptions->GetUnpack();
const char* szDefScript = g_pOptions->GetDefScript();
if (szCategory && *szCategory)
{
Options::Category* pCategory = g_pOptions->FindCategory(szCategory, false);
if (pCategory)
{
bUnpack = pCategory->GetUnpack();
if (pCategory->GetDefScript() && *pCategory->GetDefScript())
{
szDefScript = pCategory->GetDefScript();
}
}
}
pParameters->SetParameter("*Unpack:", bUnpack ? "yes" : "no");
if (szDefScript && *szDefScript)
{
// split szDefScript into tokens and create pp-parameter for each token
char* szDefScript2 = strdup(szDefScript);
char* saveptr;
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
while (szScriptName)
{
szScriptName = Util::Trim(szScriptName);
if (szScriptName[0] != '\0')
{
char szParam[1024];
snprintf(szParam, 1024, "%s:", szScriptName);
szParam[1024-1] = '\0';
pParameters->SetParameter(szParam, "yes");
}
szScriptName = strtok_r(NULL, ",;", &saveptr);
}
free(szDefScript2);
}
}
bool Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
void Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
{
const char* szBasename = Util::BaseFileName(szFilename);
@@ -470,49 +406,36 @@ bool Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, cons
info("Collection %s found", szBasename);
NZBFile* pNZBFile = NZBFile::Create(szFilename, szCategory);
bool bOK = pNZBFile != NULL;
if (!bOK)
if (!pNZBFile)
{
error("Could not add collection %s to queue", szBasename);
}
char bakname2[1024];
if (!Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024))
bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
if (!bRenameOK)
{
bOK = false;
char szSysErrStr[256];
error("Could not rename file %s to %s: %s", szFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
}
if (bOK)
if (pNZBFile && bRenameOK)
{
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
if (szNZBName && strlen(szNZBName) > 0)
{
pNZBFile->GetNZBInfo()->SetName(NULL);
#ifdef WIN32
char* szAnsiFilename = strdup(szNZBName);
WebUtil::Utf8ToAnsi(szAnsiFilename, strlen(szAnsiFilename) + 1);
pNZBFile->GetNZBInfo()->SetFilename(szAnsiFilename);
free(szAnsiFilename);
#else
pNZBFile->GetNZBInfo()->SetFilename(szNZBName);
#endif
pNZBFile->GetNZBInfo()->BuildDestDirName();
}
pNZBFile->GetNZBInfo()->SetDupeKey(szDupeKey);
pNZBFile->GetNZBInfo()->SetDupeScore(iDupeScore);
pNZBFile->GetNZBInfo()->SetDupeMode(eDupeMode);
if (pNZBFile->GetPassword())
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
{
pNZBFile->GetNZBInfo()->GetParameters()->SetParameter("*Unpack:Password", pNZBFile->GetPassword());
NZBParameter* pParameter = *it;
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
}
pNZBFile->GetNZBInfo()->GetParameters()->CopyFrom(pParameters);
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
{
FileInfo* pFileInfo = *it;
@@ -521,11 +444,13 @@ bool Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, cons
}
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
info("Collection %s added to queue", szBasename);
}
delete pNZBFile;
return bOK;
if (pNZBFile)
{
delete pNZBFile;
}
}
void Scanner::ScanNZBDir(bool bSyncMode)
@@ -541,12 +466,10 @@ void Scanner::ScanNZBDir(bool bSyncMode)
}
}
Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
bool Scanner::AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize)
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode)
{
bool bNZB = false;
char szTempFileName[1024];
if (szFileName)
@@ -567,13 +490,8 @@ Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char*
if (!Util::SaveBufferIntoFile(szTempFileName, szBuffer, iBufSize))
{
error("Could not create file %s", szTempFileName);
return asFailed;
return false;
}
char buf[1024];
strncpy(buf, szBuffer, 1024);
buf[1024-1] = '\0';
bNZB = !strncmp(buf, "<?xml", 5) && strstr(buf, "<nzb");
}
// move file into NzbDir, make sure the file name is unique
@@ -582,16 +500,6 @@ Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char*
szValidNZBName[1024-1] = '\0';
Util::MakeValidFilename(szValidNZBName, '_', false);
#ifdef WIN32
WebUtil::Utf8ToAnsi(szValidNZBName, 1024);
#endif
const char* szExtension = strrchr(szNZBName, '.');
if (bNZB && (!szExtension || strcasecmp(szExtension, ".nzb")))
{
strncat(szValidNZBName, ".nzb", 1024 - strlen(szValidNZBName) - 1);
}
char szScanFileName[1024];
snprintf(szScanFileName, 1024, "%s%s", g_pOptions->GetNzbDir(), szValidNZBName);
@@ -617,35 +525,21 @@ Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char*
iNum++;
}
m_mutexScan.Lock();
if (!Util::MoveFile(szTempFileName, szScanFileName))
{
char szSysErrStr[256];
error("Could not move file %s to %s: %s", szTempFileName, szScanFileName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
remove(szTempFileName);
m_mutexScan.Unlock(); // UNLOCK
return asFailed;
return false;
}
char* szUseCategory = strdup(szCategory ? szCategory : "");
Options::Category *pCategory = g_pOptions->FindCategory(szCategory, true);
if (pCategory && strcmp(szCategory, pCategory->GetName()))
{
free(szUseCategory);
szUseCategory = strdup(pCategory->GetName());
detail("Category %s matched to %s for %s", szCategory, szUseCategory, szNZBName);
}
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szCategory, iPriority, pParameters, bAddTop, bAddPaused);
EAddStatus eAddStatus = asSkipped;
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szUseCategory,
iPriority, szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused, &eAddStatus);
free(szUseCategory);
m_mutexScan.Lock();
m_QueueList.push_back(pQueueData);
m_mutexScan.Unlock();
ScanNZBDir(true);
ScanNZBDir(bSyncMode);
return eAddStatus;
return true;
}

View File

@@ -33,14 +33,6 @@
class Scanner
{
public:
enum EAddStatus
{
asSkipped,
asSuccess,
asFailed
};
private:
class FileData
{
@@ -68,30 +60,21 @@ private:
char* m_szNZBName;
char* m_szCategory;
int m_iPriority;
char* m_szDupeKey;
int m_iDupeScore;
EDupeMode m_eDupeMode;
NZBParameterList m_Parameters;
bool m_bAddTop;
bool m_bAddPaused;
EAddStatus* m_pAddStatus;
public:
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus);
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
~QueueData();
const char* GetFilename() { return m_szFilename; }
const char* GetNZBName() { return m_szNZBName; }
const char* GetCategory() { return m_szCategory; }
int GetPriority() { return m_iPriority; }
const char* GetDupeKey() { return m_szDupeKey; }
int GetDupeScore() { return m_iDupeScore; }
EDupeMode GetDupeMode() { return m_eDupeMode; }
NZBParameterList* GetParameters() { return &m_Parameters; }
bool GetAddTop() { return m_bAddTop; }
bool GetAddPaused() { return m_bAddPaused; }
void SetAddStatus(EAddStatus eAddStatus);
};
typedef std::deque<QueueData*> QueueList;
@@ -106,13 +89,10 @@ private:
Mutex m_mutexScan;
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
bool AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
void AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
const char* szFullFilename, const char* szCategory);
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
void InitPPParameters(const char* szCategory, NZBParameterList* pParameters);
void DropOldFiles();
void ClearQueueList();
@@ -121,10 +101,9 @@ public:
~Scanner();
void ScanNZBDir(bool bSyncMode);
void Check();
EAddStatus AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
const char* szFileName, const char* szBuffer, int iBufSize);
bool AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
NZBParameterList* pParameters, bool bAddPaused, bool bAddTop,
const char* szFileName, const char* szBuffer, int iBufSize, bool bSyncMode);
};
#endif

View File

@@ -42,28 +42,31 @@
#include "ScriptController.h"
#include "Options.h"
#include "Log.h"
#include "NewsServer.h"
#include "ServerPool.h"
#include "FeedInfo.h"
#include "FeedCoordinator.h"
extern Options* g_pOptions;
extern ServerPool* g_pServerPool;
extern FeedCoordinator* g_pFeedCoordinator;
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, const char* szParam)
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
int iDownloadRate, const char* szProcess)
{
m_iHours = iHours;
m_iMinutes = iMinutes;
m_iWeekDaysBits = iWeekDaysBits;
m_eCommand = eCommand;
m_szParam = szParam ? strdup(szParam) : NULL;
m_iDownloadRate = iDownloadRate;
m_szProcess = NULL;
if (szProcess)
{
m_szProcess = strdup(szProcess);
}
m_tLastExecuted = 0;
}
Scheduler::Task::~Task()
{
free(m_szParam);
if (m_szProcess)
{
free(m_szProcess);
}
}
@@ -109,6 +112,9 @@ void Scheduler::FirstCheck()
m_tLastCheck = tCurrent - 60*60*24*7;
m_bDetectClockChanges = false;
m_bExecuteProcess = false;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
@@ -116,16 +122,21 @@ void Scheduler::IntervalCheck()
{
m_bDetectClockChanges = true;
m_bExecuteProcess = true;
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
CheckTasks();
}
void Scheduler::CheckTasks()
{
PrepareLog();
m_mutexTaskList.Lock();
time_t tCurrent = time(NULL);
struct tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
struct tm tmLastCheck;
if (m_bDetectClockChanges)
{
@@ -145,12 +156,9 @@ void Scheduler::CheckTasks()
}
}
tm tmCurrent;
localtime_r(&tCurrent, &tmCurrent);
tm tmLastCheck;
localtime_r(&m_tLastCheck, &tmLastCheck);
tm tmLoop;
struct tm tmLoop;
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
tmLoop.tm_hour = tmCurrent.tm_hour;
tmLoop.tm_min = tmCurrent.tm_min;
@@ -164,15 +172,13 @@ void Scheduler::CheckTasks()
Task* pTask = *it;
if (pTask->m_tLastExecuted != tLoop)
{
tm tmAppoint;
struct tm tmAppoint;
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
tmAppoint.tm_hour = pTask->m_iHours;
tmAppoint.tm_min = pTask->m_iMinutes;
tmAppoint.tm_sec = 0;
time_t tAppoint = mktime(&tmAppoint);
tAppoint -= g_pOptions->GetTimeCorrection();
int iWeekDay = tmAppoint.tm_wday;
if (iWeekDay == 0)
{
@@ -198,24 +204,25 @@ void Scheduler::CheckTasks()
m_tLastCheck = tCurrent;
m_mutexTaskList.Unlock();
PrintLog();
}
void Scheduler::ExecuteTask(Task* pTask)
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan",
"Enable Server", "Disable Server", "Fetch Feed" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
if (pTask->m_eCommand == scDownloadRate)
{
debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate);
}
else
{
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan" };
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
}
switch (pTask->m_eCommand)
{
case scDownloadRate:
if (!Util::EmptyStr(pTask->m_szParam))
{
g_pOptions->SetDownloadRate(atoi(pTask->m_szParam) * 1024);
m_bDownloadRateChanged = true;
}
m_iDownloadRate = pTask->m_iDownloadRate;
m_bDownloadRateChanged = true;
break;
case scPauseDownload:
@@ -227,126 +234,14 @@ void Scheduler::ExecuteTask(Task* pTask)
case scProcess:
if (m_bExecuteProcess)
{
SchedulerScriptController::StartScript(pTask->m_szParam);
SchedulerScriptController::StartScript(pTask->m_szProcess);
}
break;
case scPauseScan:
case scUnpauseScan:
g_pOptions->SetPauseScan(pTask->m_eCommand == scPauseScan);
m_bPauseScan = pTask->m_eCommand == scPauseScan;
m_bPauseScanChanged = true;
break;
case scActivateServer:
case scDeactivateServer:
EditServer(pTask->m_eCommand == scActivateServer, pTask->m_szParam);
break;
case scFetchFeed:
if (m_bExecuteProcess)
{
FetchFeed(pTask->m_szParam);
break;
}
}
}
void Scheduler::PrepareLog()
{
m_bDownloadRateChanged = false;
m_bPauseDownloadChanged = false;
m_bPauseScanChanged = false;
m_bServerChanged = false;
}
void Scheduler::PrintLog()
{
if (m_bDownloadRateChanged)
{
info("Scheduler: setting download rate to %i KB/s", g_pOptions->GetDownloadRate() / 1024);
}
if (m_bPauseScanChanged)
{
info("Scheduler: %s scan", g_pOptions->GetPauseScan() ? "pausing" : "unpausing");
}
if (m_bServerChanged)
{
int index = 0;
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++, index++)
{
NewsServer* pServer = *it;
if (pServer->GetActive() != m_ServerStatusList[index])
{
info("Scheduler: %s %s", pServer->GetActive() ? "activating" : "deactivating", pServer->GetName());
}
}
g_pServerPool->Changed();
}
}
void Scheduler::EditServer(bool bActive, const char* szServerList)
{
char* szServerList2 = strdup(szServerList);
char* saveptr;
char* szServer = strtok_r(szServerList2, ",;", &saveptr);
while (szServer)
{
szServer = Util::Trim(szServer);
if (!Util::EmptyStr(szServer))
{
int iID = atoi(szServer);
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
{
NewsServer* pServer = *it;
if ((iID > 0 && pServer->GetID() == iID) ||
!strcasecmp(pServer->GetName(), szServer))
{
if (!m_bServerChanged)
{
// store old server status for logging
m_ServerStatusList.clear();
m_ServerStatusList.reserve(g_pServerPool->GetServers()->size());
for (Servers::iterator it2 = g_pServerPool->GetServers()->begin(); it2 != g_pServerPool->GetServers()->end(); it2++)
{
NewsServer* pServer2 = *it2;
m_ServerStatusList.push_back(pServer2->GetActive());
}
}
m_bServerChanged = true;
pServer->SetActive(bActive);
break;
}
}
}
szServer = strtok_r(NULL, ",;", &saveptr);
}
free(szServerList2);
}
void Scheduler::FetchFeed(const char* szFeedList)
{
char* szFeedList2 = strdup(szFeedList);
char* saveptr;
char* szFeed = strtok_r(szFeedList2, ",;", &saveptr);
while (szFeed)
{
szFeed = Util::Trim(szFeed);
if (!Util::EmptyStr(szFeed))
{
int iID = atoi(szFeed);
for (Feeds::iterator it = g_pFeedCoordinator->GetFeeds()->begin(); it != g_pFeedCoordinator->GetFeeds()->end(); it++)
{
FeedInfo* pFeed = *it;
if (pFeed->GetID() == iID ||
!strcasecmp(pFeed->GetName(), szFeed) ||
!strcasecmp("0", szFeed))
{
g_pFeedCoordinator->FetchFeed(pFeed->GetID());
break;
}
}
}
szFeed = strtok_r(NULL, ",;", &saveptr);
}
free(szFeedList2);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2008-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,7 +27,6 @@
#define SCHEDULER_H
#include <list>
#include <vector>
#include <time.h>
#include "Thread.h"
@@ -42,10 +41,7 @@ public:
scDownloadRate,
scProcess,
scPauseScan,
scUnpauseScan,
scActivateServer,
scDeactivateServer,
scFetchFeed
scUnpauseScan
};
class Task
@@ -55,12 +51,13 @@ public:
int m_iMinutes;
int m_iWeekDaysBits;
ECommand m_eCommand;
char* m_szParam;
int m_iDownloadRate;
char* m_szProcess;
time_t m_tLastExecuted;
public:
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
const char* szParam);
int iDownloadRate, const char* szProcess);
~Task();
friend class Scheduler;
};
@@ -68,7 +65,6 @@ public:
private:
typedef std::list<Task*> TaskList;
typedef std::vector<bool> ServerStatusList;
TaskList m_TaskList;
Mutex m_mutexTaskList;
@@ -76,18 +72,14 @@ private:
bool m_bDetectClockChanges;
bool m_bDownloadRateChanged;
bool m_bExecuteProcess;
int m_iDownloadRate;
bool m_bPauseDownloadChanged;
bool m_bPauseDownload;
bool m_bPauseScanChanged;
bool m_bServerChanged;
ServerStatusList m_ServerStatusList;
bool m_bPauseScan;
void ExecuteTask(Task* pTask);
void CheckTasks();
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
void PrepareLog();
void PrintLog();
void EditServer(bool bActive, const char* szServerList);
void FetchFeed(const char* szFeedList);
public:
Scheduler();
@@ -95,8 +87,12 @@ public:
void AddTask(Task* pTask);
void FirstCheck();
void IntervalCheck();
bool GetDownloadRateChanged() { return m_bDownloadRateChanged; }
int GetDownloadRate() { return m_iDownloadRate; }
bool GetPauseDownloadChanged() { return m_bPauseDownloadChanged; }
bool GetPauseDownload() { return m_bPauseDownload; }
bool GetPauseScanChanged() { return m_bPauseScanChanged; }
bool GetPauseScan() { return m_bPauseScan; }
};
#endif

View File

@@ -199,7 +199,6 @@ ScriptController::ScriptController()
m_szInfoName = NULL;
m_szLogPrefix = NULL;
m_bTerminated = false;
m_bDetached = false;
m_environmentStrings.InitFromCurrentProcess();
}
@@ -229,14 +228,6 @@ void ScriptController::SetEnvVar(const char* szName, const char* szValue)
m_environmentStrings.Append(szVar);
}
void ScriptController::SetIntEnvVar(const char* szName, int iValue)
{
char szValue[1024];
snprintf(szValue, 10, "%i", iValue);
szValue[1024-1] = '\0';
SetEnvVar(szName, szValue);
}
/**
* If szStripPrefix is not NULL, only options, whose names start with the prefix
* are processed. The prefix is then stripped from the names.
@@ -277,26 +268,15 @@ void ScriptController::PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStr
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
{
NZBParameter* pParameter = *it;
const char* szValue = pParameter->GetValue();
#ifdef WIN32
char* szAnsiValue = strdup(szValue);
WebUtil::Utf8ToAnsi(szAnsiValue, strlen(szAnsiValue) + 1);
szValue = szAnsiValue;
#endif
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, szValue);
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, pParameter->GetValue());
}
else if (!szStripPrefix)
{
SetEnvVarSpecial("NZBPR", pParameter->GetName(), szValue);
SetEnvVarSpecial("NZBPR", pParameter->GetName(), pParameter->GetValue());
}
#ifdef WIN32
free(szAnsiValue);
#endif
}
}
@@ -517,15 +497,6 @@ int ScriptController::Execute()
chdir(m_szWorkingDir);
environ = pEnvironmentStrings;
execvp(m_szScript, (char* const*)m_szArgs);
if (errno == EACCES)
{
fprintf(stdout, "[WARNING] Fixing permissions for %s\n", m_szScript);
fflush(stdout);
Util::FixExecPermission(m_szScript);
execvp(m_szScript, (char* const*)m_szArgs);
}
// NOTE: the text "[ERROR] Could not start " is checked later,
// by changing adjust the dependent code below.
fprintf(stdout, "[ERROR] Could not start %s: %s", m_szScript, strerror(errno));
@@ -546,8 +517,8 @@ int ScriptController::Execute()
#endif
// open the read end
m_pReadpipe = fdopen(pipein, "r");
if (!m_pReadpipe)
FILE* readpipe = fdopen(pipein, "r");
if (!readpipe)
{
error("Could not open pipe to %s", m_szInfoName);
return -1;
@@ -566,9 +537,9 @@ int ScriptController::Execute()
debug("Entering pipe-loop");
bool bFirstLine = true;
bool bStartError = false;
while (!m_bTerminated && !m_bDetached && !feof(m_pReadpipe))
while (!feof(readpipe) && !m_bTerminated)
{
if (ReadLine(buf, 10240, m_pReadpipe) && m_pReadpipe)
if (ReadLine(buf, 10240, readpipe))
{
#ifdef CHILD_WATCHDOG
if (!bChildConfirmed)
@@ -603,10 +574,7 @@ int ScriptController::Execute()
#endif
free(buf);
if (m_pReadpipe)
{
fclose(m_pReadpipe);
}
fclose(readpipe);
if (m_bTerminated)
{
@@ -615,8 +583,6 @@ int ScriptController::Execute()
iExitCode = 0;
if (!m_bTerminated && !m_bDetached)
{
#ifdef WIN32
WaitForSingleObject(m_hProcess, INFINITE);
DWORD dExitCode = 0;
@@ -634,7 +600,6 @@ int ScriptController::Execute()
}
}
#endif
}
#ifdef CHILD_WATCHDOG
} // while (!bChildConfirmed && !m_bTerminated)
@@ -674,16 +639,6 @@ void ScriptController::Terminate()
debug("Stopped %s", m_szInfoName);
}
void ScriptController::Detach()
{
debug("Detaching %s", m_szInfoName);
m_bDetached = true;
FILE* pReadpipe = m_pReadpipe;
m_pReadpipe = NULL;
fclose(pReadpipe);
}
bool ScriptController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
{
return fgets(szBuf, iBufSize, pStream);
@@ -703,27 +658,27 @@ void ScriptController::ProcessOutput(char* szText)
if (!strncmp(szText, "[INFO] ", 7))
{
PrintMessage(Message::mkInfo, "%s", szText + 7);
PrintMessage(Message::mkInfo, szText + 7);
}
else if (!strncmp(szText, "[WARNING] ", 10))
{
PrintMessage(Message::mkWarning, "%s", szText + 10);
PrintMessage(Message::mkWarning, szText + 10);
}
else if (!strncmp(szText, "[ERROR] ", 8))
{
PrintMessage(Message::mkError, "%s", szText + 8);
PrintMessage(Message::mkError, szText + 8);
}
else if (!strncmp(szText, "[DETAIL] ", 9))
{
PrintMessage(Message::mkDetail, "%s", szText + 9);
PrintMessage(Message::mkDetail, szText + 9);
}
else if (!strncmp(szText, "[DEBUG] ", 8))
{
PrintMessage(Message::mkDebug, "%s", szText + 8);
PrintMessage(Message::mkDebug, szText + 8);
}
else
{
PrintMessage(Message::mkInfo, "%s", szText);
PrintMessage(Message::mkInfo, szText);
}
debug("Processing output received from script - completed");
@@ -785,7 +740,6 @@ void PostScriptController::StartJob(PostInfo* pPostInfo)
pScriptController->m_pPostInfo = pPostInfo;
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
pScriptController->SetAutoDestroy(false);
pScriptController->m_iPrefixLen = 0;
pPostInfo->SetPostThread(pScriptController);
@@ -852,7 +806,6 @@ void PostScriptController::ExecuteScript(const char* szScriptName, const char* s
SetInfoName(szInfoName);
SetLogPrefix(szDisplayName);
m_iPrefixLen = strlen(szDisplayName) + 2; // 2 = strlen(": ");
PrepareParams(szScriptName);
int iExitCode = Execute();
@@ -873,43 +826,46 @@ void PostScriptController::PrepareParams(const char* szScriptName)
// the locking is needed for accessing the members of NZBInfo
g_pDownloadQueueHolder->LockQueue();
char szNZBName[1024];
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
szNZBName[1024-1] = '\0';
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
char szParStatus[10];
snprintf(szParStatus, 10, "%i", iParStatus[m_pPostInfo->GetNZBInfo()->GetParStatus()]);
szParStatus[10-1] = '\0';
int iUnpackStatus[] = { 0, 0, 1, 2 };
char szUnpackStatus[10];
snprintf(szUnpackStatus, 10, "%i", iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
szUnpackStatus[10-1] = '\0';
char szDestDir[1024];
strncpy(szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
szDestDir[1024-1] = '\0';
char szNZBID[10];
snprintf(szNZBID, 10, "%i", m_pPostInfo->GetNZBInfo()->GetID());
szNZBID[10-1] = '\0';
char szNZBFilename[1024];
strncpy(szNZBFilename, m_pPostInfo->GetNZBInfo()->GetFilename(), 1024);
szNZBFilename[1024-1] = '\0';
char szCategory[1024];
strncpy(szCategory, m_pPostInfo->GetNZBInfo()->GetCategory(), 1024);
szCategory[1024-1] = '\0';
// Reset
ResetEnv();
SetEnvVar("NZBPP_NZBNAME", m_pPostInfo->GetNZBInfo()->GetName());
SetEnvVar("NZBPP_DIRECTORY", m_pPostInfo->GetNZBInfo()->GetDestDir());
SetEnvVar("NZBPP_NZBFILENAME", m_pPostInfo->GetNZBInfo()->GetFilename());
SetEnvVar("NZBPP_FINALDIR", m_pPostInfo->GetNZBInfo()->GetFinalDir());
SetEnvVar("NZBPP_CATEGORY", m_pPostInfo->GetNZBInfo()->GetCategory());
SetIntEnvVar("NZBPP_HEALTH", m_pPostInfo->GetNZBInfo()->CalcHealth());
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_pPostInfo->GetNZBInfo()->CalcCriticalHealth());
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
SetIntEnvVar("NZBPP_PARSTATUS", iParStatus[m_pPostInfo->GetNZBInfo()->GetParStatus()]);
int iUnpackStatus[] = { 0, 0, 1, 2, 3, 4 };
SetIntEnvVar("NZBPP_UNPACKSTATUS", iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
SetIntEnvVar("NZBPP_NZBID", m_pPostInfo->GetNZBInfo()->GetID());
SetIntEnvVar("NZBPP_HEALTHDELETED", (int)m_pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsHealth);
SetIntEnvVar("NZBPP_TOTALARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetTotalArticles());
SetIntEnvVar("NZBPP_SUCCESSARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetSuccessArticles());
SetIntEnvVar("NZBPP_FAILEDARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetFailedArticles());
for (ServerStatList::iterator it = m_pPostInfo->GetNZBInfo()->GetServerStats()->begin(); it != m_pPostInfo->GetNZBInfo()->GetServerStats()->end(); it++)
{
ServerStat* pServerStat = *it;
char szName[50];
snprintf(szName, 50, "NZBPP_SERVER%i_SUCCESSARTICLES", pServerStat->GetServerID());
szName[50-1] = '\0';
SetIntEnvVar(szName, pServerStat->GetSuccessArticles());
snprintf(szName, 50, "NZBPP_SERVER%i_FAILEDARTICLES", pServerStat->GetServerID());
szName[50-1] = '\0';
SetIntEnvVar(szName, pServerStat->GetFailedArticles());
}
SetEnvVar("NZBPP_NZBNAME", szNZBName);
SetEnvVar("NZBPP_NZBID", szNZBID);
SetEnvVar("NZBPP_DIRECTORY", szDestDir);
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
SetEnvVar("NZBPP_UNPACKSTATUS", szUnpackStatus);
SetEnvVar("NZBPP_CATEGORY", szCategory);
PrepareEnvParameters(m_pPostInfo->GetNZBInfo(), NULL);
@@ -966,42 +922,9 @@ ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int iExitCode)
void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
{
const char* szMsgText = szText + m_iPrefixLen;
if (!strncmp(szMsgText, "[NZB] ", 6))
if (!strncmp(szText, "[HISTORY] ", 10))
{
debug("Command %s detected", szMsgText + 6);
if (!strncmp(szMsgText + 6, "FINALDIR=", 9))
{
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->SetFinalDir(szMsgText + 6 + 9);
g_pDownloadQueueHolder->UnlockQueue();
}
else if (!strncmp(szMsgText + 6, "NZBPR_", 6))
{
char* szParam = strdup(szMsgText + 6 + 6);
char* szValue = strchr(szParam, '=');
if (szValue)
{
*szValue = '\0';
g_pDownloadQueueHolder->LockQueue();
m_pPostInfo->GetNZBInfo()->GetParameters()->SetParameter(szParam, szValue + 1);
g_pDownloadQueueHolder->UnlockQueue();
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
free(szParam);
}
else
{
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
}
}
else if (!strncmp(szMsgText, "[HISTORY] ", 10))
{
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szMsgText);
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText);
}
else
{
@@ -1044,6 +967,45 @@ void PostScriptController::Stop()
Terminate();
}
/**
* DownloadQueue must be locked prior to call of this function.
*/
void PostScriptController::InitParamsForNewNZB(NZBInfo* pNZBInfo)
{
const char* szDefScript = g_pOptions->GetDefScript();
if (pNZBInfo->GetCategory() && strlen(pNZBInfo->GetCategory()) > 0)
{
Options::Category* pCategory = g_pOptions->FindCategory(pNZBInfo->GetCategory());
if (pCategory && pCategory->GetDefScript() && strlen(pCategory->GetDefScript()) > 0)
{
szDefScript = pCategory->GetDefScript();
}
}
if (!szDefScript || strlen(szDefScript) == 0)
{
return;
}
// split szDefScript into tokens and create pp-parameter for each token
char* szDefScript2 = strdup(szDefScript);
char* saveptr;
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
while (szScriptName)
{
szScriptName = Util::Trim(szScriptName);
if (szScriptName[0] != '\0')
{
char szParam[1024];
snprintf(szParam, 1024, "%s:", szScriptName);
szParam[1024-1] = '\0';
pNZBInfo->GetParameters()->SetParameter(szParam, "yes");
}
szScriptName = strtok_r(NULL, ",;", &saveptr);
}
free(szDefScript2);
}
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused)
@@ -1064,13 +1026,6 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
szInfoName[1024-1] = '\0';
pScriptController->SetInfoName(szInfoName);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->SetEnvVar("NZBNP_NZBNAME", strlen(*pNZBName) > 0 ? *pNZBName : Util::BaseFileName(szNZBFilename));
pScriptController->SetEnvVar("NZBNP_CATEGORY", *pCategory);
pScriptController->SetIntEnvVar("NZBNP_PRIORITY", *iPriority);
pScriptController->SetIntEnvVar("NZBNP_TOP", *bAddTop ? 1 : 0);
pScriptController->SetIntEnvVar("NZBNP_PAUSED", *bAddPaused ? 1 : 0);
// remove trailing slash
char szDir[1024];
strncpy(szDir, szDirectory, 1024);
@@ -1080,7 +1035,18 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
{
szDir[iLen-1] = '\0';
}
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
char szPriority[20];
snprintf(szPriority, 20, "%i", *iPriority);
szPriority[20-1] = '\0';
char szAddTop[10];
snprintf(szAddTop, 10, "%i", *bAddTop ? 1 : 0);
szAddTop[10-1] = '\0';
char szAddPaused[10];
snprintf(szAddPaused, 10, "%i", *bAddPaused ? 1 : 0);
szAddPaused[10-1] = '\0';
char szLogPrefix[1024];
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
@@ -1089,6 +1055,14 @@ void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBF
pScriptController->SetLogPrefix(szLogPrefix);
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
pScriptController->SetEnvVar("NZBNP_NZBNAME", strlen(*pNZBName) > 0 ? *pNZBName : Util::BaseFileName(szNZBFilename));
pScriptController->SetEnvVar("NZBNP_CATEGORY", *pCategory);
pScriptController->SetEnvVar("NZBNP_PRIORITY", szPriority);
pScriptController->SetEnvVar("NZBNP_TOP", szAddTop);
pScriptController->SetEnvVar("NZBNP_PAUSED", szAddPaused);
pScriptController->Execute();
delete pScriptController;
@@ -1176,8 +1150,13 @@ void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInf
}
}
pScriptController->SetIntEnvVar("NZBNA_LASTID", iLastID);
pScriptController->SetIntEnvVar("NZBNA_PRIORITY", iMaxPriority);
char buf[100];
snprintf(buf, 100, "%i", iLastID);
pScriptController->SetEnvVar("NZBNA_LASTID", buf);
snprintf(buf, 100, "%i", iMaxPriority);
pScriptController->SetEnvVar("NZBNA_PRIORITY", buf);
pScriptController->PrepareEnvParameters(pNZBInfo, NULL);

View File

@@ -65,8 +65,6 @@ private:
const char* m_szLogPrefix;
EnvironmentStrings m_environmentStrings;
bool m_bTerminated;
bool m_bDetached;
FILE* m_pReadpipe;
#ifdef WIN32
HANDLE m_hProcess;
char m_szCmdLine[2048];
@@ -90,7 +88,6 @@ public:
virtual ~ScriptController();
int Execute();
void Terminate();
void Detach();
void SetScript(const char* szScript) { m_szScript = szScript; }
const char* GetScript() { return m_szScript; }
@@ -101,7 +98,6 @@ public:
void SetLogPrefix(const char* szLogPrefix) { m_szLogPrefix = szLogPrefix; }
void SetEnvVar(const char* szName, const char* szValue);
void SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue);
void SetIntEnvVar(const char* szName, int iValue);
};
class PostScriptController : public Thread, public ScriptController
@@ -109,8 +105,7 @@ class PostScriptController : public Thread, public ScriptController
private:
PostInfo* m_pPostInfo;
char m_szNZBName[1024];
int m_iPrefixLen;
void ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation);
void PrepareParams(const char* szScriptName);
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,9 +56,8 @@ ServerPool::ServerPool()
{
debug("Creating ServerPool");
m_iMaxNormLevel = 0;
m_iMaxLevel = 0;
m_iTimeout = 60;
m_iGeneration = 0;
}
ServerPool::~ ServerPool()
@@ -72,7 +71,6 @@ ServerPool::~ ServerPool()
delete *it;
}
m_Servers.clear();
m_SortedServers.clear();
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
@@ -85,16 +83,16 @@ void ServerPool::AddServer(NewsServer* pNewsServer)
{
debug("Adding server to ServerPool");
m_Servers.push_back(pNewsServer);
m_SortedServers.push_back(pNewsServer);
if (pNewsServer->GetMaxConnections() > 0)
{
m_Servers.push_back(pNewsServer);
}
else
{
delete pNewsServer;
}
}
/*
* Calculate normalized levels for all servers.
* Normalized Level means: starting from 0 with step 1.
* The servers of minimum Level must be always used even if they are not active;
* this is to prevent backup servers to act as main servers.
**/
void ServerPool::NormalizeLevels()
{
if (m_Servers.empty())
@@ -102,38 +100,21 @@ void ServerPool::NormalizeLevels()
return;
}
std::sort(m_SortedServers.begin(), m_SortedServers.end(), CompareServers);
std::sort(m_Servers.begin(), m_Servers.end(), CompareServers);
// find minimum level
int iMinLevel = m_SortedServers.front()->GetLevel();
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
NewsServer* pNewsServer = m_Servers.front();
m_iMaxLevel = 0;
int iCurLevel = pNewsServer->GetLevel();
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
if (pNewsServer->GetLevel() < iMinLevel)
if (pNewsServer->GetLevel() != iCurLevel)
{
iMinLevel = pNewsServer->GetLevel();
m_iMaxLevel++;
}
}
m_iMaxNormLevel = 0;
int iLastLevel = iMinLevel;
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
{
NewsServer* pNewsServer = *it;
if ((pNewsServer->GetActive() && pNewsServer->GetMaxConnections() > 0) ||
(pNewsServer->GetLevel() == iMinLevel))
{
if (pNewsServer->GetLevel() != iLastLevel)
{
m_iMaxNormLevel++;
}
pNewsServer->SetNormLevel(m_iMaxNormLevel);
iLastLevel = pNewsServer->GetLevel();
}
else
{
pNewsServer->SetNormLevel(-1);
}
pNewsServer->SetLevel(m_iMaxLevel);
}
}
@@ -146,51 +127,28 @@ void ServerPool::InitConnections()
{
debug("Initializing connections in ServerPool");
m_mutexConnections.Lock();
NormalizeLevels();
m_Levels.clear();
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
int iNormLevel = pNewsServer->GetNormLevel();
if (pNewsServer->GetNormLevel() > -1)
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
{
if ((int)m_Levels.size() <= iNormLevel)
{
m_Levels.push_back(0);
}
if (pNewsServer->GetActive())
{
int iConnections = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
if (pConnection->GetNewsServer() == pNewsServer)
{
iConnections++;
}
}
for (int i = iConnections; i < pNewsServer->GetMaxConnections(); i++)
{
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
iConnections++;
}
m_Levels[iNormLevel] += iConnections;
}
PooledConnection* pConnection = new PooledConnection(pNewsServer);
pConnection->SetTimeout(m_iTimeout);
m_Connections.push_back(pConnection);
}
if ((int)m_Levels.size() <= pNewsServer->GetLevel())
{
m_Levels.push_back(0);
}
m_Levels[pNewsServer->GetLevel()] += pNewsServer->GetMaxConnections();
}
m_iGeneration++;
m_mutexConnections.Unlock();
if (m_Levels.empty())
{
warn("No news servers defined, download is not possible");
}
}
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
@@ -205,8 +163,7 @@ NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, S
{
PooledConnection* pCandidateConnection = *it;
NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer();
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetActive() &&
pCandidateServer->GetNormLevel() == iLevel &&
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetLevel() == iLevel &&
(!pWantServer || pCandidateServer == pWantServer ||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
{
@@ -219,7 +176,7 @@ NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, S
NewsServer* pIgnoreServer = *it;
if (pIgnoreServer == pCandidateServer ||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
pIgnoreServer->GetLevel() == pCandidateServer->GetLevel()))
{
bUseConnection = false;
break;
@@ -261,11 +218,7 @@ void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
{
((PooledConnection*)pConnection)->SetFreeTimeNow();
}
if (pConnection->GetNewsServer()->GetNormLevel() > -1 && pConnection->GetNewsServer()->GetActive())
{
m_Levels[pConnection->GetNewsServer()->GetNormLevel()]++;
}
m_Levels[pConnection->GetNewsServer()->GetLevel()]++;
m_mutexConnections.Unlock();
}
@@ -276,88 +229,36 @@ void ServerPool::CloseUnusedConnections()
time_t curtime = ::time(NULL);
int i = 0;
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); )
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
bool bDeleted = false;
if (!pConnection->GetInUse() &&
(pConnection->GetNewsServer()->GetNormLevel() == -1 ||
!pConnection->GetNewsServer()->GetActive()))
{
debug("Closing (and deleting) unused connection to server%i", pConnection->GetNewsServer()->GetID());
if (pConnection->GetStatus() == Connection::csConnected)
{
pConnection->Disconnect();
}
delete pConnection;
m_Connections.erase(it);
it = m_Connections.begin() + i;
bDeleted = true;
}
if (!bDeleted && !pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
{
int tdiff = (int)(curtime - pConnection->GetFreeTime());
if (tdiff > CONNECTION_HOLD_SECODNS)
{
debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID());
debug("Closing unused connection to %s", pConnection->GetHost());
pConnection->Disconnect();
}
}
if (!bDeleted)
{
it++;
i++;
}
}
m_mutexConnections.Unlock();
}
void ServerPool::Changed()
{
debug("Server config has been changed");
InitConnections();
CloseUnusedConnections();
}
void ServerPool::LogDebugInfo()
{
debug(" ServerPool");
debug(" ----------------");
debug(" Max-Level: %i", m_iMaxNormLevel);
debug(" Max-Level: %i", m_iMaxLevel);
m_mutexConnections.Lock();
debug(" Servers: %i", m_Servers.size());
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
{
NewsServer* pNewsServer = *it;
debug(" %i) %s (%s): Level=%i, NormLevel=%i", pNewsServer->GetID(), pNewsServer->GetName(),
pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel());
}
debug(" Levels: %i", m_Levels.size());
int index = 0;
for (Levels::iterator it = m_Levels.begin(); it != m_Levels.end(); it++, index++)
{
int iSize = *it;
debug(" %i: Size=%i", index, iSize);
}
debug(" Connections: %i", m_Connections.size());
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
{
PooledConnection* pConnection = *it;
debug(" %i) %s (%s): Level=%i, NormLevel=%i, InUse:%i", pConnection->GetNewsServer()->GetID(),
pConnection->GetNewsServer()->GetName(), pConnection->GetNewsServer()->GetHost(),
pConnection->GetNewsServer()->GetLevel(), pConnection->GetNewsServer()->GetNormLevel(),
(int)pConnection->GetInUse());
debug(" %s: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetHost(), (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
}
m_mutexConnections.Unlock();

View File

@@ -2,7 +2,7 @@
* This file is part of nzbget
*
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -36,6 +36,9 @@
class ServerPool
{
public:
typedef std::vector<NewsServer*> Servers;
private:
class PooledConnection : public NNTPConnection
{
@@ -54,13 +57,11 @@ private:
typedef std::vector<PooledConnection*> Connections;
Servers m_Servers;
Servers m_SortedServers;
Connections m_Connections;
Levels m_Levels;
int m_iMaxNormLevel;
int m_iMaxLevel;
Mutex m_mutexConnections;
int m_iTimeout;
int m_iGeneration;
void NormalizeLevels();
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
@@ -71,13 +72,11 @@ public:
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
void AddServer(NewsServer* pNewsServer);
void InitConnections();
int GetMaxNormLevel() { return m_iMaxNormLevel; }
int GetMaxLevel() { return m_iMaxLevel; }
Servers* GetServers() { return &m_Servers; } // Only for read access (no lockings)
NNTPConnection* GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers);
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
void CloseUnusedConnections();
void Changed();
int GetGeneration() { return m_iGeneration; }
void LogDebugInfo();
};

15
TLS.cpp
View File

@@ -275,9 +275,18 @@ TLSSocket::TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, con
TLSSocket::~TLSSocket()
{
free(m_szCertFile);
free(m_szKeyFile);
free(m_szCipher);
if (m_szCertFile)
{
free(m_szCertFile);
}
if (m_szKeyFile)
{
free(m_szKeyFile);
}
if (m_szCipher)
{
free(m_szCipher);
}
Close();
}

View File

@@ -72,6 +72,11 @@ bool UnpackController::FileList::Exists(const char* szFilename)
return false;
}
UnpackController::~UnpackController()
{
m_archiveFiles.Clear();
}
void UnpackController::StartJob(PostInfo* pPostInfo)
{
UnpackController* pUnpackController = new UnpackController();
@@ -95,18 +100,22 @@ void UnpackController::Run()
m_szName[1024-1] = '\0';
m_bCleanedUpDisk = false;
bool bUnpack = true;
m_szPassword[0] = '\0';
m_szFinalDir[0] = '\0';
m_bFinalDirCreated = false;
NZBParameter* pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
bool bUnpack = !(pParameter && !strcasecmp(pParameter->GetValue(), "no"));
pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:Password", false);
if (pParameter)
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
NZBParameter* pParameter = *it;
if (!strcasecmp(pParameter->GetName(), "*Unpack:") && !strcasecmp(pParameter->GetValue(), "no"))
{
bUnpack = false;
}
if (!strcasecmp(pParameter->GetName(), "*Unpack:Password"))
{
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
m_szPassword[1024-1] = '\0';
}
}
g_pDownloadQueueHolder->UnlockQueue();
@@ -117,7 +126,17 @@ void UnpackController::Run()
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
m_szInfoNameUp[1024-1] = '\0';
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
CheckStateFiles();
#ifndef DISABLE_PARCHECK
if (bUnpack && m_bHasBrokenFiles && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
{
PrintMessage(Message::mkInfo, "%s has broken files", m_szName);
RequestParCheck(false);
m_pPostInfo->SetWorking(false);
return;
}
#endif
if (bUnpack)
{
@@ -138,8 +157,6 @@ void UnpackController::Run()
m_bUnpackOK = true;
m_bUnpackStartError = false;
m_bUnpackSpaceError = false;
m_bUnpackPasswordError = false;
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
{
@@ -163,10 +180,9 @@ void UnpackController::Run()
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
#ifndef DISABLE_PARCHECK
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped && m_bHasParFiles)
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && m_bHasParFiles)
{
RequestParCheck();
RequestParCheck(m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped);
}
else
#endif
@@ -214,8 +230,6 @@ void UnpackController::ExecuteUnrar()
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
m_bUnpackStartError = iExitCode == -1;
m_bUnpackSpaceError = iExitCode == 5;
m_bUnpackPasswordError = iExitCode == 11; // only for rar5-archives
if (!m_bUnpackOK && iExitCode > 0)
{
@@ -281,44 +295,51 @@ void UnpackController::Completed()
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
if (g_pOptions->GetParRename())
{
//request par-rename check for extracted files
m_pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsNone);
}
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
else
{
#ifndef DISABLE_PARCHECK
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
!m_bUnpackStartError && !m_bUnpackSpaceError && !m_bUnpackPasswordError &&
!GetTerminated() && m_bHasParFiles)
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped && !m_bUnpackStartError && !GetTerminated() && m_bHasParFiles)
{
RequestParCheck();
RequestParCheck(false);
}
else
#endif
{
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(
m_bUnpackSpaceError ? NZBInfo::usSpace :
m_bUnpackPasswordError ? NZBInfo::usPassword :
NZBInfo::usFailure);
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usFailure);
m_pPostInfo->SetStage(PostInfo::ptQueued);
}
}
}
#ifndef DISABLE_PARCHECK
void UnpackController::RequestParCheck()
void UnpackController::RequestParCheck(bool bRename)
{
PrintMessage(Message::mkInfo, "%s requested par-check/repair", m_szInfoNameUp);
m_pPostInfo->SetRequestParCheck(true);
PrintMessage(Message::mkInfo, "%s requested %s", m_szInfoNameUp, bRename ? "par-rename": "par-check/repair");
if (bRename)
{
m_pPostInfo->SetRequestParRename(true);
}
else
{
m_pPostInfo->SetRequestParCheck(true);
}
m_pPostInfo->SetStage(PostInfo::ptFinished);
}
#endif
void UnpackController::CheckStateFiles()
{
char szBrokenLog[1024];
snprintf(szBrokenLog, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_brokenlog.txt");
szBrokenLog[1024-1] = '\0';
m_bHasBrokenFiles = Util::FileExists(szBrokenLog);
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
}
void UnpackController::CreateUnpackDir()
{
m_bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
@@ -328,7 +349,6 @@ void UnpackController::CreateUnpackDir()
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
m_szFinalDir[1024-1] = '\0';
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
m_bFinalDirCreated = !Util::DirectoryExists(m_szFinalDir);
}
else
{
@@ -354,8 +374,7 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
@@ -378,37 +397,28 @@ void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
{
m_bHasSevenZipMultiFiles = true;
}
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles &&
!regExRarMultiSeq.Match(filename) && regExNumExt.Match(filename) &&
FileHasRarSignature(szFullFilename))
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles && !regExRarMultiSeq.Match(filename))
{
m_bHasNonStdRarFiles = true;
// Check if file has RAR signature
char rarSignature[] = {0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00};
char fileSignature[7];
FILE* infile;
infile = fopen(szFullFilename, "rb");
if (infile)
{
int cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
fclose(infile);
if (cnt == sizeof(fileSignature) && !strcmp(rarSignature, fileSignature))
{
m_bHasNonStdRarFiles = true;
}
}
}
}
}
}
bool UnpackController::FileHasRarSignature(const char* szFilename)
{
char rar4Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 };
char rar5Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00 };
char fileSignature[8];
int cnt = 0;
FILE* infile;
infile = fopen(szFilename, "rb");
if (infile)
{
cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
fclose(infile);
}
bool bRar = cnt == sizeof(fileSignature) &&
(!strcmp(rar4Signature, fileSignature) || !strcmp(rar5Signature, fileSignature));
return bRar;
}
bool UnpackController::Cleanup()
{
// By success:
@@ -428,8 +438,7 @@ bool UnpackController::Cleanup()
DirBrowser dir(m_szUnpackDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
@@ -458,19 +467,35 @@ bool UnpackController::Cleanup()
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
}
if (!m_bUnpackOK && m_bFinalDirCreated)
{
Util::RemoveDirectory(m_szFinalDir);
}
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
{
PrintMessage(Message::mkInfo, "Deleting archive files");
RegEx regExRar(".*\\.rar$");
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]+$");
RegEx regExNumExt(".*\\.[0-9]+$");
// Delete rar-files (only files which were used by unrar)
for (FileList::iterator it = m_archiveFiles.begin(); it != m_archiveFiles.end(); it++)
{
char* szFilename = *it;
if (m_bInterDir || !extractedFiles.Exists(szFilename))
{
char szFullFilename[1024];
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, szFilename);
szFullFilename[1024-1] = '\0';
PrintMessage(Message::mkInfo, "Deleting file %s", szFilename);
if (remove(szFullFilename) != 0)
{
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
}
}
}
// Unfortunately 7-Zip doesn't print the processed archive-files to the output.
// Therefore we don't know for sure which files were extracted.
// We just delete all 7z-files in the directory.
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]*$");
DirBrowser dir(m_szDestDir);
while (const char* filename = dir.Next())
@@ -479,12 +504,8 @@ bool UnpackController::Cleanup()
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szFullFilename[1024-1] = '\0';
if (strcmp(filename, ".") && strcmp(filename, "..") &&
!Util::DirectoryExists(szFullFilename) &&
(m_bInterDir || !extractedFiles.Exists(filename)) &&
(regExRar.Match(filename) || regExSevenZip.Match(filename) ||
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(szFullFilename)) ||
(m_bHasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(szFullFilename))))
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename)
&& regExSevenZip.Match(filename) && (m_bInterDir || !extractedFiles.Exists(filename)))
{
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
@@ -601,6 +622,7 @@ void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
{
const char *szFilename = szText + 23;
debug("Filename: %s", szFilename);
m_archiveFiles.push_back(strdup(szFilename));
SetProgressLabel(szText + 7);
}
@@ -697,15 +719,24 @@ bool MoveController::MoveFiles()
DirBrowser dir(m_szInterDir);
while (const char* filename = dir.Next())
{
if (strcmp(filename, ".") && strcmp(filename, "..") &&
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
if (strcmp(filename, ".") && strcmp(filename, ".."))
{
char szSrcFile[1024];
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
szSrcFile[1024-1] = '\0';
char szDstFile[1024];
Util::MakeUniqueFilename(szDstFile, 1024, m_szDestDir, filename);
snprintf(szDstFile, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
szDstFile[1024-1] = '\0';
// prevent overwriting of existing files
int dupcount = 0;
while (Util::FileExists(szDstFile))
{
dupcount++;
snprintf(szDstFile, 1024, "%s%c%s_duplicate%d", m_szDestDir, PATH_SEPARATOR, filename, dupcount);
szDstFile[1024-1] = '\0';
}
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
if (!Util::MoveFile(szSrcFile, szDstFile))
@@ -716,10 +747,7 @@ bool MoveController::MoveFiles()
}
}
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir))
{
PrintMessage(Message::mkError, "Could not remove intermediate directory %s", m_szInterDir);
}
Util::RemoveDirectory(m_szInterDir);
return bOK;
}

View File

@@ -63,17 +63,16 @@ private:
bool m_bAllOKMessageReceived;
bool m_bNoFilesMessageReceived;
bool m_bHasParFiles;
bool m_bHasBrokenFiles;
bool m_bHasRarFiles;
bool m_bHasNonStdRarFiles;
bool m_bHasSevenZipFiles;
bool m_bHasSevenZipMultiFiles;
bool m_bUnpackOK;
bool m_bUnpackStartError;
bool m_bUnpackSpaceError;
bool m_bUnpackPasswordError;
bool m_bCleanedUpDisk;
EUnpacker m_eUnpacker;
bool m_bFinalDirCreated;
FileList m_archiveFiles;
protected:
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
@@ -83,14 +82,15 @@ protected:
void Completed();
void CreateUnpackDir();
bool Cleanup();
void CheckStateFiles();
void CheckArchiveFiles(bool bScanNonStdFiles);
void SetProgressLabel(const char* szProgressLabel);
#ifndef DISABLE_PARCHECK
void RequestParCheck();
void RequestParCheck(bool bRename);
#endif
bool FileHasRarSignature(const char* szFilename);
public:
virtual ~UnpackController();
virtual void Run();
virtual void Stop();
static void StartJob(PostInfo* pPostInfo);

View File

@@ -64,46 +64,36 @@ UrlDownloader::UrlDownloader() : WebDownloader()
UrlDownloader::~UrlDownloader()
{
free(m_szCategory);
if (m_szCategory)
{
free(m_szCategory);
}
}
void UrlDownloader::ProcessHeader(const char* szLine)
{
WebDownloader::ProcessHeader(szLine);
if (!strncmp(szLine, "X-DNZB-Category:", 16))
if (!strncmp(szLine, "X-DNZB-Category: ", 17))
{
free(m_szCategory);
char* szCategory = strdup(szLine + 16);
m_szCategory = strdup(Util::Trim(szCategory));
free(szCategory);
if (m_szCategory)
{
free(m_szCategory);
}
const char *szCat = szLine + 17;
int iCatLen = strlen(szCat);
// trim trailing CR/LF/spaces
while (iCatLen > 0 && (szCat[iCatLen-1] == '\n' || szCat[iCatLen-1] == '\r' || szCat[iCatLen-1] == ' ')) iCatLen--;
m_szCategory = (char*)malloc(iCatLen + 1);
strncpy(m_szCategory, szCat, iCatLen);
m_szCategory[iCatLen] = '\0';
debug("Category: %s", m_szCategory);
}
else if (!strncmp(szLine, "X-DNZB-", 7))
{
char* szModLine = strdup(szLine);
char* szValue = strchr(szModLine, ':');
if (szValue)
{
*szValue = NULL;
szValue++;
while (*szValue == ' ') szValue++;
Util::Trim(szValue);
debug("X-DNZB: %s", szModLine);
debug("Value: %s", szValue);
char szParamName[100];
snprintf(szParamName, 100, "*DNZB:%s", szModLine + 7);
szParamName[100-1] = '\0';
char* szVal = WebUtil::Latin1ToUtf8(szValue);
m_ppParameters.SetParameter(szParamName, szVal);
free(szVal);
}
free(szModLine);
}
}
UrlCoordinator::UrlCoordinator()
@@ -111,7 +101,6 @@ UrlCoordinator::UrlCoordinator()
debug("Creating UrlCoordinator");
m_bHasMoreJobs = true;
m_bForce = false;
}
UrlCoordinator::~UrlCoordinator()
@@ -145,7 +134,7 @@ void UrlCoordinator::Run()
while (!IsStopped())
{
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
// start download for next URL
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
@@ -160,10 +149,6 @@ void UrlCoordinator::Run()
{
StartUrlDownload(pUrlInfo);
}
if (!bHasMoreUrls)
{
m_bForce = false;
}
}
g_pQueueCoordinator->UnlockQueue();
}
@@ -278,11 +263,6 @@ void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
if (pUrlInfo->GetForce())
{
m_bForce = true;
}
g_pQueueCoordinator->UnlockQueue();
}
@@ -291,19 +271,19 @@ void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
*/
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
{
bool bPauseDownload = g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2();
bool bOK = false;
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
{
pUrlInfo = *at;
if (pUrlInfo->GetStatus() == 0 && (!bPauseDownload || pUrlInfo->GetForce() || g_pOptions->GetUrlForce()))
if (pUrlInfo->GetStatus() == 0)
{
return true;
bOK = true;
break;
}
}
return false;
return bOK;
}
void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
@@ -315,7 +295,6 @@ void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
pUrlDownloader->Attach(this);
pUrlDownloader->SetUrlInfo(pUrlInfo);
pUrlDownloader->SetURL(pUrlInfo->GetURL());
pUrlDownloader->SetForce(pUrlInfo->GetForce() || g_pOptions->GetUrlForce());
char tmp[1024];
@@ -332,11 +311,11 @@ void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
pUrlDownloader->Start();
}
void UrlCoordinator::Update(Subject* pCaller, void* pAspect)
void UrlCoordinator::Update(Subject* Caller, void* Aspect)
{
debug("Notification from UrlDownloader received");
UrlDownloader* pUrlDownloader = (UrlDownloader*) pCaller;
UrlDownloader* pUrlDownloader = (UrlDownloader*) Caller;
if ((pUrlDownloader->GetStatus() == WebDownloader::adFinished) ||
(pUrlDownloader->GetStatus() == WebDownloader::adFailed) ||
(pUrlDownloader->GetStatus() == WebDownloader::adRetry))
@@ -382,8 +361,9 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
debug("Filename: [%s]", filename);
// delete Download from active jobs
g_pQueueCoordinator->LockQueue();
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// delete Download from Queue
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
{
UrlDownloader* pa = *it;
@@ -393,32 +373,12 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
break;
}
}
g_pQueueCoordinator->UnlockQueue();
Aspect aspect = { eaUrlCompleted, pUrlInfo };
Notify(&aspect);
bool bDeleteObj = false;
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished || pUrlInfo->GetStatus() == UrlInfo::aiFailed)
{
// add nzb-file to download queue
Scanner::EAddStatus eAddStatus = g_pScanner->AddExternalFile(
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : filename,
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : pUrlDownloader->GetCategory(),
pUrlInfo->GetPriority(), pUrlInfo->GetDupeKey(), pUrlInfo->GetDupeScore(), pUrlInfo->GetDupeMode(),
pUrlDownloader->GetParameters(), pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(),
pUrlDownloader->GetOutputFilename(), NULL, 0);
if (eAddStatus != Scanner::asSuccess)
{
pUrlInfo->SetStatus(eAddStatus == Scanner::asFailed ? UrlInfo::aiScanFailed : UrlInfo::aiScanSkipped);
}
}
// delete Download from Url Queue
if (pUrlInfo->GetStatus() != UrlInfo::aiRetry)
{
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
// delete UrlInfo from Queue
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
{
UrlInfo* pa = *it;
@@ -429,9 +389,9 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
}
}
bool bDeleteObj = true;
bDeleteObj = true;
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() != UrlInfo::aiFinished)
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() == UrlInfo::aiFailed)
{
HistoryInfo* pHistoryInfo = new HistoryInfo(pUrlInfo);
pHistoryInfo->SetTime(time(NULL));
@@ -443,12 +403,26 @@ void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
{
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
}
}
g_pQueueCoordinator->UnlockQueue();
g_pQueueCoordinator->UnlockQueue();
if (bDeleteObj)
{
delete pUrlInfo;
}
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
{
// add nzb-file to download queue
AddToNZBQueue(pUrlInfo, pUrlDownloader->GetOutputFilename(), filename, pUrlDownloader->GetCategory());
}
if (bDeleteObj)
{
delete pUrlInfo;
}
}
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
{
g_pScanner->AddExternalFile(
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename,
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : szOriginalCategory,
pUrlInfo->GetPriority(), NULL, pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(), szTempFilename, NULL, 0, false);
}

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -41,33 +41,23 @@ class UrlCoordinator : public Thread, public Observer, public Subject
{
public:
typedef std::list<UrlDownloader*> ActiveDownloads;
enum EAspectAction
{
eaUrlAdded,
eaUrlCompleted
};
struct Aspect
{
EAspectAction eAction;
UrlInfo* pUrlInfo;
};
private:
ActiveDownloads m_ActiveDownloads;
bool m_bHasMoreJobs;
bool m_bForce;
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
void StartUrlDownload(UrlInfo* pUrlInfo);
void UrlCompleted(UrlDownloader* pUrlDownloader);
void ResetHangingDownloads();
void AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory);
public:
UrlCoordinator();
virtual ~UrlCoordinator();
virtual void Run();
virtual void Stop();
void Update(Subject* pCaller, void* pAspect);
void Update(Subject* Caller, void* Aspect);
// Editing the queue
void AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst);
@@ -81,7 +71,6 @@ class UrlDownloader : public WebDownloader
private:
UrlInfo* m_pUrlInfo;
char* m_szCategory;
NZBParameterList m_ppParameters;
protected:
virtual void ProcessHeader(const char* szLine);
@@ -92,7 +81,6 @@ public:
void SetUrlInfo(UrlInfo* pUrlInfo) { m_pUrlInfo = pUrlInfo; }
UrlInfo* GetUrlInfo() { return m_pUrlInfo; }
const char* GetCategory() { return m_szCategory; }
NZBParameterList* GetParameters() { return &m_ppParameters; }
};
#endif

531
Util.cpp
View File

@@ -36,7 +36,6 @@
#include <sys/stat.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#ifdef WIN32
#include <direct.h>
#include <WinIoCtl.h>
@@ -51,7 +50,6 @@
#ifndef DISABLE_GZIP
#include <zlib.h>
#endif
#include <time.h>
#include "nzbget.h"
#include "Util.h"
@@ -215,7 +213,10 @@ StringBuilder::StringBuilder()
StringBuilder::~StringBuilder()
{
free(m_szBuffer);
if (m_szBuffer)
{
free(m_szBuffer);
}
}
void StringBuilder::Append(const char* szStr)
@@ -427,7 +428,7 @@ bool Util::SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int
int iWrittenBytes = fwrite(szBuffer, 1, iBufLen, pFile);
fclose(pFile);
return iWrittenBytes == iBufLen;
return iWrittenBytes = iBufLen;
}
bool Util::CreateSparseFile(const char* szFilename, int iSize)
@@ -520,48 +521,6 @@ void Util::MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSla
}
}
// returns TRUE if the name was changed by adding duplicate-suffix
bool Util::MakeUniqueFilename(char* szDestBufFilename, int iDestBufSize, const char* szDestDir, const char* szBasename)
{
snprintf(szDestBufFilename, iDestBufSize, "%s%c%s", szDestDir, (int)PATH_SEPARATOR, szBasename);
szDestBufFilename[iDestBufSize-1] = '\0';
int iDupeNumber = 0;
while (FileExists(szDestBufFilename))
{
iDupeNumber++;
const char* szExtension = strrchr(szBasename, '.');
if (szExtension && szExtension != szBasename)
{
char szFilenameWithoutExt[1024];
strncpy(szFilenameWithoutExt, szBasename, 1024);
int iEnd = szExtension - szBasename;
szFilenameWithoutExt[iEnd < 1024 ? iEnd : 1024-1] = '\0';
if (!strcasecmp(szExtension, ".par2"))
{
char* szVolExtension = strrchr(szFilenameWithoutExt, '.');
if (szVolExtension && szVolExtension != szFilenameWithoutExt && !strncasecmp(szVolExtension, ".vol", 4))
{
*szVolExtension = '\0';
szExtension = szBasename + (szVolExtension - szFilenameWithoutExt);
}
}
snprintf(szDestBufFilename, iDestBufSize, "%s%c%s.duplicate%d%s", szDestDir, (int)PATH_SEPARATOR, szFilenameWithoutExt, iDupeNumber, szExtension);
}
else
{
snprintf(szDestBufFilename, iDestBufSize, "%s%c%s.duplicate%d", szDestDir, (int)PATH_SEPARATOR, szBasename, iDupeNumber);
}
szDestBufFilename[iDestBufSize-1] = '\0';
}
return iDupeNumber > 0;
}
long long Util::JoinInt64(unsigned long Hi, unsigned long Lo)
{
return (((long long)Hi) << 32) + Lo;
@@ -697,15 +656,6 @@ bool Util::FileExists(const char* szFilename)
return bExists;
}
bool Util::FileExists(const char* szPath, const char* szFilenameWithoutPath)
{
char fullFilename[1024];
snprintf(fullFilename, 1024, "%s%c%s", szPath, (int)PATH_SEPARATOR, szFilenameWithoutPath);
fullFilename[1024-1] = '\0';
bool bExists = Util::FileExists(fullFilename);
return bExists;
}
bool Util::DirectoryExists(const char* szDirFilename)
{
struct stat buffer;
@@ -917,19 +867,6 @@ bool Util::SameFilename(const char* szFilename1, const char* szFilename2)
#endif
}
#ifndef WIN32
void Util::FixExecPermission(const char* szFilename)
{
struct stat buffer;
bool bOK = !stat(szFilename, &buffer);
if (bOK)
{
buffer.st_mode = buffer.st_mode | S_IXUSR | S_IXGRP | S_IXOTH;
chmod(szFilename, buffer.st_mode);
}
}
#endif
char* Util::GetLastErrorMessage(char* szBuffer, int iBufLen)
{
szBuffer[0] = '\0';
@@ -1034,114 +971,28 @@ bool Util::SplitCommandLine(const char* szCommandLine, char*** argv)
void Util::TrimRight(char* szStr)
{
char* szEnd = szStr + strlen(szStr) - 1;
while (szEnd >= szStr && (*szEnd == '\n' || *szEnd == '\r' || *szEnd == ' ' || *szEnd == '\t'))
int iLen = strlen(szStr);
char ch = szStr[iLen-1];
while (iLen > 0 && (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t'))
{
*szEnd = '\0';
szEnd--;
szStr[iLen-1] = 0;
iLen--;
ch = szStr[iLen-1];
}
}
char* Util::Trim(char* szStr)
{
TrimRight(szStr);
while (*szStr == '\n' || *szStr == '\r' || *szStr == ' ' || *szStr == '\t')
char ch = *szStr;
while (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
{
szStr++;
ch = *szStr;
}
return szStr;
}
char* Util::ReduceStr(char* szStr, const char* szFrom, const char* szTo)
{
int iLenFrom = strlen(szFrom);
int iLenTo = strlen(szTo);
// assert(iLenTo < iLenFrom);
while (char* p = strstr(szStr, szFrom))
{
const char* src = szTo;
while ((*p++ = *src++)) ;
src = --p - iLenTo + iLenFrom;
while ((*p++ = *src++)) ;
}
return szStr;
}
/* Calculate Hash using Bob Jenkins (1996) algorithm
* http://burtleburtle.net/bob/c/lookup2.c
*/
typedef unsigned int ub4; /* unsigned 4-byte quantities */
typedef unsigned char ub1;
#define hashsize(n) ((ub4)1<<(n))
#define hashmask(n) (hashsize(n)-1)
#define mix(a,b,c) \
{ \
a -= b; a -= c; a ^= (c>>13); \
b -= c; b -= a; b ^= (a<<8); \
c -= a; c -= b; c ^= (b>>13); \
a -= b; a -= c; a ^= (c>>12); \
b -= c; b -= a; b ^= (a<<16); \
c -= a; c -= b; c ^= (b>>5); \
a -= b; a -= c; a ^= (c>>3); \
b -= c; b -= a; b ^= (a<<10); \
c -= a; c -= b; c ^= (b>>15); \
}
ub4 hash(register ub1 *k, register ub4 length, register ub4 initval)
// register ub1 *k; /* the key */
// register ub4 length; /* the length of the key */
// register ub4 initval; /* the previous hash, or an arbitrary value */
{
register ub4 a,b,c,len;
/* Set up the internal state */
len = length;
a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
c = initval; /* the previous hash value */
/*---------------------------------------- handle most of the key */
while (len >= 12)
{
a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
mix(a,b,c);
k += 12; len -= 12;
}
/*------------------------------------- handle the last 11 bytes */
c += length;
switch(len) /* all the case statements fall through */
{
case 11: c+=((ub4)k[10]<<24);
case 10: c+=((ub4)k[9]<<16);
case 9 : c+=((ub4)k[8]<<8);
/* the first byte of c is reserved for the length */
case 8 : b+=((ub4)k[7]<<24);
case 7 : b+=((ub4)k[6]<<16);
case 6 : b+=((ub4)k[5]<<8);
case 5 : b+=k[4];
case 4 : a+=((ub4)k[3]<<24);
case 3 : a+=((ub4)k[2]<<16);
case 2 : a+=((ub4)k[1]<<8);
case 1 : a+=k[0];
/* case 0: nothing left to add */
}
mix(a,b,c);
/*-------------------------------------------- report the result */
return c;
}
unsigned int Util::HashBJ96(const char* szBuffer, int iBufSize, unsigned int iInitValue)
{
return (unsigned int)hash((ub1*)szBuffer, (ub4)iBufSize, (ub4)iInitValue);
}
#ifdef WIN32
bool Util::RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen)
{
@@ -1356,13 +1207,6 @@ void WebUtil::XmlDecode(char* raw)
*output++ = '\"';
p += 5;
}
else if (*p == '#')
{
int code = atoi(p+1);
p = strchr(p+1, ';');
if (p) p++;
*output++ = (char)code;
}
else
{
// unknown entity
@@ -1693,103 +1537,6 @@ BreakLoop:
*output = '\0';
}
#ifdef WIN32
bool WebUtil::Utf8ToAnsi(char* szBuffer, int iBufLen)
{
WCHAR* wstr = (WCHAR*)malloc(iBufLen * 2);
int errcode = MultiByteToWideChar(CP_UTF8, 0, szBuffer, -1, wstr, iBufLen);
if (errcode > 0)
{
errcode = WideCharToMultiByte(CP_ACP, 0, wstr, -1, szBuffer, iBufLen, "_", NULL);
}
free(wstr);
return errcode > 0;
}
bool WebUtil::AnsiToUtf8(char* szBuffer, int iBufLen)
{
WCHAR* wstr = (WCHAR*)malloc(iBufLen * 2);
int errcode = MultiByteToWideChar(CP_ACP, 0, szBuffer, -1, wstr, iBufLen);
if (errcode > 0)
{
errcode = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, szBuffer, iBufLen, NULL, NULL);
}
free(wstr);
return errcode > 0;
}
#endif
char* WebUtil::Latin1ToUtf8(const char* szStr)
{
char *res = (char*)malloc(strlen(szStr) * 2 + 1);
const unsigned char *in = (const unsigned char*)szStr;
unsigned char *out = (unsigned char*)res;
while (*in)
{
if (*in < 128)
{
*out++ = *in++;
}
else
{
*out++ = 0xc2 + (*in > 0xbf);
*out++ = (*in++ & 0x3f) + 0x80;
}
}
*out = '\0';
return res;
}
/*
The date/time can be formatted according to RFC822 in different ways. Examples:
Wed, 26 Jun 2013 01:02:54 -0600
Wed, 26 Jun 2013 01:02:54 GMT
26 Jun 2013 01:02:54 -0600
26 Jun 2013 01:02 -0600
26 Jun 2013 01:02 A
This function however supports only the first format!
*/
time_t WebUtil::ParseRfc822DateTime(const char* szDateTimeStr)
{
char month[4];
int day, year, hours, minutes, seconds, zonehours, zoneminutes;
int r = sscanf(szDateTimeStr, "%*s %d %3s %d %d:%d:%d %3d %2d", &day, &month[0], &year, &hours, &minutes, &seconds, &zonehours, &zoneminutes);
if (r != 8)
{
return 0;
}
int mon = 0;
if (!strcasecmp(month, "Jan")) mon = 0;
else if (!strcasecmp(month, "Feb")) mon = 1;
else if (!strcasecmp(month, "Mar")) mon = 2;
else if (!strcasecmp(month, "Apr")) mon = 3;
else if (!strcasecmp(month, "May")) mon = 4;
else if (!strcasecmp(month, "Jun")) mon = 5;
else if (!strcasecmp(month, "Jul")) mon = 6;
else if (!strcasecmp(month, "Aug")) mon = 7;
else if (!strcasecmp(month, "Sep")) mon = 8;
else if (!strcasecmp(month, "Oct")) mon = 9;
else if (!strcasecmp(month, "Nov")) mon = 10;
else if (!strcasecmp(month, "Dec")) mon = 11;
struct tm rawtime;
memset(&rawtime, 0, sizeof(rawtime));
rawtime.tm_year = year - 1900;
rawtime.tm_mon = mon;
rawtime.tm_mday = day;
rawtime.tm_hour = hours;
rawtime.tm_min = minutes;
rawtime.tm_sec = seconds;
time_t enctime = mktime(&rawtime);
enctime = enctime - (zonehours * 60 + (zonehours > 0 ? zoneminutes : -zoneminutes)) * 60;
return enctime;
}
URL::URL(const char* szAddress)
{
@@ -1812,12 +1559,30 @@ URL::URL(const char* szAddress)
URL::~URL()
{
free(m_szAddress);
free(m_szProtocol);
free(m_szUser);
free(m_szPassword);
free(m_szHost);
free(m_szResource);
if (m_szAddress)
{
free(m_szAddress);
}
if (m_szProtocol)
{
free(m_szProtocol);
}
if (m_szUser)
{
free(m_szUser);
}
if (m_szPassword)
{
free(m_szPassword);
}
if (m_szHost)
{
free(m_szHost);
}
if (m_szResource)
{
free(m_szResource);
}
}
void URL::ParseURL()
@@ -1903,20 +1668,11 @@ void URL::ParseURL()
m_bValid = true;
}
RegEx::RegEx(const char *szPattern, int iMatchBufSize)
RegEx::RegEx(const char *szPattern)
{
#ifdef HAVE_REGEX_H
m_pContext = malloc(sizeof(regex_t));
m_bValid = regcomp((regex_t*)m_pContext, szPattern, REG_EXTENDED | REG_ICASE | (iMatchBufSize > 0 ? 0 : REG_NOSUB)) == 0;
m_iMatchBufSize = iMatchBufSize;
if (iMatchBufSize > 0)
{
m_pMatches = malloc(sizeof(regmatch_t) * iMatchBufSize);
}
else
{
m_pMatches = NULL;
}
m_bValid = regcomp((regex_t*)m_pContext, szPattern, REG_EXTENDED | REG_ICASE | REG_NOSUB) == 0;
#else
m_bValid = false;
#endif
@@ -1927,223 +1683,18 @@ RegEx::~RegEx()
#ifdef HAVE_REGEX_H
regfree((regex_t*)m_pContext);
free(m_pContext);
free(m_pMatches);
#endif
}
bool RegEx::Match(const char *szStr)
{
#ifdef HAVE_REGEX_H
return m_bValid ? regexec((regex_t*)m_pContext, szStr, m_iMatchBufSize, (regmatch_t*)m_pMatches, 0) == 0 : false;
return m_bValid ? regexec((regex_t*)m_pContext, szStr, 0, NULL, 0) == 0 : false;
#else
return false;
#endif
}
int RegEx::GetMatchCount()
{
#ifdef HAVE_REGEX_H
int iCount = 0;
if (m_pMatches)
{
regmatch_t* pMatches = (regmatch_t*)m_pMatches;
while (iCount < m_iMatchBufSize && pMatches[iCount].rm_so > -1)
{
iCount++;
}
}
return iCount;
#else
return 0;
#endif
}
int RegEx::GetMatchStart(int index)
{
#ifdef HAVE_REGEX_H
regmatch_t* pMatches = (regmatch_t*)m_pMatches;
return pMatches[index].rm_so;
#else
return NULL;
#endif
}
int RegEx::GetMatchLen(int index)
{
#ifdef HAVE_REGEX_H
regmatch_t* pMatches = (regmatch_t*)m_pMatches;
return pMatches[index].rm_eo - pMatches[index].rm_so;
#else
return 0;
#endif
}
WildMask::WildMask(const char *szPattern, bool bWantsPositions)
{
m_szPattern = strdup(szPattern);
m_bWantsPositions = bWantsPositions;
m_WildStart = NULL;
m_WildLen = NULL;
m_iArrLen = 0;
}
WildMask::~WildMask()
{
free(m_szPattern);
free(m_WildStart);
free(m_WildLen);
}
void WildMask::ExpandArray()
{
m_iWildCount++;
if (m_iWildCount > m_iArrLen)
{
m_iArrLen += 100;
m_WildStart = (int*)realloc(m_WildStart, sizeof(*m_WildStart) * m_iArrLen);
m_WildLen = (int*)realloc(m_WildLen, sizeof(*m_WildLen) * m_iArrLen);
}
}
// Based on code from http://bytes.com/topic/c/answers/212179-string-matching
// Extended to save positions of matches.
bool WildMask::Match(const char *szStr)
{
const char* pat = m_szPattern;
const char* str = szStr;
const char *spos, *wpos;
m_iWildCount = 0;
bool qmark = false;
bool star = false;
spos = wpos = str;
while (*str && *pat != '*')
{
if (m_bWantsPositions && (*pat == '?' || *pat == '#'))
{
if (!qmark)
{
ExpandArray();
m_WildStart[m_iWildCount-1] = str - szStr;
m_WildLen[m_iWildCount-1] = 0;
qmark = true;
}
}
else if (m_bWantsPositions && qmark)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
qmark = false;
}
if (!(tolower(*pat) == tolower(*str) || *pat == '?' ||
(*pat == '#' && strchr("0123456789", *str))))
{
return false;
}
str++;
pat++;
}
if (m_bWantsPositions && qmark)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
qmark = false;
}
while (*str)
{
if (*pat == '*')
{
if (m_bWantsPositions && qmark)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
qmark = false;
}
if (m_bWantsPositions && !star)
{
ExpandArray();
m_WildStart[m_iWildCount-1] = str - szStr;
m_WildLen[m_iWildCount-1] = 0;
star = true;
}
if (*++pat == '\0')
{
if (m_bWantsPositions && star)
{
m_WildLen[m_iWildCount-1] = strlen(str);
}
return true;
}
wpos = pat;
spos = str + 1;
}
else if (*pat == '?' || (*pat == '#' && strchr("0123456789", *str)))
{
if (m_bWantsPositions && !qmark)
{
ExpandArray();
m_WildStart[m_iWildCount-1] = str - szStr;
m_WildLen[m_iWildCount-1] = 0;
qmark = true;
}
pat++;
str++;
}
else if (tolower(*pat) == tolower(*str))
{
if (m_bWantsPositions && qmark)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
qmark = false;
}
else if (m_bWantsPositions && star)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
star = false;
}
pat++;
str++;
}
else
{
if (m_bWantsPositions && qmark)
{
m_iWildCount--;
qmark = false;
}
pat = wpos;
str = spos++;
star = true;
}
}
if (m_bWantsPositions && qmark)
{
m_WildLen[m_iWildCount-1] = str - (szStr + m_WildStart[m_iWildCount-1]);
}
if (*pat == '*' && m_bWantsPositions && !star)
{
ExpandArray();
m_WildStart[m_iWildCount-1] = str - szStr;
m_WildLen[m_iWildCount-1] = strlen(str);
}
while (*pat == '*')
{
pat++;
}
return *pat == '\0';
}
#ifndef DISABLE_GZIP
unsigned int ZLib::GZipLen(int iInputBufferLength)
{

52
Util.h
View File

@@ -81,10 +81,8 @@ public:
static bool CreateSparseFile(const char* szFilename, int iSize);
static bool TruncateFile(const char* szFilename, int iSize);
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
static bool MakeUniqueFilename(char* szDestBufFilename, int iDestBufSize, const char* szDestDir, const char* szBasename);
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
static bool FileExists(const char* szFilename);
static bool FileExists(const char* szPath, const char* szFilenameWithoutPath);
static bool DirectoryExists(const char* szDirFilename);
static bool CreateDirectory(const char* szDirFilename);
static bool RemoveDirectory(const char* szDirFilename);
@@ -98,7 +96,6 @@ public:
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
#ifndef WIN32
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
static void FixExecPermission(const char* szFilename);
#endif
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
@@ -130,14 +127,8 @@ public:
static float Int64ToFloat(long long Int64);
static void TrimRight(char* szStr);
static char* Trim(char* szStr);
static bool EmptyStr(const char* szStr) { return !szStr || !*szStr; }
/* replace all occurences of szFrom to szTo in string szStr with a limitation that szTo must be shorter than szFrom */
static char* ReduceStr(char* szStr, const char* szFrom, const char* szTo);
/* Calculate Hash using Bob Jenkins (1996) algorithm */
static unsigned int HashBJ96(const char* szBuffer, int iBufSize, unsigned int iInitValue);
#ifdef WIN32
static bool RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen);
@@ -216,19 +207,6 @@ public:
* The string is decoded on the place overwriting the content of raw-data.
*/
static void HttpUnquote(char* raw);
#ifdef WIN32
static bool Utf8ToAnsi(char* szBuffer, int iBufLen);
static bool AnsiToUtf8(char* szBuffer, int iBufLen);
#endif
/*
* Converts ISO-8859-1 (aka Latin-1) into UTF-8.
* Returns new string allocated with malloc, it needs to be freed by caller.
*/
static char* Latin1ToUtf8(const char* szStr);
static time_t ParseRfc822DateTime(const char* szDateTimeStr);
};
class URL
@@ -264,38 +242,12 @@ class RegEx
private:
void* m_pContext;
bool m_bValid;
void* m_pMatches;
int m_iMatchBufSize;
public:
RegEx(const char *szPattern, int iMatchBufSize = 100);
RegEx(const char *szPattern);
~RegEx();
bool IsValid() { return m_bValid; }
bool Match(const char *szStr);
int GetMatchCount();
int GetMatchStart(int index);
int GetMatchLen(int index);
};
class WildMask
{
private:
char* m_szPattern;
bool m_bWantsPositions;
int m_iWildCount;
int* m_WildStart;
int* m_WildLen;
int m_iArrLen;
void ExpandArray();
public:
WildMask(const char *szPattern, bool bWantsPositions = false);
~WildMask();
bool Match(const char *szStr);
int GetMatchCount() { return m_iWildCount; }
int GetMatchStart(int index) { return m_WildStart[index]; }
int GetMatchLen(int index) { return m_WildLen[index]; }
};
#ifndef DISABLE_GZIP

View File

@@ -62,8 +62,6 @@ WebDownloader::WebDownloader()
m_bConfirmedLength = false;
m_eStatus = adUndefined;
m_szOriginalFilename = NULL;
m_bForce = false;
m_bRetry = true;
SetLastUpdateTimeNow();
}
@@ -71,10 +69,22 @@ WebDownloader::~WebDownloader()
{
debug("Destroying WebDownloader");
free(m_szURL);
free(m_szInfoName);
free(m_szOutputFilename);
free(m_szOriginalFilename);
if (m_szURL)
{
free(m_szURL);
}
if (m_szInfoName)
{
free(m_szInfoName);
}
if (m_szOutputFilename)
{
free(m_szOutputFilename);
}
if (m_szOriginalFilename)
{
free(m_szOriginalFilename);
}
}
void WebDownloader::SetOutputFilename(const char* v)
@@ -89,7 +99,6 @@ void WebDownloader::SetInfoName(const char* v)
void WebDownloader::SetURL(const char * szURL)
{
free(m_szURL);
m_szURL = strdup(szURL);
}
@@ -107,13 +116,7 @@ void WebDownloader::Run()
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
int iRemainedConnectRetries = iRemainedDownloadRetries > 10 ? iRemainedDownloadRetries : 10;
if (!m_bRetry)
{
iRemainedDownloadRetries = 1;
iRemainedConnectRetries = 1;
}
m_iRedirects = 0;
EStatus Status = adFailed;
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
@@ -124,19 +127,19 @@ void WebDownloader::Run()
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
&& !IsStopped() && !(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
&& !IsStopped() && !(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
int msec = 0;
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
!(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
{
usleep(100 * 1000);
msec += 100;
}
}
if (IsStopped() || (!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
{
Status = adRetry;
break;
@@ -147,17 +150,6 @@ void WebDownloader::Run()
break;
}
if (Status == adRedirect)
{
m_iRedirects++;
if (m_iRedirects > 5)
{
warn("Too many redirects for %s", m_szInfoName);
Status = adFailed;
break;
}
}
if (Status != adConnectError)
{
iRemainedDownloadRetries--;
@@ -320,8 +312,6 @@ WebDownloader::EStatus WebDownloader::DownloadHeaders()
m_iContentLen = -1;
bool bFirstLine = true;
m_bGZip = false;
m_bRedirecting = false;
m_bRedirected = false;
// Headers
while (!IsStopped())
@@ -364,14 +354,7 @@ WebDownloader::EStatus WebDownloader::DownloadHeaders()
break;
}
Util::TrimRight(line);
ProcessHeader(line);
if (m_bRedirected)
{
Status = adRedirect;
break;
}
}
free(szLineBuf);
@@ -414,7 +397,7 @@ WebDownloader::EStatus WebDownloader::DownloadBody()
// Have we encountered a timeout?
if (iLen <= 0)
{
if (m_iContentLen == -1 && iWrittenLen > 0)
if (m_iContentLen == -1)
{
bEnd = true;
break;
@@ -447,7 +430,10 @@ WebDownloader::EStatus WebDownloader::DownloadBody()
free(szLineBuf);
#ifndef DISABLE_GZIP
delete m_pGUnzipStream;
if (m_pGUnzipStream)
{
delete m_pGUnzipStream;
}
#endif
if (m_pOutFile)
@@ -499,11 +485,6 @@ WebDownloader::EStatus WebDownloader::CheckResponse(const char* szResponse)
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
return adNotFound;
}
else if (!strncmp(szHTTPResponse, "301", 3) || !strncmp(szHTTPResponse, "302", 3))
{
m_bRedirecting = true;
return adRunning;
}
else if (!strncmp(szHTTPResponse, "200", 3))
{
// OK
@@ -519,24 +500,21 @@ WebDownloader::EStatus WebDownloader::CheckResponse(const char* szResponse)
void WebDownloader::ProcessHeader(const char* szLine)
{
if (!strncasecmp(szLine, "Content-Length: ", 16))
if (!strncmp(szLine, "Content-Length: ", 16))
{
m_iContentLen = atoi(szLine + 16);
m_bConfirmedLength = true;
}
else if (!strncasecmp(szLine, "Content-Encoding: gzip", 22))
if (!strncmp(szLine, "Content-Encoding: gzip", 22))
{
m_bGZip = true;
}
else if (!strncasecmp(szLine, "Content-Disposition: ", 21))
if (!strncmp(szLine, "Content-Disposition: ", 21))
{
ParseFilename(szLine);
}
else if (m_bRedirecting && !strncasecmp(szLine, "Location: ", 10))
{
ParseRedirect(szLine + 10);
m_bRedirected = true;
}
}
void WebDownloader::ParseFilename(const char* szContentDisposition)
@@ -573,36 +551,15 @@ void WebDownloader::ParseFilename(const char* szContentDisposition)
WebUtil::HttpUnquote(fname);
free(m_szOriginalFilename);
if (m_szOriginalFilename)
{
free(m_szOriginalFilename);
}
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
debug("OriginalFilename: %s", m_szOriginalFilename);
}
void WebDownloader::ParseRedirect(const char* szLocation)
{
const char* szNewURL = szLocation;
char szUrlBuf[1024];
URL newUrl(szNewURL);
if (!newUrl.IsValid())
{
// relative address
URL oldUrl(m_szURL);
if (oldUrl.GetPort() > 0)
{
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szNewURL);
}
else
{
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szNewURL);
}
szUrlBuf[1024-1] = '\0';
szNewURL = szUrlBuf;
}
detail("URL %s redirected to %s", m_szURL, szNewURL);
SetURL(szNewURL);
}
bool WebDownloader::Write(void* pBuffer, int iLen)
{
if (!m_pOutFile && !PrepareFile())

View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2012-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,7 +44,6 @@ public:
adFailed,
adRetry,
adNotFound,
adRedirect,
adConnectError,
adFatalError
};
@@ -61,12 +60,7 @@ private:
int m_iContentLen;
bool m_bConfirmedLength;
char* m_szOriginalFilename;
bool m_bForce;
bool m_bRedirecting;
bool m_bRedirected;
int m_iRedirects;
bool m_bGZip;
bool m_bRetry;
#ifndef DISABLE_GZIP
GUnzipStream* m_pGUnzipStream;
#endif
@@ -76,12 +70,12 @@ private:
bool PrepareFile();
void FreeConnection();
EStatus CheckResponse(const char* szResponse);
EStatus Download();
EStatus CreateConnection(URL *pUrl);
void ParseFilename(const char* szContentDisposition);
void SendHeaders(URL *pUrl);
EStatus DownloadHeaders();
EStatus DownloadBody();
void ParseRedirect(const char* szLocation);
protected:
virtual void ProcessHeader(const char* szLine);
@@ -92,7 +86,6 @@ public:
EStatus GetStatus() { return m_eStatus; }
virtual void Run();
virtual void Stop();
EStatus Download();
bool Terminate();
void SetInfoName(const char* v);
const char* GetInfoName() { return m_szInfoName; }
@@ -103,8 +96,6 @@ public:
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
bool GetConfirmedLength() { return m_bConfirmedLength; }
const char* GetOriginalFilename() { return m_szOriginalFilename; }
void SetForce(bool bForce) { m_bForce = bForce; }
void SetRetry(bool bRetry) { m_bRetry = bRetry; }
void LogDebugInfo();
};

View File

@@ -66,9 +66,18 @@ WebProcessor::WebProcessor()
WebProcessor::~WebProcessor()
{
free(m_szRequest);
free(m_szUrl);
free(m_szOrigin);
if (m_szRequest)
{
free(m_szRequest);
}
if (m_szUrl)
{
free(m_szUrl);
}
if (m_szOrigin)
{
free(m_szOrigin);
}
}
void WebProcessor::SetUrl(const char* szUrl)
@@ -176,8 +185,7 @@ void WebProcessor::Execute()
debug("Final URL=%s", m_szUrl);
if (strlen(g_pOptions->GetControlPassword()) > 0 &&
!(strlen(g_pOptions->GetAuthorizedIP()) > 0 && IsAuthorizedIP(m_pConnection->GetRemoteAddr())))
if (strlen(g_pOptions->GetControlPassword()) > 0)
{
if (strlen(szAuthInfo) == 0)
{
@@ -217,30 +225,6 @@ void WebProcessor::Execute()
Dispatch();
}
bool WebProcessor::IsAuthorizedIP(const char* szRemoteAddr)
{
const char* szRemoteIP = m_pConnection->GetRemoteAddr();
// split option AuthorizedIP into tokens and check each token
bool bAuthorized = false;
char* szAuthorizedIP = strdup(g_pOptions->GetAuthorizedIP());
char* saveptr;
char* szIP = strtok_r(szAuthorizedIP, ",;", &saveptr);
while (szIP)
{
szIP = Util::Trim(szIP);
if (szIP[0] != '\0' && !strcmp(szIP, szRemoteIP))
{
bAuthorized = true;
break;
}
szIP = strtok_r(NULL, ",;", &saveptr);
}
free(szAuthorizedIP);
return bAuthorized;
}
void WebProcessor::Dispatch()
{
if (*m_szUrl != '/')
@@ -441,7 +425,10 @@ void WebProcessor::SendBodyResponse(const char* szBody, int iBodyLen, const char
m_pConnection->Send(szBody, iBodyLen);
#ifndef DISABLE_GZIP
free(szGBuf);
if (szGBuf)
{
free(szGBuf);
}
#endif
}

View File

@@ -54,7 +54,6 @@ private:
void SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType);
void SendRedirectResponse(const char* szURL);
const char* DetectContentType(const char* szFilename);
bool IsAuthorizedIP(const char* szRemoteAddr);
public:
WebProcessor();

1593
XmlRpc.cpp
View File

File diff suppressed because it is too large Load Diff

171
XmlRpc.h
View File

@@ -1,7 +1,7 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -109,4 +109,173 @@ public:
bool GetFault() { return m_bFault; }
};
class ErrorXmlCommand: public XmlCommand
{
private:
int m_iErrCode;
const char* m_szErrText;
public:
ErrorXmlCommand(int iErrCode, const char* szErrText);
virtual void Execute();
};
class PauseUnpauseXmlCommand: public XmlCommand
{
public:
enum EPauseAction
{
paDownload,
paDownload2,
paPostProcess,
paScan
};
private:
bool m_bPause;
EPauseAction m_eEPauseAction;
public:
PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction);
virtual void Execute();
};
class ScheduleResumeXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ShutdownXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ReloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class VersionXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DumpDebugXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SetDownloadRateXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class StatusXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListFilesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ListGroupsXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class EditQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class PostQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class WriteLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ClearLogXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ScanXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class HistoryXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class DownloadUrlXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class UrlQueueXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class LoadConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class SaveConfigXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
class ConfigTemplatesXmlCommand: public XmlCommand
{
public:
virtual void Execute();
};
#endif

20
configure vendored
View File

@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for nzbget 12.0.
# Generated by GNU Autoconf 2.61 for nzbget 11.0.
#
# Report bugs to <hugbug@users.sourceforge.net>.
#
@@ -574,8 +574,8 @@ SHELL=${CONFIG_SHELL-/bin/sh}
# Identity of this package.
PACKAGE_NAME='nzbget'
PACKAGE_TARNAME='nzbget'
PACKAGE_VERSION='12.0'
PACKAGE_STRING='nzbget 12.0'
PACKAGE_VERSION='11.0'
PACKAGE_STRING='nzbget 11.0'
PACKAGE_BUGREPORT='hugbug@users.sourceforge.net'
ac_unique_file="nzbget.cpp"
@@ -1235,7 +1235,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures nzbget 12.0 to adapt to many kinds of systems.
\`configure' configures nzbget 11.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1306,7 +1306,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of nzbget 12.0:";;
short | recursive ) echo "Configuration of nzbget 11.0:";;
esac
cat <<\_ACEOF
@@ -1453,7 +1453,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
nzbget configure 12.0
nzbget configure 11.0
generated by GNU Autoconf 2.61
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
@@ -1467,7 +1467,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by nzbget $as_me 12.0, which was
It was created by nzbget $as_me 11.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
$ $0 $@
@@ -2263,7 +2263,7 @@ fi
# Define the identity of the package.
PACKAGE=nzbget
VERSION=12.0
VERSION=11.0
cat >>confdefs.h <<_ACEOF
@@ -9537,7 +9537,7 @@ exec 6>&1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by nzbget $as_me 12.0, which was
This file was extended by nzbget $as_me 11.0, which was
generated by GNU Autoconf 2.61. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -9590,7 +9590,7 @@ Report bugs to <bug-autoconf@gnu.org>."
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF
ac_cs_version="\\
nzbget config.status 12.0
nzbget config.status 11.0
configured by $0, generated by GNU Autoconf 2.61,
with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"

View File

@@ -2,9 +2,9 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.59)
AC_INIT(nzbget, 12.0, hugbug@users.sourceforge.net)
AC_INIT(nzbget, 11.0, hugbug@users.sourceforge.net)
AC_CANONICAL_SYSTEM
AM_INIT_AUTOMAKE(nzbget, 12.0)
AM_INIT_AUTOMAKE(nzbget, 11.0)
AC_CONFIG_SRCDIR([nzbget.cpp])
AC_CONFIG_HEADERS([config.h])

View File

@@ -21,8 +21,8 @@
# Root directory for all tasks.
#
# On POSIX you can use "~" as alias for home directory (e.g. "~/downloads").
# On Windows use absolute paths (e.g. "C:\Downloads").
# On POSIX you can use "~" as alias for home directory (e.g. "~/download").
# On Windows use absolute paths (e.g. "C:\Download").
MainDir=~/downloads
# Destination directory for downloaded files.
@@ -46,24 +46,16 @@ DestDir=${MainDir}/dst
#
# NOTE: If the option <InterDir> is set to empty value the downloaded
# files are put directly to destination directory (option <DestDir>).
InterDir=${MainDir}/inter
InterDir=
# Directory for incoming nzb-files.
# Directory to monitor for incoming nzb-jobs.
#
# If a new nzb-file is added to queue via web-interface or RPC-API, it
# is saved into this directory and then processed by pre-processing
# script (option <NzbProcess>).
#
# This directory is also monitored for new nzb-files. If a new file
# is found it is added to download queue. The directory can have
# sub-directories. A nzb-file queued from a subdirectory is automatically
# assigned to category with sub-directory-name.
# Can have subdirectories.
# A nzb-file queued from a subdirectory will be automatically assigned to
# category with the directory-name.
NzbDir=${MainDir}/nzb
# Directory to store program state.
#
# This directory is used to save download queue, history, information
# about fetched RSS feeds, statistics, etc.
# Directory to store download queue.
QueueDir=${MainDir}/queue
# Directory to store temporary files.
@@ -120,17 +112,6 @@ ConfigTemplate=
# change the name of Server3 to Server2. Otherwise it will not be properly
# read from the config file. Server number doesn't affect its priority (level).
# Use this news server (yes, no).
#
# Set to "no" to temporary disable the server.
Server1.Active=yes
# Name of news server.
#
# The name is used in UI and for logging. It can be any string, you
# may even leave it empty.
Server1.Name=
# Level (priority) of news server (0-99).
#
# The servers are ordered by their level. NZBGet first tries to download
@@ -219,7 +200,7 @@ Server1.Connections=4
##############################################################################
### SECURITY ###
### REMOTE CONTROL ###
# IP on which NZBGet server listen and which clients use to contact NZBGet.
#
@@ -276,16 +257,9 @@ SecureCert=
# Full path to key file for encrypted communication.
SecureKey=
# IP-addresses allowed to connect without authorization.
#
# Comma separated list of privileged IPs for easy access to NZBGet
# built-in web-server (web-interface and RPC).
#
# Example: 127.0.0.1,192.168.178.2.
#
# NOTE: Do not use this option if the program works behind another
# web-server because all requests will have the address of this server.
AuthorizedIP=
##############################################################################
### PERMISSIONS ###
# User name for daemon-mode, POSIX only.
#
@@ -309,228 +283,6 @@ DaemonUsername=root
UMask=1000
##############################################################################
### CATEGORIES ###
# This section defines categories available in web-interface.
# Category name.
#
# Each nzb-file can be assigned to a category.
# Category name is passed to post-processing script and can be used by it
# to perform category specific processing.
Category1.Name=Movies
# Destination directory for this category.
#
# If this option is empty, then the default destination directory
# (option <DestDir>) is used. In this case if the option <AppendCategoryDir>
# is active, the program creates a subdirectory with category name within
# destination directory.
Category1.DestDir=
# Unpack downloaded nzb-files (yes, no).
#
# For more information see global option <Unpack>.
Category1.Unpack=yes
# Default list of post-processing scripts.
#
# For more information see global option <DefScript>.
Category1.DefScript=
# List of aliases.
#
# When a nzb-file is added from URL, RSS or RPC the category name
# is usually supplied by nzb-site or by application accessing
# NZBGet. Using Aliases you can match their categories with your owns.
#
# Separate aliases with commas or semicolons. Use wildcard-characters
# * and ? for pattern matching.
#
# Example: TV - HD, TV - SD, TV*
Category1.Aliases=
Category2.Name=Series
Category3.Name=Music
Category4.Name=Software
##############################################################################
### RSS FEEDS ###
# Name of RSS Feed.
#
# The name is used in UI and for logging. It can be any string.
#Feed1.Name=my feed
# Address (URL) of RSS Feed.
#
# Example: https://myindexer.com/api?apikey=3544646bfd1c535a9654645609800901&t=search&q=game.
#
# NOTE: When the feed is fetched for the very first time all existing
# items are ignored. The items found on subsequentional fetches are processed.
#Feed1.URL=
# Filter rules for items.
#
# Use filter to ignore unwanted items in the feed. In its simplest version
# the filter is a space separated list of words which must be present in
# the item title.
#
# Example: linux debian dvd.
#
# MORE INFO:
# NOTE: This is a short documentation, for more information visit
# http://nzbget.sourceforge.net/RSS.
#
# Feed filter consists of rules - one rule per line. Each rule defines
# a search string and a command, which must be performed if the search
# string matches. There are five kinds of rule-commands: Accept,
# Reject, Require, Options, Comment.
#
# NOTE: Since options in the configuration file can not span multiple
# lines, the lines (rules) must be separated with %-character (percent).
#
# Definition of a rule:
# [A:|A(options):|R:|Q:|O(options):|#] search-string
#
# A - declares Accept-rule. Rules are accept-rules by default, the
# "A:" can be imitted. If the feed item matches to the rule the
# item is considered good and no further rules are checked.
# R - declares Reject-rule. If the feed item matches to the rule the
# item is considered bad and no further rules are checked.
# Q - declares Require-rule. If the feed item DOES NOT match to the rule
# the item is considered bad and no further rules are checked.
# O - declares Options-rule. If the feed item matches to the rule the
# options declared in the rule are set for the item. The item is
# neither accepted nor rejected via this rule but can be accepted
# later by one of Accept-rules. In this case the item will have its
# options already set (unless the Accept-rule overrides them).
# # - lines starting with # are considered comments and are ignored. You
# can use comments to explain complex rules or to temporary disable
# rules for debugging.
#
# Options allow to set properties on nzb-file. It's a comma-separated
# list of property names with their values.
#
# Definition of an option:
# name:value
#
# Options can be defined using long option names or short names:
# category (cat, c) - set category name, value is a string;
# pause (p) - add nzb in paused or unpaused state, possible
# values are: yes (y), no (n);
# priority (pr, r) - set priority, value is a signed integer number;
# priority+ (pr+, r+) - increase priority, value is a signed integer number;
# dupescore (ds, s) - set duplicate score, value is a signed integer number;
# dupescore+ (ds+, s+) - increase duplicate score, value is a signed integer number;
# dupekey (dk, k) - set duplicate key, value is a string;
# dupekey+ (dk+, k+) - add to duplicate key, value is a string;
# dupemode (dm, m) - set duplicate check mode, possible values
# are: score (s), all (a), force (f);
# rageid - generate duplicate key using this rageid
# (integer number) and season/episode numbers;
# series - generate duplicate key using series identifier
# (any unique string) and season/episode numbers.
#
# Examples of option definitions:
# Accept(category:my series, pause:yes, priority:100): my show 1080p;
# Options(c:my series, p:y, r:100): 1080p;
# Options(s:1000): 1080p;
# Options(k+:1080p): 1080p;
# Options(dupemode:force): BluRay.
#
# Rule-options override values set in feed-options.
#
# The search-string is similar to used in search engines. It consists of
# search terms separated with spaces. Every term is checked for a feed
# item and if they all succeed the rule is considered matching.
#
# Definition of a term:
# [+|-][field:][command]param
#
# + - declares a positive term. Terms are positive by default,
# the "+" can be omitted;
# - - declares a negative term. If the term succeed the feed
# item is ignored;
# field - field to which apply the term. If not specified
# the default field "title" is used;
# command - a special character defining how to interpret the
# parameter (followed after the command):
# @ - search for string "param". This is default command,
# the "@" can be omitted;
# $ - "param" defines a regular expression (using POSIX Extended
# Regular Expressions syntax);
# = - equal;
# < - less than;
# <= - equal or less than;
# > - greater than;
# >= - equal or greater than;
# param - parameter for command.
#
# Commands @ and $ are for use with text fields (title, filename, category,
# link, description, dupekey). Commands =, <, <=, > and >= are for use
# with numeric fields (size, age, imdbid, rageid, season, episode, priority,
# dupescore).
#
# Only fields title, filename and age are always present. The availability of
# other fields depend on rss feed provider.
#
# Any newznab attribute (encoded as "newznab:attr" in the RSS feed) can
# be used as search field with prefix "attr-", for example "attr-genre".
#
# Text search (Command @) supports supports wildcard characters * (matches
# any number of any characters), ? (matches any one character)
# and # (matches one digit).
# Text search is by default performed against words (word-search mode): the
# field content is separated into words and then each word is checked
# against pattern. If the search pattern starts and ends with * (star)
# the search is performed against the whole field content
# (substring-search mode). If the search pattern contains word separator
# characters (except * and ?) the search is performed on the whole
# field (the word-search would be obviously never successful in this
# case). Word separators are: !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~.
#
# Field "size" can have suffixes "K" or "KB" for kilobytes, "M" or "MB"
# for megabytes and "G" or "GB" for gigabytes. Field "age" can have
# suffixes "m" for minutes, "h" for hours and "d" for days. If suffix
# is not specified default is days.
#
# Examples (the trailing ; or . is not part of filter):
# 1) A: s01* -category:anime;
# 2) my show WEB-DL;
# 3) *my?show* WEB-DL size:<1.8GB age:>2h;
# 4) R: size:>9GB;
# 5) Q: HDTV.
#
# NOTE: This is a short documentation, for more information visit
# http://nzbget.sourceforge.net/RSS.
#Feed1.Filter=
# How often to check for new items (minutes).
#
# Value "0" disables the automatic check of this feed.
#Feed1.Interval=15
# Add nzb-files as paused (yes, no).
#Feed1.PauseNzb=no
# Category for added nzb-files.
#
# NOTE: Feed providers may include category name within response when nzb-file
# is downloaded. If you want to use the providers category leave the option empty.
#Feed1.Category=
# Priority for added nzb-files (number).
#
# Priority can be any integer value. The web-interface however operates
# with only five predefined priorities: -100 (very low priority), -50
# (low priority), 0 (normal priority, default), 50 (high priority),
# 100 (very high priority).
#Feed1.Priority=0
##############################################################################
### INCOMING NZBS ###
@@ -551,6 +303,17 @@ NzbDirInterval=5
# downloaded in web-browser.
NzbDirFileAge=60
# Automatic merging of nzb-files with the same filename (yes, no).
#
# A typical scenario: you put nzb-file into incoming directory, NZBGet adds
# file to queue. You find out, that the file doesn't have par-files. You
# find required par-files, put nzb-file with the par-files into incoming
# directory, NZBGet adds it to queue as a separate group. You want the second
# file to be merged with the first for parchecking to work properly. With
# option "MergeNzb" NZBGet can merge files automatically. You only need to
# save the second file under the same filename as the first one.
MergeNzb=no
# Set path to program, that must be executed before a nzb-file is added
# to queue.
#
@@ -684,29 +447,20 @@ NzbProcess=
# "$NZBOP_APPBIN" -c "$NZBOP_CONFIGFILE" -E FR P "$NZBNA_NZBNAME/.*\.nzb";
NzbAddedProcess=
# Check for duplicate titles (yes, no).
# Check for duplicate files (yes, no).
#
# If this option is enabled the program checks by adding of a new nzb-file:
# 1) if history contains the same title (see below) with success status
# the nzb-file is not added to queue;
# 2) if download queue already contains the same title the nzb-file is
# added to queue for backup (if firt file fails);
# 3) if nzb-file contains duplicate entries. This helps to find errors
# in bad nzb-files.
# 1) if nzb-file contains duplicate entries. This check aims on detecting
# of reposted files (if first file was not fully uploaded).
# If the program find two files with identical names, only the
# biggest of these files will be added to queue;
# 2) if download queue already contains file with the same name;
# 3) if destination file on disk already exists.
# In last two cases: if the file exists it will not be added to queue.
#
# "Same title" means the nzb file name is same or the duplicate key is
# same. Duplicate keys are set by fetching from RSS feeds using title
# identifier fields provided by RSS provider (imdbid or rageid/season/episode).
#
# If duplicates were detected only one of them is downloaded. If download
# fails another duplicate is tried. If download succeeds all remaining
# duplicates are deleted from queue.
#
# NOTE: for automatic duplicate handling option <HealthCheck> must be
# set to "Delete" or "None". If it is set to "Pause" you will need to
# manually unpause another duplicate (if any exists in queue).
#
# NOTE: For more info on duplicates see http://nzbget.sourceforge.net/RSS.
# If this option is disabled, all files are downloaded and duplicate files
# are renamed to "filename_duplicate1".
# Existing files are never deleted or overwritten.
DupeCheck=yes
@@ -828,9 +582,8 @@ TerminateTimeout=600
# Set the maximum download rate on program start (kilobytes/sec).
#
# The download rate can be changed later via remote calls.
#
# Value "0" means no speed control.
# The download rate can be changed later via remote calls.
DownloadRate=0
# Accurate speed rate calculation (yes, no).
@@ -876,65 +629,34 @@ WriteBufferSize=0
# Pause if disk space gets below this value (megabytes).
#
# Disk space is checked for directories pointed by option <DestDir> and
# option <InterDir>.
#
# Value "0" disables the check.
# Only the disk space on the drive with <DestDir> is checked.
# The drive with <TempDir> is not checked.
DiskSpace=250
# Delete already downloaded files from disk when nzb-file is deleted
# (yes, no).
# Delete already downloaded files from disk, if the download of nzb-file was
# cancelled (nzb-file was deleted from queue) (yes, no).
#
# This option defines if downloaded files must be deleted when:
# 1) download of nzb-file is cancelled (deleted from queue);
# 2) history record with failure-status (par-failure or unpack-failure)
# is deleted from history.
DeleteCleanupDisk=yes
# Delete source nzb-file when it is not needed anymore (yes, no).
#
# Enable this option for automatic deletion of source nzb-file from
# incoming directory when the program doesn't require it anymore (the
# nzb-file has been deleted from queue and history).
NzbCleanupDisk=yes
# NOTE: NZBGet does not delete files in a case if all remaining files in
# queue are par-files. That prevents the accidental deletion if the option
# <ParCleanupQueue> is disabled or if the program was interrupted during
# parcheck and later restarted without reloading of post queue (option
# <ReloadPostQueue> disabled).
DeleteCleanupDisk=no
# Keep the history of downloaded nzb-files (days).
#
# After download and post-processing the items are added to history where
# their status can be checked and they can be post-processed again if
# neccessary.
# Value "0" disables the history.
#
# After expiring of defined period:
#
# If option <DupeCheck> is active the items become hidden and the amount
# of data kept is significantly reduced (for better performance), only
# fields necessary for duplicate check are kept. The item remain in the
# hidden history (forever);
#
# If option <DupeCheck> is NOT active the items are removed from history.
#
# Value "0" disables history. Duplicate check will not work.
KeepHistory=30
# Keep the history of outdated feed items (days).
#
# After fetching of an RSS feed the information about included items (nzb-files)
# is saved to disk. This allows to detect new items on next fetch. Feed
# providers update RSS feeds constantly. Since the feed length is limited
# (usually 100 items or less) the old items get pushed away by new
# ones. When an item is not present in the feed anymore it's not necessary
# to keep the information about this item on the disk.
#
# If option is set to "0", the outdated items are deleted from history
# immediately.
#
# Otherwise the items are held in the history for defined number of
# days. Keeping of items for few days helps in situations when feed provider
# has technical issues and may response with empty feeds (or with missing
# items). When the technical issue is fixed the items may reappear in the
# feed causing the program to redownload items if they were not found in
# the feed history.
FeedHistory=7
# NOTE: When a collection having paused files is added to history all remaining
# files are moved from download queue to a list of parked files. It holds files
# which could be required later if the collection will be moved back to
# download queue for downloading of remaining files. The parked files still
# consume some amount of memory and disk space. If the collection was downloaded
# and successfully par-checked or postprocessed it is recommended to discard the
# unneeded parked files before adding the collection to history. For par2-files
# that can be achieved with the option <ParCleanupQueue>.
KeepHistory=7
# Maximum number of simultaneous connections for nzb URL downloads (0-999).
#
@@ -943,12 +665,35 @@ FeedHistory=7
# used for this purpose, when multiple URLs were added at the same time.
UrlConnections=4
# Force URL-downloads even if download queue is paused (yes, no).
##############################################################################
### CATEGORIES ###
# This section defines categories available in web-interface.
# Category name.
#
# If option is active the URL-downloads (such as appending of nzb-files
# via URL or fetching of RSS feeds and nzb-files from feeds) are performed
# even if download is in paused state.
UrlForce=yes
# Each nzb-file can be assigned to a category.
# Category name is passed to post-processing script and can be used by it
# to perform category specific processing.
Category1.Name=Movies
# Destination directory for this category.
#
# If this option is empty, then the default destination directory
# (option <DestDir>) is used. In this case if the option <AppendCategoryDir>
# is active, the program creates a subdirectory with category name within
# destination directory.
Category1.DestDir=
# Default list of post-processing scripts.
#
# For more information see global option <DefScript>.
Category1.DefScript=
Category2.Name=Series
Category3.Name=Music
Category4.Name=Software
##############################################################################
@@ -996,22 +741,6 @@ CreateBrokenLog=yes
# newsserver etc.
DumpCore=no
# Local time correction (hours or minutes).
#
# The option allows to adjust timestamps when converting system time to
# local time and vice versa. The conversion is used when printing messages
# to the log-file and by option "TaskX.Time" in the scheduler settings.
#
# The option is usually not needed if the time zone is set up correctly.
# However, sometimes, especially when using a binary compiled on onother
# platform (cross-compiling) the conversion between system and local time
# may not work properly and requires adjustment.
#
# Values in the range -24..+24 are interpreted as hours, other values as minutes.
# Example 1: set time correction to one hour: TimeCorrection=1;
# Example 2: set time correction to one hour and a half: TimeCorrection=90.
TimeCorrection=0
# See also option <LogFile> in section "PATHS"
@@ -1068,8 +797,6 @@ UpdateInterval=200
# Asterix as hours-part means "every hour".
#
# Examples: "08:00", "00:00,06:00,12:00,18:00", "*:00", "*:00,*:30".
#
# NOTE: also see option <TimeCorrection>.
#Task1.Time=08:00
# Week days to execute the command (1-7).
@@ -1081,56 +808,47 @@ UpdateInterval=200
# Examples: "1-7", "1-5", "5,6", "1-5, 7".
#Task1.WeekDays=1-7
# Command to be executed ( PauseDownload, UnpauseDownload, PauseScan, UnpauseScan,
# DownloadRate, Process, ActivateServer, DeactivateServer, FetchFeed).
# Command to be executed (DownloadRate, PauseDownload, UnpauseDownload, PauseScan,
# UnpauseScan, Process).
#
# Possible commands:
# PauseDownload - pauses download;
# UnpauseDownload - resumes download;
# PauseScan - pauses scan of incoming nzb-directory;
# UnpauseScan - resumes scan of incoming nzb-directory;
# DownloadRate - sets download rate limit;
# Process - executes external program;
# ActivateServer - activate news-server;
# DeactivateServer - deactivate news-server;
# FetchFeed - fetch RSS feed.
#
# On start the program checks all tasks and determines current state
# for download-pause, scan-pause, download-rate and active servers.
#Task1.Command=PauseDownload
# DownloadRate - sets download rate in KB/s;
# PauseDownload - pauses download;
# UnpauseDownload - resumes download;
# PauseScan - pauses scan of incoming nzb-directory;
# UnpauseScan - resumes scan of incoming nzb-directory;
# Process - executes external program.
#Task1.Command=DownloadRate
# Parameters for the command if needed.
# Download rate to be set if the command is "DownloadRate" (kilobytes/sec).
#
# Some scheduler commands require additional parameters:
# DownloadRate - download rate limit to be set (kilobytes/sec).
# Example: 1000;
# Process - path to the program to execute and its parameters.
# Example: /home/user/fetch.sh.
# If filename or any parameter contains spaces it
# must be surrounded with single quotation
# marks. If filename/parameter contains single quotation marks,
# each of them must be replaced with two single quotation
# marks and the resulting filename/parameter must be
# surrounded with single quotation marks.
# Example: '/home/user/download/my scripts/task process.sh' 'world''s fun'.
# In this example one parameter (world's fun) is passed
# to the script (task process.sh).
# ActivateServer - comma separated list of news server ids or server names.
# Example: 1,3.
# Example: my news server 1, my news server 2.
# NOTE: server names should not have commas.
# DeactivateServer - see ActivateServer.
# FetchFeed - comma separated list of RSS feed ids or feed names.
# Example: 1,3.
# Example: bookmarks feed, another feed.
# NOTE: feed names should not have commas.
# NOTE: use feed id "0" to fetch all feeds.
#Task1.Param=
# Value "0" means no speed control.
#
# If the option <TaskX.Command> is not set to "DownloadRate" this option
# is ignored and can be omitted.
#Task1.DownloadRate=100
# Path to the program to execute if the command is "Process".
#
# Example: /home/user/fetch-nzb.sh.
#
# If the option <TaskX.Command> is not set to "Process" this option
# is ignored and can be omitted.
#
# NOTE: It's allowed to add parameters to command line. If filename or
# any parameter contains spaces it must be surrounded with single quotation
# marks. If filename/parameter contains single quotation marks, each of them
# must be replaced with two single quotation marks and the resulting filename/
# parameter must be surrounded with single quotation marks.
# Example: '/home/user/download/my scripts/task process.sh' 'world''s fun'.
# In this example one parameter (world's fun) is passed to the script
# (task process.sh).
#Task1.Process=/home/user/script.sh
#Task2.Time=20:00
#Task2.WeekDays=1-7
#Task2.Command=UnpauseDownload
#Task2.Param=
#Task2.Command=DownloadRate
#Task2.DownloadRate=0
##############################################################################
@@ -1160,8 +878,8 @@ ParCheck=auto
# may take too much resources and time on a slow computers.
ParRepair=yes
# What files should be scanned during par-verification (auto, limited,
# full).
# What files should be scanned during par-verification (limited,
# full, auto).
#
# Limited - scan only files belonging to the par-set;
# Full - scan all files in the directory. This helps if the
@@ -1170,38 +888,35 @@ ParRepair=yes
# detects missing files, it scans other files in the
# directory until all required files are found.
#
# NOTE: for par-check/repair NZBGet uses library libpar2. The widely
# used version 0.2 of the library has few bugs, sometimes causing
# NOTE: for par-check/repair NZBGet uses library libpar2. The last and
# widely used version 0.2 of the library has few bugs, sometimes causing
# a crash of the program. This is especially true when using "full" or
# "auto" par-scan. NZBGet is supplied with patches addressing these
# issues. Please apply the patches to libpar2 and recompile it.
ParScan=auto
# Check for renamed files (yes, no).
# Use only par2-files with matching names (yes, no).
#
# Par-rename restores original file names using information stored
# in par2-files. When enabled the par-rename is performed as a first
# step of post-processing for every nzb-file having par2-files.
# If par-check needs extra par-blocks it looks for paused par2-files
# in the download queue. These par2-files should have the same base name
# as the main par2-file, currently loaded in par-checker. Sometimes extra
# par2-files have non-matching names (especially if they were uploaded
# by a different poster). Normally par-checker does not use these files, but
# you can allow it to use them by setting <StrictParName> to "no".
# There is a small side effect then: if NZB-file contains more than one
# collection of files (with different par-sets), par-checker may download
# par2-files from a wrong collection and will need to unpause other
# par2-files until all required files are downloaded. This increases the
# traffic (but not harm the par-check).
#
# Par-rename is very fast and is highly recommended, especially if
# unpack is disabled.
ParRename=yes
# What to do if download health goes down below critical health (delete,
# pause, none).
#
# Delete - delete nzb-file from queue. If option <DeleteCleanupDisk>
# is active the already downloaded files will be deleted too;
# Pause - pause nzb-file;
# None - do nothing (continue download).
#
# NOTE: for automatic duplicate handling option must be set to "Delete"
# or "None". If it is set to "Pause" you will need to manually unpause
# another duplicate (if any exists in queue). See also option <DupeCheck>.
HealthCheck=delete
# NOTE: Par-checker always uses only par-files added from the same NZB-file
# and the option <StrictParName> does not change this behavior.
StrictParName=yes
# Maximum allowed time for par-repair (minutes).
#
# Value "0" means unlimited.
#
# If you use NZBGet on a very slow computer like NAS-device, it may be good to
# limit the time allowed for par-repair. NZBGet calculates the estimated time
# required for par-repair. If the estimated value exceeds the limit defined
@@ -1213,8 +928,6 @@ HealthCheck=delete
# set to a value smaller than 5 minutes, the comparison is made after the first
# whole minute.
#
# Value "0" means unlimited.
#
# NOTE: The option limits only the time required for repairing. It doesn't
# affect the first stage of parcheck - verification of files. However the
# verification speed is constant, it doesn't depend on files integrity and
@@ -1242,6 +955,12 @@ ParPauseQueue=no
# from download queue after successful check/repair.
ParCleanupQueue=yes
# Delete source nzb-file after successful check/repair (yes, no).
#
# Enable this option for automatic deletion of nzb-file from incoming directory
# after successful check/repair.
NzbCleanupDisk=no
# Files to delete after successful check/repair.
#
# List of file extensions or file names to delete after successful
@@ -1257,19 +976,12 @@ ExtCleanupDisk=.par2, .sfv, _brokenlog.txt
# Unpack downloaded nzb-files (yes, no).
#
# Each download (nzb-file) has a post-processing parameter "Unpack". The option
# <Unpack> is the default value assigned to this pp-parameter of the download
# when it is added to queue.
#
# When nzb-file is added to queue it can have a category assigned to it. In this
# case the option <CategoryX.Unpack> overrides the global option <Unpack>.
#
# If the download is damaged and could not be repaired using par-files
# the unpacking is not performed.
#
# If the option <ParCheck> is set to "Auto" the program tries to unpack
# If the option <ParCheck> is disabled the program will try to unpack
# downloaded files first. If the unpacking fails the par-check/repair
# is performed and the unpack is executed again.
# is performed and the unpack will be executed again.
Unpack=yes
# Pause download queue during unpack (yes, no).
@@ -1343,19 +1055,7 @@ SevenZipCmd=7z
# If download was renamed, this parameter reflects the new name;
# NZBPP_NZBFILENAME - name of processed nzb-file. It includes file extension and also
# may include full path;
# NZBPP_FINALDIR - final destination path if set by one of previous pp-scripts;
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string);
# NZBPP_HEALTH - download health: an integer value in the range
# from 0 (all articles failed) to 1000 (all articles
# successfully downloaded);
# NZBPP_CRITICALHEALTH - critical health for this nzb-file: an integer
# value in the range 0-1000. The critical health
# is calculated based on number and size of
# par-files. If nzb-file doesn't have any par-files
# the critical health is 1000 (100.0%). If a half
# of nzb-file were par-files its critical health
# would be 0. If NZBPP_HEALTH goes down below
# NZBPP_CRITICALHEALTH the download becomes unrepairable;
# NZBPP_PARSTATUS - result of par-check:
# 0 = not checked: par-check is disabled or nzb-file does
# not contain any par-files;
@@ -1365,20 +1065,9 @@ SevenZipCmd=7z
# 4 = par-check needed but skipped (option ParCheck=manual);
# NZBPP_UNPACKSTATUS - result of unpack:
# 0 = unpack is disabled or was skipped due to nzb-file
# parameters or due to errors during par-check;
# properties or due to errors during par-check;
# 1 = unpack failed;
# 2 = unpack successful;
# 3 = write error (usually not enough disk space);
# 4 = wrong password (only for rar5 archives);
# NZBPP_HEALTHDELETED - indicates if nzb-file was deleted by health
# check (1);
# NZBPP_TOTALARTICLES - number of articles in nzb-file;
# NZBPP_SUCCESSARTICLES - number of successfully downloaded articles;
# NZBPP_FAILEDARTICLES - number of failed articles;
# NZBPP_SERVERX_SUCCESSARTICLES - number of successfully downloaded
# articles from ServerX (X is replaced with server
# number, for example NZBPP_SERVER1_SUCCESSARTICLES);
# NZBPP_SERVERX_FAILEDARTICLES - number of failed articles from ServerX.
# 2 = unpack successful.
#
# If the script defines own options they are also passed as environment
# variables. These variables have prefix "NZBPO_" in their names. For
@@ -1398,18 +1087,6 @@ SevenZipCmd=7z
# "SERVER1_HOST". For options with predefined possible values (yes/no, etc.)
# the values are passed always in lower case.
#
# If the script moves files it can inform the program about new location
# by printing special message into standard output (which is processed
# by NZBGet):
# echo "[NZB] FINALDIR=/path/to/moved/files";
#
# To assign post-processing parameters:
# echo "[NZB] NZBPR_myvar=my value";
#
# The prefix "NZBPR_" will be removed. In this example a post-processing
# parameter with name "myvar" and value "my value" will be associated
# with nzb-file.
#
# Return value: NZBGet processes the exit code returned by the script:
# 93 - post-process successful (status = SUCCESS);
# 94 - post-process failed (status = FAILURE);

View File

@@ -75,8 +75,6 @@
#include "ParChecker.h"
#include "Scheduler.h"
#include "Scanner.h"
#include "FeedCoordinator.h"
#include "Maintenance.h"
#include "Util.h"
#ifdef WIN32
#include "NTService.h"
@@ -117,8 +115,6 @@ PrePostProcessor* g_pPrePostProcessor = NULL;
DiskState* g_pDiskState = NULL;
Scheduler* g_pScheduler = NULL;
Scanner* g_pScanner = NULL;
FeedCoordinator* g_pFeedCoordinator = NULL;
Maintenance* g_pMaintenance = NULL;
int g_iArgumentCount;
char* (*g_szEnvironmentVariables)[] = NULL;
char* (*g_szArguments)[] = NULL;
@@ -151,8 +147,6 @@ int main(int argc, char *argv[], char *argp[])
DisableCout();
#endif
srand (time(NULL));
g_iArgumentCount = argc;
g_szArguments = (char*(*)[])argv;
g_szEnvironmentVariables = (char*(*)[])argp;
@@ -211,12 +205,6 @@ void Run(bool bReload)
g_pServerPool = new ServerPool();
g_pScheduler = new Scheduler();
g_pQueueCoordinator = new QueueCoordinator();
g_pDownloadSpeedMeter = g_pQueueCoordinator;
g_pDownloadQueueHolder = g_pQueueCoordinator;
g_pUrlCoordinator = new UrlCoordinator();
g_pFeedCoordinator = new FeedCoordinator();
g_pMaintenance = new Maintenance();
debug("Reading options");
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
@@ -266,6 +254,9 @@ void Run(bool bReload)
if (!g_pOptions->GetRemoteClientMode())
{
g_pServerPool->InitConnections();
#ifdef DEBUG
g_pServerPool->LogDebugInfo();
#endif
}
#ifndef WIN32
@@ -295,6 +286,16 @@ void Run(bool bReload)
return;
}
// Create the queue coordinator
if (!g_pOptions->GetRemoteClientMode())
{
g_pQueueCoordinator = new QueueCoordinator();
g_pDownloadSpeedMeter = g_pQueueCoordinator;
g_pDownloadQueueHolder = g_pQueueCoordinator;
g_pUrlCoordinator = new UrlCoordinator();
}
// Setup the network-server
if (g_pOptions->GetServerMode())
{
@@ -364,13 +365,11 @@ void Run(bool bReload)
g_pQueueCoordinator->Start();
g_pUrlCoordinator->Start();
g_pPrePostProcessor->Start();
g_pFeedCoordinator->Start();
// enter main program-loop
while (g_pQueueCoordinator->IsRunning() ||
g_pUrlCoordinator->IsRunning() ||
g_pPrePostProcessor->IsRunning() ||
g_pFeedCoordinator->IsRunning())
g_pPrePostProcessor->IsRunning())
{
if (!g_pOptions->GetServerMode() &&
!g_pQueueCoordinator->HasMoreJobs() &&
@@ -390,10 +389,6 @@ void Run(bool bReload)
{
g_pPrePostProcessor->Stop();
}
if (!g_pFeedCoordinator->IsStopped())
{
g_pFeedCoordinator->Stop();
}
}
usleep(100 * 1000);
}
@@ -402,7 +397,6 @@ void Run(bool bReload)
debug("QueueCoordinator stopped");
debug("UrlCoordinator stopped");
debug("PrePostProcessor stopped");
debug("FeedCoordinator stopped");
}
// Stop network-server
@@ -601,7 +595,6 @@ void ExitProc()
g_pQueueCoordinator->Stop();
g_pUrlCoordinator->Stop();
g_pPrePostProcessor->Stop();
g_pFeedCoordinator->Stop();
}
}
}
@@ -726,40 +719,64 @@ void Cleanup()
debug("Cleaning up global objects");
debug("Deleting UrlCoordinator");
delete g_pUrlCoordinator;
g_pUrlCoordinator = NULL;
if (g_pUrlCoordinator)
{
delete g_pUrlCoordinator;
g_pUrlCoordinator = NULL;
}
debug("UrlCoordinator deleted");
debug("Deleting RemoteServer");
delete g_pRemoteServer;
g_pRemoteServer = NULL;
if (g_pRemoteServer)
{
delete g_pRemoteServer;
g_pRemoteServer = NULL;
}
debug("RemoteServer deleted");
debug("Deleting RemoteSecureServer");
delete g_pRemoteSecureServer;
g_pRemoteSecureServer = NULL;
if (g_pRemoteSecureServer)
{
delete g_pRemoteSecureServer;
g_pRemoteSecureServer = NULL;
}
debug("RemoteSecureServer deleted");
debug("Deleting PrePostProcessor");
delete g_pPrePostProcessor;
g_pPrePostProcessor = NULL;
delete g_pScanner;
g_pScanner = NULL;
if (g_pPrePostProcessor)
{
delete g_pPrePostProcessor;
g_pPrePostProcessor = NULL;
}
if (g_pScanner)
{
delete g_pScanner;
g_pScanner = NULL;
}
debug("PrePostProcessor deleted");
debug("Deleting Frontend");
delete g_pFrontend;
g_pFrontend = NULL;
if (g_pFrontend)
{
delete g_pFrontend;
g_pFrontend = NULL;
}
debug("Frontend deleted");
debug("Deleting QueueCoordinator");
delete g_pQueueCoordinator;
g_pQueueCoordinator = NULL;
if (g_pQueueCoordinator)
{
delete g_pQueueCoordinator;
g_pQueueCoordinator = NULL;
}
debug("QueueCoordinator deleted");
debug("Deleting DiskState");
delete g_pDiskState;
g_pDiskState = NULL;
if (g_pDiskState)
{
delete g_pDiskState;
g_pDiskState = NULL;
}
debug("DiskState deleted");
debug("Deleting Options");
@@ -776,25 +793,21 @@ void Cleanup()
debug("Options deleted");
debug("Deleting ServerPool");
delete g_pServerPool;
g_pServerPool = NULL;
if (g_pServerPool)
{
delete g_pServerPool;
g_pServerPool = NULL;
}
debug("ServerPool deleted");
debug("Deleting Scheduler");
delete g_pScheduler;
g_pScheduler = NULL;
if (g_pScheduler)
{
delete g_pScheduler;
g_pScheduler = NULL;
}
debug("Scheduler deleted");
debug("Deleting FeedCoordinator");
delete g_pFeedCoordinator;
g_pFeedCoordinator = NULL;
debug("FeedCoordinator deleted");
debug("Deleting Maintenance");
delete g_pMaintenance;
g_pMaintenance = NULL;
debug("Maintenance deleted");
if (!g_bReloading)
{
Connection::Final();
@@ -803,8 +816,11 @@ void Cleanup()
debug("Global objects cleaned up");
delete g_pLog;
g_pLog = NULL;
if (g_pLog)
{
delete g_pLog;
g_pLog = NULL;
}
}
#ifndef WIN32

View File

@@ -58,7 +58,6 @@
#define ALT_PATH_SEPARATOR '/'
#define LINE_ENDING "\r\n"
#define pid_t int
#define atoll _atoi64
#ifndef FSCTL_SET_SPARSE
#define FSCTL_SET_SPARSE 590020
#endif

View File

@@ -299,46 +299,6 @@
RelativePath=".\DownloadInfo.h"
>
</File>
<File
RelativePath=".\DupeCoordinator.cpp"
>
</File>
<File
RelativePath=".\DupeCoordinator.h"
>
</File>
<File
RelativePath=".\FeedCoordinator.cpp"
>
</File>
<File
RelativePath=".\FeedCoordinator.h"
>
</File>
<File
RelativePath=".\FeedFile.cpp"
>
</File>
<File
RelativePath=".\FeedFile.h"
>
</File>
<File
RelativePath=".\FeedFilter.cpp"
>
</File>
<File
RelativePath=".\FeedFilter.h"
>
</File>
<File
RelativePath=".\FeedInfo.cpp"
>
</File>
<File
RelativePath=".\FeedInfo.h"
>
</File>
<File
RelativePath=".\Frontend.cpp"
>
@@ -363,14 +323,6 @@
RelativePath=".\LoggableFrontend.h"
>
</File>
<File
RelativePath=".\Maintenance.h"
>
</File>
<File
RelativePath=".\Maintenance.cpp"
>
</File>
<File
RelativePath=".\MessageBase.h"
>

View File

@@ -1,41 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#ifdef __OBJC__
#import <Cocoa/Cocoa.h>
#endif
#ifdef DEBUG
# define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
#else
# define DLog(...) do {} while (0)
#endif
#define SuppressPerformSelectorLeakWarning(Stuff) \
do { \
_Pragma("clang diagnostic push") \
_Pragma("clang diagnostic ignored \"-Warc-performSelector-leaks\"") \
Stuff; \
_Pragma("clang diagnostic pop") \
} while (0)

View File

@@ -1,73 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
@protocol DaemonControllerDelegate
- (void)daemonConfigLoaded;
- (void)daemonStatusUpdated;
@end
@interface DaemonController : NSObject {
NSString* lockFilePath;
NSDictionary* config;
int restartCounter;
int restartPid;
NSTimer* updateTimer;
int lastUptime;
BOOL factoryReset;
}
@property (nonatomic, assign) NSTimeInterval updateInterval;
@property (nonatomic, readonly) BOOL connected;
@property (nonatomic, readonly) BOOL restarting;
@property (nonatomic, readonly) BOOL recoveryMode;
@property (nonatomic, readonly) NSString* configFilePath;
@property (nonatomic, readonly) NSString* browserUrl;
@property (nonatomic, readonly) NSDate* lastUpdate;
@property (nonatomic, assign) id<DaemonControllerDelegate> delegate;
@property (nonatomic, readonly) NSDictionary* status;
- (id)init;
- (NSString*)valueForOption:(NSString*)option;
- (void)start;
- (void)stop;
- (void)restartInRecoveryMode:(BOOL)recovery withFactoryReset:(BOOL)reset;
- (NSString *)browserUrl;
- (void)updateStatus;
- (void)rpc:(NSString*)method success:(SEL)successCallback failure:(SEL)failureCallback;
- (void)setUpdateInterval:(NSTimeInterval)updateInterval;
@end

View File

@@ -1,339 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#include <libproc.h>
#import <Cocoa/Cocoa.h>
#import "DaemonController.h"
#import "RPC.h"
NSString* MAIN_DIR = @"${AppSupDir}";
NSString* LOCK_FILE = @"${AppSupDir}/nzbget.lock";
NSString* CONFIG_FILE = @"${AppSupDir}/nzbget.conf";
NSString* PPSCRIPTS_DIR = @"${AppSupDir}/ppscripts";
NSString* NZB_DIR = @"${AppSupDir}/nzb";
NSString* QUEUE_DIR = @"${AppSupDir}/queue";
NSString* TMP_DIR = @"${AppSupDir}/tmp";
@implementation DaemonController
- (id)init {
self = [super init];
_configFilePath = [self resolveAppSupDir:CONFIG_FILE];
lockFilePath = [self resolveAppSupDir:LOCK_FILE];
return self;
}
- (NSString *) bundlePath {
return [[NSBundle mainBundle] pathForResource:@"daemon" ofType:nil];
}
- (NSString *) resolveAppSupDir:(NSString *)dir {
NSString *appSupPath = [@"${AppSupDir}/Application Support/NZBGet" stringByExpandingTildeInPath];
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if (paths.count > 0)
{
appSupPath = [paths objectAtIndex:0];
appSupPath = [appSupPath stringByAppendingPathComponent:@"NZBGet"];
}
dir = [dir stringByReplacingOccurrencesOfString:@"${AppSupDir}"
withString:appSupPath
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, dir.length)];
return dir;
}
- (void) checkDefaults {
NSString* mainDir = [self resolveAppSupDir:MAIN_DIR];
if (![[NSFileManager defaultManager] fileExistsAtPath:mainDir]) {
[[NSFileManager defaultManager] createDirectoryAtPath:mainDir withIntermediateDirectories:YES attributes:nil error:nil];
}
NSString* bundlePath = [self bundlePath];
if (![[NSFileManager defaultManager] fileExistsAtPath:_configFilePath]) {
NSString* configTemplate = [NSString stringWithFormat:@"%@/usr/local/share/nzbget/nzbget.conf", bundlePath];
[[NSFileManager defaultManager] copyItemAtPath:configTemplate toPath:_configFilePath error:nil];
}
NSString* ppscriptsDir = [self resolveAppSupDir:PPSCRIPTS_DIR];
if (![[NSFileManager defaultManager] fileExistsAtPath:ppscriptsDir]) {
NSString* ppscriptsTemplate = [NSString stringWithFormat:@"%@/usr/local/share/nzbget/ppscripts", bundlePath];
[[NSFileManager defaultManager] copyItemAtPath:ppscriptsTemplate toPath:ppscriptsDir error:nil];
}
}
- (int)readLockFilePid {
if ([[NSFileManager defaultManager] fileExistsAtPath:lockFilePath]) {
// Lock file exists
// read pid from lock file
int pid = [[NSString stringWithContentsOfFile:lockFilePath encoding:NSUTF8StringEncoding error:nil] intValue];
DLog(@"pid: %i", pid);
// check if the process name is "nzbget" to avoid killing of other proceses
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
int ret = proc_pidpath(pid, pathbuf, sizeof(pathbuf));
if (ret <= 0) {
// error
return 0;
}
DLog(@"proc %d: %s\n", pid, pathbuf);
NSString* instancePath = [NSString stringWithUTF8String:pathbuf];
if ([instancePath hasSuffix:@".app/Contents/Resources/daemon/usr/local/bin/nzbget"]) {
return pid;
}
}
return 0;
}
- (void)killDaemonWithSignal:(int)signal {
int pid = [self readLockFilePid];
if (pid > 0) {
kill(pid, signal);
[[NSFileManager defaultManager] removeItemAtPath:lockFilePath error:nil];
}
}
- (void)start {
DLog(@"DaemonController->start");
[self checkDefaults];
[self killDaemonWithSignal:SIGKILL];
[self readConfigFile];
[self initRpcUrl];
[self initBrowserUrl];
NSString* bundlePath = [self bundlePath];
NSString* daemonPath = [NSString stringWithFormat:@"%@/usr/local/bin/nzbget", bundlePath];
NSString* optionWebDir = [NSString stringWithFormat:@"WebDir=%@/usr/local/share/nzbget/webui", bundlePath];
NSString* optionConfigTemplate = [NSString stringWithFormat:@"ConfigTemplate=%@/usr/local/share/nzbget/nzbget.conf", bundlePath];
NSString* optionLockFile = [NSString stringWithFormat:@"LockFile=%@", lockFilePath];
NSMutableArray* arguments = [NSMutableArray arrayWithObjects:
@"-c", _configFilePath,
@"-D",
@"-o", optionWebDir,
@"-o", optionConfigTemplate,
@"-o", optionLockFile,
nil];
if (_recoveryMode) {
[arguments addObjectsFromArray: [NSArray arrayWithObjects:
@"-o", @"ControlIP=127.0.0.1",
@"-o", @"ControlPort=6789",
@"-o", @"ControlPassword=",
@"-o", @"SecureControl=no",
nil
]];
}
NSTask* task = [[NSTask alloc] init];
[task setLaunchPath: daemonPath];
[task setArguments: arguments];
[task launch];
_restarting = NO;
[self scheduleNextUpdate];
}
- (void)stop {
DLog(@"DaemonController->stop");
[self killDaemonWithSignal:SIGTERM];
}
- (void)restartInRecoveryMode:(BOOL)recovery withFactoryReset:(BOOL)reset {
_recoveryMode = recovery;
factoryReset = reset;
_restarting = YES;
restartPid = [self readLockFilePid];
[self stop];
// in timer wait for deletion of lockfile for 10 seconds,
// after that call "start" which will kill the old process.
restartCounter = 0;
[self restartWait];
}
- (void)restartWait {
DLog(@"DaemonController->restartWait");
restartCounter++;
char pathbuf[PROC_PIDPATHINFO_MAXSIZE];
int ret = proc_pidpath(restartPid, pathbuf, sizeof(pathbuf));
if (ret > 0 && restartCounter < 100) {
DLog(@"restartWait: scheduleNextRestartWait");
[self scheduleNextRestartWait];
} else {
DLog(@"restartWait: start");
if (factoryReset) {
[self resetToFactoryDefaults];
}
[self start];
}
}
- (void)resetToFactoryDefaults {
DLog(@"DaemonController->resetToFactoryDefaults");
[[NSFileManager defaultManager] removeItemAtPath:_configFilePath error:nil];
[[NSFileManager defaultManager] removeItemAtPath:[self resolveAppSupDir:QUEUE_DIR] error:nil];
[[NSFileManager defaultManager] removeItemAtPath:[self resolveAppSupDir:PPSCRIPTS_DIR] error:nil];
[[NSFileManager defaultManager] removeItemAtPath:[self resolveAppSupDir:NZB_DIR] error:nil];
[[NSFileManager defaultManager] removeItemAtPath:[self resolveAppSupDir:TMP_DIR] error:nil];
}
- (void)scheduleNextRestartWait {
NSTimer* timer = [NSTimer timerWithTimeInterval:0.100 target:self selector:@selector(restartWait) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
- (void)readConfigFile {
DLog(@"DaemonController->readConfigFile");
NSString* str = [NSString stringWithContentsOfFile:_configFilePath encoding:NSUTF8StringEncoding error:nil];
NSArray* conf = [str componentsSeparatedByString: @"\n"];
config = [[NSMutableDictionary alloc] init];
for (NSString* opt in conf) {
if ([opt hasPrefix:@"#"]) {
continue;
}
NSRange pos = [opt rangeOfString:@"="];
if (pos.location != NSNotFound) {
NSString* name = [opt substringToIndex:pos.location];
name = [name stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
NSString* value = [opt substringFromIndex:pos.location + 1];
value = [value stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
[config setValue:value forKey:[name uppercaseString]];
}
}
if (_recoveryMode) {
[config setValue:@"localhost" forKey:@"CONTROLIP"];
[config setValue:@"6789" forKey:@"CONTROLPORT"];
[config setValue:@"" forKey:@"CONTROLPASSWORD"];
}
[_delegate daemonConfigLoaded];
}
- (NSString*)valueForOption:(NSString*)option {
return [config valueForKey:[option uppercaseString]];
}
- (void)initBrowserUrl {
NSString* ip = [self valueForOption:@"ControlIP"];
if ([ip isEqualToString:@"0.0.0.0"] || [ip isEqualToString:@"127.0.0.1"]) {
ip = @"localhost";
}
NSString* port = [self valueForOption:@"ControlPort"];
_browserUrl = [NSString stringWithFormat:@"http://@%@:%@", ip, port];
}
- (void)initRpcUrl {
NSString* ip = [self valueForOption:@"ControlIP"];
if ([ip isEqualToString:@"0.0.0.0"]) {
ip = @"127.0.0.1";
}
NSString* port = [self valueForOption:@"ControlPort"];
NSString* username = [self valueForOption:@"ControlUsername"];
NSString* password = [self valueForOption:@"ControlPassword"];
NSString* RpcUrl = [NSString stringWithFormat:@"http://%@:%@/%@:%@/jsonrpc/", ip, port, username, password];
[RPC setRpcUrl:RpcUrl];
}
- (void)setUpdateInterval:(NSTimeInterval)updateInterval {
_updateInterval = updateInterval;
if (_connected) {
[updateTimer invalidate];
[self updateStatus];
}
}
- (void)scheduleNextUpdate {
updateTimer = [NSTimer timerWithTimeInterval:_updateInterval target:self selector:@selector(updateStatus) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:updateTimer forMode:NSRunLoopCommonModes];
}
- (void)rpc:(NSString*)method success:(SEL)successCallback failure:(SEL)failureCallback {
RPC* rpc = [[RPC alloc] initWithMethod:method receiver:self success:successCallback failure:failureCallback];
[rpc start];
}
- (void)receiveStatus:(NSDictionary*)status {
//DLog(@"receiveStatus");
if (_restarting) {
return;
}
_connected = YES;
_lastUpdate = [NSDate date];
_status = status;
//DLog(@"response: %@", status);
int uptime = [(NSNumber*)[status objectForKey:@"UpTimeSec"] integerValue];
if (lastUptime == 0) {
lastUptime = uptime;
} else if (lastUptime > uptime) {
// daemon was reloaded (soft-restart)
[self readConfigFile];
}
[_delegate daemonStatusUpdated];
[self scheduleNextUpdate];
}
- (void)failureStatus {
DLog(@"failureStatus");
if (_restarting) {
return;
}
_connected = NO;
int pid = [self readLockFilePid];
if (pid == 0) {
// Daemon is not running. Crashed?
_restarting = YES;
[_delegate daemonStatusUpdated];
[self start];
} else {
[_delegate daemonStatusUpdated];
[self scheduleNextUpdate];
}
}
- (void)updateStatus {
if (_restarting) {
return;
}
[self rpc:@"status" success:@selector(receiveStatus:) failure:@selector(failureStatus)];
}
@end

View File

@@ -1,81 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
#import "DaemonController.h"
@interface MainApp : NSObject <NSMenuDelegate, DaemonControllerDelegate> {
IBOutlet NSMenu *statusMenu;
NSStatusItem *statusItem;
IBOutlet NSMenuItem *webuiItem;
IBOutlet NSMenuItem *homePageItem;
IBOutlet NSMenuItem *downloadsItem;
IBOutlet NSMenuItem *forumItem;
IBOutlet NSMenuItem *info1Item;
IBOutlet NSMenuItem *info2Item;
IBOutlet NSMenuItem *restartRecoveryItem;
IBOutlet NSMenuItem *factoryResetItem;
IBOutlet NSMenuItem *destDirItem;
IBOutlet NSMenuItem *interDirItem;
IBOutlet NSMenuItem *nzbDirItem;
IBOutlet NSMenuItem *scriptDirItem;
IBOutlet NSMenuItem *configFileItem;
IBOutlet NSMenuItem *logFileItem;
IBOutlet NSMenuItem *destDirSeparator;
NSWindowController *welcomeDialog;
NSWindowController *preferencesDialog;
DaemonController *daemonController;
int connectionAttempts;
BOOL restarting;
BOOL resetting;
NSTimer* restartTimer;
NSMutableArray* categoryItems;
NSMutableArray* categoryDirs;
}
+ (void)setupAppDefaults;
- (void)setupDefaultsObserver;
- (IBAction)quitClicked:(id)sender;
- (IBAction)preferencesClicked:(id)sender;
- (void)userDefaultsDidChange:(id)sender;
- (IBAction)aboutClicked:(id)sender;
+ (BOOL)wasLaunchedAsLoginItem;
- (IBAction)webuiClicked:(id)sender;
- (IBAction)infoLinkClicked:(id)sender;
- (IBAction)openConfigInTextEditClicked:(id)sender;
- (IBAction)restartClicked:(id)sender;
- (IBAction)showInFinderClicked:(id)sender;
@end

View File

@@ -1,579 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import "MainApp.h"
#import "PreferencesDialog.h"
#import "WelcomeDialog.h"
#import "PFMoveApplication.h"
NSString *PreferencesContext = @"PreferencesContext";
const NSTimeInterval NORMAL_UPDATE_INTERVAL = 10.000;
const NSTimeInterval MENUOPEN_UPDATE_INTERVAL = 1.000;
const NSTimeInterval START_UPDATE_INTERVAL = 0.500;
int main(int argc, char *argv[]) {
return NSApplicationMain(argc, (const char **)argv);
}
/*
* Signal handler
*/
void SignalProc(int iSignal)
{
switch (iSignal)
{
case SIGINT:
case SIGTERM:
signal(iSignal, SIG_DFL); // Reset the signal handler
[NSApp terminate:nil];
break;
}
}
// we install seignal handler in order to properly terminat app from Activity Mo1nitor
void InstallSignalHandlers()
{
signal(SIGINT, SignalProc);
signal(SIGTERM, SignalProc);
signal(SIGPIPE, SIG_IGN);
}
@implementation MainApp
- (void)applicationWillFinishLaunching:(NSNotification *)aNotification {
[self checkOtherRunningInstances];
#ifndef DEBUG
PFMoveToApplicationsFolderIfNecessary();
#endif
}
- (void)checkOtherRunningInstances {
for (NSRunningApplication *runningApplication in [NSRunningApplication runningApplicationsWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier]]) {
if (![[runningApplication executableURL] isEqualTo:[[NSRunningApplication currentApplication] executableURL]]) {
NSString *executablePath = [[runningApplication executableURL] path];
executablePath = [[[executablePath stringByDeletingLastPathComponent] stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
DLog(@"Switching to an already running instance: %@", executablePath);
[[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open"
arguments:[NSArray arrayWithObjects:executablePath, @"--args", @"--second-instance", nil]] waitUntilExit];
exit(0);
}
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
BOOL autoStartWebUI = [[NSUserDefaults standardUserDefaults] boolForKey:@"AutoStartWebUI"];
daemonController = [[DaemonController alloc] init];
daemonController.updateInterval = autoStartWebUI ? START_UPDATE_INTERVAL : NORMAL_UPDATE_INTERVAL;
daemonController.delegate = self;
[self setupDefaultsObserver];
[self userDefaultsDidChange:nil];
if (![MainApp wasLaunchedAsLoginItem]) {
[self showWelcomeScreen];
}
InstallSignalHandlers();
DLog(@"Start Daemon");
[daemonController start];
}
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag {
DLog(@"applicationShouldHandleReopen");
[self showAlreadyRunning];
return YES;
}
+ (void)initialize {
[self setupAppDefaults];
}
- (void)awakeFromNib {
DLog(@"awakeFromNib");
[statusMenu setDelegate:self];
}
+ (void)setupAppDefaults {
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDictionary *appDefaults = [NSDictionary dictionaryWithObjectsAndKeys:
@"YES", @"ShowInMenubar",
@"NO", @"Autostart",
@"YES", @"AutoStartWebUI",
nil];
[userDefaults registerDefaults:appDefaults];
}
- (void)setupDefaultsObserver {
NSUserDefaultsController *sdc = [NSUserDefaultsController sharedUserDefaultsController];
[sdc addObserver:self forKeyPath:@"values.ShowInMenubar" options:0 context:(__bridge void *)(PreferencesContext)];
[sdc addObserver:self forKeyPath:@"values.AutoStartWebUI" options:0 context:(__bridge void *)(PreferencesContext)];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == (__bridge void *)(PreferencesContext)) {
[self userDefaultsDidChange:nil];
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
- (void)userDefaultsDidChange:(id)sender {
DLog(@"userDefaultsDidChange: %@", sender);
BOOL showInMenubar = [[NSUserDefaults standardUserDefaults] boolForKey:@"ShowInMenubar"];
if (showInMenubar != (statusItem != nil)) {
if (showInMenubar) {
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength];
[statusItem setHighlightMode:YES];
[statusItem setMenu:statusMenu];
[statusItem setImage:[NSImage imageNamed:@"statusicon.png"]];
[statusItem setAlternateImage:[NSImage imageNamed:@"statusicon-inv.png"]];
}
else {
statusItem = nil;
}
}
}
- (IBAction)preferencesClicked:(id)sender {
[self showPreferences];
}
- (void)showPreferences {
[NSApp activateIgnoringOtherApps:TRUE];
if (preferencesDialog) {
return;
}
preferencesDialog = [[PreferencesDialog alloc] init];
[[preferencesDialog window] center];
[preferencesDialog showWindow:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(preferencesWillClose:)
name:NSWindowWillCloseNotification
object:[preferencesDialog window]];
}
- (void)preferencesWillClose:(NSNotification *)notification {
DLog(@"Pref Closed");
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSWindowWillCloseNotification
object:[preferencesDialog window]];
preferencesDialog = nil;
[self userDefaultsDidChange:nil];
}
- (IBAction)aboutClicked:(id)sender {
[NSApp activateIgnoringOtherApps:TRUE];
[NSApp orderFrontStandardAboutPanel:nil];
}
- (IBAction)quitClicked:(id)sender {
DLog(@"Quit");
[NSApp terminate:nil];
}
- (void)showWelcomeScreen {
welcomeDialog = [[WelcomeDialog alloc] init];
[(WelcomeDialog*)welcomeDialog setMainDelegate:self];
[(WelcomeDialog*)welcomeDialog showDialog];
}
- (void)showAlreadyRunning {
BOOL showInMenubar = [[NSUserDefaults standardUserDefaults] boolForKey:@"ShowInMenubar"];
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"AlreadyRunning.MessageText", nil)];
[alert setInformativeText:NSLocalizedString(showInMenubar ? @"AlreadyRunning.InformativeTextWithIcon" : @"AlreadyRunning.InformativeTextWithoutIcon", nil)];
NSButton* webUIButton = [alert addButtonWithTitle:NSLocalizedString(@"AlreadyRunning.WebUI", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"AlreadyRunning.Preferences", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"AlreadyRunning.Quit", nil)];
[alert.window makeFirstResponder:webUIButton];
[webUIButton setKeyEquivalent:@"\r"];
switch ([alert runModal]) {
case NSAlertFirstButtonReturn:
[self showWebUI];
break;
case NSAlertSecondButtonReturn:
[self showPreferences];
break;
case NSAlertThirdButtonReturn:
[self quitClicked:nil];
break;
}
}
+ (BOOL)wasLaunchedByProcess:(NSString*)creator {
BOOL wasLaunchedByProcess = NO;
// Get our PSN
OSStatus err;
ProcessSerialNumber currPSN;
err = GetCurrentProcess (&currPSN);
if (!err) {
// We don't use ProcessInformationCopyDictionary() because the 'ParentPSN' item in the dictionary
// has endianness problems in 10.4, fixed in 10.5 however.
ProcessInfoRec procInfo;
bzero (&procInfo, sizeof (procInfo));
procInfo.processInfoLength = (UInt32)sizeof (ProcessInfoRec);
err = GetProcessInformation (&currPSN, &procInfo);
if (!err) {
ProcessSerialNumber parentPSN = procInfo.processLauncher;
// Get info on the launching process
NSDictionary* parentDict = (__bridge NSDictionary*)ProcessInformationCopyDictionary (&parentPSN, kProcessDictionaryIncludeAllInformationMask);
// Test the creator code of the launching app
if (parentDict) {
wasLaunchedByProcess = [[parentDict objectForKey:@"FileCreator"] isEqualToString:creator];
}
}
}
return wasLaunchedByProcess;
}
+ (BOOL)wasLaunchedAsLoginItem {
// If the launching process was 'loginwindow', we were launched as a login item
return [self wasLaunchedByProcess:@"lgnw"];
}
- (IBAction)webuiClicked:(id)sender {
if (daemonController.connected) {
[self showWebUI];
} else {
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"ShowWebUINoConnection.MessageText", nil)];
[alert setInformativeText:NSLocalizedString(@"ShowWebUINoConnection.InformativeText", nil)];
[alert setAlertStyle:NSWarningAlertStyle];
[alert runModal];
}
}
- (void)showWebUI {
DLog(@"showWebUI");
[[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString:daemonController.browserUrl]];
}
- (IBAction)openConfigInTextEditClicked:(id)sender {
NSString *configFile = [daemonController configFilePath];
[[NSWorkspace sharedWorkspace] openFile:configFile withApplication:@"TextEdit"];
}
- (IBAction)restartClicked:(id)sender {
if (sender == factoryResetItem) {
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"FactoryReset.MessageText", nil)];
[alert setInformativeText:NSLocalizedString(@"FactoryReset.InformativeText", nil)];
[alert setAlertStyle:NSCriticalAlertStyle];
NSButton* cancelButton = [alert addButtonWithTitle:NSLocalizedString(@"FactoryReset.Cancel", nil)];
// we use middle invisible button to align the third RESET-button at left side
[[alert addButtonWithTitle:@"Cancel"] setHidden:YES];
[alert addButtonWithTitle:NSLocalizedString(@"FactoryReset.Reset", nil)];
[alert.window makeFirstResponder:cancelButton];
[cancelButton setKeyEquivalent:@"\E"];
if ([alert runModal] != NSAlertThirdButtonReturn) {
return;
}
}
restarting = YES;
resetting = sender == factoryResetItem;
[self updateStatus];
[daemonController restartInRecoveryMode: sender == restartRecoveryItem withFactoryReset: sender == factoryResetItem];
daemonController.updateInterval = START_UPDATE_INTERVAL;
restartTimer = [NSTimer timerWithTimeInterval:10.000 target:self selector:@selector(restartFailed) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:restartTimer forMode:NSRunLoopCommonModes];
}
- (void)welcomeContinue {
DLog(@"welcomeContinue");
BOOL autoStartWebUI = [[NSUserDefaults standardUserDefaults] boolForKey:@"AutoStartWebUI"];
if (autoStartWebUI) {
if (daemonController.connected) {
if (daemonController.updateInterval == START_UPDATE_INTERVAL)
{
daemonController.updateInterval = NORMAL_UPDATE_INTERVAL;
}
[self showWebUI];
} else {
// try again in 100 msec for max. 25 seconds, then give up
connectionAttempts++;
if (connectionAttempts < 250) {
[self performSelector:@selector(welcomeContinue) withObject:nil afterDelay: 0.100];
} else {
// show error message
[self webuiClicked:nil];
}
}
}
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
DLog(@"Stop Daemon");
[daemonController stop];
}
- (void)menuWillOpen:(NSMenu *)menu {
DLog(@"menuWillOpen");
daemonController.updateInterval = MENUOPEN_UPDATE_INTERVAL;
}
- (void)menuDidClose:(NSMenu *)menu {
DLog(@"menuDidClose");
daemonController.updateInterval = NORMAL_UPDATE_INTERVAL;
}
- (IBAction)infoLinkClicked:(id)sender {
NSString *url;
if (sender == homePageItem) {
url = NSLocalizedString(@"Menu.LinkHomePage", nil);
}
else if (sender == downloadsItem) {
url = NSLocalizedString(@"Menu.LinkDownloads", nil);
}
else if (sender == forumItem) {
url = NSLocalizedString(@"Menu.LinkForum", nil);
}
[[NSWorkspace sharedWorkspace] openURL: [NSURL URLWithString:url]];
}
- (void)restartFailed {
if (restarting) {
restarting = NO;
resetting = NO;
daemonController.updateInterval = NORMAL_UPDATE_INTERVAL;
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(@"RestartNoConnection.MessageText", nil)];
[alert setInformativeText:NSLocalizedString(@"RestartNoConnection.InformativeText", nil)];
[alert setAlertStyle:NSWarningAlertStyle];
[alert runModal];
}
}
- (IBAction)showInFinderClicked:(id)sender {
NSString* dir = nil;
NSString* option = nil;
if (sender == destDirItem) {
option = @"DestDir";
} else if (sender == interDirItem) {
option = @"InterDir";
} else if (sender == nzbDirItem) {
option = @"NzbDir";
} else if (sender == scriptDirItem) {
option = @"ScriptDir";
} else if (sender == logFileItem) {
option = @"LogFile";
} else if (sender == configFileItem) {
dir = [daemonController configFilePath];
} else if ([categoryItems containsObject:sender]) {
int index = [categoryItems indexOfObject:sender];
dir = [categoryDirs objectAtIndex:index];
} else {
return;
}
if (dir == nil) {
NSString* mainDir = [[daemonController valueForOption:@"MainDir"] stringByExpandingTildeInPath];
dir = [[daemonController valueForOption:option] stringByExpandingTildeInPath];
dir = [dir stringByReplacingOccurrencesOfString:@"${MainDir}"
withString:mainDir
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [dir length])];
}
if (dir.length == 0 || ![[NSFileManager defaultManager] fileExistsAtPath:dir]) {
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:[NSString stringWithFormat:NSLocalizedString(@"CantShowInFinder.MessageText", nil), dir]];
[alert setInformativeText:[NSString stringWithFormat:NSLocalizedString(option == nil ? @"CantShowInFinder.InformativeTextForCategory" : @"CantShowInFinder.InformativeTextWithOption", nil), option]];
[alert runModal];
return;
}
[[NSWorkspace sharedWorkspace] activateFileViewerSelectingURLs:@[[NSURL fileURLWithPath:dir isDirectory:YES]]];
}
- (void)daemonStatusUpdated {
if (restarting && daemonController.connected) {
restarting = NO;
[restartTimer invalidate];
[NSApp activateIgnoringOtherApps:TRUE];
NSAlert *alert = [[NSAlert alloc] init];
[alert setMessageText:NSLocalizedString(resetting ? @"FactoryResetted.MessageText" : daemonController.recoveryMode ? @"RestartedRecoveryMode.MessageText" : @"Restarted.MessageText", nil)];
[alert setInformativeText:NSLocalizedString(resetting ? @"FactoryResetted.InformativeText" : daemonController.recoveryMode ? @"RestartedRecoveryMode.InformativeText" : @"Restarted.InformativeText", nil)];
[alert setAlertStyle:NSInformationalAlertStyle];
[alert addButtonWithTitle:NSLocalizedString(@"Restarted.OK", nil)];
[alert addButtonWithTitle:NSLocalizedString(@"Restarted.WebUI", nil)];
if ([alert runModal] == NSAlertSecondButtonReturn) {
[self showWebUI];
}
resetting = NO;
} else {
[self updateStatus];
}
}
- (void)updateStatus {
//DLog(@"updateStatus");
NSString* info1 = @"";
NSString* info2 = nil;
NSDictionary* status = [daemonController status];
if (restarting || daemonController.restarting) {
info1 = NSLocalizedString(@"Status.Restarting", nil);
} else if (!daemonController.connected) {
info1 = NSLocalizedString(@"Status.NoConnection", nil);
} else if ([(NSNumber*)[status objectForKey:@"ServerStandBy"] integerValue] == 1) {
if ([(NSNumber*)[status objectForKey:@"PostJobCount"] integerValue] > 0) {
info1 = NSLocalizedString(@"Status.Post-Processing", nil);
}
else if ([(NSNumber*)[status objectForKey:@"UrlCount"] integerValue] > 0) {
info1 = NSLocalizedString(@"Status.Fetching NZBs", nil);
}
else if ([(NSNumber*)[status objectForKey:@"FeedActive"] integerValue] == 1) {
info1 = NSLocalizedString(@"Status.Fetching Feeds", nil);
}
else if ([(NSNumber*)[status objectForKey:@"DownloadPaused"] integerValue] == 1 ||
[(NSNumber*)[status objectForKey:@"Download2Paused"] integerValue] == 1) {
info1 = NSLocalizedString(@"Status.Paused", nil);
} else {
info1 = NSLocalizedString(@"Status.Idle", nil);
}
} else {
int speed = [(NSNumber*)[status objectForKey:@"DownloadRate"] integerValue];
info1 = [NSString stringWithFormat:NSLocalizedString(@"Status.Downloading", nil), speed / 1024];
if (speed > 0) {
long long remaining = ([(NSNumber*)[status objectForKey:@"RemainingSizeHi"] integerValue] << 32) + [(NSNumber*)[status objectForKey:@"RemainingSizeLo"] integerValue];
int secondsLeft = remaining / speed;
info2 = [NSString stringWithFormat:NSLocalizedString(@"Status.Left", nil), [self formatTimeLeft:secondsLeft]];
}
}
[info1Item setTitle:info1];
[info2Item setHidden:info2 == nil];
if (info2 != nil) {
[info2Item setTitle:info2];
}
}
- (NSString*)formatTimeLeft:(int)sec {
int days = floor(sec / 86400);
int hours = floor((sec % 86400) / 3600);
int minutes = floor((sec / 60) % 60);
int seconds = floor(sec % 60);
if (days > 10)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Days", nil), days];
}
if (days > 0)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Days.Hours", nil), days, hours];
}
if (hours > 10)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Hours", nil), hours];
}
if (hours > 0)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Hours.Minutes", nil), hours, minutes];
}
if (minutes > 10)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Minutes", nil), minutes];
}
if (minutes > 0)
{
return [NSString stringWithFormat:NSLocalizedString(@"Left.Minutes.Seconds", nil), minutes, seconds];
}
return [NSString stringWithFormat:NSLocalizedString(@"Left.Seconds", nil), seconds];
}
- (void)daemonConfigLoaded {
DLog(@"config loaded");
[self updateCategoriesMenu];
}
- (void)updateCategoriesMenu {
NSMenu *submenu = destDirItem.parentItem.submenu;
for (NSMenuItem* item in categoryItems) {
[submenu removeItem:item];
}
categoryItems = [NSMutableArray array];
categoryDirs = [NSMutableArray array];
NSString* mainDir = [[daemonController valueForOption:@"MainDir"] stringByExpandingTildeInPath];
NSString* destDir = [[daemonController valueForOption:@"DestDir"] stringByExpandingTildeInPath];
for (int i=1; ; i++) {
NSString* catName = [daemonController valueForOption:[NSString stringWithFormat:@"Category%i.Name", i]];
NSString* catDir = [daemonController valueForOption:[NSString stringWithFormat:@"Category%i.DestDir", i]];
if (catName.length == 0) {
break;
}
if (catDir.length == 0) {
catDir = [destDir stringByAppendingPathComponent:catName];
}
NSString* dir = [catDir stringByExpandingTildeInPath];
dir = [dir stringByReplacingOccurrencesOfString:@"${MainDir}"
withString:mainDir
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, dir.length)];
dir = [dir stringByReplacingOccurrencesOfString:@"${DestDir}"
withString:destDir
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, dir.length)];
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:[NSString stringWithFormat:@"Category%i: %@", i, catName]
action:@selector(showInFinderClicked:) keyEquivalent:@""];
[item setTarget:self];
[submenu insertItem:item atIndex:[submenu indexOfItem:destDirSeparator]];
[categoryItems addObject:item];
[categoryDirs addObject:dir];
}
}
@end

View File

@@ -1,989 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1070</int>
<string key="IBDocument.SystemVersion">11G63</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1138.51</string>
<string key="IBDocument.HIToolboxVersion">569.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">3084</string>
</object>
<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSCustomObject</string>
<string>NSMenu</string>
<string>NSMenuItem</string>
<string>NSUserDefaultsController</string>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="1021">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSCustomObject" id="1014">
<string key="NSClassName">FirstResponder</string>
</object>
<object class="NSCustomObject" id="1050">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSCustomObject" id="163992474">
<string key="NSClassName">NSFontManager</string>
</object>
<object class="NSCustomObject" id="584004514">
<string key="NSClassName">MainApp</string>
</object>
<object class="NSMenu" id="1071488544">
<string key="NSTitle"/>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMenuItem" id="913321237">
<reference key="NSMenu" ref="1071488544"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Starting</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<object class="NSCustomResource" key="NSOnImage" id="723905397">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuCheckmark</string>
</object>
<object class="NSCustomResource" key="NSMixedImage" id="488763290">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSMenuMixedState</string>
</object>
</object>
<object class="NSMenuItem" id="687450617">
<reference key="NSMenu" ref="1071488544"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsHidden">YES</bool>
<string key="NSTitle">Left</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="893657456">
<reference key="NSMenu" ref="1071488544"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="332065098">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Show Web-Interface</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="984846223">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Show in Finder</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="309075451">
<string key="NSTitle">Show in Finder</string>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMenuItem" id="19318460">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Default Destination</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="945655763">
<reference key="NSMenu" ref="309075451"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="833448292">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Intermediate Files</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="374625387">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Incoming NZBs</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="942754376">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Post-Processing Scripts</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="831099071">
<reference key="NSMenu" ref="309075451"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="163691210">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Config-File</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="297452147">
<reference key="NSMenu" ref="309075451"/>
<string key="NSTitle">Log-File</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
</object>
</object>
</object>
<object class="NSMenuItem" id="750438089">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Info-Links</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="322188981">
<string key="NSTitle">Info-Links</string>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMenuItem" id="812981948">
<reference key="NSMenu" ref="322188981"/>
<string key="NSTitle">Home Page</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="179994147">
<reference key="NSMenu" ref="322188981"/>
<string key="NSTitle">Downloads</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="100406801">
<reference key="NSMenu" ref="322188981"/>
<string key="NSTitle">Forum</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
</object>
</object>
</object>
<object class="NSMenuItem" id="22060917">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Troubleshooting</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
<string key="NSAction">submenuAction:</string>
<object class="NSMenu" key="NSSubmenu" id="987038733">
<string key="NSTitle">Troubleshooting</string>
<object class="NSMutableArray" key="NSMenuItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSMenuItem" id="305652885">
<reference key="NSMenu" ref="987038733"/>
<string key="NSTitle">Restart</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="298404849">
<reference key="NSMenu" ref="987038733"/>
<string key="NSTitle">Restart in Recovery Mode</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="846476020">
<reference key="NSMenu" ref="987038733"/>
<string key="NSTitle">Open Config in TextEdit</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="631055199">
<reference key="NSMenu" ref="987038733"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="408663019">
<reference key="NSMenu" ref="987038733"/>
<string key="NSTitle">Reset to Factory Defaults</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
</object>
</object>
</object>
<object class="NSMenuItem" id="239974611">
<reference key="NSMenu" ref="1071488544"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="7270580">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">About NZBGet</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="845819171">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Preferences...</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="4615620">
<reference key="NSMenu" ref="1071488544"/>
<bool key="NSIsDisabled">YES</bool>
<bool key="NSIsSeparator">YES</bool>
<string key="NSTitle"/>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
<object class="NSMenuItem" id="667042401">
<reference key="NSMenu" ref="1071488544"/>
<string key="NSTitle">Quit NZBGet</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="723905397"/>
<reference key="NSMixedImage" ref="488763290"/>
</object>
</object>
</object>
<object class="NSUserDefaultsController" id="821481866">
<bool key="NSSharedInstance">YES</bool>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="1021"/>
<reference key="destination" ref="584004514"/>
</object>
<int key="connectionID">824</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">statusMenu</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="1071488544"/>
</object>
<int key="connectionID">831</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">quitClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="667042401"/>
</object>
<int key="connectionID">832</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">preferencesClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="845819171"/>
</object>
<int key="connectionID">850</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">aboutClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="7270580"/>
</object>
<int key="connectionID">856</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">webuiClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="332065098"/>
</object>
<int key="connectionID">865</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">webuiItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="332065098"/>
</object>
<int key="connectionID">866</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">homePageItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="812981948"/>
</object>
<int key="connectionID">874</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">downloadsItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="179994147"/>
</object>
<int key="connectionID">875</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">forumItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="100406801"/>
</object>
<int key="connectionID">876</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">infoLinkClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="812981948"/>
</object>
<int key="connectionID">877</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">infoLinkClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="179994147"/>
</object>
<int key="connectionID">878</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">infoLinkClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="100406801"/>
</object>
<int key="connectionID">879</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">infoItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="913321237"/>
</object>
<int key="connectionID">893</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">restartRecoveryItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="298404849"/>
</object>
<int key="connectionID">896</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">destDirItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="19318460"/>
</object>
<int key="connectionID">911</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">interDirItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="833448292"/>
</object>
<int key="connectionID">912</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">nzbDirItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="374625387"/>
</object>
<int key="connectionID">913</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">scriptDirItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="942754376"/>
</object>
<int key="connectionID">915</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="19318460"/>
</object>
<int key="connectionID">916</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="833448292"/>
</object>
<int key="connectionID">917</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="374625387"/>
</object>
<int key="connectionID">918</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="942754376"/>
</object>
<int key="connectionID">919</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="163691210"/>
</object>
<int key="connectionID">922</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">info2Item</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="687450617"/>
</object>
<int key="connectionID">925</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">info1Item</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="913321237"/>
</object>
<int key="connectionID">926</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">destDirSeparator</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="945655763"/>
</object>
<int key="connectionID">927</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">showInFinderClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="297452147"/>
</object>
<int key="connectionID">929</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">configFileItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="163691210"/>
</object>
<int key="connectionID">930</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">logFileItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="297452147"/>
</object>
<int key="connectionID">931</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">restartClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="298404849"/>
</object>
<int key="connectionID">940</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">openConfigInTextEditClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="846476020"/>
</object>
<int key="connectionID">941</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">restartClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="305652885"/>
</object>
<int key="connectionID">943</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">restartClicked:</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="408663019"/>
</object>
<int key="connectionID">944</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">factoryResetItem</string>
<reference key="source" ref="584004514"/>
<reference key="destination" ref="408663019"/>
</object>
<int key="connectionID">945</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<object class="NSArray" key="object" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="children" ref="1048"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="1021"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="1014"/>
<reference key="parent" ref="0"/>
<string key="objectName">First Responder</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-3</int>
<reference key="object" ref="1050"/>
<reference key="parent" ref="0"/>
<string key="objectName">Application</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">373</int>
<reference key="object" ref="163992474"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">823</int>
<reference key="object" ref="584004514"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">827</int>
<reference key="object" ref="1071488544"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="667042401"/>
<reference ref="332065098"/>
<reference ref="893657456"/>
<reference ref="22060917"/>
<reference ref="239974611"/>
<reference ref="913321237"/>
<reference ref="7270580"/>
<reference ref="845819171"/>
<reference ref="4615620"/>
<reference ref="750438089"/>
<reference ref="984846223"/>
<reference ref="687450617"/>
</object>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">828</int>
<reference key="object" ref="667042401"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">840</int>
<reference key="object" ref="821481866"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">849</int>
<reference key="object" ref="845819171"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">851</int>
<reference key="object" ref="7270580"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">860</int>
<reference key="object" ref="893657456"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">861</int>
<reference key="object" ref="332065098"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">867</int>
<reference key="object" ref="750438089"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="322188981"/>
</object>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">868</int>
<reference key="object" ref="322188981"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="812981948"/>
<reference ref="100406801"/>
<reference ref="179994147"/>
</object>
<reference key="parent" ref="750438089"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">869</int>
<reference key="object" ref="812981948"/>
<reference key="parent" ref="322188981"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">870</int>
<reference key="object" ref="100406801"/>
<reference key="parent" ref="322188981"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">872</int>
<reference key="object" ref="179994147"/>
<reference key="parent" ref="322188981"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">880</int>
<reference key="object" ref="22060917"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="987038733"/>
</object>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">881</int>
<reference key="object" ref="987038733"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="305652885"/>
<reference ref="846476020"/>
<reference ref="298404849"/>
<reference ref="631055199"/>
<reference ref="408663019"/>
</object>
<reference key="parent" ref="22060917"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">882</int>
<reference key="object" ref="305652885"/>
<reference key="parent" ref="987038733"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">885</int>
<reference key="object" ref="846476020"/>
<reference key="parent" ref="987038733"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">887</int>
<reference key="object" ref="298404849"/>
<reference key="parent" ref="987038733"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">889</int>
<reference key="object" ref="239974611"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">890</int>
<reference key="object" ref="913321237"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">897</int>
<reference key="object" ref="4615620"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">898</int>
<reference key="object" ref="984846223"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="309075451"/>
</object>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">899</int>
<reference key="object" ref="309075451"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="19318460"/>
<reference ref="942754376"/>
<reference ref="831099071"/>
<reference ref="945655763"/>
<reference ref="374625387"/>
<reference ref="833448292"/>
<reference ref="163691210"/>
<reference ref="297452147"/>
</object>
<reference key="parent" ref="984846223"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">900</int>
<reference key="object" ref="19318460"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">902</int>
<reference key="object" ref="833448292"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">906</int>
<reference key="object" ref="374625387"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">908</int>
<reference key="object" ref="942754376"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">910</int>
<reference key="object" ref="831099071"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">920</int>
<reference key="object" ref="945655763"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">921</int>
<reference key="object" ref="163691210"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">924</int>
<reference key="object" ref="687450617"/>
<reference key="parent" ref="1071488544"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">928</int>
<reference key="object" ref="297452147"/>
<reference key="parent" ref="309075451"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">934</int>
<reference key="object" ref="631055199"/>
<reference key="parent" ref="987038733"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">935</int>
<reference key="object" ref="408663019"/>
<reference key="parent" ref="987038733"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.IBPluginDependency</string>
<string>-2.IBPluginDependency</string>
<string>-3.IBPluginDependency</string>
<string>373.IBPluginDependency</string>
<string>823.IBPluginDependency</string>
<string>827.IBPluginDependency</string>
<string>828.IBPluginDependency</string>
<string>840.IBPluginDependency</string>
<string>849.IBPluginDependency</string>
<string>851.IBPluginDependency</string>
<string>860.IBPluginDependency</string>
<string>861.IBPluginDependency</string>
<string>867.IBPluginDependency</string>
<string>868.IBPluginDependency</string>
<string>869.IBPluginDependency</string>
<string>870.IBPluginDependency</string>
<string>872.IBPluginDependency</string>
<string>880.IBPluginDependency</string>
<string>881.IBPluginDependency</string>
<string>882.IBPluginDependency</string>
<string>885.IBPluginDependency</string>
<string>887.IBPluginDependency</string>
<string>889.IBPluginDependency</string>
<string>890.IBPluginDependency</string>
<string>897.IBPluginDependency</string>
<string>898.IBPluginDependency</string>
<string>899.IBPluginDependency</string>
<string>900.IBPluginDependency</string>
<string>902.IBPluginDependency</string>
<string>906.IBPluginDependency</string>
<string>908.IBPluginDependency</string>
<string>910.IBPluginDependency</string>
<string>920.IBPluginDependency</string>
<string>921.IBPluginDependency</string>
<string>924.IBPluginDependency</string>
<string>928.IBPluginDependency</string>
<string>934.IBPluginDependency</string>
<string>935.IBPluginDependency</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<reference key="dict.values" ref="0"/>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<reference key="dict.values" ref="0"/>
</object>
<nil key="sourceID"/>
<int key="maxID">945</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<real value="1070" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSMenuCheckmark</string>
<string>NSMenuMixedState</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{11, 11}</string>
<string>{10, 3}</string>
</object>
</object>
</data>
</archive>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>mainicon.icns</string>
<key>CFBundleIdentifier</key>
<string>net.sourceforge.nzbget</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>12.0-testing-r831M</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>10.7</string>
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2007-2013 Andrey Prygunkov</string>
<key>NSMainNibFile</key>
<string>MainApp</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

View File

@@ -1,409 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
F20FC6E217C6BAAE00C392AC /* PFMoveApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = F20FC6DF17C6B9FC00C392AC /* PFMoveApplication.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
F20FC6E417C6BC8D00C392AC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F20FC6E317C6BC8D00C392AC /* Security.framework */; };
F21D369B13BF387F00E6D821 /* PreferencesDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = F21D369A13BF387F00E6D821 /* PreferencesDialog.m */; };
F26CBA8B136DE86A00DCB596 /* MainApp.m in Sources */ = {isa = PBXBuildFile; fileRef = F26CBA8A136DE86A00DCB596 /* MainApp.m */; };
F26D959317C0E81800E58E5D /* Welcome.rtf in Resources */ = {isa = PBXBuildFile; fileRef = F26D959217C0E81800E58E5D /* Welcome.rtf */; };
F26D959517C0E86300E58E5D /* MainApp.xib in Resources */ = {isa = PBXBuildFile; fileRef = F26D959417C0E86300E58E5D /* MainApp.xib */; };
F26D959717C0E87E00E58E5D /* PreferencesDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = F26D959617C0E87E00E58E5D /* PreferencesDialog.xib */; };
F26D959917C0E88700E58E5D /* WelcomeDialog.xib in Resources */ = {isa = PBXBuildFile; fileRef = F26D959817C0E88700E58E5D /* WelcomeDialog.xib */; };
F26D959B17C0E89D00E58E5D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F26D959A17C0E89D00E58E5D /* Localizable.strings */; };
F29ABB2617BFC61B0023A423 /* DaemonController.m in Sources */ = {isa = PBXBuildFile; fileRef = F29ABB2417BFC03D0023A423 /* DaemonController.m */; };
F29ABB2B17C00E150023A423 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97324FDCFA39411CA2CEA /* AppKit.framework */; };
F29ABB2C17C00E190023A423 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29B97325FDCFA39411CA2CEA /* Foundation.framework */; };
F2A55D3717C4CA9000D6FFE1 /* daemon in Resources */ = {isa = PBXBuildFile; fileRef = F2A55D3617C4CA9000D6FFE1 /* daemon */; };
F2A55D3B17C4CAF800D6FFE1 /* tools in Resources */ = {isa = PBXBuildFile; fileRef = F2A55D3A17C4CAF800D6FFE1 /* tools */; };
F2A6E11017C8E42300D910CB /* statusicon-inv@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F2A6E10E17C8E42300D910CB /* statusicon-inv@2x.png */; };
F2A6E11117C8E42300D910CB /* statusicon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F2A6E10F17C8E42300D910CB /* statusicon@2x.png */; };
F2BBD9C613E083160037473A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = F2FC0F8B13BF595700D834E3 /* Credits.rtf */; };
F2CD856817C282080019D2CA /* RPC.m in Sources */ = {isa = PBXBuildFile; fileRef = F2CD856517C254A90019D2CA /* RPC.m */; };
F2CD856B17C282820019D2CA /* WebClient.m in Sources */ = {isa = PBXBuildFile; fileRef = F2CD856A17C282800019D2CA /* WebClient.m */; };
F2D2A2F113CBA680000824B4 /* WelcomeDialog.m in Sources */ = {isa = PBXBuildFile; fileRef = F2D2A2EF13CBA680000824B4 /* WelcomeDialog.m */; };
F2F9804A17C9081D004623D6 /* licenses in Resources */ = {isa = PBXBuildFile; fileRef = F2F9804917C9081D004623D6 /* licenses */; };
F2FCD73B13BB9CE900FC81F5 /* mainicon.icns in Resources */ = {isa = PBXBuildFile; fileRef = F2FCD73A13BB9CE900FC81F5 /* mainicon.icns */; };
F2FCD7D913BBDAED00FC81F5 /* statusicon.png in Resources */ = {isa = PBXBuildFile; fileRef = F2FCD7D813BBDAED00FC81F5 /* statusicon.png */; };
F2FCD84D13BCFD0900FC81F5 /* statusicon-inv.png in Resources */ = {isa = PBXBuildFile; fileRef = F2FCD84C13BCFD0900FC81F5 /* statusicon-inv.png */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
256AC3F00F4B6AF500CF3369 /* App_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = App_Prefix.pch; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
8D1107310486CEB800E47090 /* NZBGet-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "NZBGet-Info.plist"; sourceTree = "<group>"; };
8D1107320486CEB800E47090 /* NZBGet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NZBGet.app; sourceTree = BUILT_PRODUCTS_DIR; };
F20FC6DE17C6B9FC00C392AC /* PFMoveApplication.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PFMoveApplication.h; sourceTree = "<group>"; };
F20FC6DF17C6B9FC00C392AC /* PFMoveApplication.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PFMoveApplication.m; sourceTree = "<group>"; };
F20FC6E317C6BC8D00C392AC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
F21D369913BF387F00E6D821 /* PreferencesDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PreferencesDialog.h; sourceTree = "<group>"; };
F21D369A13BF387F00E6D821 /* PreferencesDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PreferencesDialog.m; sourceTree = "<group>"; };
F26CBA89136DE86A00DCB596 /* MainApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MainApp.h; sourceTree = "<group>"; };
F26CBA8A136DE86A00DCB596 /* MainApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = MainApp.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
F26D959217C0E81800E58E5D /* Welcome.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; name = Welcome.rtf; path = Resources/Welcome.rtf; sourceTree = "<group>"; };
F26D959417C0E86300E58E5D /* MainApp.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MainApp.xib; sourceTree = "<group>"; };
F26D959617C0E87E00E58E5D /* PreferencesDialog.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PreferencesDialog.xib; sourceTree = "<group>"; };
F26D959817C0E88700E58E5D /* WelcomeDialog.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = WelcomeDialog.xib; sourceTree = "<group>"; };
F26D959A17C0E89D00E58E5D /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = Localizable.strings; path = Resources/Localizable.strings; sourceTree = "<group>"; };
F29ABB2317BFC03D0023A423 /* DaemonController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DaemonController.h; sourceTree = "<group>"; };
F29ABB2417BFC03D0023A423 /* DaemonController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DaemonController.m; sourceTree = "<group>"; };
F2A55D3617C4CA9000D6FFE1 /* daemon */ = {isa = PBXFileReference; lastKnownFileType = folder; name = daemon; path = Resources/daemon; sourceTree = "<group>"; };
F2A55D3A17C4CAF800D6FFE1 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = Resources/tools; sourceTree = "<group>"; };
F2A6E10E17C8E42300D910CB /* statusicon-inv@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "statusicon-inv@2x.png"; path = "Images/statusicon-inv@2x.png"; sourceTree = "<group>"; };
F2A6E10F17C8E42300D910CB /* statusicon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "statusicon@2x.png"; path = "Images/statusicon@2x.png"; sourceTree = "<group>"; };
F2CD856417C254A90019D2CA /* RPC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RPC.h; sourceTree = "<group>"; };
F2CD856517C254A90019D2CA /* RPC.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RPC.m; sourceTree = "<group>"; };
F2CD856917C282800019D2CA /* WebClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebClient.h; sourceTree = "<group>"; };
F2CD856A17C282800019D2CA /* WebClient.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WebClient.m; sourceTree = "<group>"; };
F2D2A2EE13CBA680000824B4 /* WelcomeDialog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WelcomeDialog.h; sourceTree = "<group>"; };
F2D2A2EF13CBA680000824B4 /* WelcomeDialog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WelcomeDialog.m; sourceTree = "<group>"; };
F2F9804917C9081D004623D6 /* licenses */ = {isa = PBXFileReference; lastKnownFileType = folder; name = licenses; path = Resources/licenses; sourceTree = "<group>"; };
F2FC0F8B13BF595700D834E3 /* Credits.rtf */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = Credits.rtf; path = Resources/Credits.rtf; sourceTree = "<group>"; };
F2FCD73A13BB9CE900FC81F5 /* mainicon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = mainicon.icns; path = Images/mainicon.icns; sourceTree = "<group>"; };
F2FCD7D813BBDAED00FC81F5 /* statusicon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = statusicon.png; path = Images/statusicon.png; sourceTree = "<group>"; };
F2FCD84C13BCFD0900FC81F5 /* statusicon-inv.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "statusicon-inv.png"; path = "Images/statusicon-inv.png"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
8D11072E0486CEB800E47090 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F20FC6E417C6BC8D00C392AC /* Security.framework in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
F29ABB2B17C00E150023A423 /* AppKit.framework in Frameworks */,
F29ABB2C17C00E190023A423 /* Foundation.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
19C28FACFE9D520D11CA2CBB /* Products */ = {
isa = PBXGroup;
children = (
8D1107320486CEB800E47090 /* NZBGet.app */,
);
name = Products;
sourceTree = "<group>";
};
29B97314FDCFA39411CA2CEA /* NZBGet */ = {
isa = PBXGroup;
children = (
F26CBA8C136DE87500DCB596 /* Main */,
F29ABB2517BFC0450023A423 /* DaemonController */,
F2D2A2F213CBA7C8000824B4 /* WelcomeDialog */,
F21D343013BE339300E6D821 /* Preferences */,
F20FC6E017C6BA0100C392AC /* LetsMove */,
29B97315FDCFA39411CA2CEA /* Other Sources */,
29B97317FDCFA39411CA2CEA /* Resources */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
);
name = "NZBGet";
sourceTree = "<group>";
};
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
isa = PBXGroup;
children = (
256AC3F00F4B6AF500CF3369 /* App_Prefix.pch */,
);
name = "Other Sources";
sourceTree = "<group>";
};
29B97317FDCFA39411CA2CEA /* Resources */ = {
isa = PBXGroup;
children = (
F2F9804917C9081D004623D6 /* licenses */,
F2A55D3617C4CA9000D6FFE1 /* daemon */,
F2A55D3A17C4CAF800D6FFE1 /* tools */,
F2D370AE13C0859A002C0573 /* Images */,
F26D959A17C0E89D00E58E5D /* Localizable.strings */,
F26D959217C0E81800E58E5D /* Welcome.rtf */,
F2FC0F8B13BF595700D834E3 /* Credits.rtf */,
8D1107310486CEB800E47090 /* NZBGet-Info.plist */,
);
name = Resources;
sourceTree = "<group>";
};
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
29B97324FDCFA39411CA2CEA /* AppKit.framework */,
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */,
29B97325FDCFA39411CA2CEA /* Foundation.framework */,
F20FC6E317C6BC8D00C392AC /* Security.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
F20FC6E017C6BA0100C392AC /* LetsMove */ = {
isa = PBXGroup;
children = (
F20FC6DE17C6B9FC00C392AC /* PFMoveApplication.h */,
F20FC6DF17C6B9FC00C392AC /* PFMoveApplication.m */,
);
name = LetsMove;
sourceTree = "<group>";
};
F21D343013BE339300E6D821 /* Preferences */ = {
isa = PBXGroup;
children = (
F26D959617C0E87E00E58E5D /* PreferencesDialog.xib */,
F21D369913BF387F00E6D821 /* PreferencesDialog.h */,
F21D369A13BF387F00E6D821 /* PreferencesDialog.m */,
);
name = Preferences;
sourceTree = "<group>";
};
F26CBA8C136DE87500DCB596 /* Main */ = {
isa = PBXGroup;
children = (
F26D959417C0E86300E58E5D /* MainApp.xib */,
F26CBA89136DE86A00DCB596 /* MainApp.h */,
F26CBA8A136DE86A00DCB596 /* MainApp.m */,
);
name = Main;
sourceTree = "<group>";
};
F29ABB2517BFC0450023A423 /* DaemonController */ = {
isa = PBXGroup;
children = (
F2CD856917C282800019D2CA /* WebClient.h */,
F2CD856A17C282800019D2CA /* WebClient.m */,
F2CD856417C254A90019D2CA /* RPC.h */,
F2CD856517C254A90019D2CA /* RPC.m */,
F29ABB2317BFC03D0023A423 /* DaemonController.h */,
F29ABB2417BFC03D0023A423 /* DaemonController.m */,
);
name = DaemonController;
sourceTree = "<group>";
};
F2D2A2F213CBA7C8000824B4 /* WelcomeDialog */ = {
isa = PBXGroup;
children = (
F26D959817C0E88700E58E5D /* WelcomeDialog.xib */,
F2D2A2EE13CBA680000824B4 /* WelcomeDialog.h */,
F2D2A2EF13CBA680000824B4 /* WelcomeDialog.m */,
);
name = WelcomeDialog;
sourceTree = "<group>";
};
F2D370AE13C0859A002C0573 /* Images */ = {
isa = PBXGroup;
children = (
F2FCD73A13BB9CE900FC81F5 /* mainicon.icns */,
F2A6E10E17C8E42300D910CB /* statusicon-inv@2x.png */,
F2A6E10F17C8E42300D910CB /* statusicon@2x.png */,
F2FCD84C13BCFD0900FC81F5 /* statusicon-inv.png */,
F2FCD7D813BBDAED00FC81F5 /* statusicon.png */,
);
name = Images;
path = Resources;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
8D1107260486CEB800E47090 /* NZBGet */ = {
isa = PBXNativeTarget;
buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "NZBGet" */;
buildPhases = (
8D1107290486CEB800E47090 /* Resources */,
8D11072C0486CEB800E47090 /* Sources */,
8D11072E0486CEB800E47090 /* Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = NZBGet;
productInstallPath = "$(HOME)/Applications";
productName = "NZBGet";
productReference = 8D1107320486CEB800E47090 /* NZBGet.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0460;
ORGANIZATIONNAME = "Andrey Prygunkov";
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NZBGet" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 1;
knownRegions = (
English,
Japanese,
French,
German,
Russian,
de,
ru,
);
mainGroup = 29B97314FDCFA39411CA2CEA /* NZBGet */;
projectDirPath = "";
projectRoot = "";
targets = (
8D1107260486CEB800E47090 /* NZBGet */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
8D1107290486CEB800E47090 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F2FCD73B13BB9CE900FC81F5 /* mainicon.icns in Resources */,
F2FCD7D913BBDAED00FC81F5 /* statusicon.png in Resources */,
F2FCD84D13BCFD0900FC81F5 /* statusicon-inv.png in Resources */,
F2BBD9C613E083160037473A /* Credits.rtf in Resources */,
F26D959317C0E81800E58E5D /* Welcome.rtf in Resources */,
F26D959517C0E86300E58E5D /* MainApp.xib in Resources */,
F26D959717C0E87E00E58E5D /* PreferencesDialog.xib in Resources */,
F26D959917C0E88700E58E5D /* WelcomeDialog.xib in Resources */,
F26D959B17C0E89D00E58E5D /* Localizable.strings in Resources */,
F2A55D3717C4CA9000D6FFE1 /* daemon in Resources */,
F2A55D3B17C4CAF800D6FFE1 /* tools in Resources */,
F2A6E11017C8E42300D910CB /* statusicon-inv@2x.png in Resources */,
F2A6E11117C8E42300D910CB /* statusicon@2x.png in Resources */,
F2F9804A17C9081D004623D6 /* licenses in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
8D11072C0486CEB800E47090 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F26CBA8B136DE86A00DCB596 /* MainApp.m in Sources */,
F21D369B13BF387F00E6D821 /* PreferencesDialog.m in Sources */,
F2D2A2F113CBA680000824B4 /* WelcomeDialog.m in Sources */,
F29ABB2617BFC61B0023A423 /* DaemonController.m in Sources */,
F2CD856817C282080019D2CA /* RPC.m in Sources */,
F2CD856B17C282820019D2CA /* WebClient.m in Sources */,
F20FC6E217C6BAAE00C392AC /* PFMoveApplication.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks\"",
);
GCC_DYNAMIC_NO_PIC = NO;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = App_Prefix.pch;
"GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = DEBUG;
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NZBGet-Info.plist";
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.7;
OTHER_LDFLAGS = "";
PRODUCT_NAME = NZBGet;
SDKROOT = macosx10.7;
VALID_ARCHS = x86_64;
};
name = Debug;
};
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_IDENTITY = "";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SDKROOT)$(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks\"",
);
GCC_MODEL_TUNING = G5;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = App_Prefix.pch;
INFOPLIST_FILE = "NZBGet-Info.plist";
INSTALL_PATH = "$(HOME)/Applications";
MACOSX_DEPLOYMENT_TARGET = 10.7;
PRODUCT_NAME = NZBGet;
SDKROOT = macosx10.7;
VALID_ARCHS = x86_64;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_NAME = "";
SDKROOT = macosx10.6;
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
CODE_SIGN_IDENTITY = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
PRODUCT_NAME = "";
SDKROOT = macosx10.6;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "NZBGet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4B08A954540054247B /* Debug */,
C01FCF4C08A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NZBGet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C01FCF4F08A954540054247B /* Debug */,
C01FCF5008A954540054247B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29B97313FDCFA39411CA2CEA /* Project object */;
}

View File

@@ -1,11 +0,0 @@
//
// PFMoveApplication.h, version 1.8
// LetsMove
//
// Created by Andy Kim at Potion Factory LLC on 9/17/09
//
// The contents of this file are dedicated to the public domain.
#import <Foundation/Foundation.h>
void PFMoveToApplicationsFolderIfNecessary(void);

View File

@@ -1,460 +0,0 @@
//
// PFMoveApplication.m, version 1.8
// LetsMove
//
// Created by Andy Kim at Potion Factory LLC on 9/17/09
//
// The contents of this file are dedicated to the public domain.
#import "PFMoveApplication.h"
// Andrey Prygunkov (NZBGet):
// all references to "NSString+SymlinksAndAliases.h" removed because
// it has a BSD like license which might not be compatible with GPLv2.
// This makes the function a little bit less robust not properly handle some rare cases.
//#import "NSString+SymlinksAndAliases.h"
#import <Security/Security.h>
#import <dlfcn.h>
// Strings
// These are macros to be able to use custom i18n tools
#define _I10NS(nsstr) NSLocalizedStringFromTable(nsstr, @"MoveApplication", nil)
#define kStrMoveApplicationCouldNotMove _I10NS(@"Could not move to Applications folder")
#define kStrMoveApplicationQuestionTitle _I10NS(@"Move to Applications folder?")
#define kStrMoveApplicationQuestionTitleHome _I10NS(@"Move to Applications folder in your Home folder?")
#define kStrMoveApplicationQuestionMessage _I10NS(@"NZBGet can move itself to the Applications folder if you'd like.")
#define kStrMoveApplicationButtonMove _I10NS(@"Move to Applications Folder")
#define kStrMoveApplicationButtonDoNotMove _I10NS(@"Do Not Move")
#define kStrMoveApplicationQuestionInfoWillRequirePasswd _I10NS(@"Note that this will require an administrator password.")
#define kStrMoveApplicationQuestionInfoInDownloadsFolder _I10NS(@"This will keep your Downloads folder uncluttered.")
// Needs to be defined for compiling under 10.4 SDK
#ifndef NSAppKitVersionNumber10_4
#define NSAppKitVersionNumber10_4 824
#endif
// Needs to be defined for compiling under 10.5 SDK
#ifndef NSAppKitVersionNumber10_5
#define NSAppKitVersionNumber10_5 949
#endif
// By default, we use a small control/font for the suppression button.
// If you prefer to use the system default (to match your other alerts),
// set this to 0.
#define PFUseSmallAlertSuppressCheckbox 1
static NSString *AlertSuppressKey = @"moveToApplicationsFolderAlertSuppress";
// Helper functions
static NSString *PreferredInstallLocation(BOOL *isUserDirectory);
static BOOL IsInApplicationsFolder(NSString *path);
static BOOL IsInDownloadsFolder(NSString *path);
static BOOL IsLaunchedFromDMG();
static BOOL Trash(NSString *path);
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled);
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath);
static void Relaunch();
// Main worker function
void PFMoveToApplicationsFolderIfNecessary(void) {
// Skip if user suppressed the alert before
if ([[NSUserDefaults standardUserDefaults] boolForKey:AlertSuppressKey]) return;
// Path of the bundle
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
// Skip if the application is already in some Applications folder
if (IsInApplicationsFolder(bundlePath)) return;
// File Manager
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isLaunchedFromDMG = IsLaunchedFromDMG();
// Since we are good to go, get the preferred installation directory.
BOOL installToUserApplications = NO;
NSString *applicationsDirectory = PreferredInstallLocation(&installToUserApplications);
NSString *bundleName = [bundlePath lastPathComponent];
NSString *destinationPath = [applicationsDirectory stringByAppendingPathComponent:bundleName];
// Check if we need admin password to write to the Applications directory
BOOL needAuthorization = ([fm isWritableFileAtPath:applicationsDirectory] == NO);
// Check if the destination bundle is already there but not writable
needAuthorization |= ([fm fileExistsAtPath:destinationPath] && ![fm isWritableFileAtPath:destinationPath]);
// Setup the alert
NSAlert *alert = [[[NSAlert alloc] init] autorelease];
{
NSString *informativeText = nil;
[alert setMessageText:(installToUserApplications ? kStrMoveApplicationQuestionTitleHome : kStrMoveApplicationQuestionTitle)];
informativeText = kStrMoveApplicationQuestionMessage;
if (needAuthorization) {
informativeText = [informativeText stringByAppendingString:@" "];
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoWillRequirePasswd];
}
else if (IsInDownloadsFolder(bundlePath)) {
// Don't mention this stuff if we need authentication. The informative text is long enough as it is in that case.
informativeText = [informativeText stringByAppendingString:@" "];
informativeText = [informativeText stringByAppendingString:kStrMoveApplicationQuestionInfoInDownloadsFolder];
}
[alert setInformativeText:informativeText];
// Add accept button
[alert addButtonWithTitle:kStrMoveApplicationButtonMove];
// Add deny button
NSButton *cancelButton = [alert addButtonWithTitle:kStrMoveApplicationButtonDoNotMove];
[cancelButton setKeyEquivalent:@"\e"];
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
// Setup suppression button
[alert setShowsSuppressionButton:YES];
if (PFUseSmallAlertSuppressCheckbox) {
[[[alert suppressionButton] cell] setControlSize:NSSmallControlSize];
[[[alert suppressionButton] cell] setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
}
}
#endif
}
// Activate app -- work-around for focus issues related to "scary file from internet" OS dialog.
if (![NSApp isActive]) {
[NSApp activateIgnoringOtherApps:YES];
}
if ([alert runModal] == NSAlertFirstButtonReturn) {
DLog(@"INFO -- Moving myself to the Applications folder");
// Move
if (needAuthorization) {
BOOL authorizationCanceled;
if (!AuthorizedInstall(bundlePath, destinationPath, &authorizationCanceled)) {
if (authorizationCanceled) {
DLog(@"INFO -- Not moving because user canceled authorization");
return;
}
else {
DLog(@"ERROR -- Could not copy myself to /Applications with authorization");
goto fail;
}
}
}
else {
// If a copy already exists in the Applications folder, put it in the Trash
if ([fm fileExistsAtPath:destinationPath]) {
// But first, make sure that it's not running
BOOL destinationIsRunning = NO;
// Use the shell to determine if the app is already running on systems 10.5 or lower
if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_5) {
NSString *script = [NSString stringWithFormat:@"ps ax -o comm | grep '%@/' | grep -v grep >/dev/null", destinationPath];
NSTask *task = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
[task waitUntilExit];
// If the task terminated with status 0, it means that the final grep produced 1 or more lines of output.
// Which means that the app is already running
destinationIsRunning = ([task terminationStatus] == 0);
}
// Use the new API on 10.6 or higher
else {
for (NSRunningApplication *runningApplication in [[NSWorkspace sharedWorkspace] runningApplications]) {
NSString *executablePath = [[runningApplication executableURL] path];
if ([executablePath hasPrefix:destinationPath]) {
destinationIsRunning = YES;
break;
}
}
}
if (destinationIsRunning) {
// Give the running app focus and terminate myself
DLog(@"INFO -- Switching to an already running version");
[[NSTask launchedTaskWithLaunchPath:@"/usr/bin/open" arguments:[NSArray arrayWithObject:destinationPath]] waitUntilExit];
exit(0);
}
else {
if (!Trash([applicationsDirectory stringByAppendingPathComponent:bundleName]))
goto fail;
}
}
if (!CopyBundle(bundlePath, destinationPath)) {
DLog(@"ERROR -- Could not copy myself to %@", destinationPath);
goto fail;
}
}
// Trash the original app. It's okay if this fails.
// NOTE: This final delete does not work if the source bundle is in a network mounted volume.
// Calling rm or file manager's delete method doesn't work either. It's unlikely to happen
// but it'd be great if someone could fix this.
if (!isLaunchedFromDMG && !Trash(bundlePath)) {
DLog(@"WARNING -- Could not delete application after moving it to Applications folder");
}
// Relaunch.
Relaunch(destinationPath);
}
else {
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
// Save the alert suppress preference if checked
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
if ([[alert suppressionButton] state] == NSOnState) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:AlertSuppressKey];
}
#endif
}
else {
// Always suppress after the first decline on 10.4 since there is no suppression checkbox
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:AlertSuppressKey];
}
}
return;
fail:
{
// Show failure message
alert = [[[NSAlert alloc] init] autorelease];
[alert setMessageText:kStrMoveApplicationCouldNotMove];
[alert runModal];
}
}
#pragma mark -
#pragma mark Helper Functions
static NSString *PreferredInstallLocation(BOOL *isUserDirectory) {
// Return the preferred install location.
// Assume that if the user has a ~/Applications folder, they'd prefer their
// applications to go there.
NSFileManager *fm = [NSFileManager defaultManager];
NSArray *userApplicationsDirs = NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSUserDomainMask, YES);
if ([userApplicationsDirs count] > 0) {
NSString *userApplicationsDir = [userApplicationsDirs objectAtIndex:0];
BOOL isDirectory;
if ([fm fileExistsAtPath:userApplicationsDir isDirectory:&isDirectory] && isDirectory) {
// User Applications directory exists. Get the directory contents.
NSArray *contents = [fm contentsOfDirectoryAtPath:userApplicationsDir error:NULL];
// Check if there is at least one ".app" inside the directory.
for (NSString *contentsPath in contents) {
if ([[contentsPath pathExtension] isEqualToString:@"app"]) {
if (isUserDirectory) *isUserDirectory = YES;
//return [userApplicationsDir stringByResolvingSymlinksAndAliases];
return userApplicationsDir;
}
}
}
}
// No user Applications directory in use. Return the machine local Applications directory
if (isUserDirectory) *isUserDirectory = NO;
//return [[NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSLocalDomainMask, YES) lastObject] stringByResolvingSymlinksAndAliases];
return [NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSLocalDomainMask, YES) lastObject];
}
static BOOL IsInApplicationsFolder(NSString *path) {
// Check all the normal Application directories
NSEnumerator *e = [NSSearchPathForDirectoriesInDomains(NSApplicationDirectory, NSAllDomainsMask, YES) objectEnumerator];
NSString *appDirPath = nil;
while ((appDirPath = [e nextObject])) {
if ([path hasPrefix:appDirPath]) return YES;
}
// Also, handle the case that the user has some other Application directory (perhaps on a separate data partition).
if ([[path pathComponents] containsObject:@"Applications"]) {
return YES;
}
return NO;
}
static BOOL IsInDownloadsFolder(NSString *path) {
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
// 10.5 or higher has NSDownloadsDirectory
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
NSEnumerator *e = [NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, NSAllDomainsMask, YES) objectEnumerator];
NSString *downloadsDirPath = nil;
while ((downloadsDirPath = [e nextObject])) {
if ([path hasPrefix:downloadsDirPath]) return YES;
}
return NO;
}
#endif
// 10.4
return [[[path stringByDeletingLastPathComponent] lastPathComponent] isEqualToString:@"Downloads"];
}
static BOOL IsLaunchedFromDMG() {
// Guess if we have launched from a disk image
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSFileManager *fm = [NSFileManager defaultManager];
BOOL bundlePathIsWritable = [fm isWritableFileAtPath:bundlePath];
return [bundlePath hasPrefix:@"/Volumes/"] && !bundlePathIsWritable;
}
static BOOL Trash(NSString *path) {
if ([[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation
source:[path stringByDeletingLastPathComponent]
destination:@""
files:[NSArray arrayWithObject:[path lastPathComponent]]
tag:NULL]) {
return YES;
}
else {
DLog(@"ERROR -- Could not trash '%@'", path);
return NO;
}
}
static BOOL AuthorizedInstall(NSString *srcPath, NSString *dstPath, BOOL *canceled) {
if (canceled) *canceled = NO;
// Make sure that the destination path is an app bundle. We're essentially running 'sudo rm -rf'
// so we really don't want to fuck this up.
if (![dstPath hasSuffix:@".app"]) return NO;
// Do some more checks
if ([[dstPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
if ([[srcPath stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) return NO;
int pid, status;
AuthorizationRef myAuthorizationRef;
// Get the authorization
OSStatus err = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &myAuthorizationRef);
if (err != errAuthorizationSuccess) return NO;
AuthorizationItem myItems = {kAuthorizationRightExecute, 0, NULL, 0};
AuthorizationRights myRights = {1, &myItems};
AuthorizationFlags myFlags = kAuthorizationFlagInteractionAllowed | kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
err = AuthorizationCopyRights(myAuthorizationRef, &myRights, NULL, myFlags, NULL);
if (err != errAuthorizationSuccess) {
if (err == errAuthorizationCanceled && canceled)
*canceled = YES;
goto fail;
}
static OSStatus (*security_AuthorizationExecuteWithPrivileges)(AuthorizationRef authorization, const char *pathToTool,
AuthorizationFlags options, char * const *arguments,
FILE **communicationsPipe) = NULL;
if (!security_AuthorizationExecuteWithPrivileges) {
// On 10.7, AuthorizationExecuteWithPrivileges is deprecated. We want to still use it since there's no
// good alternative (without requiring code signing). We'll look up the function through dyld and fail
// if it is no longer accessible. If Apple removes the function entirely this will fail gracefully. If
// they keep the function and throw some sort of exception, this won't fail gracefully, but that's a
// risk we'll have to take for now.
security_AuthorizationExecuteWithPrivileges = dlsym(RTLD_DEFAULT, "AuthorizationExecuteWithPrivileges");
}
if (!security_AuthorizationExecuteWithPrivileges) {
goto fail;
}
// Delete the destination
{
char *args[] = {"-rf", (char *)[dstPath fileSystemRepresentation], NULL};
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/rm", kAuthorizationFlagDefaults, args, NULL);
if (err != errAuthorizationSuccess) goto fail;
// Wait until it's done
pid = wait(&status);
if (pid == -1 || !WIFEXITED(status)) goto fail; // We don't care about exit status as the destination most likely does not exist
}
// Copy
{
char *args[] = {"-pR", (char *)[srcPath fileSystemRepresentation], (char *)[dstPath fileSystemRepresentation], NULL};
err = security_AuthorizationExecuteWithPrivileges(myAuthorizationRef, "/bin/cp", kAuthorizationFlagDefaults, args, NULL);
if (err != errAuthorizationSuccess) goto fail;
// Wait until it's done
pid = wait(&status);
if (pid == -1 || !WIFEXITED(status) || WEXITSTATUS(status)) goto fail;
}
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
return YES;
fail:
AuthorizationFree(myAuthorizationRef, kAuthorizationFlagDefaults);
return NO;
}
static BOOL CopyBundle(NSString *srcPath, NSString *dstPath) {
NSFileManager *fm = [NSFileManager defaultManager];
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
// 10.5 or higher
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
NSError *error = nil;
if ([fm copyItemAtPath:srcPath toPath:dstPath error:&error]) {
return YES;
}
else {
DLog(@"ERROR -- Could not copy '%@' to '%@' (%@)", srcPath, dstPath, error);
}
}
#endif
#if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4
if ([fm copyPath:srcPath toPath:dstPath handler:nil]) {
return YES;
}
else {
DLog(@"ERROR -- Could not copy '%@' to '%@'", srcPath, dstPath);
}
#endif
return NO;
}
static void Relaunch(NSString *destinationPath) {
// The shell script waits until the original app process terminates.
// This is done so that the relaunched app opens as the front-most app.
int pid = [[NSProcessInfo processInfo] processIdentifier];
// Command run just before running open /final/path
NSString *preOpenCmd = @"";
// OS X >=10.5:
// Before we launch the new app, clear xattr:com.apple.quarantine to avoid
// duplicate "scary file from the internet" dialog.
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4
if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_5) {
// Add the -r flag on 10.6
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d -r com.apple.quarantine '%@';", destinationPath];
}
else if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_4) {
preOpenCmd = [NSString stringWithFormat:@"/usr/bin/xattr -d com.apple.quarantine '%@';", destinationPath];
}
#endif
NSString *script = [NSString stringWithFormat:@"(while [ `ps -p %d | wc -l` -gt 1 ]; do sleep 0.1; done; %@ open '%@') &", pid, preOpenCmd, destinationPath];
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
// Launched from within a DMG? -- unmount (if no files are open after 5 seconds,
// otherwise leave it mounted).
if (IsLaunchedFromDMG()) {
script = [NSString stringWithFormat:@"(sleep 5 && hdiutil detach '%@') &", [[[NSBundle mainBundle] bundlePath] stringByDeletingLastPathComponent]];
[NSTask launchedTaskWithLaunchPath:@"/bin/sh" arguments:[NSArray arrayWithObjects:@"-c", script, nil]];
}
exit(0);
}

View File

@@ -1,37 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
@interface PreferencesDialog : NSWindowController {
IBOutlet NSButton *autostartButton;
IBOutlet NSButton *showStatusIconButton;
IBOutlet NSTextField *generalText;
IBOutlet NSTextField *appearanceText;
IBOutlet NSButton *autoShowWebUI;
}
- (IBAction)autostartButtonClicked:(id)sender;
@end

View File

@@ -1,116 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import "PreferencesDialog.h"
@implementation PreferencesDialog
- (id)init {
return [super initWithWindowNibName:@"PreferencesDialog"];
}
- (void)windowDidLoad {
}
- (void)enableLoginItemWithLoginItemsReference:(LSSharedFileListRef )theLoginItemsRefs ForPath:(NSString *)appPath {
// We call LSSharedFileListInsertItemURL to insert the item at the bottom of Login Items list.
CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:appPath];
LSSharedFileListItemRef item = LSSharedFileListInsertItemURL(theLoginItemsRefs, kLSSharedFileListItemLast, NULL, NULL, url, NULL, NULL);
if (item)
CFRelease(item);
}
- (void)disableLoginItemWithLoginItemsReference:(LSSharedFileListRef )theLoginItemsRefs ForPath:(NSString *)appPath {
UInt32 seedValue;
CFURLRef thePath;
// We're going to grab the contents of the shared file list (LSSharedFileListItemRef objects)
// and pop it in an array so we can iterate through it to find our item.
CFArrayRef loginItemsArray = LSSharedFileListCopySnapshot(theLoginItemsRefs, &seedValue);
for (id item in (__bridge NSArray *)loginItemsArray) {
LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
if (LSSharedFileListItemResolve(itemRef, 0, (CFURLRef*) &thePath, NULL) == noErr) {
if ([[(__bridge NSURL *)thePath path] hasPrefix:appPath]) {
LSSharedFileListItemRemove(theLoginItemsRefs, itemRef); // Deleting the item
}
// Docs for LSSharedFileListItemResolve say we're responsible
// for releasing the CFURLRef that is returned
CFRelease(thePath);
}
}
CFRelease(loginItemsArray);
}
- (BOOL)loginItemExistsWithLoginItemReference:(LSSharedFileListRef)theLoginItemsRefs ForPath:(NSString *)appPath {
BOOL found = NO;
UInt32 seedValue;
CFURLRef thePath;
// We're going to grab the contents of the shared file list (LSSharedFileListItemRef objects)
// and pop it in an array so we can iterate through it to find our item.
CFArrayRef loginItemsArray = LSSharedFileListCopySnapshot(theLoginItemsRefs, &seedValue);
for (id item in (__bridge NSArray *)loginItemsArray) {
LSSharedFileListItemRef itemRef = (__bridge LSSharedFileListItemRef)item;
if (LSSharedFileListItemResolve(itemRef, 0, (CFURLRef*) &thePath, NULL) == noErr) {
if ([[(__bridge NSURL *)thePath path] hasPrefix:appPath]) {
found = YES;
break;
}
// Docs for LSSharedFileListItemResolve say we're responsible
// for releasing the CFURLRef that is returned
CFRelease(thePath);
}
}
CFRelease(loginItemsArray);
return found;
}
- (void)awakeFromNib {
// This will retrieve the path for the application
// For example, /Applications/test.app
NSString * appPath = [[NSBundle mainBundle] bundlePath];
LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if ([self loginItemExistsWithLoginItemReference:loginItems ForPath:appPath]) {
[autostartButton setState:NSOnState];
}
CFRelease(loginItems);
}
- (IBAction)autostartButtonClicked:(id)sender {
NSString * appPath = [[NSBundle mainBundle] bundlePath];
// Create a reference to the shared file list.
LSSharedFileListRef loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
if (loginItems) {
if ([sender state] == NSOnState)
[self enableLoginItemWithLoginItemsReference:loginItems ForPath:appPath];
else
[self disableLoginItemWithLoginItemsReference:loginItems ForPath:appPath];
}
CFRelease(loginItems);
}
@end

View File

@@ -1,623 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1070</int>
<string key="IBDocument.SystemVersion">11G63</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1138.51</string>
<string key="IBDocument.HIToolboxVersion">569.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">3084</string>
</object>
<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSButton</string>
<string>NSButtonCell</string>
<string>NSCustomObject</string>
<string>NSImageCell</string>
<string>NSImageView</string>
<string>NSTextField</string>
<string>NSTextFieldCell</string>
<string>NSUserDefaultsController</string>
<string>NSView</string>
<string>NSWindowTemplate</string>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="1001">
<string key="NSClassName">PreferencesDialog</string>
</object>
<object class="NSCustomObject" id="1003">
<string key="NSClassName">FirstResponder</string>
</object>
<object class="NSCustomObject" id="1004">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSWindowTemplate" id="1005">
<int key="NSWindowStyleMask">3</int>
<int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{196, 436}, {357, 155}}</string>
<int key="NSWTFlags">544735232</int>
<string key="NSWindowTitle">NZBGet Preferences</string>
<string key="NSWindowClass">NSWindow</string>
<nil key="NSViewClass"/>
<nil key="NSUserInterfaceItemIdentifier"/>
<object class="NSView" key="NSWindowView" id="1006">
<reference key="NSNextResponder"/>
<int key="NSvFlags">256</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSButton" id="668723838">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{67, 119}, {213, 18}}</string>
<reference key="NSSuperview" ref="1006"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="445769"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="474631304">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Start at login</string>
<object class="NSFont" key="NSSupport" id="1028951332">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">13</double>
<int key="NSfFlags">1044</int>
</object>
<reference key="NSControlView" ref="668723838"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<object class="NSCustomResource" key="NSNormalImage" id="167410980">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSSwitch</string>
</object>
<object class="NSButtonImageSource" key="NSAlternateImage" id="341711700">
<string key="NSImageName">NSSwitch</string>
</object>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSButton" id="445769">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{67, 99}, {213, 18}}</string>
<reference key="NSSuperview" ref="1006"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="897618223"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="741315823">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Show in menubar</string>
<reference key="NSSupport" ref="1028951332"/>
<reference key="NSControlView" ref="445769"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="167410980"/>
<reference key="NSAlternateImage" ref="341711700"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSButton" id="897618223">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{67, 79}, {213, 18}}</string>
<reference key="NSSuperview" ref="1006"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="205669972"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="395093705">
<int key="NSCellFlags">67239424</int>
<int key="NSCellFlags2">0</int>
<string key="NSContents">Show Web-Interface on start</string>
<reference key="NSSupport" ref="1028951332"/>
<reference key="NSControlView" ref="897618223"/>
<int key="NSButtonFlags">1211912703</int>
<int key="NSButtonFlags2">2</int>
<reference key="NSNormalImage" ref="167410980"/>
<reference key="NSAlternateImage" ref="341711700"/>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
</object>
<object class="NSImageView" id="205669972">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<object class="NSMutableSet" key="NSDragTypes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="set.sortedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>Apple PDF pasteboard type</string>
<string>Apple PICT pasteboard type</string>
<string>Apple PNG pasteboard type</string>
<string>NSFilenamesPboardType</string>
<string>NeXT Encapsulated PostScript v1.2 pasteboard type</string>
<string>NeXT TIFF v4.0 pasteboard type</string>
</object>
</object>
<string key="NSFrame">{{20, 30}, {32, 32}}</string>
<reference key="NSSuperview" ref="1006"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="371541245"/>
<int key="NSViewLayerContentsRedrawPolicy">2</int>
<bool key="NSEnabled">YES</bool>
<object class="NSImageCell" key="NSCell" id="15570902">
<int key="NSCellFlags">134348288</int>
<int key="NSCellFlags2">33554432</int>
<object class="NSCustomResource" key="NSContents">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSInfo</string>
</object>
<int key="NSAlign">0</int>
<int key="NSScale">3</int>
<int key="NSStyle">0</int>
<bool key="NSAnimates">NO</bool>
</object>
<bool key="NSEditable">YES</bool>
</object>
<object class="NSTextField" id="371541245">
<reference key="NSNextResponder" ref="1006"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{66, 20}, {244, 42}}</string>
<reference key="NSSuperview" ref="1006"/>
<reference key="NSWindow"/>
<string key="NSReuseIdentifierKey">_NS:1535</string>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="833412041">
<int key="NSCellFlags">67108864</int>
<int key="NSCellFlags2">4325376</int>
<string key="NSContents">Only OSX-specific options are located here. For all other options see Settings page in Web-Interface.</string>
<object class="NSFont" key="NSSupport">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">11</double>
<int key="NSfFlags">3100</int>
</object>
<string key="NSCellIdentifier">_NS:1535</string>
<reference key="NSControlView" ref="371541245"/>
<object class="NSColor" key="NSBackgroundColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
</object>
<object class="NSColor" key="NSTextColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlTextColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
</object>
</object>
</object>
</object>
<string key="NSFrameSize">{357, 155}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="668723838"/>
</object>
<string key="NSScreenRect">{{0, 0}, {1440, 878}}</string>
<string key="NSMaxSize">{10000000000000, 10000000000000}</string>
<bool key="NSWindowIsRestorable">YES</bool>
</object>
<object class="NSUserDefaultsController" id="575306214">
<bool key="NSSharedInstance">YES</bool>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">window</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="1005"/>
</object>
<int key="connectionID">11</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">autostartButtonClicked:</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="668723838"/>
</object>
<int key="connectionID">24</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">autostartButton</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="668723838"/>
</object>
<int key="connectionID">25</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">showStatusIconButton</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="445769"/>
</object>
<int key="connectionID">28</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">autoShowWebUI</string>
<reference key="source" ref="1001"/>
<reference key="destination" ref="897618223"/>
</object>
<int key="connectionID">32</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="1005"/>
<reference key="destination" ref="1001"/>
</object>
<int key="connectionID">12</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.ShowInMenubar</string>
<reference key="source" ref="445769"/>
<reference key="destination" ref="575306214"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="445769"/>
<reference key="NSDestination" ref="575306214"/>
<string key="NSLabel">value: values.ShowInMenubar</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.ShowInMenubar</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSValidatesImmediately</string>
<boolean value="YES" key="NS.object.0"/>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">23</int>
</object>
<object class="IBConnectionRecord">
<object class="IBBindingConnection" key="connection">
<string key="label">value: values.AutoStartWebUI</string>
<reference key="source" ref="897618223"/>
<reference key="destination" ref="575306214"/>
<object class="NSNibBindingConnector" key="connector">
<reference key="NSSource" ref="897618223"/>
<reference key="NSDestination" ref="575306214"/>
<string key="NSLabel">value: values.AutoStartWebUI</string>
<string key="NSBinding">value</string>
<string key="NSKeyPath">values.AutoStartWebUI</string>
<object class="NSDictionary" key="NSOptions">
<string key="NS.key.0">NSValidatesImmediately</string>
<boolean value="YES" key="NS.object.0"/>
</object>
<int key="NSNibBindingConnectorVersion">2</int>
</object>
</object>
<int key="connectionID">34</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<object class="NSArray" key="object" id="0">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="1001"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="1003"/>
<reference key="parent" ref="0"/>
<string key="objectName">First Responder</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-3</int>
<reference key="object" ref="1004"/>
<reference key="parent" ref="0"/>
<string key="objectName">Application</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">1</int>
<reference key="object" ref="1005"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="1006"/>
</object>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="1006"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="668723838"/>
<reference ref="445769"/>
<reference ref="897618223"/>
<reference ref="371541245"/>
<reference ref="205669972"/>
</object>
<reference key="parent" ref="1005"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">3</int>
<reference key="object" ref="668723838"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="474631304"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">4</int>
<reference key="object" ref="474631304"/>
<reference key="parent" ref="668723838"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">8</int>
<reference key="object" ref="445769"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="741315823"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">9</int>
<reference key="object" ref="741315823"/>
<reference key="parent" ref="445769"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">14</int>
<reference key="object" ref="575306214"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">29</int>
<reference key="object" ref="897618223"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="395093705"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">30</int>
<reference key="object" ref="395093705"/>
<reference key="parent" ref="897618223"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">35</int>
<reference key="object" ref="205669972"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="15570902"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">36</int>
<reference key="object" ref="15570902"/>
<reference key="parent" ref="205669972"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">37</int>
<reference key="object" ref="371541245"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="833412041"/>
</object>
<reference key="parent" ref="1006"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">38</int>
<reference key="object" ref="833412041"/>
<reference key="parent" ref="371541245"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.IBPluginDependency</string>
<string>-2.IBPluginDependency</string>
<string>-3.IBPluginDependency</string>
<string>1.IBPluginDependency</string>
<string>1.IBWindowTemplateEditedContentRect</string>
<string>1.NSWindowTemplate.visibleAtLaunch</string>
<string>14.IBPluginDependency</string>
<string>2.IBPluginDependency</string>
<string>2.IBUserGuides</string>
<string>29.IBPluginDependency</string>
<string>3.IBPluginDependency</string>
<string>30.IBPluginDependency</string>
<string>35.IBAttributePlaceholdersKey</string>
<string>35.IBPluginDependency</string>
<string>36.IBPluginDependency</string>
<string>37.IBPluginDependency</string>
<string>38.IBPluginDependency</string>
<string>4.IBPluginDependency</string>
<string>8.IBPluginDependency</string>
<string>9.IBPluginDependency</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{322, 782}, {298, 74}}</string>
<boolean value="NO"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSMutableArray">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<object class="NSMutableDictionary">
<string key="NS.key.0">AccessibilityDescription</string>
<object class="IBAccessibilityAttribute" key="NS.object.0">
<string key="name">AccessibilityDescription</string>
<reference key="object" ref="205669972"/>
<string key="accessibilityValue">information</string>
</object>
</object>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<reference key="dict.values" ref="0"/>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="0"/>
<reference key="dict.values" ref="0"/>
</object>
<nil key="sourceID"/>
<int key="maxID">38</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">PreferencesDialog</string>
<string key="superclassName">NSWindowController</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">autostartButtonClicked:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">autostartButtonClicked:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">autostartButtonClicked:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appearanceText</string>
<string>autoShowWebUI</string>
<string>autostartButton</string>
<string>generalText</string>
<string>showStatusIconButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSTextField</string>
<string>NSButton</string>
<string>NSButton</string>
<string>NSTextField</string>
<string>NSButton</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appearanceText</string>
<string>autoShowWebUI</string>
<string>autostartButton</string>
<string>generalText</string>
<string>showStatusIconButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">appearanceText</string>
<string key="candidateClassName">NSTextField</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">autoShowWebUI</string>
<string key="candidateClassName">NSButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">autostartButton</string>
<string key="candidateClassName">NSButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">generalText</string>
<string key="candidateClassName">NSTextField</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">showStatusIconButton</string>
<string key="candidateClassName">NSButton</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/PreferencesDialog.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<real value="1070" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSInfo</string>
<string>NSSwitch</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{32, 32}</string>
<string>{15, 15}</string>
</object>
</object>
</data>
</archive>

View File

@@ -1,38 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
#import "WebClient.h"
@interface RPC : WebClient {
}
- (id)initWithMethod:(NSString*)method
receiver:(id)receiver
success:(SEL)successCallback
failure:(SEL)failureCallback;
+ (void)setRpcUrl:(NSString*)url;
@end

View File

@@ -1,62 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
#import <Cocoa/Cocoa.h>
#import "RPC.h"
@implementation RPC
NSString* rpcUrl;
- (id)initWithMethod:(NSString*)method
receiver:(id)receiver
success:(SEL)successCallback
failure:(SEL)failureCallback {
NSString* urlStr = [rpcUrl stringByAppendingString:method];
self = [super initWithURLString:urlStr receiver:receiver success:successCallback failure:failureCallback];
return self;
}
+ (void)setRpcUrl:(NSString*)url {
rpcUrl = url;
}
- (void)success {
NSError *error = nil;
id dataObj = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&error];
if (error || ![dataObj isKindOfClass:[NSDictionary class]]) {
/* JSON was malformed, act appropriately here */
failureCode = 999;
[self failure];
}
id result = [dataObj valueForKey:@"result"];
SuppressPerformSelectorLeakWarning([_receiver performSelector:_successCallback withObject:result];);
}
@end

View File

@@ -1,17 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue255;}
\vieww10800\viewh8400\viewkind0
\deftab720
\pard\pardeftab720\sa260\qc
\f0\fs22 \cf0 Lightweight binary newsgrabber\
\pard\pardeftab720\qc
\cf0 NZBGet is free software; use it under the\
terms of the {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/gpl-2.0.html"}}{\fldrslt GNU General Public License}}.\
\
The package also includes other software;\
see folder "licenses" inside the package.\
\
\pard\pardeftab720\qc
{\field{\*\fldinst{HYPERLINK "http://nzbget.sourceforge.net"}}{\fldrslt \cf2 \ul \ulc2 nzbget.sourceforge.net}}}

View File

Binary file not shown.

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 480 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 820 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

View File

@@ -1,78 +0,0 @@
/*
* This file is part of nzbget
*
* Copyright (C) 2007-2013 Andrey Prygunkov <hugbug@users.sourceforge.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $Revision$
* $Date$
*
*/
"Status.Downloading"="Downloading at %i KB/s";
"Status.Post-Processing"="Post-Processing";
"Status.Fetching NZBs"="Fetching NZBs";
"Status.Fetching Feeds"="Fetching Feeds";
"Status.Paused"="Paused";
"Status.Idle"="Idle";
"Status.Left"="%@ left";
"Status.NoConnection"="Downloader Not Responding";
"Status.Restarting"="Restarting";
"Left.Days"="%i days";
"Left.Days.Hours"="%id %ih";
"Left.Hours"="%i hours";
"Left.Hours.Minutes"="%ih %02im";
"Left.Minutes"="%im";
"Left.Minutes.Seconds"="%im %02is";
"Left.Seconds"="%is";
"Menu.LinkHomePage"="http://nzbget.sourceforge.net";
"Menu.LinkDownloads"="http://nzbget.sourceforge.net/Download";
"Menu.LinkForum"="http://nzbget.sourceforge.net/forum";
"ShowWebUINoConnection.MessageText"="Could not establish connection to downloader (background process).";
"ShowWebUINoConnection.InformativeText"="Try to restart the program via Troubleshooting-menu.";
"RestartNoConnection.MessageText"="Could not establish connection to downloader (background process).";
"RestartNoConnection.InformativeText"="Try to restart the program in recovery mode via Troubleshooting-menu.";
"Restarted.MessageText"="The program has been successfully restarted.";
"Restarted.InformativeText"="";
"Restarted.WebUI"="Show Web-Interface";
"Restarted.OK"="OK";
"RestartedRecoveryMode.MessageText"="The program has been successfully restarted and is now working on address http://127.0.0.1:6789 without password.";
"RestartedRecoveryMode.InformativeText"="To access the program via address, port and password defined in settings the program must be restarted in normal mode using Troubleshooting-menu. A soft-restart via web-interface is not sufficient!";
"FactoryResetted.MessageText"="The program has been reset to factory defaults.";
"FactoryResetted.InformativeText"="";
"FactoryReset.MessageText"="Reset to factory defaults?";
"FactoryReset.InformativeText"="All settings will be reset to defaults. The download queue, history, statstics, log-file, default incoming nzb-directory and default ppscripts-directory will be erased. ";
"FactoryReset.Cancel"="Cancel";
"FactoryReset.Reset"="Erase and Reset";
"AlreadyRunning.MessageText"="NZBGet is already running.";
"AlreadyRunning.InformativeTextWithIcon" = "You can control NZBGet using web-interface or icon in menubar.";
"AlreadyRunning.InformativeTextWithoutIcon" = "You can control NZBGet using web-interface. The icon in menubar is currently not displayed because the option 'Show in menubar' was unselected in preferences.";
"AlreadyRunning.Quit"="Quit NZBGet";
"AlreadyRunning.Preferences"="Open Preferences";
"AlreadyRunning.WebUI"="Show Web-Interface";
"CantShowInFinder.MessageText"="Cannot open %@.";
"CantShowInFinder.InformativeTextWithOption"="The path doesn't exist or refers to a volume not mounted at the moment. Check option %@.";
"CantShowInFinder.InformativeTextForCategory"="The path doesn't exist or refers to a volume not mounted at the moment. Please also note that destination directories for categories are created upon first use.";

View File

@@ -1,23 +0,0 @@
{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf510
{\fonttbl\f0\fnil\fcharset0 LucidaGrande;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
\margl1440\margr1440\vieww9000\viewh8400\viewkind0
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural
\f0\fs32 \cf0 Welcome!
\f1\fs24 \
\
\pard\pardeftab720\sa260
\f0\fs26 \cf0 NZBGet is a downloader from binary newsgroups. An account on a news server is needed to use this program.
\fs18 \
\pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural
\fs26 \cf0 NZBGet works as a background process and can be controlled from a web-browser. An icon in menubar gives a quick access to few important functions.
\fs18 \
\pard\pardeftab720
\fs26 \cf0 \
This program is free software; you can redistribute it and/or modify it under the terms of the {\field{\*\fldinst{HYPERLINK "http://www.gnu.org/licenses/gpl-2.0.html"}}{\fldrslt GNU General Public License}}.\
\
For more information visit {\field{\*\fldinst{HYPERLINK "http://nzbget.sourceforge.net"}}{\fldrslt NZBGet home page}}.}

View File

@@ -1,176 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@@ -1,340 +0,0 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
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
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
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 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
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
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
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
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
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
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
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
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
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
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
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
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
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
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
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
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 Library General
Public License instead of this License.

View File

@@ -1,21 +0,0 @@
Copyright 2013 jQuery Foundation and other contributors
http://jquery.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More